wasmparser-0.217.0/.cargo_vcs_info.json0000644000000001570000000000100133720ustar { "git": { "sha1": "46cb2e80abe9e57c422a3496032dd4c49bdefcd9" }, "path_in_vcs": "crates/wasmparser" }wasmparser-0.217.0/Cargo.lock0000644000000427740000000000100113600ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ahash" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", "version_check", "zerocopy", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", ] [[package]] name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "env_filter" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" dependencies = [ "log", "regex", ] [[package]] name = "env_logger" version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" dependencies = [ "anstream", "anstyle", "env_filter", "humantime", "log", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "half" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "serde", ] [[package]] name = "hermit-abi" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "indexmap" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown", "serde", ] [[package]] name = "is-terminal" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ "hermit-abi", "libc", "windows-sys 0.52.0", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "regex" version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "syn" version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasmparser" version = "0.217.0" dependencies = [ "ahash", "anyhow", "bitflags", "criterion", "env_logger", "hashbrown", "indexmap", "log", "once_cell", "rayon", "semver", "serde", ] [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[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" [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] wasmparser-0.217.0/Cargo.toml0000644000000052510000000000100113700ustar # 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 = "wasmparser" version = "0.217.0" authors = ["Yury Delendik "] build = false exclude = ["benches/*.wasm"] autobins = false autoexamples = false autotests = false autobenches = false description = """ A simple event-driven library for parsing WebAssembly binary files. """ homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser" readme = "README.md" keywords = [ "parser", "WebAssembly", "wasm", ] license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser" [package.metadata.docs.rs] all-features = true [lib] name = "wasmparser" path = "src/lib.rs" [[example]] name = "simple" path = "examples/simple.rs" [[test]] name = "big-module" path = "tests/big-module.rs" [[bench]] name = "benchmark" path = "benches/benchmark.rs" harness = false [dependencies.ahash] version = "0.8.11" optional = true default-features = false [dependencies.bitflags] version = "2.4.1" [dependencies.hashbrown] version = "0.14.3" features = ["ahash"] optional = true default-features = false [dependencies.indexmap] version = "2.0.0" optional = true default-features = false [dependencies.semver] version = "1.0.0" optional = true default-features = false [dependencies.serde] version = "1.0.166" features = ["alloc"] optional = true default-features = false [dev-dependencies.anyhow] version = "1.0.58" [dev-dependencies.criterion] version = "0.5.1" default-features = false [dev-dependencies.env_logger] version = "0.11" [dev-dependencies.log] version = "0.4.17" [dev-dependencies.once_cell] version = "1.13.0" [dev-dependencies.rayon] version = "1.3" [features] default = [ "std", "validate", "serde", "features", ] features = [] no-hash-maps = [] serde = [ "dep:serde", "indexmap/serde", "hashbrown/serde", ] std = ["indexmap/std"] validate = [ "dep:indexmap", "dep:semver", "dep:hashbrown", "dep:ahash", ] [lints.clippy] clone_on_copy = "warn" manual_strip = "warn" map_clone = "warn" unnecessary_to_owned = "warn" [lints.clippy.all] level = "allow" priority = -1 [lints.rust] unsafe_code = "deny" wasmparser-0.217.0/Cargo.toml.orig000064400000000000000000000050211046102023000150440ustar 00000000000000[package] name = "wasmparser" version.workspace = true authors = ["Yury Delendik "] license.workspace = true repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser" homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser" keywords = ["parser", "WebAssembly", "wasm"] description = """ A simple event-driven library for parsing WebAssembly binary files. """ edition.workspace = true exclude = ["benches/*.wasm"] rust-version.workspace = true [lints] workspace = true [package.metadata.docs.rs] all-features = true [dependencies] bitflags = "2.4.1" indexmap = { workspace = true, optional = true } semver = { workspace = true, optional = true } hashbrown = { workspace = true, optional = true } ahash = { workspace = true, optional = true } serde = { workspace = true, optional = true } [dev-dependencies] anyhow = { workspace = true } criterion = { workspace = true } wat = { path = "../wat" } wast = { path = "../wast" } rayon = { workspace = true } once_cell = "1.13.0" wasm-encoder = { path = "../wasm-encoder" } env_logger.workspace = true log.workspace = true [[bench]] name = "benchmark" harness = false [features] default = ['std', 'validate', 'serde', 'features'] # A feature which enables implementations of `std::error::Error` as appropriate # along with other convenience APIs. This additionally uses the standard # library's source of randomness for seeding hash maps. std = ['indexmap/std'] # Tells the wasmparser crate to avoid using hash based maps and sets. # # Some embedded environments cannot provide a random source which is required # to properly initialize hashmap based data structures for resilience against # malious actors that control their inputs. no-hash-maps = [] # A feature that enables validating WebAssembly files. This is enabled by # default but not required if you're only parsing a file, for example, as # opposed to validating all of its contents. validate = [ 'dep:indexmap', 'dep:semver', 'dep:hashbrown', 'dep:ahash', ] # Enable Serialize/Deserialize implementations for types in # `wasmparser::collections` serde = ['dep:serde', 'indexmap/serde', 'hashbrown/serde'] # A feature that enables the guts of the `WasmFeatures` type in this crate. # # This feature is enabled by default. When disabled this crate does not support # runtime configuration of WebAssembly features. Instead the set of WebAssembly # features/proposals support are fixed at compile time to `wasmparser`'s default # set of supported features. features = [] wasmparser-0.217.0/README.md000064400000000000000000000025041046102023000134370ustar 00000000000000# `wasmparser`: A WebAssembly Binary Parser **A [Bytecode Alliance](https://bytecodealliance.org/) project** [![crates.io link](https://img.shields.io/crates/v/wasmparser.svg)](https://crates.io/crates/wasmparser) [![docs.rs docs](https://img.shields.io/static/v1?label=docs&message=wasmparser&color=blue&style=flat-square)](https://docs.rs/wasmparser/) A simple, event-driven library for parsing WebAssembly binary files (or streams). The library reports events as they happen and only stores parsing information for a brief period of time, making it fast and memory-efficient. The event-driven model, however, has some drawbacks. If you need random access to the entire WebAssembly data-structure, this is not the right library for you. You could however, build such a data-structure using this library. To get started, create a [`Parser`](https://docs.rs/wasmparser/latest/wasmparser/struct.Parser.html) using [`Parser::new`](https://docs.rs/wasmparser/latest/wasmparser/struct.Parser.html#method.new) and then follow the examples documented for [`Parser::parse`](https://docs.rs/wasmparser/latest/wasmparser/struct.Parser.html#method.parse) or [`Parser::parse_all`](https://docs.rs/wasmparser/latest/wasmparser/struct.Parser.html#method.parse_all). ## Documentation Documentation and examples can be found at https://docs.rs/wasmparser/ wasmparser-0.217.0/benches/.gitignore000064400000000000000000000000101046102023000155450ustar 00000000000000!*.wasm wasmparser-0.217.0/benches/benchmark.rs000064400000000000000000000250571046102023000160770ustar 00000000000000use anyhow::Result; use criterion::{criterion_group, criterion_main, Criterion}; use once_cell::unsync::Lazy; use std::fs; use std::path::Path; use std::path::PathBuf; use wasmparser::{DataKind, ElementKind, Parser, Payload, Validator, VisitOperator, WasmFeatures}; /// A benchmark input. pub struct BenchmarkInput { /// The path to the benchmark file important for handling errors. pub path: PathBuf, /// The encoded Wasm module that is run by the benchmark. pub wasm: Vec, } impl BenchmarkInput { /// Creates a new benchmark input. pub fn new(test_path: PathBuf, encoded_wasm: Vec) -> Self { Self { path: test_path, wasm: encoded_wasm, } } } /// Returns a vector of all found benchmark input files under the given directory. /// /// Benchmark input files can be `.wat` or `.wast` formatted files. /// For `.wast` files we pull out all the module directives and run them in the benchmarks. fn collect_test_files(path: &Path, list: &mut Vec) -> Result<()> { for entry in path.read_dir()? { let entry = entry?; let path = entry.path(); if path.is_dir() { collect_test_files(&path, list)?; continue; } match path.extension().and_then(|ext| ext.to_str()) { Some("wasm") => { let wasm = fs::read(&path)?; list.push(BenchmarkInput::new(path, wasm)); } Some("wat") | Some("txt") => { if let Ok(wasm) = wat::parse_file(&path) { list.push(BenchmarkInput::new(path, wasm)); } } Some("wast") => { let contents = fs::read_to_string(&path)?; let buf = match wast::parser::ParseBuffer::new(&contents) { Ok(buf) => buf, Err(_) => continue, }; let wast: wast::Wast<'_> = match wast::parser::parse(&buf) { Ok(wast) => wast, Err(_) => continue, }; for directive in wast.directives { match directive { wast::WastDirective::Module(mut module) | wast::WastDirective::ModuleDefinition(mut module) => { let wasm = module.encode()?; list.push(BenchmarkInput::new(path.clone(), wasm)); } _ => continue, } } } _ => (), } } Ok(()) } /// Reads the input given the Wasm parser or validator. /// /// The `path` specifies which benchmark input file we are currently operating on /// so that we can report better errors in case of failures. fn read_all_wasm(wasm: &[u8]) -> Result<()> { use Payload::*; for item in Parser::new(0).parse_all(wasm) { match item? { TypeSection(s) => { for item in s { item?; } } ImportSection(s) => { for item in s { item?; } } FunctionSection(s) => { for item in s { item?; } } TableSection(s) => { for item in s { item?; } } MemorySection(s) => { for item in s { item?; } } TagSection(s) => { for item in s { item?; } } GlobalSection(s) => { for item in s { for op in item?.init_expr.get_operators_reader() { op?; } } } ExportSection(s) => { for item in s { item?; } } ElementSection(s) => { for item in s { let item = item?; if let ElementKind::Active { offset_expr, .. } = item.kind { for op in offset_expr.get_operators_reader() { op?; } } match item.items { wasmparser::ElementItems::Functions(r) => { for op in r { op?; } } wasmparser::ElementItems::Expressions(_, r) => { for op in r { op?; } } } } } DataSection(s) => { for item in s { let item = item?; if let DataKind::Active { offset_expr, .. } = item.kind { for op in offset_expr.get_operators_reader() { op?; } } } } CodeSectionEntry(body) => { let mut reader = body.get_binary_reader(); for _ in 0..reader.read_var_u32()? { reader.read_var_u32()?; reader.read::()?; } while !reader.eof() { reader.visit_operator(&mut NopVisit)?; } } // Component sections ModuleSection { .. } => {} InstanceSection(s) => { for item in s { item?; } } CoreTypeSection(s) => { for item in s { item?; } } ComponentSection { .. } => {} ComponentInstanceSection(s) => { for item in s { item?; } } ComponentAliasSection(s) => { for item in s { item?; } } ComponentTypeSection(s) => { for item in s { item?; } } ComponentCanonicalSection(s) => { for item in s { item?; } } ComponentStartSection { .. } => {} ComponentImportSection(s) => { for item in s { item?; } } ComponentExportSection(s) => { for item in s { item?; } } Version { .. } | StartSection { .. } | DataCountSection { .. } | UnknownSection { .. } | CustomSection { .. } | CodeSectionStart { .. } | End(_) => {} } } Ok(()) } /// Returns the default benchmark inputs that are proper `wasmparser` benchmark /// test inputs. fn collect_benchmark_inputs() -> Vec { let mut ret = Vec::new(); collect_test_files("../../tests".as_ref(), &mut ret).unwrap(); // Sort to ideally get more deterministic perf that ignores filesystems ret.sort_by_key(|p| p.path.clone()); ret } fn skip_validation(test: &Path) -> bool { let broken = [ "gc/gc-rec-sub.wat", "proposals/gc/type-equivalence.wast", "proposals/gc/type-subtyping.wast", ]; let test_path = test.to_str().unwrap().replace("\\", "/"); // for windows paths if broken.iter().any(|x| test_path.contains(x)) { return true; } false } fn define_benchmarks(c: &mut Criterion) { let _ = env_logger::try_init(); fn validator() -> Validator { Validator::new_with_features(WasmFeatures::all()) } let test_inputs = once_cell::unsync::Lazy::new(collect_benchmark_inputs); let parse_inputs = once_cell::unsync::Lazy::new(|| { let mut list = Vec::new(); for input in test_inputs.iter() { if read_all_wasm(&input.wasm).is_ok() { list.push(&input.wasm); } } list }); c.bench_function("parse/tests", |b| { Lazy::force(&parse_inputs); b.iter(|| { for wasm in parse_inputs.iter() { read_all_wasm(wasm).unwrap(); } }) }); let validate_inputs = once_cell::unsync::Lazy::new(|| { let mut list = Vec::new(); for input in test_inputs.iter() { if skip_validation(&input.path) { continue; } log::debug!("Validating {}", input.path.display()); if validator().validate_all(&input.wasm).is_ok() { list.push(&input.wasm); } } list }); c.bench_function("validate/tests", |b| { Lazy::force(&validate_inputs); b.iter(|| { for wasm in validate_inputs.iter() { validator().validate_all(wasm).unwrap(); } }) }); for file in std::fs::read_dir("benches").unwrap() { let file = file.unwrap(); let path = file.path(); if path.extension().and_then(|s| s.to_str()) != Some("wasm") { continue; } let name = path.file_stem().unwrap().to_str().unwrap(); let wasm = Lazy::new(|| std::fs::read(&path).unwrap()); c.bench_function(&format!("validate/{name}"), |b| { Lazy::force(&wasm); b.iter(|| { validator().validate_all(&wasm).unwrap(); }) }); c.bench_function(&format!("parse/{name}"), |b| { Lazy::force(&wasm); b.iter(|| { read_all_wasm(&wasm).unwrap(); }) }); } } criterion_group!(benchmark, define_benchmarks); criterion_main!(benchmark); struct NopVisit; macro_rules! define_visit_operator { ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( fn $visit(&mut self $($(,$arg: $argty)*)?) { define_visit_operator!(@visit $op $( $($arg)* )?); } )* }; (@visit BrTable $table:ident) => { for target in $table.targets() { target.unwrap(); } }; (@visit $($rest:tt)*) => {} } #[allow(unused_variables)] impl<'a> VisitOperator<'a> for NopVisit { type Output = (); wasmparser::for_each_operator!(define_visit_operator); } wasmparser-0.217.0/examples/simple.rs000064400000000000000000000020351046102023000156340ustar 00000000000000use anyhow::Result; use std::env; use wasmparser::{Parser, Payload}; fn main() -> Result<()> { let args = env::args().collect::>(); if args.len() != 2 { println!("Usage: {} in.wasm", args[0]); return Ok(()); } let buf: Vec = std::fs::read(&args[1])?; for payload in Parser::new(0).parse_all(&buf) { match payload? { Payload::Version { .. } => { println!("====== Module"); } Payload::ExportSection(s) => { for export in s { let export = export?; println!(" Export {} {:?}", export.name, export.kind); } } Payload::ImportSection(s) => { for import in s { let import = import?; println!(" Import {}::{}", import.module, import.name); } } _other => { // println!("found payload {:?}", _other); } } } Ok(()) } wasmparser-0.217.0/src/binary_reader.rs000064400000000000000000002500241046102023000161250ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::prelude::*; use crate::{limits::*, *}; use core::fmt; use core::marker; use core::ops::Range; use core::str; pub(crate) const WASM_MAGIC_NUMBER: &[u8; 4] = b"\0asm"; /// A binary reader for WebAssembly modules. #[derive(Debug, Clone)] pub struct BinaryReaderError { // Wrap the actual error data in a `Box` so that the error is just one // word. This means that we can continue returning small `Result`s in // registers. pub(crate) inner: Box, } #[derive(Debug, Clone)] pub(crate) struct BinaryReaderErrorInner { pub(crate) message: String, pub(crate) offset: usize, pub(crate) needed_hint: Option, } /// The result for `BinaryReader` operations. pub type Result = core::result::Result; #[cfg(feature = "std")] impl std::error::Error for BinaryReaderError {} impl fmt::Display for BinaryReaderError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{} (at offset 0x{:x})", self.inner.message, self.inner.offset ) } } impl BinaryReaderError { #[cold] pub(crate) fn new(message: impl Into, offset: usize) -> Self { let message = message.into(); BinaryReaderError { inner: Box::new(BinaryReaderErrorInner { message, offset, needed_hint: None, }), } } #[cold] pub(crate) fn fmt(args: fmt::Arguments<'_>, offset: usize) -> Self { BinaryReaderError::new(args.to_string(), offset) } #[cold] pub(crate) fn eof(offset: usize, needed_hint: usize) -> Self { BinaryReaderError { inner: Box::new(BinaryReaderErrorInner { message: "unexpected end-of-file".to_string(), offset, needed_hint: Some(needed_hint), }), } } /// Get this error's message. pub fn message(&self) -> &str { &self.inner.message } /// Get the offset within the Wasm binary where the error occurred. pub fn offset(&self) -> usize { self.inner.offset } #[cfg(feature = "validate")] pub(crate) fn add_context(&mut self, mut context: String) { context.push_str("\n"); self.inner.message.insert_str(0, &context); } } /// A binary reader of the WebAssembly structures and types. #[derive(Clone, Debug, Hash)] pub struct BinaryReader<'a> { buffer: &'a [u8], position: usize, original_offset: usize, // When the `features` feature is disabled then the `WasmFeatures` type // still exists but this field is still omitted. When `features` is // disabled then the only constructor of this type is `BinaryReader::new` // which documents all known features being active. All known features // being active isn't represented by `WasmFeatures` when the feature is // disabled so the field is omitted here to prevent accidentally using the // wrong listing of features. // // Feature accessors are defined by `foreach_wasm_feature!` below with a // method-per-feature on `BinaryReader` which when the `features` feature // is disabled returns `true` by default. #[cfg(feature = "features")] features: WasmFeatures, } impl<'a> BinaryReader<'a> { /// Creates a new binary reader which will parse the `data` provided. /// /// The `original_offset` provided is used for byte offsets in errors that /// are generated. That offset is added to the current position in `data`. /// This can be helpful when `data` is just a window of a view into a larger /// wasm binary perhaps not even entirely stored locally. /// /// The returned binary reader will have all features known to this crate /// enabled. To reject binaries that aren't valid unless a certain feature /// is enabled use the [`BinaryReader::new_features`] constructor instead. pub fn new(data: &[u8], original_offset: usize) -> BinaryReader { BinaryReader { buffer: data, position: 0, original_offset, #[cfg(feature = "features")] features: WasmFeatures::all(), } } /// Creates a new binary reader which will parse the `data` provided. /// /// The `original_offset` provided is used for byte offsets in errors that /// are generated. That offset is added to the current position in `data`. /// This can be helpful when `data` is just a window of a view into a larger /// wasm binary perhaps not even entirely stored locally. /// /// The `features` argument provided controls which WebAssembly features are /// active when parsing this data. Wasm features typically don't affect /// parsing too too much and are generally more applicable during /// validation, but features and proposals will often reinterpret /// previously-invalid constructs as now-valid things meaning something /// slightly different. This means that invalid bytes before a feature may /// now be interpreted differently after a feature is implemented. This /// means that the set of activated features can affect what errors are /// generated and when they are generated. /// /// In general it's safe to pass `WasmFeatures::all()` here. There's no /// downside to enabling all features while parsing and only enabling a /// subset of features during validation. /// /// Note that the activated set of features does not guarantee that /// `BinaryReader` will return an error for disabled features. For example /// if SIMD is disabled then SIMD instructions will still be parsed via /// [`BinaryReader::visit_operator`]. Validation must still be performed to /// provide a strict guarantee that if a feature is disabled that a binary /// doesn't leverage the feature. The activated set of features here instead /// only affects locations where preexisting bytes are reinterpreted in /// different ways with future proposals, such as the `memarg` moving from a /// 32-bit offset to a 64-bit offset with the `memory64` proposal. #[cfg(feature = "features")] pub fn new_features( data: &[u8], original_offset: usize, features: WasmFeatures, ) -> BinaryReader { BinaryReader { buffer: data, position: 0, original_offset, features, } } /// "Shrinks" this binary reader to retain only the buffer left-to-parse. /// /// The primary purpose of this method is to change the return value of the /// `range()` method. That method returns the range of the original buffer /// within the wasm binary so calling `range()` on the returned /// `BinaryReader` will return a smaller range than if `range()` is called /// on `self`. /// /// Otherwise parsing values from either `self` or the return value should /// return the same thing. pub(crate) fn shrink(&self) -> BinaryReader<'a> { BinaryReader { buffer: &self.buffer[self.position..], position: 0, original_offset: self.original_offset + self.position, #[cfg(feature = "features")] features: self.features, } } /// Gets the original position of the binary reader. #[inline] pub fn original_position(&self) -> usize { self.original_offset + self.position } /// Returns the currently active set of wasm features that this reader is /// using while parsing. /// /// For more information see [`BinaryReader::new`]. #[cfg(feature = "features")] pub fn features(&self) -> WasmFeatures { self.features } /// Sets the wasm features active while parsing to the `features` specified. /// /// For more information see [`BinaryReader::new`]. #[cfg(feature = "features")] pub fn set_features(&mut self, features: WasmFeatures) { self.features = features; } /// Returns a range from the starting offset to the end of the buffer. pub fn range(&self) -> Range { self.original_offset..self.original_offset + self.buffer.len() } pub(crate) fn remaining_buffer(&self) -> &'a [u8] { &self.buffer[self.position..] } fn ensure_has_byte(&self) -> Result<()> { if self.position < self.buffer.len() { Ok(()) } else { Err(BinaryReaderError::eof(self.original_position(), 1)) } } pub(crate) fn ensure_has_bytes(&self, len: usize) -> Result<()> { if self.position + len <= self.buffer.len() { Ok(()) } else { let hint = self.position + len - self.buffer.len(); Err(BinaryReaderError::eof(self.original_position(), hint)) } } /// Reads a value of type `T` from this binary reader, advancing the /// internal position in this reader forward as data is read. #[inline] pub fn read(&mut self) -> Result where T: FromReader<'a>, { T::from_reader(self) } pub(crate) fn read_u7(&mut self) -> Result { let b = self.read_u8()?; if (b & 0x80) != 0 { return Err(BinaryReaderError::new( "invalid u7", self.original_position() - 1, )); } Ok(b) } pub(crate) fn external_kind_from_byte(byte: u8, offset: usize) -> Result { match byte { 0x00 => Ok(ExternalKind::Func), 0x01 => Ok(ExternalKind::Table), 0x02 => Ok(ExternalKind::Memory), 0x03 => Ok(ExternalKind::Global), 0x04 => Ok(ExternalKind::Tag), x => Err(Self::invalid_leading_byte_error(x, "external kind", offset)), } } /// Reads a variable-length 32-bit size from the byte stream while checking /// against a limit. pub fn read_size(&mut self, limit: usize, desc: &str) -> Result { let pos = self.original_position(); let size = self.read_var_u32()? as usize; if size > limit { bail!(pos, "{desc} size is out of bounds"); } Ok(size) } /// Reads a variable-length 32-bit size from the byte stream while checking /// against a limit. /// /// Then reads that many values of type `T` and returns them as an iterator. /// /// Note that regardless of how many items are read from the returned /// iterator the items will still be parsed from this reader. pub fn read_iter<'me, T>( &'me mut self, limit: usize, desc: &str, ) -> Result> where T: FromReader<'a>, { let size = self.read_size(limit, desc)?; Ok(BinaryReaderIter { remaining: size, reader: self, _marker: marker::PhantomData, }) } fn read_memarg(&mut self, max_align: u8) -> Result { let flags_pos = self.original_position(); let mut flags = self.read_var_u32()?; let memory = if self.multi_memory() && flags & (1 << 6) != 0 { flags ^= 1 << 6; self.read_var_u32()? } else { 0 }; let align = if flags >= (1 << 6) { return Err(BinaryReaderError::new( "malformed memop alignment: alignment too large", flags_pos, )); } else { flags as u8 }; let offset = if self.memory64() { self.read_var_u64()? } else { u64::from(self.read_var_u32()?) }; Ok(MemArg { align, max_align, offset, memory, }) } fn read_ordering(&mut self) -> Result { let byte = self.read_var_u32()?; match byte { 0 => Ok(Ordering::SeqCst), 1 => Ok(Ordering::AcqRel), x => Err(BinaryReaderError::new( &format!("invalid atomic consistency ordering {}", x), self.original_position() - 1, )), } } fn read_br_table(&mut self) -> Result> { let cnt = self.read_size(MAX_WASM_BR_TABLE_SIZE, "br_table")?; let reader = self.skip(|reader| { for _ in 0..cnt { reader.read_var_u32()?; } Ok(()) })?; let default = self.read_var_u32()?; Ok(BrTable { reader, cnt: cnt as u32, default, }) } /// Returns whether the `BinaryReader` has reached the end of the file. #[inline] pub fn eof(&self) -> bool { self.position >= self.buffer.len() } /// Returns the `BinaryReader`'s current position. #[inline] pub fn current_position(&self) -> usize { self.position } /// Returns the number of bytes remaining in the `BinaryReader`. #[inline] pub fn bytes_remaining(&self) -> usize { self.buffer.len() - self.position } /// Advances the `BinaryReader` `size` bytes, and returns a slice from the /// current position of `size` length. /// /// # Errors /// If `size` exceeds the remaining length in `BinaryReader`. pub fn read_bytes(&mut self, size: usize) -> Result<&'a [u8]> { self.ensure_has_bytes(size)?; let start = self.position; self.position += size; Ok(&self.buffer[start..self.position]) } /// Reads a length-prefixed list of bytes from this reader and returns a /// new `BinaryReader` to read that list of bytes. pub fn read_reader(&mut self) -> Result> { let size = self.read_var_u32()? as usize; self.skip(|reader| { reader.read_bytes(size)?; Ok(()) }) } /// Advances the `BinaryReader` four bytes and returns a `u32`. /// # Errors /// If `BinaryReader` has less than four bytes remaining. pub fn read_u32(&mut self) -> Result { self.ensure_has_bytes(4)?; let word = u32::from_le_bytes( self.buffer[self.position..self.position + 4] .try_into() .unwrap(), ); self.position += 4; Ok(word) } /// Advances the `BinaryReader` eight bytes and returns a `u64`. /// # Errors /// If `BinaryReader` has less than eight bytes remaining. pub fn read_u64(&mut self) -> Result { self.ensure_has_bytes(8)?; let word = u64::from_le_bytes( self.buffer[self.position..self.position + 8] .try_into() .unwrap(), ); self.position += 8; Ok(word) } /// Advances the `BinaryReader` a single byte. /// /// # Errors /// /// If `BinaryReader` has no bytes remaining. #[inline] pub fn read_u8(&mut self) -> Result { let b = match self.buffer.get(self.position) { Some(b) => *b, None => return Err(self.eof_err()), }; self.position += 1; Ok(b) } #[cold] fn eof_err(&self) -> BinaryReaderError { BinaryReaderError::eof(self.original_position(), 1) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `u32`. /// /// # Errors /// /// If `BinaryReader` has less than one or up to four bytes remaining, or /// the integer is larger than 32 bits. #[inline] pub fn read_var_u32(&mut self) -> Result { // Optimization for single byte i32. let byte = self.read_u8()?; if (byte & 0x80) == 0 { Ok(u32::from(byte)) } else { self.read_var_u32_big(byte) } } fn read_var_u32_big(&mut self, byte: u8) -> Result { let mut result = (byte & 0x7F) as u32; let mut shift = 7; loop { let byte = self.read_u8()?; result |= ((byte & 0x7F) as u32) << shift; if shift >= 25 && (byte >> (32 - shift)) != 0 { let msg = if byte & 0x80 != 0 { "invalid var_u32: integer representation too long" } else { "invalid var_u32: integer too large" }; // The continuation bit or unused bits are set. return Err(BinaryReaderError::new(msg, self.original_position() - 1)); } shift += 7; if (byte & 0x80) == 0 { break; } } Ok(result) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `u64`. /// /// # Errors /// /// If `BinaryReader` has less than one or up to eight bytes remaining, or /// the integer is larger than 64 bits. #[inline] pub fn read_var_u64(&mut self) -> Result { // Optimization for single byte u64. let byte = u64::from(self.read_u8()?); if (byte & 0x80) == 0 { Ok(byte) } else { self.read_var_u64_big(byte) } } fn read_var_u64_big(&mut self, byte: u64) -> Result { let mut result = byte & 0x7F; let mut shift = 7; loop { let byte = u64::from(self.read_u8()?); result |= (byte & 0x7F) << shift; if shift >= 57 && (byte >> (64 - shift)) != 0 { let msg = if byte & 0x80 != 0 { "invalid var_u64: integer representation too long" } else { "invalid var_u64: integer too large" }; // The continuation bit or unused bits are set. return Err(BinaryReaderError::new(msg, self.original_position() - 1)); } shift += 7; if (byte & 0x80) == 0 { break; } } Ok(result) } /// Executes `f` to skip some data in this binary reader and then returns a /// reader which will read the skipped data. pub fn skip(&mut self, f: impl FnOnce(&mut Self) -> Result<()>) -> Result { let start = self.position; f(self)?; let mut ret = self.clone(); ret.buffer = &self.buffer[start..self.position]; ret.position = 0; ret.original_offset = self.original_offset + start; Ok(ret) } /// Advances the `BinaryReader` past a WebAssembly string. This method does /// not perform any utf-8 validation. /// # Errors /// If `BinaryReader` has less than four bytes, the string's length exceeds /// the remaining bytes, or the string length /// exceeds `limits::MAX_WASM_STRING_SIZE`. pub fn skip_string(&mut self) -> Result<()> { let len = self.read_var_u32()? as usize; if len > MAX_WASM_STRING_SIZE { return Err(BinaryReaderError::new( "string size out of bounds", self.original_position() - 1, )); } self.ensure_has_bytes(len)?; self.position += len; Ok(()) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `i32`. /// # Errors /// If `BinaryReader` has less than one or up to four bytes remaining, or /// the integer is larger than 32 bits. #[inline] pub fn read_var_i32(&mut self) -> Result { // Optimization for single byte i32. let byte = self.read_u8()?; if (byte & 0x80) == 0 { Ok(((byte as i32) << 25) >> 25) } else { self.read_var_i32_big(byte) } } fn read_var_i32_big(&mut self, byte: u8) -> Result { let mut result = (byte & 0x7F) as i32; let mut shift = 7; loop { let byte = self.read_u8()?; result |= ((byte & 0x7F) as i32) << shift; if shift >= 25 { let continuation_bit = (byte & 0x80) != 0; let sign_and_unused_bit = (byte << 1) as i8 >> (32 - shift); if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { let msg = if continuation_bit { "invalid var_i32: integer representation too long" } else { "invalid var_i32: integer too large" }; return Err(BinaryReaderError::new(msg, self.original_position() - 1)); } return Ok(result); } shift += 7; if (byte & 0x80) == 0 { break; } } let ashift = 32 - shift; Ok((result << ashift) >> ashift) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a signed 33 bit integer, returned as a `i64`. /// # Errors /// If `BinaryReader` has less than one or up to five bytes remaining, or /// the integer is larger than 33 bits. pub fn read_var_s33(&mut self) -> Result { // Optimization for single byte. let byte = self.read_u8()?; if (byte & 0x80) == 0 { return Ok(((byte as i8) << 1) as i64 >> 1); } let mut result = (byte & 0x7F) as i64; let mut shift = 7; loop { let byte = self.read_u8()?; result |= ((byte & 0x7F) as i64) << shift; if shift >= 25 { let continuation_bit = (byte & 0x80) != 0; let sign_and_unused_bit = (byte << 1) as i8 >> (33 - shift); if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { return Err(BinaryReaderError::new( "invalid var_s33: integer representation too long", self.original_position() - 1, )); } return Ok(result); } shift += 7; if (byte & 0x80) == 0 { break; } } let ashift = 64 - shift; Ok((result << ashift) >> ashift) } /// Advances the `BinaryReader` up to eight bytes to parse a variable /// length integer as a 64 bit integer, returned as a `i64`. /// # Errors /// If `BinaryReader` has less than one or up to eight bytes remaining, or /// the integer is larger than 64 bits. pub fn read_var_i64(&mut self) -> Result { let mut result: i64 = 0; let mut shift = 0; loop { let byte = self.read_u8()?; result |= i64::from(byte & 0x7F) << shift; if shift >= 57 { let continuation_bit = (byte & 0x80) != 0; let sign_and_unused_bit = ((byte << 1) as i8) >> (64 - shift); if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { let msg = if continuation_bit { "invalid var_i64: integer representation too long" } else { "invalid var_i64: integer too large" }; return Err(BinaryReaderError::new(msg, self.original_position() - 1)); } return Ok(result); } shift += 7; if (byte & 0x80) == 0 { break; } } let ashift = 64 - shift; Ok((result << ashift) >> ashift) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a 32 bit floating point integer, returned as `Ieee32`. /// # Errors /// If `BinaryReader` has less than one or up to four bytes remaining, or /// the integer is larger than 32 bits. pub fn read_f32(&mut self) -> Result { let value = self.read_u32()?; Ok(Ieee32(value)) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a 32 bit floating point integer, returned as `Ieee32`. /// # Errors /// If `BinaryReader` has less than one or up to four bytes remaining, or /// the integer is larger than 32 bits. pub fn read_f64(&mut self) -> Result { let value = self.read_u64()?; Ok(Ieee64(value)) } /// (internal) Reads a fixed-size WebAssembly string from the module. fn internal_read_string(&mut self, len: usize) -> Result<&'a str> { let bytes = self.read_bytes(len)?; str::from_utf8(bytes).map_err(|_| { BinaryReaderError::new("malformed UTF-8 encoding", self.original_position() - 1) }) } /// Reads a WebAssembly string from the module. /// /// # Errors /// /// If `BinaryReader` has less than up to four bytes remaining, the string's /// length exceeds the remaining bytes, the string's length exceeds /// `limits::MAX_WASM_STRING_SIZE`, or the string contains invalid utf-8. pub fn read_string(&mut self) -> Result<&'a str> { let len = self.read_var_u32()? as usize; if len > MAX_WASM_STRING_SIZE { return Err(BinaryReaderError::new( "string size out of bounds", self.original_position() - 1, )); } return self.internal_read_string(len); } /// Reads a unlimited WebAssembly string from the module. /// /// Note that this is similar to [`BinaryReader::read_string`] except that /// it will not limit the size of the returned string by /// `limits::MAX_WASM_STRING_SIZE`. pub fn read_unlimited_string(&mut self) -> Result<&'a str> { let len = self.read_var_u32()? as usize; return self.internal_read_string(len); } #[cold] pub(crate) fn invalid_leading_byte(&self, byte: u8, desc: &str) -> Result { Err(Self::invalid_leading_byte_error( byte, desc, self.original_position() - 1, )) } pub(crate) fn invalid_leading_byte_error( byte: u8, desc: &str, offset: usize, ) -> BinaryReaderError { format_err!(offset, "invalid leading byte (0x{byte:x}) for {desc}") } pub(crate) fn peek(&self) -> Result { self.ensure_has_byte()?; Ok(self.buffer[self.position]) } pub(crate) fn read_block_type(&mut self) -> Result { let b = self.peek()?; // Block types are encoded as either 0x40, a `valtype`, or `s33`. All // current `valtype` encodings are negative numbers when encoded with // sleb128, but it's also required that valtype encodings are in their // canonical form. For example an overlong encoding of -1 as `0xff 0x7f` // is not valid and it is required to be `0x7f`. This means that we // can't simply match on the `s33` that pops out below since reading the // whole `s33` might read an overlong encoding. // // To test for this the first byte `b` is inspected. The highest bit, // the continuation bit in LEB128 encoding, must be clear. The next bit, // the sign bit, must be set to indicate that the number is negative. If // these two conditions hold then we're guaranteed that this is a // negative number. // // After this a value type is read directly instead of looking for an // indexed value type. if b & 0x80 == 0 && b & 0x40 != 0 { if b == 0x40 { self.position += 1; return Ok(BlockType::Empty); } return Ok(BlockType::Type(self.read()?)); } // Not empty or a singular type, so read the function type index let idx = self.read_var_s33()?; match u32::try_from(idx) { Ok(idx) => Ok(BlockType::FuncType(idx)), Err(_) => { return Err(BinaryReaderError::new( "invalid function type", self.original_position(), )); } } } /// Visit the next available operator with the specified [`VisitOperator`] instance. /// /// Note that this does not implicitly propagate any additional information such as instruction /// offsets. In order to do so, consider storing such data within the visitor before visiting. /// /// # Errors /// /// If `BinaryReader` has less bytes remaining than required to parse the `Operator`. /// /// # Examples /// /// Store an offset for use in diagnostics or any other purposes: /// /// ``` /// # use wasmparser::{BinaryReader, VisitOperator, Result, for_each_operator}; /// /// pub fn dump(mut reader: BinaryReader) -> Result<()> { /// let mut visitor = Dumper { offset: 0 }; /// while !reader.eof() { /// visitor.offset = reader.original_position(); /// reader.visit_operator(&mut visitor)?; /// } /// Ok(()) /// } /// /// struct Dumper { /// offset: usize /// } /// /// macro_rules! define_visit_operator { /// ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { /// $( /// fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { /// println!("{}: {}", self.offset, stringify!($visit)); /// } /// )* /// } /// } /// /// impl<'a> VisitOperator<'a> for Dumper { /// type Output = (); /// for_each_operator!(define_visit_operator); /// } /// /// ``` pub fn visit_operator(&mut self, visitor: &mut T) -> Result<>::Output> where T: VisitOperator<'a>, { let pos = self.original_position(); let code = self.read_u8()? as u8; Ok(match code { 0x00 => visitor.visit_unreachable(), 0x01 => visitor.visit_nop(), 0x02 => visitor.visit_block(self.read_block_type()?), 0x03 => visitor.visit_loop(self.read_block_type()?), 0x04 => visitor.visit_if(self.read_block_type()?), 0x05 => visitor.visit_else(), 0x06 => visitor.visit_try(self.read_block_type()?), 0x07 => visitor.visit_catch(self.read_var_u32()?), 0x08 => visitor.visit_throw(self.read_var_u32()?), 0x09 => visitor.visit_rethrow(self.read_var_u32()?), 0x0a => visitor.visit_throw_ref(), 0x0b => visitor.visit_end(), 0x0c => visitor.visit_br(self.read_var_u32()?), 0x0d => visitor.visit_br_if(self.read_var_u32()?), 0x0e => visitor.visit_br_table(self.read_br_table()?), 0x0f => visitor.visit_return(), 0x10 => visitor.visit_call(self.read_var_u32()?), 0x11 => { let index = self.read_var_u32()?; let table = self.read_table_index_or_zero_if_not_reference_types()?; visitor.visit_call_indirect(index, table) } 0x12 => visitor.visit_return_call(self.read_var_u32()?), 0x13 => visitor.visit_return_call_indirect(self.read_var_u32()?, self.read_var_u32()?), 0x14 => visitor.visit_call_ref(self.read()?), 0x15 => visitor.visit_return_call_ref(self.read()?), 0x18 => visitor.visit_delegate(self.read_var_u32()?), 0x19 => visitor.visit_catch_all(), 0x1a => visitor.visit_drop(), 0x1b => visitor.visit_select(), 0x1c => { let results = self.read_var_u32()?; if results != 1 { return Err(BinaryReaderError::new( "invalid result arity", self.position, )); } visitor.visit_typed_select(self.read()?) } 0x1f => visitor.visit_try_table(self.read()?), 0x20 => visitor.visit_local_get(self.read_var_u32()?), 0x21 => visitor.visit_local_set(self.read_var_u32()?), 0x22 => visitor.visit_local_tee(self.read_var_u32()?), 0x23 => visitor.visit_global_get(self.read_var_u32()?), 0x24 => visitor.visit_global_set(self.read_var_u32()?), 0x25 => visitor.visit_table_get(self.read_var_u32()?), 0x26 => visitor.visit_table_set(self.read_var_u32()?), 0x28 => visitor.visit_i32_load(self.read_memarg(2)?), 0x29 => visitor.visit_i64_load(self.read_memarg(3)?), 0x2a => visitor.visit_f32_load(self.read_memarg(2)?), 0x2b => visitor.visit_f64_load(self.read_memarg(3)?), 0x2c => visitor.visit_i32_load8_s(self.read_memarg(0)?), 0x2d => visitor.visit_i32_load8_u(self.read_memarg(0)?), 0x2e => visitor.visit_i32_load16_s(self.read_memarg(1)?), 0x2f => visitor.visit_i32_load16_u(self.read_memarg(1)?), 0x30 => visitor.visit_i64_load8_s(self.read_memarg(0)?), 0x31 => visitor.visit_i64_load8_u(self.read_memarg(0)?), 0x32 => visitor.visit_i64_load16_s(self.read_memarg(1)?), 0x33 => visitor.visit_i64_load16_u(self.read_memarg(1)?), 0x34 => visitor.visit_i64_load32_s(self.read_memarg(2)?), 0x35 => visitor.visit_i64_load32_u(self.read_memarg(2)?), 0x36 => visitor.visit_i32_store(self.read_memarg(2)?), 0x37 => visitor.visit_i64_store(self.read_memarg(3)?), 0x38 => visitor.visit_f32_store(self.read_memarg(2)?), 0x39 => visitor.visit_f64_store(self.read_memarg(3)?), 0x3a => visitor.visit_i32_store8(self.read_memarg(0)?), 0x3b => visitor.visit_i32_store16(self.read_memarg(1)?), 0x3c => visitor.visit_i64_store8(self.read_memarg(0)?), 0x3d => visitor.visit_i64_store16(self.read_memarg(1)?), 0x3e => visitor.visit_i64_store32(self.read_memarg(2)?), 0x3f => { let mem = self.read_memory_index_or_zero_if_not_multi_memory()?; visitor.visit_memory_size(mem) } 0x40 => { let mem = self.read_memory_index_or_zero_if_not_multi_memory()?; visitor.visit_memory_grow(mem) } 0x41 => visitor.visit_i32_const(self.read_var_i32()?), 0x42 => visitor.visit_i64_const(self.read_var_i64()?), 0x43 => visitor.visit_f32_const(self.read_f32()?), 0x44 => visitor.visit_f64_const(self.read_f64()?), 0x45 => visitor.visit_i32_eqz(), 0x46 => visitor.visit_i32_eq(), 0x47 => visitor.visit_i32_ne(), 0x48 => visitor.visit_i32_lt_s(), 0x49 => visitor.visit_i32_lt_u(), 0x4a => visitor.visit_i32_gt_s(), 0x4b => visitor.visit_i32_gt_u(), 0x4c => visitor.visit_i32_le_s(), 0x4d => visitor.visit_i32_le_u(), 0x4e => visitor.visit_i32_ge_s(), 0x4f => visitor.visit_i32_ge_u(), 0x50 => visitor.visit_i64_eqz(), 0x51 => visitor.visit_i64_eq(), 0x52 => visitor.visit_i64_ne(), 0x53 => visitor.visit_i64_lt_s(), 0x54 => visitor.visit_i64_lt_u(), 0x55 => visitor.visit_i64_gt_s(), 0x56 => visitor.visit_i64_gt_u(), 0x57 => visitor.visit_i64_le_s(), 0x58 => visitor.visit_i64_le_u(), 0x59 => visitor.visit_i64_ge_s(), 0x5a => visitor.visit_i64_ge_u(), 0x5b => visitor.visit_f32_eq(), 0x5c => visitor.visit_f32_ne(), 0x5d => visitor.visit_f32_lt(), 0x5e => visitor.visit_f32_gt(), 0x5f => visitor.visit_f32_le(), 0x60 => visitor.visit_f32_ge(), 0x61 => visitor.visit_f64_eq(), 0x62 => visitor.visit_f64_ne(), 0x63 => visitor.visit_f64_lt(), 0x64 => visitor.visit_f64_gt(), 0x65 => visitor.visit_f64_le(), 0x66 => visitor.visit_f64_ge(), 0x67 => visitor.visit_i32_clz(), 0x68 => visitor.visit_i32_ctz(), 0x69 => visitor.visit_i32_popcnt(), 0x6a => visitor.visit_i32_add(), 0x6b => visitor.visit_i32_sub(), 0x6c => visitor.visit_i32_mul(), 0x6d => visitor.visit_i32_div_s(), 0x6e => visitor.visit_i32_div_u(), 0x6f => visitor.visit_i32_rem_s(), 0x70 => visitor.visit_i32_rem_u(), 0x71 => visitor.visit_i32_and(), 0x72 => visitor.visit_i32_or(), 0x73 => visitor.visit_i32_xor(), 0x74 => visitor.visit_i32_shl(), 0x75 => visitor.visit_i32_shr_s(), 0x76 => visitor.visit_i32_shr_u(), 0x77 => visitor.visit_i32_rotl(), 0x78 => visitor.visit_i32_rotr(), 0x79 => visitor.visit_i64_clz(), 0x7a => visitor.visit_i64_ctz(), 0x7b => visitor.visit_i64_popcnt(), 0x7c => visitor.visit_i64_add(), 0x7d => visitor.visit_i64_sub(), 0x7e => visitor.visit_i64_mul(), 0x7f => visitor.visit_i64_div_s(), 0x80 => visitor.visit_i64_div_u(), 0x81 => visitor.visit_i64_rem_s(), 0x82 => visitor.visit_i64_rem_u(), 0x83 => visitor.visit_i64_and(), 0x84 => visitor.visit_i64_or(), 0x85 => visitor.visit_i64_xor(), 0x86 => visitor.visit_i64_shl(), 0x87 => visitor.visit_i64_shr_s(), 0x88 => visitor.visit_i64_shr_u(), 0x89 => visitor.visit_i64_rotl(), 0x8a => visitor.visit_i64_rotr(), 0x8b => visitor.visit_f32_abs(), 0x8c => visitor.visit_f32_neg(), 0x8d => visitor.visit_f32_ceil(), 0x8e => visitor.visit_f32_floor(), 0x8f => visitor.visit_f32_trunc(), 0x90 => visitor.visit_f32_nearest(), 0x91 => visitor.visit_f32_sqrt(), 0x92 => visitor.visit_f32_add(), 0x93 => visitor.visit_f32_sub(), 0x94 => visitor.visit_f32_mul(), 0x95 => visitor.visit_f32_div(), 0x96 => visitor.visit_f32_min(), 0x97 => visitor.visit_f32_max(), 0x98 => visitor.visit_f32_copysign(), 0x99 => visitor.visit_f64_abs(), 0x9a => visitor.visit_f64_neg(), 0x9b => visitor.visit_f64_ceil(), 0x9c => visitor.visit_f64_floor(), 0x9d => visitor.visit_f64_trunc(), 0x9e => visitor.visit_f64_nearest(), 0x9f => visitor.visit_f64_sqrt(), 0xa0 => visitor.visit_f64_add(), 0xa1 => visitor.visit_f64_sub(), 0xa2 => visitor.visit_f64_mul(), 0xa3 => visitor.visit_f64_div(), 0xa4 => visitor.visit_f64_min(), 0xa5 => visitor.visit_f64_max(), 0xa6 => visitor.visit_f64_copysign(), 0xa7 => visitor.visit_i32_wrap_i64(), 0xa8 => visitor.visit_i32_trunc_f32_s(), 0xa9 => visitor.visit_i32_trunc_f32_u(), 0xaa => visitor.visit_i32_trunc_f64_s(), 0xab => visitor.visit_i32_trunc_f64_u(), 0xac => visitor.visit_i64_extend_i32_s(), 0xad => visitor.visit_i64_extend_i32_u(), 0xae => visitor.visit_i64_trunc_f32_s(), 0xaf => visitor.visit_i64_trunc_f32_u(), 0xb0 => visitor.visit_i64_trunc_f64_s(), 0xb1 => visitor.visit_i64_trunc_f64_u(), 0xb2 => visitor.visit_f32_convert_i32_s(), 0xb3 => visitor.visit_f32_convert_i32_u(), 0xb4 => visitor.visit_f32_convert_i64_s(), 0xb5 => visitor.visit_f32_convert_i64_u(), 0xb6 => visitor.visit_f32_demote_f64(), 0xb7 => visitor.visit_f64_convert_i32_s(), 0xb8 => visitor.visit_f64_convert_i32_u(), 0xb9 => visitor.visit_f64_convert_i64_s(), 0xba => visitor.visit_f64_convert_i64_u(), 0xbb => visitor.visit_f64_promote_f32(), 0xbc => visitor.visit_i32_reinterpret_f32(), 0xbd => visitor.visit_i64_reinterpret_f64(), 0xbe => visitor.visit_f32_reinterpret_i32(), 0xbf => visitor.visit_f64_reinterpret_i64(), 0xc0 => visitor.visit_i32_extend8_s(), 0xc1 => visitor.visit_i32_extend16_s(), 0xc2 => visitor.visit_i64_extend8_s(), 0xc3 => visitor.visit_i64_extend16_s(), 0xc4 => visitor.visit_i64_extend32_s(), 0xd0 => visitor.visit_ref_null(self.read()?), 0xd1 => visitor.visit_ref_is_null(), 0xd2 => visitor.visit_ref_func(self.read_var_u32()?), 0xd3 => visitor.visit_ref_eq(), 0xd4 => visitor.visit_ref_as_non_null(), 0xd5 => visitor.visit_br_on_null(self.read_var_u32()?), 0xd6 => visitor.visit_br_on_non_null(self.read_var_u32()?), 0xfb => self.visit_0xfb_operator(pos, visitor)?, 0xfc => self.visit_0xfc_operator(pos, visitor)?, 0xfd => self.visit_0xfd_operator(pos, visitor)?, 0xfe => self.visit_0xfe_operator(pos, visitor)?, _ => bail!(pos, "illegal opcode: 0x{code:x}"), }) } fn visit_0xfb_operator( &mut self, pos: usize, visitor: &mut T, ) -> Result<>::Output> where T: VisitOperator<'a>, { let code = self.read_var_u32()?; Ok(match code { 0x0 => { let type_index = self.read_var_u32()?; visitor.visit_struct_new(type_index) } 0x01 => { let type_index = self.read_var_u32()?; visitor.visit_struct_new_default(type_index) } 0x02 => { let type_index = self.read_var_u32()?; let field_index = self.read_var_u32()?; visitor.visit_struct_get(type_index, field_index) } 0x03 => { let type_index = self.read_var_u32()?; let field_index = self.read_var_u32()?; visitor.visit_struct_get_s(type_index, field_index) } 0x04 => { let type_index = self.read_var_u32()?; let field_index = self.read_var_u32()?; visitor.visit_struct_get_u(type_index, field_index) } 0x05 => { let type_index = self.read_var_u32()?; let field_index = self.read_var_u32()?; visitor.visit_struct_set(type_index, field_index) } 0x06 => { let type_index = self.read_var_u32()?; visitor.visit_array_new(type_index) } 0x07 => { let type_index = self.read_var_u32()?; visitor.visit_array_new_default(type_index) } 0x08 => { let type_index = self.read_var_u32()?; let n = self.read_var_u32()?; visitor.visit_array_new_fixed(type_index, n) } 0x09 => { let type_index = self.read_var_u32()?; let data_index = self.read_var_u32()?; visitor.visit_array_new_data(type_index, data_index) } 0x0a => { let type_index = self.read_var_u32()?; let elem_index = self.read_var_u32()?; visitor.visit_array_new_elem(type_index, elem_index) } 0x0b => { let type_index = self.read_var_u32()?; visitor.visit_array_get(type_index) } 0x0c => { let type_index = self.read_var_u32()?; visitor.visit_array_get_s(type_index) } 0x0d => { let type_index = self.read_var_u32()?; visitor.visit_array_get_u(type_index) } 0x0e => { let type_index = self.read_var_u32()?; visitor.visit_array_set(type_index) } 0x0f => visitor.visit_array_len(), 0x10 => { let type_index = self.read_var_u32()?; visitor.visit_array_fill(type_index) } 0x11 => { let type_index_dst = self.read_var_u32()?; let type_index_src = self.read_var_u32()?; visitor.visit_array_copy(type_index_dst, type_index_src) } 0x12 => { let type_index = self.read_var_u32()?; let data_index = self.read_var_u32()?; visitor.visit_array_init_data(type_index, data_index) } 0x13 => { let type_index = self.read_var_u32()?; let elem_index = self.read_var_u32()?; visitor.visit_array_init_elem(type_index, elem_index) } 0x14 => visitor.visit_ref_test_non_null(self.read()?), 0x15 => visitor.visit_ref_test_nullable(self.read()?), 0x16 => visitor.visit_ref_cast_non_null(self.read()?), 0x17 => visitor.visit_ref_cast_nullable(self.read()?), 0x18 => { let pos = self.original_position(); let cast_flags = self.read_u8()?; let relative_depth = self.read_var_u32()?; let (from_type_nullable, to_type_nullable) = match cast_flags { 0b00 => (false, false), 0b01 => (true, false), 0b10 => (false, true), 0b11 => (true, true), _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), }; let from_heap_type = self.read()?; let from_ref_type = RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { format_err!(pos, "implementation error: type index too large") })?; let to_heap_type = self.read()?; let to_ref_type = RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { format_err!(pos, "implementation error: type index too large") })?; visitor.visit_br_on_cast(relative_depth, from_ref_type, to_ref_type) } 0x19 => { let pos = self.original_position(); let cast_flags = self.read_u8()?; let relative_depth = self.read_var_u32()?; let (from_type_nullable, to_type_nullable) = match cast_flags { 0 => (false, false), 1 => (true, false), 2 => (false, true), 3 => (true, true), _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), }; let from_heap_type = self.read()?; let from_ref_type = RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { format_err!(pos, "implementation error: type index too large") })?; let to_heap_type = self.read()?; let to_ref_type = RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { format_err!(pos, "implementation error: type index too large") })?; visitor.visit_br_on_cast_fail(relative_depth, from_ref_type, to_ref_type) } 0x1a => visitor.visit_any_convert_extern(), 0x1b => visitor.visit_extern_convert_any(), 0x1c => visitor.visit_ref_i31(), 0x1d => visitor.visit_i31_get_s(), 0x1e => visitor.visit_i31_get_u(), _ => bail!(pos, "unknown 0xfb subopcode: 0x{code:x}"), }) } fn visit_0xfc_operator( &mut self, pos: usize, visitor: &mut T, ) -> Result<>::Output> where T: VisitOperator<'a>, { let code = self.read_var_u32()?; Ok(match code { 0x00 => visitor.visit_i32_trunc_sat_f32_s(), 0x01 => visitor.visit_i32_trunc_sat_f32_u(), 0x02 => visitor.visit_i32_trunc_sat_f64_s(), 0x03 => visitor.visit_i32_trunc_sat_f64_u(), 0x04 => visitor.visit_i64_trunc_sat_f32_s(), 0x05 => visitor.visit_i64_trunc_sat_f32_u(), 0x06 => visitor.visit_i64_trunc_sat_f64_s(), 0x07 => visitor.visit_i64_trunc_sat_f64_u(), 0x08 => { let segment = self.read_var_u32()?; let mem = self.read_var_u32()?; visitor.visit_memory_init(segment, mem) } 0x09 => { let segment = self.read_var_u32()?; visitor.visit_data_drop(segment) } 0x0a => { let dst = self.read_var_u32()?; let src = self.read_var_u32()?; visitor.visit_memory_copy(dst, src) } 0x0b => { let mem = self.read_var_u32()?; visitor.visit_memory_fill(mem) } 0x0c => { let segment = self.read_var_u32()?; let table = self.read_var_u32()?; visitor.visit_table_init(segment, table) } 0x0d => { let segment = self.read_var_u32()?; visitor.visit_elem_drop(segment) } 0x0e => { let dst_table = self.read_var_u32()?; let src_table = self.read_var_u32()?; visitor.visit_table_copy(dst_table, src_table) } 0x0f => { let table = self.read_var_u32()?; visitor.visit_table_grow(table) } 0x10 => { let table = self.read_var_u32()?; visitor.visit_table_size(table) } 0x11 => { let table = self.read_var_u32()?; visitor.visit_table_fill(table) } 0x12 => { let mem = self.read_var_u32()?; visitor.visit_memory_discard(mem) } _ => bail!(pos, "unknown 0xfc subopcode: 0x{code:x}"), }) } fn visit_0xfd_operator( &mut self, pos: usize, visitor: &mut T, ) -> Result<>::Output> where T: VisitOperator<'a>, { let code = self.read_var_u32()?; Ok(match code { 0x00 => visitor.visit_v128_load(self.read_memarg(4)?), 0x01 => visitor.visit_v128_load8x8_s(self.read_memarg(3)?), 0x02 => visitor.visit_v128_load8x8_u(self.read_memarg(3)?), 0x03 => visitor.visit_v128_load16x4_s(self.read_memarg(3)?), 0x04 => visitor.visit_v128_load16x4_u(self.read_memarg(3)?), 0x05 => visitor.visit_v128_load32x2_s(self.read_memarg(3)?), 0x06 => visitor.visit_v128_load32x2_u(self.read_memarg(3)?), 0x07 => visitor.visit_v128_load8_splat(self.read_memarg(0)?), 0x08 => visitor.visit_v128_load16_splat(self.read_memarg(1)?), 0x09 => visitor.visit_v128_load32_splat(self.read_memarg(2)?), 0x0a => visitor.visit_v128_load64_splat(self.read_memarg(3)?), 0x0b => visitor.visit_v128_store(self.read_memarg(4)?), 0x0c => visitor.visit_v128_const(self.read_v128()?), 0x0d => { let mut lanes: [u8; 16] = [0; 16]; for lane in &mut lanes { *lane = self.read_lane_index(32)? } visitor.visit_i8x16_shuffle(lanes) } 0x0e => visitor.visit_i8x16_swizzle(), 0x0f => visitor.visit_i8x16_splat(), 0x10 => visitor.visit_i16x8_splat(), 0x11 => visitor.visit_i32x4_splat(), 0x12 => visitor.visit_i64x2_splat(), 0x13 => visitor.visit_f32x4_splat(), 0x14 => visitor.visit_f64x2_splat(), 0x15 => visitor.visit_i8x16_extract_lane_s(self.read_lane_index(16)?), 0x16 => visitor.visit_i8x16_extract_lane_u(self.read_lane_index(16)?), 0x17 => visitor.visit_i8x16_replace_lane(self.read_lane_index(16)?), 0x18 => visitor.visit_i16x8_extract_lane_s(self.read_lane_index(8)?), 0x19 => visitor.visit_i16x8_extract_lane_u(self.read_lane_index(8)?), 0x1a => visitor.visit_i16x8_replace_lane(self.read_lane_index(8)?), 0x1b => visitor.visit_i32x4_extract_lane(self.read_lane_index(4)?), 0x1c => visitor.visit_i32x4_replace_lane(self.read_lane_index(4)?), 0x1d => visitor.visit_i64x2_extract_lane(self.read_lane_index(2)?), 0x1e => visitor.visit_i64x2_replace_lane(self.read_lane_index(2)?), 0x1f => visitor.visit_f32x4_extract_lane(self.read_lane_index(4)?), 0x20 => visitor.visit_f32x4_replace_lane(self.read_lane_index(4)?), 0x21 => visitor.visit_f64x2_extract_lane(self.read_lane_index(2)?), 0x22 => visitor.visit_f64x2_replace_lane(self.read_lane_index(2)?), 0x23 => visitor.visit_i8x16_eq(), 0x24 => visitor.visit_i8x16_ne(), 0x25 => visitor.visit_i8x16_lt_s(), 0x26 => visitor.visit_i8x16_lt_u(), 0x27 => visitor.visit_i8x16_gt_s(), 0x28 => visitor.visit_i8x16_gt_u(), 0x29 => visitor.visit_i8x16_le_s(), 0x2a => visitor.visit_i8x16_le_u(), 0x2b => visitor.visit_i8x16_ge_s(), 0x2c => visitor.visit_i8x16_ge_u(), 0x2d => visitor.visit_i16x8_eq(), 0x2e => visitor.visit_i16x8_ne(), 0x2f => visitor.visit_i16x8_lt_s(), 0x30 => visitor.visit_i16x8_lt_u(), 0x31 => visitor.visit_i16x8_gt_s(), 0x32 => visitor.visit_i16x8_gt_u(), 0x33 => visitor.visit_i16x8_le_s(), 0x34 => visitor.visit_i16x8_le_u(), 0x35 => visitor.visit_i16x8_ge_s(), 0x36 => visitor.visit_i16x8_ge_u(), 0x37 => visitor.visit_i32x4_eq(), 0x38 => visitor.visit_i32x4_ne(), 0x39 => visitor.visit_i32x4_lt_s(), 0x3a => visitor.visit_i32x4_lt_u(), 0x3b => visitor.visit_i32x4_gt_s(), 0x3c => visitor.visit_i32x4_gt_u(), 0x3d => visitor.visit_i32x4_le_s(), 0x3e => visitor.visit_i32x4_le_u(), 0x3f => visitor.visit_i32x4_ge_s(), 0x40 => visitor.visit_i32x4_ge_u(), 0x41 => visitor.visit_f32x4_eq(), 0x42 => visitor.visit_f32x4_ne(), 0x43 => visitor.visit_f32x4_lt(), 0x44 => visitor.visit_f32x4_gt(), 0x45 => visitor.visit_f32x4_le(), 0x46 => visitor.visit_f32x4_ge(), 0x47 => visitor.visit_f64x2_eq(), 0x48 => visitor.visit_f64x2_ne(), 0x49 => visitor.visit_f64x2_lt(), 0x4a => visitor.visit_f64x2_gt(), 0x4b => visitor.visit_f64x2_le(), 0x4c => visitor.visit_f64x2_ge(), 0x4d => visitor.visit_v128_not(), 0x4e => visitor.visit_v128_and(), 0x4f => visitor.visit_v128_andnot(), 0x50 => visitor.visit_v128_or(), 0x51 => visitor.visit_v128_xor(), 0x52 => visitor.visit_v128_bitselect(), 0x53 => visitor.visit_v128_any_true(), 0x54 => { let memarg = self.read_memarg(0)?; let lane = self.read_lane_index(16)?; visitor.visit_v128_load8_lane(memarg, lane) } 0x55 => { let memarg = self.read_memarg(1)?; let lane = self.read_lane_index(8)?; visitor.visit_v128_load16_lane(memarg, lane) } 0x56 => { let memarg = self.read_memarg(2)?; let lane = self.read_lane_index(4)?; visitor.visit_v128_load32_lane(memarg, lane) } 0x57 => { let memarg = self.read_memarg(3)?; let lane = self.read_lane_index(2)?; visitor.visit_v128_load64_lane(memarg, lane) } 0x58 => { let memarg = self.read_memarg(0)?; let lane = self.read_lane_index(16)?; visitor.visit_v128_store8_lane(memarg, lane) } 0x59 => { let memarg = self.read_memarg(1)?; let lane = self.read_lane_index(8)?; visitor.visit_v128_store16_lane(memarg, lane) } 0x5a => { let memarg = self.read_memarg(2)?; let lane = self.read_lane_index(4)?; visitor.visit_v128_store32_lane(memarg, lane) } 0x5b => { let memarg = self.read_memarg(3)?; let lane = self.read_lane_index(2)?; visitor.visit_v128_store64_lane(memarg, lane) } 0x5c => visitor.visit_v128_load32_zero(self.read_memarg(2)?), 0x5d => visitor.visit_v128_load64_zero(self.read_memarg(3)?), 0x5e => visitor.visit_f32x4_demote_f64x2_zero(), 0x5f => visitor.visit_f64x2_promote_low_f32x4(), 0x60 => visitor.visit_i8x16_abs(), 0x61 => visitor.visit_i8x16_neg(), 0x62 => visitor.visit_i8x16_popcnt(), 0x63 => visitor.visit_i8x16_all_true(), 0x64 => visitor.visit_i8x16_bitmask(), 0x65 => visitor.visit_i8x16_narrow_i16x8_s(), 0x66 => visitor.visit_i8x16_narrow_i16x8_u(), 0x67 => visitor.visit_f32x4_ceil(), 0x68 => visitor.visit_f32x4_floor(), 0x69 => visitor.visit_f32x4_trunc(), 0x6a => visitor.visit_f32x4_nearest(), 0x6b => visitor.visit_i8x16_shl(), 0x6c => visitor.visit_i8x16_shr_s(), 0x6d => visitor.visit_i8x16_shr_u(), 0x6e => visitor.visit_i8x16_add(), 0x6f => visitor.visit_i8x16_add_sat_s(), 0x70 => visitor.visit_i8x16_add_sat_u(), 0x71 => visitor.visit_i8x16_sub(), 0x72 => visitor.visit_i8x16_sub_sat_s(), 0x73 => visitor.visit_i8x16_sub_sat_u(), 0x74 => visitor.visit_f64x2_ceil(), 0x75 => visitor.visit_f64x2_floor(), 0x76 => visitor.visit_i8x16_min_s(), 0x77 => visitor.visit_i8x16_min_u(), 0x78 => visitor.visit_i8x16_max_s(), 0x79 => visitor.visit_i8x16_max_u(), 0x7a => visitor.visit_f64x2_trunc(), 0x7b => visitor.visit_i8x16_avgr_u(), 0x7c => visitor.visit_i16x8_extadd_pairwise_i8x16_s(), 0x7d => visitor.visit_i16x8_extadd_pairwise_i8x16_u(), 0x7e => visitor.visit_i32x4_extadd_pairwise_i16x8_s(), 0x7f => visitor.visit_i32x4_extadd_pairwise_i16x8_u(), 0x80 => visitor.visit_i16x8_abs(), 0x81 => visitor.visit_i16x8_neg(), 0x82 => visitor.visit_i16x8_q15mulr_sat_s(), 0x83 => visitor.visit_i16x8_all_true(), 0x84 => visitor.visit_i16x8_bitmask(), 0x85 => visitor.visit_i16x8_narrow_i32x4_s(), 0x86 => visitor.visit_i16x8_narrow_i32x4_u(), 0x87 => visitor.visit_i16x8_extend_low_i8x16_s(), 0x88 => visitor.visit_i16x8_extend_high_i8x16_s(), 0x89 => visitor.visit_i16x8_extend_low_i8x16_u(), 0x8a => visitor.visit_i16x8_extend_high_i8x16_u(), 0x8b => visitor.visit_i16x8_shl(), 0x8c => visitor.visit_i16x8_shr_s(), 0x8d => visitor.visit_i16x8_shr_u(), 0x8e => visitor.visit_i16x8_add(), 0x8f => visitor.visit_i16x8_add_sat_s(), 0x90 => visitor.visit_i16x8_add_sat_u(), 0x91 => visitor.visit_i16x8_sub(), 0x92 => visitor.visit_i16x8_sub_sat_s(), 0x93 => visitor.visit_i16x8_sub_sat_u(), 0x94 => visitor.visit_f64x2_nearest(), 0x95 => visitor.visit_i16x8_mul(), 0x96 => visitor.visit_i16x8_min_s(), 0x97 => visitor.visit_i16x8_min_u(), 0x98 => visitor.visit_i16x8_max_s(), 0x99 => visitor.visit_i16x8_max_u(), 0x9b => visitor.visit_i16x8_avgr_u(), 0x9c => visitor.visit_i16x8_extmul_low_i8x16_s(), 0x9d => visitor.visit_i16x8_extmul_high_i8x16_s(), 0x9e => visitor.visit_i16x8_extmul_low_i8x16_u(), 0x9f => visitor.visit_i16x8_extmul_high_i8x16_u(), 0xa0 => visitor.visit_i32x4_abs(), 0xa1 => visitor.visit_i32x4_neg(), 0xa3 => visitor.visit_i32x4_all_true(), 0xa4 => visitor.visit_i32x4_bitmask(), 0xa7 => visitor.visit_i32x4_extend_low_i16x8_s(), 0xa8 => visitor.visit_i32x4_extend_high_i16x8_s(), 0xa9 => visitor.visit_i32x4_extend_low_i16x8_u(), 0xaa => visitor.visit_i32x4_extend_high_i16x8_u(), 0xab => visitor.visit_i32x4_shl(), 0xac => visitor.visit_i32x4_shr_s(), 0xad => visitor.visit_i32x4_shr_u(), 0xae => visitor.visit_i32x4_add(), 0xb1 => visitor.visit_i32x4_sub(), 0xb5 => visitor.visit_i32x4_mul(), 0xb6 => visitor.visit_i32x4_min_s(), 0xb7 => visitor.visit_i32x4_min_u(), 0xb8 => visitor.visit_i32x4_max_s(), 0xb9 => visitor.visit_i32x4_max_u(), 0xba => visitor.visit_i32x4_dot_i16x8_s(), 0xbc => visitor.visit_i32x4_extmul_low_i16x8_s(), 0xbd => visitor.visit_i32x4_extmul_high_i16x8_s(), 0xbe => visitor.visit_i32x4_extmul_low_i16x8_u(), 0xbf => visitor.visit_i32x4_extmul_high_i16x8_u(), 0xc0 => visitor.visit_i64x2_abs(), 0xc1 => visitor.visit_i64x2_neg(), 0xc3 => visitor.visit_i64x2_all_true(), 0xc4 => visitor.visit_i64x2_bitmask(), 0xc7 => visitor.visit_i64x2_extend_low_i32x4_s(), 0xc8 => visitor.visit_i64x2_extend_high_i32x4_s(), 0xc9 => visitor.visit_i64x2_extend_low_i32x4_u(), 0xca => visitor.visit_i64x2_extend_high_i32x4_u(), 0xcb => visitor.visit_i64x2_shl(), 0xcc => visitor.visit_i64x2_shr_s(), 0xcd => visitor.visit_i64x2_shr_u(), 0xce => visitor.visit_i64x2_add(), 0xd1 => visitor.visit_i64x2_sub(), 0xd5 => visitor.visit_i64x2_mul(), 0xd6 => visitor.visit_i64x2_eq(), 0xd7 => visitor.visit_i64x2_ne(), 0xd8 => visitor.visit_i64x2_lt_s(), 0xd9 => visitor.visit_i64x2_gt_s(), 0xda => visitor.visit_i64x2_le_s(), 0xdb => visitor.visit_i64x2_ge_s(), 0xdc => visitor.visit_i64x2_extmul_low_i32x4_s(), 0xdd => visitor.visit_i64x2_extmul_high_i32x4_s(), 0xde => visitor.visit_i64x2_extmul_low_i32x4_u(), 0xdf => visitor.visit_i64x2_extmul_high_i32x4_u(), 0xe0 => visitor.visit_f32x4_abs(), 0xe1 => visitor.visit_f32x4_neg(), 0xe3 => visitor.visit_f32x4_sqrt(), 0xe4 => visitor.visit_f32x4_add(), 0xe5 => visitor.visit_f32x4_sub(), 0xe6 => visitor.visit_f32x4_mul(), 0xe7 => visitor.visit_f32x4_div(), 0xe8 => visitor.visit_f32x4_min(), 0xe9 => visitor.visit_f32x4_max(), 0xea => visitor.visit_f32x4_pmin(), 0xeb => visitor.visit_f32x4_pmax(), 0xec => visitor.visit_f64x2_abs(), 0xed => visitor.visit_f64x2_neg(), 0xef => visitor.visit_f64x2_sqrt(), 0xf0 => visitor.visit_f64x2_add(), 0xf1 => visitor.visit_f64x2_sub(), 0xf2 => visitor.visit_f64x2_mul(), 0xf3 => visitor.visit_f64x2_div(), 0xf4 => visitor.visit_f64x2_min(), 0xf5 => visitor.visit_f64x2_max(), 0xf6 => visitor.visit_f64x2_pmin(), 0xf7 => visitor.visit_f64x2_pmax(), 0xf8 => visitor.visit_i32x4_trunc_sat_f32x4_s(), 0xf9 => visitor.visit_i32x4_trunc_sat_f32x4_u(), 0xfa => visitor.visit_f32x4_convert_i32x4_s(), 0xfb => visitor.visit_f32x4_convert_i32x4_u(), 0xfc => visitor.visit_i32x4_trunc_sat_f64x2_s_zero(), 0xfd => visitor.visit_i32x4_trunc_sat_f64x2_u_zero(), 0xfe => visitor.visit_f64x2_convert_low_i32x4_s(), 0xff => visitor.visit_f64x2_convert_low_i32x4_u(), 0x100 => visitor.visit_i8x16_relaxed_swizzle(), 0x101 => visitor.visit_i32x4_relaxed_trunc_f32x4_s(), 0x102 => visitor.visit_i32x4_relaxed_trunc_f32x4_u(), 0x103 => visitor.visit_i32x4_relaxed_trunc_f64x2_s_zero(), 0x104 => visitor.visit_i32x4_relaxed_trunc_f64x2_u_zero(), 0x105 => visitor.visit_f32x4_relaxed_madd(), 0x106 => visitor.visit_f32x4_relaxed_nmadd(), 0x107 => visitor.visit_f64x2_relaxed_madd(), 0x108 => visitor.visit_f64x2_relaxed_nmadd(), 0x109 => visitor.visit_i8x16_relaxed_laneselect(), 0x10a => visitor.visit_i16x8_relaxed_laneselect(), 0x10b => visitor.visit_i32x4_relaxed_laneselect(), 0x10c => visitor.visit_i64x2_relaxed_laneselect(), 0x10d => visitor.visit_f32x4_relaxed_min(), 0x10e => visitor.visit_f32x4_relaxed_max(), 0x10f => visitor.visit_f64x2_relaxed_min(), 0x110 => visitor.visit_f64x2_relaxed_max(), 0x111 => visitor.visit_i16x8_relaxed_q15mulr_s(), 0x112 => visitor.visit_i16x8_relaxed_dot_i8x16_i7x16_s(), 0x113 => visitor.visit_i32x4_relaxed_dot_i8x16_i7x16_add_s(), _ => bail!(pos, "unknown 0xfd subopcode: 0x{code:x}"), }) } fn visit_0xfe_operator( &mut self, pos: usize, visitor: &mut T, ) -> Result<>::Output> where T: VisitOperator<'a>, { let code = self.read_var_u32()?; Ok(match code { 0x00 => visitor.visit_memory_atomic_notify(self.read_memarg(2)?), 0x01 => visitor.visit_memory_atomic_wait32(self.read_memarg(2)?), 0x02 => visitor.visit_memory_atomic_wait64(self.read_memarg(3)?), 0x03 => { if self.read_u8()? != 0 { bail!(pos, "nonzero byte after `atomic.fence`"); } visitor.visit_atomic_fence() } 0x10 => visitor.visit_i32_atomic_load(self.read_memarg(2)?), 0x11 => visitor.visit_i64_atomic_load(self.read_memarg(3)?), 0x12 => visitor.visit_i32_atomic_load8_u(self.read_memarg(0)?), 0x13 => visitor.visit_i32_atomic_load16_u(self.read_memarg(1)?), 0x14 => visitor.visit_i64_atomic_load8_u(self.read_memarg(0)?), 0x15 => visitor.visit_i64_atomic_load16_u(self.read_memarg(1)?), 0x16 => visitor.visit_i64_atomic_load32_u(self.read_memarg(2)?), 0x17 => visitor.visit_i32_atomic_store(self.read_memarg(2)?), 0x18 => visitor.visit_i64_atomic_store(self.read_memarg(3)?), 0x19 => visitor.visit_i32_atomic_store8(self.read_memarg(0)?), 0x1a => visitor.visit_i32_atomic_store16(self.read_memarg(1)?), 0x1b => visitor.visit_i64_atomic_store8(self.read_memarg(0)?), 0x1c => visitor.visit_i64_atomic_store16(self.read_memarg(1)?), 0x1d => visitor.visit_i64_atomic_store32(self.read_memarg(2)?), 0x1e => visitor.visit_i32_atomic_rmw_add(self.read_memarg(2)?), 0x1f => visitor.visit_i64_atomic_rmw_add(self.read_memarg(3)?), 0x20 => visitor.visit_i32_atomic_rmw8_add_u(self.read_memarg(0)?), 0x21 => visitor.visit_i32_atomic_rmw16_add_u(self.read_memarg(1)?), 0x22 => visitor.visit_i64_atomic_rmw8_add_u(self.read_memarg(0)?), 0x23 => visitor.visit_i64_atomic_rmw16_add_u(self.read_memarg(1)?), 0x24 => visitor.visit_i64_atomic_rmw32_add_u(self.read_memarg(2)?), 0x25 => visitor.visit_i32_atomic_rmw_sub(self.read_memarg(2)?), 0x26 => visitor.visit_i64_atomic_rmw_sub(self.read_memarg(3)?), 0x27 => visitor.visit_i32_atomic_rmw8_sub_u(self.read_memarg(0)?), 0x28 => visitor.visit_i32_atomic_rmw16_sub_u(self.read_memarg(1)?), 0x29 => visitor.visit_i64_atomic_rmw8_sub_u(self.read_memarg(0)?), 0x2a => visitor.visit_i64_atomic_rmw16_sub_u(self.read_memarg(1)?), 0x2b => visitor.visit_i64_atomic_rmw32_sub_u(self.read_memarg(2)?), 0x2c => visitor.visit_i32_atomic_rmw_and(self.read_memarg(2)?), 0x2d => visitor.visit_i64_atomic_rmw_and(self.read_memarg(3)?), 0x2e => visitor.visit_i32_atomic_rmw8_and_u(self.read_memarg(0)?), 0x2f => visitor.visit_i32_atomic_rmw16_and_u(self.read_memarg(1)?), 0x30 => visitor.visit_i64_atomic_rmw8_and_u(self.read_memarg(0)?), 0x31 => visitor.visit_i64_atomic_rmw16_and_u(self.read_memarg(1)?), 0x32 => visitor.visit_i64_atomic_rmw32_and_u(self.read_memarg(2)?), 0x33 => visitor.visit_i32_atomic_rmw_or(self.read_memarg(2)?), 0x34 => visitor.visit_i64_atomic_rmw_or(self.read_memarg(3)?), 0x35 => visitor.visit_i32_atomic_rmw8_or_u(self.read_memarg(0)?), 0x36 => visitor.visit_i32_atomic_rmw16_or_u(self.read_memarg(1)?), 0x37 => visitor.visit_i64_atomic_rmw8_or_u(self.read_memarg(0)?), 0x38 => visitor.visit_i64_atomic_rmw16_or_u(self.read_memarg(1)?), 0x39 => visitor.visit_i64_atomic_rmw32_or_u(self.read_memarg(2)?), 0x3a => visitor.visit_i32_atomic_rmw_xor(self.read_memarg(2)?), 0x3b => visitor.visit_i64_atomic_rmw_xor(self.read_memarg(3)?), 0x3c => visitor.visit_i32_atomic_rmw8_xor_u(self.read_memarg(0)?), 0x3d => visitor.visit_i32_atomic_rmw16_xor_u(self.read_memarg(1)?), 0x3e => visitor.visit_i64_atomic_rmw8_xor_u(self.read_memarg(0)?), 0x3f => visitor.visit_i64_atomic_rmw16_xor_u(self.read_memarg(1)?), 0x40 => visitor.visit_i64_atomic_rmw32_xor_u(self.read_memarg(2)?), 0x41 => visitor.visit_i32_atomic_rmw_xchg(self.read_memarg(2)?), 0x42 => visitor.visit_i64_atomic_rmw_xchg(self.read_memarg(3)?), 0x43 => visitor.visit_i32_atomic_rmw8_xchg_u(self.read_memarg(0)?), 0x44 => visitor.visit_i32_atomic_rmw16_xchg_u(self.read_memarg(1)?), 0x45 => visitor.visit_i64_atomic_rmw8_xchg_u(self.read_memarg(0)?), 0x46 => visitor.visit_i64_atomic_rmw16_xchg_u(self.read_memarg(1)?), 0x47 => visitor.visit_i64_atomic_rmw32_xchg_u(self.read_memarg(2)?), 0x48 => visitor.visit_i32_atomic_rmw_cmpxchg(self.read_memarg(2)?), 0x49 => visitor.visit_i64_atomic_rmw_cmpxchg(self.read_memarg(3)?), 0x4a => visitor.visit_i32_atomic_rmw8_cmpxchg_u(self.read_memarg(0)?), 0x4b => visitor.visit_i32_atomic_rmw16_cmpxchg_u(self.read_memarg(1)?), 0x4c => visitor.visit_i64_atomic_rmw8_cmpxchg_u(self.read_memarg(0)?), 0x4d => visitor.visit_i64_atomic_rmw16_cmpxchg_u(self.read_memarg(1)?), 0x4e => visitor.visit_i64_atomic_rmw32_cmpxchg_u(self.read_memarg(2)?), // Decode shared-everything-threads proposal. 0x4f => visitor.visit_global_atomic_get(self.read_ordering()?, self.read_var_u32()?), 0x50 => visitor.visit_global_atomic_set(self.read_ordering()?, self.read_var_u32()?), 0x51 => { visitor.visit_global_atomic_rmw_add(self.read_ordering()?, self.read_var_u32()?) } 0x52 => { visitor.visit_global_atomic_rmw_sub(self.read_ordering()?, self.read_var_u32()?) } 0x53 => { visitor.visit_global_atomic_rmw_and(self.read_ordering()?, self.read_var_u32()?) } 0x54 => visitor.visit_global_atomic_rmw_or(self.read_ordering()?, self.read_var_u32()?), 0x55 => { visitor.visit_global_atomic_rmw_xor(self.read_ordering()?, self.read_var_u32()?) } 0x56 => { visitor.visit_global_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) } 0x57 => { visitor.visit_global_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) } 0x58 => visitor.visit_table_atomic_get(self.read_ordering()?, self.read_var_u32()?), 0x59 => visitor.visit_table_atomic_set(self.read_ordering()?, self.read_var_u32()?), 0x5a => { visitor.visit_table_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) } 0x5b => { visitor.visit_table_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) } 0x5c => visitor.visit_struct_atomic_get( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x5d => visitor.visit_struct_atomic_get_s( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x5e => visitor.visit_struct_atomic_get_u( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x5f => visitor.visit_struct_atomic_set( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x60 => visitor.visit_struct_atomic_rmw_add( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x61 => visitor.visit_struct_atomic_rmw_sub( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x62 => visitor.visit_struct_atomic_rmw_and( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x63 => visitor.visit_struct_atomic_rmw_or( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x64 => visitor.visit_struct_atomic_rmw_xor( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x65 => visitor.visit_struct_atomic_rmw_xchg( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x66 => visitor.visit_struct_atomic_rmw_cmpxchg( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x67 => visitor.visit_array_atomic_get(self.read_ordering()?, self.read_var_u32()?), 0x68 => visitor.visit_array_atomic_get_s(self.read_ordering()?, self.read_var_u32()?), 0x69 => visitor.visit_array_atomic_get_u(self.read_ordering()?, self.read_var_u32()?), 0x6a => visitor.visit_array_atomic_set(self.read_ordering()?, self.read_var_u32()?), 0x6b => visitor.visit_array_atomic_rmw_add(self.read_ordering()?, self.read_var_u32()?), 0x6c => visitor.visit_array_atomic_rmw_sub(self.read_ordering()?, self.read_var_u32()?), 0x6d => visitor.visit_array_atomic_rmw_and(self.read_ordering()?, self.read_var_u32()?), 0x6e => visitor.visit_array_atomic_rmw_or(self.read_ordering()?, self.read_var_u32()?), 0x6f => visitor.visit_array_atomic_rmw_xor(self.read_ordering()?, self.read_var_u32()?), 0x70 => { visitor.visit_array_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) } 0x71 => { visitor.visit_array_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) } 0x72 => visitor.visit_ref_i31_shared(), _ => bail!(pos, "unknown 0xfe subopcode: 0x{code:x}"), }) } /// Reads the next available `Operator`. /// /// # Errors /// /// If `BinaryReader` has less bytes remaining than required to parse /// the `Operator`. pub fn read_operator(&mut self) -> Result> { self.visit_operator(&mut OperatorFactory::new()) } /// Returns whether there is an `end` opcode followed by eof remaining in /// this reader. pub fn is_end_then_eof(&self) -> bool { self.remaining_buffer() == &[0x0b] } fn read_lane_index(&mut self, max: u8) -> Result { let index = self.read_u8()?; if index >= max { return Err(BinaryReaderError::new( "invalid lane index", self.original_position() - 1, )); } Ok(index) } fn read_v128(&mut self) -> Result { let mut bytes = [0; 16]; bytes.clone_from_slice(self.read_bytes(16)?); Ok(V128(bytes)) } pub(crate) fn read_header_version(&mut self) -> Result { let magic_number = self.read_bytes(4)?; if magic_number != WASM_MAGIC_NUMBER { return Err(BinaryReaderError::new( format!("magic header not detected: bad magic number - expected={WASM_MAGIC_NUMBER:#x?} actual={magic_number:#x?}"), self.original_position() - 4, )); } self.read_u32() } pub(crate) fn skip_const_expr(&mut self) -> Result<()> { // TODO add skip_operator() method and/or validate ConstExpr operators. loop { if let Operator::End = self.read_operator()? { return Ok(()); } } } fn read_memory_index_or_zero_if_not_multi_memory(&mut self) -> Result { if self.multi_memory() { self.read_var_u32() } else { // Before bulk memory this byte was required to be a single zero // byte, not a LEB-encoded zero, so require a precise zero byte. match self.read_u8()? { 0 => Ok(0), _ => bail!(self.original_position() - 1, "zero byte expected"), } } } fn read_table_index_or_zero_if_not_reference_types(&mut self) -> Result { if self.reference_types() { self.read_var_u32() } else { // Before reference types this byte was required to be a single zero // byte, not a LEB-encoded zero, so require a precise zero byte. match self.read_u8()? { 0 => Ok(0), _ => bail!(self.original_position() - 1, "zero byte expected"), } } } } // See documentation on `BinaryReader::features` for more on what's going on // here. macro_rules! define_feature_accessor { ($feature:ident = $default:expr) => { impl BinaryReader<'_> { #[inline] #[allow(dead_code)] pub(crate) fn $feature(&self) -> bool { #[cfg(feature = "features")] { self.features.$feature() } #[cfg(not(feature = "features"))] { true } } } }; } super::features::foreach_wasm_feature!(define_feature_accessor); impl<'a> BrTable<'a> { /// Returns the number of `br_table` entries, not including the default /// label pub fn len(&self) -> u32 { self.cnt } /// Returns whether `BrTable` doesn't have any labels apart from the default one. pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns the default target of this `br_table` instruction. pub fn default(&self) -> u32 { self.default } /// Returns the list of targets that this `br_table` instruction will be /// jumping to. /// /// This method will return an iterator which parses each target of this /// `br_table` except the default target. The returned iterator will /// yield `self.len()` elements. /// /// # Examples /// /// ```rust /// use wasmparser::{BinaryReader, Operator}; /// /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00]; /// let mut reader = BinaryReader::new(&buf, 0); /// let op = reader.read_operator().unwrap(); /// if let Operator::BrTable { targets } = op { /// let targets = targets.targets().collect::, _>>().unwrap(); /// assert_eq!(targets, [1, 2]); /// } /// ``` pub fn targets(&self) -> BrTableTargets { BrTableTargets { reader: self.reader.clone(), remaining: self.cnt, } } } /// An iterator over the targets of a [`BrTable`]. /// /// # Note /// /// This iterator parses each target of the underlying `br_table` /// except for the default target. /// The iterator will yield exactly as many targets as the `br_table` has. pub struct BrTableTargets<'a> { reader: crate::BinaryReader<'a>, remaining: u32, } impl<'a> Iterator for BrTableTargets<'a> { type Item = Result; fn size_hint(&self) -> (usize, Option) { let remaining = usize::try_from(self.remaining).unwrap_or_else(|error| { panic!("could not convert remaining `u32` into `usize`: {}", error) }); (remaining, Some(remaining)) } fn next(&mut self) -> Option { if self.remaining == 0 { if !self.reader.eof() { return Some(Err(BinaryReaderError::new( "trailing data in br_table", self.reader.original_position(), ))); } return None; } self.remaining -= 1; Some(self.reader.read_var_u32()) } } impl fmt::Debug for BrTable<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("BrTable"); f.field("count", &self.cnt); f.field("default", &self.default); match self.targets().collect::>>() { Ok(targets) => { f.field("targets", &targets); } Err(_) => { f.field("reader", &self.reader); } } f.finish() } } /// A factory to construct [`Operator`] instances via the [`VisitOperator`] trait. struct OperatorFactory<'a> { marker: core::marker::PhantomData &'a ()>, } impl<'a> OperatorFactory<'a> { /// Creates a new [`OperatorFactory`]. fn new() -> Self { Self { marker: core::marker::PhantomData, } } } macro_rules! define_visit_operator { ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( fn $visit(&mut self $($(,$arg: $argty)*)?) -> Operator<'a> { Operator::$op $({ $($arg),* })? } )* } } impl<'a> VisitOperator<'a> for OperatorFactory<'a> { type Output = Operator<'a>; for_each_operator!(define_visit_operator); } /// Iterator returned from [`BinaryReader::read_iter`]. pub struct BinaryReaderIter<'a, 'me, T: FromReader<'a>> { remaining: usize, pub(crate) reader: &'me mut BinaryReader<'a>, _marker: marker::PhantomData, } impl<'a, T> Iterator for BinaryReaderIter<'a, '_, T> where T: FromReader<'a>, { type Item = Result; fn next(&mut self) -> Option> { if self.remaining == 0 { None } else { let ret = self.reader.read::(); if ret.is_err() { self.remaining = 0; } else { self.remaining -= 1; } Some(ret) } } fn size_hint(&self) -> (usize, Option) { (self.remaining, Some(self.remaining)) } } impl<'a, T> Drop for BinaryReaderIter<'a, '_, T> where T: FromReader<'a>, { fn drop(&mut self) { while self.next().is_some() { // ... } } } wasmparser-0.217.0/src/collections/hash.rs000064400000000000000000000055221046102023000165610ustar 00000000000000//! Utilities for hashmap initialization based on random sources. use core::hash::{BuildHasher, Hasher}; /// Wasmparser's hashing state stored per-map. /// /// This is DoS-resistant when the `std` feature is activated and still somewhat /// resistant when it's not active but not as secure. #[derive(Clone, Debug)] pub struct RandomState(RandomStateImpl); impl Default for RandomState { #[inline] fn default() -> RandomState { RandomState(RandomStateImpl::default()) } } impl BuildHasher for RandomState { type Hasher = RandomStateHasher; #[inline] fn build_hasher(&self) -> RandomStateHasher { RandomStateHasher(self.0.build_hasher()) } } /// Wasmparser's hasher type used with [`RandomState`]. pub struct RandomStateHasher(::Hasher); impl Hasher for RandomStateHasher { #[inline] fn finish(&self) -> u64 { self.0.finish() } #[inline] fn write(&mut self, bytes: &[u8]) { self.0.write(bytes) } #[inline] fn write_u8(&mut self, i: u8) { self.0.write_u8(i) } #[inline] fn write_u16(&mut self, i: u16) { self.0.write_u16(i) } #[inline] fn write_u32(&mut self, i: u32) { self.0.write_u32(i) } #[inline] fn write_u64(&mut self, i: u64) { self.0.write_u64(i) } #[inline] fn write_u128(&mut self, i: u128) { self.0.write_u128(i) } #[inline] fn write_usize(&mut self, i: usize) { self.0.write_usize(i) } #[inline] fn write_i8(&mut self, i: i8) { self.0.write_i8(i) } #[inline] fn write_i16(&mut self, i: i16) { self.0.write_i16(i) } #[inline] fn write_i32(&mut self, i: i32) { self.0.write_i32(i) } #[inline] fn write_i64(&mut self, i: i64) { self.0.write_i64(i) } #[inline] fn write_i128(&mut self, i: i128) { self.0.write_i128(i) } #[inline] fn write_isize(&mut self, i: isize) { self.0.write_isize(i) } } // When the `std` feature is active reuse the standard library's implementation // of hash state and hasher. #[cfg(feature = "std")] use std::collections::hash_map::RandomState as RandomStateImpl; // When the `std` feature is NOT active then rely on `ahash::RandomState`. That // relies on ASLR by default for randomness. #[derive(Clone, Debug)] #[cfg(not(feature = "std"))] struct RandomStateImpl { state: ahash::RandomState, } #[cfg(not(feature = "std"))] impl Default for RandomStateImpl { fn default() -> RandomStateImpl { RandomStateImpl { state: ahash::RandomState::new(), } } } #[cfg(not(feature = "std"))] impl BuildHasher for RandomStateImpl { type Hasher = ahash::AHasher; #[inline] fn build_hasher(&self) -> ahash::AHasher { self.state.build_hasher() } } wasmparser-0.217.0/src/collections/index_map/detail.rs000064400000000000000000000757121046102023000210540ustar 00000000000000//! An ordered map based on a B-Tree that keeps insertion order of elements. #[cfg(not(feature = "no-hash-maps"))] mod impls { use crate::collections::hash; use indexmap::IndexMap; pub type IndexMapImpl = IndexMap; pub type EntryImpl<'a, K, V> = indexmap::map::Entry<'a, K, V>; pub type OccupiedEntryImpl<'a, K, V> = indexmap::map::OccupiedEntry<'a, K, V>; pub type VacantEntryImpl<'a, K, V> = indexmap::map::VacantEntry<'a, K, V>; pub type IterImpl<'a, K, V> = indexmap::map::Iter<'a, K, V>; pub type IterMutImpl<'a, K, V> = indexmap::map::IterMut<'a, K, V>; pub type IntoIterImpl = indexmap::map::IntoIter; pub type KeysImpl<'a, K, V> = indexmap::map::Keys<'a, K, V>; pub type ValuesImpl<'a, K, V> = indexmap::map::Values<'a, K, V>; pub type ValuesMutImpl<'a, K, V> = indexmap::map::ValuesMut<'a, K, V>; } #[cfg(feature = "no-hash-maps")] mod impls { pub type IndexMapImpl = super::IndexMap; pub type EntryImpl<'a, K, V> = super::Entry<'a, K, V>; pub type OccupiedEntryImpl<'a, K, V> = super::OccupiedEntry<'a, K, V>; pub type VacantEntryImpl<'a, K, V> = super::VacantEntry<'a, K, V>; pub type IterImpl<'a, K, V> = super::Iter<'a, K, V>; pub type IterMutImpl<'a, K, V> = super::IterMut<'a, K, V>; pub type IntoIterImpl = super::IntoIter; pub type KeysImpl<'a, K, V> = super::Keys<'a, K, V>; pub type ValuesImpl<'a, K, V> = super::Values<'a, K, V>; pub type ValuesMutImpl<'a, K, V> = super::ValuesMut<'a, K, V>; } pub use self::impls::*; use alloc::collections::{btree_map, BTreeMap}; use alloc::vec::IntoIter as VecIntoIter; use alloc::vec::Vec; use core::borrow::Borrow; use core::fmt; use core::iter::FusedIterator; use core::mem::replace; use core::ops::{Index, IndexMut}; use core::slice::Iter as SliceIter; use core::slice::IterMut as SliceIterMut; /// A slot index referencing a slot in an [`IndexMap`]. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] struct SlotIndex(usize); impl SlotIndex { /// Returns the raw `usize` index of the [`SlotIndex`]. pub fn index(self) -> usize { self.0 } } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] struct Slot { /// The key of the [`Slot`]. key: K, /// The value of the [`Slot`]. value: V, } impl Slot { /// Creates a new [`Slot`] from the given `key` and `value`. pub fn new(key: K, value: V) -> Self { Self { key, value } } /// Returns the [`Slot`] as a pair of references to its `key` and `value`. pub fn as_pair(&self) -> (&K, &V) { (&self.key, &self.value) } /// Returns the [`Slot`] as a pair of references to its `key` and `value`. pub fn as_pair_mut(&mut self) -> (&K, &mut V) { (&self.key, &mut self.value) } /// Converts the [`Slot`] into a pair of its `key` and `value`. pub fn into_pair(self) -> (K, V) { (self.key, self.value) } /// Returns a shared reference to the key of the [`Slot`]. pub fn key(&self) -> &K { &self.key } /// Returns a shared reference to the value of the [`Slot`]. pub fn value(&self) -> &V { &self.value } /// Returns an exclusive reference to the value of the [`Slot`]. pub fn value_mut(&mut self) -> &mut V { &mut self.value } } /// A b-tree map where the iteration order of the key-value /// pairs is independent of the ordering of the keys. /// /// The interface is closely compatible with the [`indexmap` crate] /// and a subset of the features that is relevant for the /// [`wasmparser-nostd` crate]. /// /// # Differences to original `IndexMap` /// /// Since the goal of this crate was to maintain a simple /// `no_std` compatible fork of the [`indexmap` crate] there are some /// downsides and differences. /// /// - Some operations such as `IndexMap::insert` now require `K: Clone`. /// - It is to be expected that this fork performs worse than the original /// [`indexmap` crate] implementation. /// - The implementation is based on `BTreeMap` internally instead of /// `HashMap` which has the effect that methods no longer require `K: Hash` /// but `K: Ord` instead. /// /// [`indexmap` crate]: https://crates.io/crates/indexmap /// [`wasmparser-nostd` crate]: https://crates.io/crates/wasmparser-nostd #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct IndexMap { /// A mapping from keys to slot indices. key2slot: BTreeMap, /// A vector holding all slots of key value pairs. slots: Vec>, } impl Default for IndexMap { fn default() -> Self { Self::new() } } impl IndexMap { /// Makes a new, empty [`IndexMap`]. /// /// Does not allocate anything on its own. pub fn new() -> Self { Self { key2slot: BTreeMap::new(), slots: Vec::new(), } } /// Constructs a new, empty [`IndexMap`] with at least the specified capacity. /// /// Does not allocate if `capacity` is zero. pub fn with_capacity(capacity: usize) -> Self { Self { key2slot: BTreeMap::new(), slots: Vec::with_capacity(capacity), } } /// Reserve capacity for at least `additional` more key-value pairs. pub fn reserve(&mut self, additional: usize) { self.slots.reserve(additional); } /// Returns the number of elements in the map. pub fn len(&self) -> usize { self.slots.len() } /// Returns `true` if the map contains no elements. pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns true if the map contains a value for the specified key. /// /// The key may be any borrowed form of the map’s key type, /// but the ordering on the borrowed form must match the ordering on the key type. pub fn contains_key(&self, key: &Q) -> bool where K: Borrow + Ord, Q: Ord, { self.key2slot.contains_key(key) } /// Inserts a key-value pair into the map. /// /// If the map did not have this key present, `None` is returned. /// /// If the map did have this key present, the value is updated, and the old /// value is returned. The key is not updated, though; this matters for /// types that can be `==` without being identical. pub fn insert(&mut self, key: K, value: V) -> Option where K: Ord + Clone, { self.insert_full(key, value).1 } /// Inserts a key-value pair into the map. /// /// Returns the unique index to the key-value pair alongside the previous value. /// /// If the map did not have this key present, `None` is returned. /// /// If the map did have this key present, the value is updated, and the old /// value is returned. The key is not updated, though; this matters for /// types that can be `==` without being identical. pub fn insert_full(&mut self, key: K, value: V) -> (usize, Option) where K: Ord + Clone, { match self.key2slot.entry(key.clone()) { btree_map::Entry::Vacant(entry) => { let index = self.slots.len(); entry.insert(SlotIndex(index)); self.slots.push(Slot::new(key, value)); (index, None) } btree_map::Entry::Occupied(entry) => { let index = entry.get().index(); let new_slot = Slot::new(key, value); let old_slot = replace(&mut self.slots[index], new_slot); (index, Some(old_slot.value)) } } } /// Remove the key-value pair equivalent to `key` and return it and /// the index it had. /// /// Like [`Vec::swap_remove`], the pair is removed by swapping it with the /// last element of the map and popping it off. **This perturbs /// the position of what used to be the last element!** /// /// Return `None` if `key` is not in map. pub fn swap_remove(&mut self, key: &Q) -> Option where K: Borrow + Ord, Q: ?Sized + Ord, { self.swap_remove_full(key) .map(|(_index, _key, value)| value) } /// Remove and return the key-value pair equivalent to `key`. /// /// Like [`Vec::swap_remove`], the pair is removed by swapping it with the /// last element of the map and popping it off. **This perturbs /// the position of what used to be the last element!** /// /// Return `None` if `key` is not in map. /// /// [`Vec::swap_remove`]: alloc::vec::Vec::swap_remove pub fn swap_remove_entry(&mut self, key: &Q) -> Option<(K, V)> where K: Borrow + Ord, Q: ?Sized + Ord, { self.swap_remove_full(key) .map(|(_index, key, value)| (key, value)) } /// Remove the key-value pair equivalent to `key` and return it and /// the index it had. /// /// Like [`Vec::swap_remove`], the pair is removed by swapping it with the /// last element of the map and popping it off. **This perturbs /// the position of what used to be the last element!** /// /// Return `None` if `key` is not in map. pub fn swap_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> where K: Borrow + Ord, Q: ?Sized + Ord, { let index = self.key2slot.remove(key)?.0; let removed = self.slots.swap_remove(index); if index != self.len() { // If the index was referring the last element // `swap_remove` would not swap any other element // thus adjustments are only needed if this was not the case. let swapped = self.slots[index].key.borrow(); let swapped_index = self .key2slot .get_mut(swapped) .expect("the swapped entry's key must be present"); *swapped_index = SlotIndex(index); } Some((index, removed.key, removed.value)) } /// Gets the given key’s corresponding entry in the map for in-place manipulation. pub fn entry(&mut self, key: K) -> Entry where K: Ord + Clone, { match self.key2slot.entry(key) { btree_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { vacant: entry, slots: &mut self.slots, }), btree_map::Entry::Occupied(entry) => Entry::Occupied(OccupiedEntry { occupied: entry, slots: &mut self.slots, }), } } /// Returns a reference to the value corresponding to the key. /// /// The key may be any borrowed form of the map’s key type, /// but the ordering on the borrowed form must match the ordering on the key type. pub fn get(&self, key: &Q) -> Option<&V> where K: Borrow + Ord, Q: ?Sized + Ord, { self.key2slot .get(key) .map(|slot| &self.slots[slot.index()].value) } /// Returns a mutable reference to the value corresponding to the key. /// /// The key may be any borrowed form of the map’s key type, /// but the ordering on the borrowed form must match the ordering on the key type. pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where K: Borrow + Ord, Q: Ord, { self.key2slot .get(key) .map(|slot| &mut self.slots[slot.index()].value) } /// Returns the key-value pair corresponding to the supplied key. /// /// The supplied key may be any borrowed form of the map's key type, /// but the ordering on the borrowed form *must* match the ordering /// on the key type. pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> where K: Borrow + Ord, Q: Ord, { self.key2slot .get_key_value(key) .map(|(key, slot)| (key, &self.slots[slot.index()].value)) } /// Returns the key-value pair corresponding to the supplied key /// as well as the unique index of the returned key-value pair. /// /// The supplied key may be any borrowed form of the map's key type, /// but the ordering on the borrowed form *must* match the ordering /// on the key type. pub fn get_full(&self, key: &Q) -> Option<(usize, &K, &V)> where K: Borrow + Ord, Q: Ord, { self.key2slot.get_key_value(key).map(|(key, slot)| { let index = slot.index(); let value = &self.slots[index].value; (index, key, value) }) } /// Returns the unique index corresponding to the supplied key. /// /// The supplied key may be any borrowed form of the map's key type, /// but the ordering on the borrowed form *must* match the ordering /// on the key type. pub fn get_index_of(&self, key: &Q) -> Option where K: Borrow + Ord, Q: Ord, { self.key2slot.get(key).copied().map(SlotIndex::index) } /// Returns a shared reference to the key-value pair at the given index. pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { self.slots.get(index).map(Slot::as_pair) } /// Returns an exclusive reference to the key-value pair at the given index. pub fn get_index_mut(&mut self, index: usize) -> Option<(&K, &mut V)> { self.slots.get_mut(index).map(Slot::as_pair_mut) } /// Gets an iterator over the entries of the map, sorted by key. pub fn iter(&self) -> Iter { Iter { iter: self.slots.iter(), } } /// Gets a mutable iterator over the entries of the map, sorted by key. pub fn iter_mut(&mut self) -> IterMut { IterMut { iter: self.slots.iter_mut(), } } /// Gets an iterator over the values of the map, in order by key. pub fn keys(&self) -> Keys { Keys { iter: self.slots.iter(), } } /// Gets an iterator over the values of the map, in order by key. pub fn values(&self) -> Values { Values { iter: self.slots.iter(), } } /// Gets a mutable iterator over the values of the map, in order by key. pub fn values_mut(&mut self) -> ValuesMut { ValuesMut { iter: self.slots.iter_mut(), } } /// Clears the map, removing all elements. pub fn clear(&mut self) { self.key2slot.clear(); self.slots.clear(); } } impl<'a, K, Q, V> Index<&'a Q> for IndexMap where K: Borrow + Ord, Q: ?Sized + Ord, { type Output = V; fn index(&self, key: &'a Q) -> &Self::Output { self.get(key).expect("no entry found for key") } } impl Index for IndexMap { type Output = V; fn index(&self, index: usize) -> &Self::Output { let (_key, value) = self .get_index(index) .expect("IndexMap: index out of bounds"); value } } impl IndexMut for IndexMap { fn index_mut(&mut self, index: usize) -> &mut Self::Output { let (_key, value) = self .get_index_mut(index) .expect("IndexMap: index out of bounds"); value } } impl<'a, K, V> Extend<(&'a K, &'a V)> for IndexMap where K: Ord + Copy, V: Copy, { fn extend(&mut self, iter: T) where T: IntoIterator, { self.extend(iter.into_iter().map(|(key, value)| (*key, *value))) } } impl Extend<(K, V)> for IndexMap where K: Ord + Clone, { fn extend(&mut self, iter: T) where T: IntoIterator, { iter.into_iter().for_each(move |(k, v)| { self.insert(k, v); }); } } impl FromIterator<(K, V)> for IndexMap where K: Ord + Clone, { fn from_iter(iter: T) -> Self where T: IntoIterator, { let mut map = IndexMap::new(); map.extend(iter); map } } impl From<[(K, V); N]> for IndexMap where K: Ord + Clone, { fn from(items: [(K, V); N]) -> Self { items.into_iter().collect() } } impl<'a, K, V> IntoIterator for &'a IndexMap { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a, K, V> IntoIterator for &'a mut IndexMap { type Item = (&'a K, &'a mut V); type IntoIter = IterMut<'a, K, V>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl IntoIterator for IndexMap { type Item = (K, V); type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter { iter: self.slots.into_iter(), } } } /// An iterator over the entries of an [`IndexMap`]. /// /// This `struct` is created by the [`iter`] method on [`IndexMap`]. See its /// documentation for more. /// /// [`iter`]: IndexMap::iter #[derive(Debug, Clone)] pub struct Iter<'a, K, V> { iter: SliceIter<'a, Slot>, } impl<'a, K, V> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn count(self) -> usize { self.iter.count() } fn next(&mut self) -> Option { self.iter.next().map(Slot::as_pair) } } impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> { fn next_back(&mut self) -> Option { self.iter.next_back().map(Slot::as_pair) } } impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { fn len(&self) -> usize { self.iter.len() } } impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} /// A mutable iterator over the entries of an [`IndexMap`]. /// /// This `struct` is created by the [`iter_mut`] method on [`IndexMap`]. See its /// documentation for more. /// /// [`iter_mut`]: IndexMap::iter_mut #[derive(Debug)] pub struct IterMut<'a, K, V> { iter: SliceIterMut<'a, Slot>, } impl<'a, K, V> Iterator for IterMut<'a, K, V> { type Item = (&'a K, &'a mut V); fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn count(self) -> usize { self.iter.count() } fn next(&mut self) -> Option { self.iter.next().map(Slot::as_pair_mut) } } impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> { fn next_back(&mut self) -> Option { self.iter.next_back().map(Slot::as_pair_mut) } } impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { fn len(&self) -> usize { self.iter.len() } } impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} /// An owning iterator over the entries of a [`IndexMap`]. /// /// This `struct` is created by the [`into_iter`] method on [`IndexMap`] /// (provided by the [`IntoIterator`] trait). See its documentation for more. /// /// [`into_iter`]: IntoIterator::into_iter /// [`IntoIterator`]: core::iter::IntoIterator #[derive(Debug)] pub struct IntoIter { iter: VecIntoIter>, } impl Iterator for IntoIter { type Item = (K, V); fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn count(self) -> usize { self.iter.count() } fn next(&mut self) -> Option { self.iter.next().map(Slot::into_pair) } } impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { self.iter.next_back().map(Slot::into_pair) } } impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { self.iter.len() } } impl FusedIterator for IntoIter {} /// An iterator over the keys of an [`IndexMap`]. /// /// This `struct` is created by the [`keys`] method on [`IndexMap`]. See its /// documentation for more. /// /// [`keys`]: IndexMap::keys #[derive(Debug, Clone)] pub struct Keys<'a, K, V> { iter: SliceIter<'a, Slot>, } impl<'a, K, V> Iterator for Keys<'a, K, V> { type Item = &'a K; fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn count(self) -> usize { self.iter.count() } fn next(&mut self) -> Option { self.iter.next().map(Slot::key) } } impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> { fn next_back(&mut self) -> Option { self.iter.next_back().map(Slot::key) } } impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { fn len(&self) -> usize { self.iter.len() } } impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} /// An iterator over the values of an [`IndexMap`]. /// /// This `struct` is created by the [`values`] method on [`IndexMap`]. See its /// documentation for more. /// /// [`values`]: IndexMap::values #[derive(Debug, Clone)] pub struct Values<'a, K, V> { iter: SliceIter<'a, Slot>, } impl<'a, K, V> Iterator for Values<'a, K, V> { type Item = &'a V; fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn count(self) -> usize { self.iter.count() } fn next(&mut self) -> Option { self.iter.next().map(Slot::value) } } impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> { fn next_back(&mut self) -> Option { self.iter.next_back().map(Slot::value) } } impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { fn len(&self) -> usize { self.iter.len() } } impl<'a, K, V> FusedIterator for Values<'a, K, V> {} /// An iterator over the values of an [`IndexMap`]. /// /// This `struct` is created by the [`values`] method on [`IndexMap`]. See its /// documentation for more. /// /// [`values`]: IndexMap::values #[derive(Debug)] pub struct ValuesMut<'a, K, V> { iter: SliceIterMut<'a, Slot>, } impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn count(self) -> usize { self.iter.count() } fn next(&mut self) -> Option { self.iter.next().map(Slot::value_mut) } } impl<'a, K, V> DoubleEndedIterator for ValuesMut<'a, K, V> { fn next_back(&mut self) -> Option { self.iter.next_back().map(Slot::value_mut) } } impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { fn len(&self) -> usize { self.iter.len() } } impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} /// A view into a single entry in a map, which may either be vacant or occupied. /// /// This `enum` is constructed from the [`entry`] method on [`IndexMap`]. /// /// [`entry`]: IndexMap::entry pub enum Entry<'a, K, V> { /// A vacant entry. Vacant(VacantEntry<'a, K, V>), /// An occupied entry. Occupied(OccupiedEntry<'a, K, V>), } impl<'a, K: Ord, V> Entry<'a, K, V> { /// Ensures a value is in the entry by inserting the default if empty, /// and returns a mutable reference to the value in the entry. pub fn or_insert(self, default: V) -> &'a mut V where K: Clone, { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => entry.insert(default), } } /// Ensures a value is in the entry by inserting the result /// of the default function if empty, /// and returns a mutable reference to the value in the entry. pub fn or_insert_with V>(self, default: F) -> &'a mut V where K: Clone, { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => entry.insert(default()), } } /// Ensures a value is in the entry by inserting, /// if empty, the result of the default function. /// /// This method allows for generating key-derived values for /// insertion by providing the default function a reference /// to the key that was moved during the `.entry(key)` method call. /// /// The reference to the moved key is provided /// so that cloning or copying the key is /// unnecessary, unlike with `.or_insert_with(|| ... )`. pub fn or_insert_with_key V>(self, default: F) -> &'a mut V where K: Clone, { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => { let value = default(entry.key()); entry.insert(value) } } } /// Returns a reference to this entry’s key. pub fn key(&self) -> &K { match *self { Self::Occupied(ref entry) => entry.key(), Self::Vacant(ref entry) => entry.key(), } } /// Provides in-place mutable access to an occupied entry /// before any potential inserts into the map. pub fn and_modify(self, f: F) -> Self where F: FnOnce(&mut V), { match self { Self::Occupied(mut entry) => { f(entry.get_mut()); Self::Occupied(entry) } Self::Vacant(entry) => Self::Vacant(entry), } } } impl<'a, K, V> Entry<'a, K, V> where K: Ord + Clone, V: Default, { /// Ensures a value is in the entry by inserting the default value if empty, /// and returns a mutable reference to the value in the entry. pub fn or_default(self) -> &'a mut V { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => entry.insert(Default::default()), } } } impl<'a, K, V> fmt::Debug for Entry<'a, K, V> where K: fmt::Debug + Ord, V: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Entry::Vacant(entry) => entry.fmt(f), Entry::Occupied(entry) => entry.fmt(f), } } } /// A view into a vacant entry in an [`IndexMap`]. It is part of the [`Entry`] `enum`. pub struct VacantEntry<'a, K, V> { /// The underlying vacant entry. vacant: btree_map::VacantEntry<'a, K, SlotIndex>, /// The vector that stores all slots. slots: &'a mut Vec>, } impl<'a, K, V> VacantEntry<'a, K, V> where K: Ord, { /// Gets a reference to the key that would be used when inserting a value through the VacantEntry. pub fn key(&self) -> &K { self.vacant.key() } /// Take ownership of the key. pub fn into_key(self) -> K { self.vacant.into_key() } /// Sets the value of the entry with the `VacantEntry`’s key, /// and returns a mutable reference to it. pub fn insert(self, value: V) -> &'a mut V where K: Clone, { let index = self.slots.len(); let key = self.vacant.key().clone(); self.vacant.insert(SlotIndex(index)); self.slots.push(Slot::new(key, value)); &mut self.slots[index].value } } impl<'a, K, V> fmt::Debug for VacantEntry<'a, K, V> where K: fmt::Debug + Ord, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("VacantEntry") .field("key", self.key()) .finish() } } /// A view into an occupied entry in a [`IndexMap`]. It is part of the [`Entry`] `enum`. pub struct OccupiedEntry<'a, K, V> { /// The underlying occupied entry. occupied: btree_map::OccupiedEntry<'a, K, SlotIndex>, /// The vector that stores all slots. slots: &'a mut Vec>, } impl<'a, K, V> OccupiedEntry<'a, K, V> where K: Ord, { /// Gets a reference to the key in the entry. pub fn key(&self) -> &K { self.occupied.key() } /// Gets a reference to the value in the entry. pub fn get(&self) -> &V { let index = self.occupied.get().index(); &self.slots[index].value } /// Gets a mutable reference to the value in the entry. /// /// If you need a reference to the `OccupiedEntry` that may outlive the /// destruction of the `Entry` value, see [`into_mut`]. /// /// [`into_mut`]: OccupiedEntry::into_mut pub fn get_mut(&mut self) -> &mut V { let index = self.occupied.get().index(); &mut self.slots[index].value } /// Converts the entry into a mutable reference to its value. /// /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. /// /// [`get_mut`]: OccupiedEntry::get_mut pub fn into_mut(self) -> &'a mut V { let index = self.occupied.get().index(); &mut self.slots[index].value } /// Sets the value of the entry with the `OccupiedEntry`’s key, /// and returns the entry’s old value. pub fn insert(&mut self, value: V) -> V where K: Clone, { let index = self.occupied.get().index(); let key = self.key().clone(); let new_slot = Slot::new(key, value); let old_slot = replace(&mut self.slots[index], new_slot); old_slot.value } } impl<'a, K, V> fmt::Debug for OccupiedEntry<'a, K, V> where K: fmt::Debug + Ord, V: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OccupiedEntry") .field("key", self.key()) .field("value", self.get()) .finish() } } #[cfg(feature = "serde")] mod serde_impls { use super::IndexMap; use core::fmt; use core::marker::PhantomData; use serde::de::{Deserialize, MapAccess, Visitor}; use serde::ser::{Serialize, SerializeMap, Serializer}; impl Serialize for IndexMap where K: Serialize + Ord, V: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut map = serializer.serialize_map(Some(self.len()))?; for (k, v) in self.iter() { map.serialize_entry(k, v)?; } map.end() } } impl<'a, K, V> Deserialize<'a> for IndexMap where K: Deserialize<'a> + Clone + Ord, V: Deserialize<'a>, { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'a>, { deserializer.deserialize_map(IndexMapVisitor { _marker: PhantomData, }) } } struct IndexMapVisitor { _marker: PhantomData IndexMap>, } impl<'de, K, V> Visitor<'de> for IndexMapVisitor where K: Deserialize<'de> + Clone + Ord, V: Deserialize<'de>, { type Value = IndexMap; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map") } fn visit_map(self, mut access: M) -> Result where M: MapAccess<'de>, { let mut map = IndexMap::with_capacity(access.size_hint().unwrap_or(0)); while let Some((key, value)) = access.next_entry()? { map.insert(key, value); } Ok(map) } } } wasmparser-0.217.0/src/collections/index_map/tests.rs000064400000000000000000000142751046102023000207510ustar 00000000000000use super::detail::{Entry, IndexMap}; type TestMap = IndexMap; fn assert_get(map: &mut TestMap, key: char, expected: impl Into>) { let mut expected = expected.into(); assert_eq!(map.contains_key(&key), expected.is_some()); assert_eq!( map.get(&key), expected.as_ref().map(|(_index, value)| value) ); assert_eq!( map.get_key_value(&key), expected.as_ref().map(|(_index, value)| (&key, value)) ); assert_eq!( map.get_full(&key), expected .as_ref() .map(|(index, value)| (*index, &key, value)) ); assert_eq!( map.get_index_of(&key), expected.map(|(index, _value)| index) ); if let Some((index, _value)) = expected { assert_eq!( map.get_index(index), expected.as_ref().map(|(_index, value)| (&key, value)) ); assert_eq!( map.get_index_mut(index), expected.as_mut().map(|(_index, value)| (&key, value)) ); } } #[test] fn new_works() { let mut map = >::new(); assert!(map.is_empty()); assert_eq!(map.len(), 0); assert!(map.iter().eq([])); assert!(map.keys().eq([].iter())); assert!(map.values().eq([].iter())); assert!(map.values_mut().eq([].iter())); assert!(map.iter_mut().eq([])); assert!(map.into_iter().eq([])); } #[test] fn insert_works() { let mut map = >::new(); let (k0, None) = map.insert_full('a', 10) else { panic!() }; assert_get(&mut map, 'a', (k0, 10)); let (k1, None) = map.insert_full('b', 20) else { panic!() }; assert_get(&mut map, 'b', (k1, 20)); assert_eq!(map.insert('a', 30), Some(10)); assert_get(&mut map, 'a', (k0, 30)); assert_eq!(map.len(), 2); assert!(!map.is_empty()); } #[test] fn extend_works() { let mut map = >::new(); let mut values = [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5)]; map.extend(values); assert!(!map.is_empty()); assert_eq!(map.len(), values.len()); for (index, (key, value)) in values.into_iter().enumerate() { assert_get(&mut map, key, (index, value)); } assert!(map.iter().eq(values.iter().map(|(k, v)| (k, v)))); assert!(map.iter_mut().eq(values.iter_mut().map(|(k, v)| (&*k, v)))); assert!(map.keys().eq(values.iter().map(|(k, _v)| k))); assert!(map.values().eq(values.iter().map(|(_k, v)| v))); assert!(map.values_mut().eq(values.iter_mut().map(|(_k, v)| v))); assert!(map.into_iter().eq(values)); } #[test] fn clear_works() { let mut map = >::new(); map.extend([('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5)]); map.clear(); assert!(map.is_empty()); assert_eq!(map.len(), 0); assert!(map.iter().eq([])); assert!(map.keys().eq([].iter())); assert!(map.values().eq([].iter())); assert!(map.values_mut().eq([].iter())); assert!(map.iter_mut().eq([])); assert!(map.into_iter().eq([])); } #[test] fn swap_remove_works_ascending() { let mut map = >::new(); let values = [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5)]; map.extend(values); assert_eq!(map.swap_remove_full(&'a'), Some((0, 'a', 0))); // moves 'f' to 0 assert_eq!(map.swap_remove(&'a'), None); assert_eq!(map.swap_remove_full(&'b'), Some((1, 'b', 1))); // moves 'e' to 1 assert_eq!(map.swap_remove(&'b'), None); assert_eq!(map.swap_remove_full(&'c'), Some((2, 'c', 2))); // moves 'd' to 2 assert_eq!(map.swap_remove(&'c'), None); assert_eq!(map.swap_remove_full(&'d'), Some((2, 'd', 3))); assert_eq!(map.swap_remove(&'d'), None); assert_eq!(map.swap_remove_full(&'e'), Some((1, 'e', 4))); assert_eq!(map.swap_remove(&'e'), None); assert_eq!(map.swap_remove_full(&'f'), Some((0, 'f', 5))); assert_eq!(map.swap_remove(&'f'), None); for (key, _value) in values { assert_get(&mut map, key, None); } } #[test] fn swap_remove_works_descending() { let mut map = >::new(); let values = [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5)]; map.extend(values); assert_eq!(map.swap_remove_full(&'f'), Some((5, 'f', 5))); assert_eq!(map.swap_remove(&'f'), None); assert_eq!(map.swap_remove_full(&'e'), Some((4, 'e', 4))); assert_eq!(map.swap_remove(&'e'), None); assert_eq!(map.swap_remove_full(&'d'), Some((3, 'd', 3))); assert_eq!(map.swap_remove(&'d'), None); assert_eq!(map.swap_remove_full(&'c'), Some((2, 'c', 2))); assert_eq!(map.swap_remove(&'c'), None); assert_eq!(map.swap_remove_full(&'b'), Some((1, 'b', 1))); assert_eq!(map.swap_remove(&'b'), None); assert_eq!(map.swap_remove_full(&'a'), Some((0, 'a', 0))); assert_eq!(map.swap_remove(&'a'), None); for (key, _value) in values { assert_get(&mut map, key, None); } } #[test] fn entry_works_occupied() { let mut map = >::new(); let values = [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5)]; map.extend(values); for (key, mut value) in values { match map.entry(key) { Entry::Occupied(mut entry) => { assert_eq!(entry.get(), &value); assert_eq!(entry.get_mut(), &mut value); assert_eq!(entry.key(), &key); let new_value = value + 10; assert_eq!(entry.insert(new_value), value); assert_eq!(entry.get(), &new_value); } Entry::Vacant(_) => panic!(), } } } #[test] fn entry_works_vacant() { let mut map = >::new(); let values = [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5)]; for (key, mut value) in values { match map.entry(key) { Entry::Occupied(_) => panic!(), Entry::Vacant(entry) => { assert_eq!(entry.key(), &key); assert_eq!(entry.insert(value), &mut value); } } } assert!(!map.is_empty()); assert_eq!(map.len(), values.len()); for (index, (key, value)) in values.into_iter().enumerate() { assert_get(&mut map, key, (index, value)); } } wasmparser-0.217.0/src/collections/index_map.rs000064400000000000000000000372741046102023000176130ustar 00000000000000//! Type definitions for an ordered map. use core::borrow::Borrow; use core::hash::Hash; use core::iter::FusedIterator; use core::ops::Index; mod detail; #[cfg(test)] mod tests; /// A hash table where the iteration order of the key-value pairs is independent of the hash values of the keys. /// /// Provides an API compatible with both [`IndexMap`] and a custom implementation based on [`BTreeMap`]. /// /// [`IndexMap`]: indexmap::IndexMap /// [`BTreeMap`]: alloc::collections::BTreeMap #[derive(Debug, Clone)] pub struct IndexMap { inner: detail::IndexMapImpl, } impl Default for IndexMap { #[inline] fn default() -> Self { Self { inner: detail::IndexMapImpl::default(), } } } impl IndexMap { /// Clears the [`IndexMap`], removing all elements. #[inline] pub fn clear(&mut self) { self.inner.clear() } /// Returns the number of elements in the [`IndexMap`]. #[inline] pub fn len(&self) -> usize { self.inner.len() } /// Returns `true` if the [`IndexMap`] contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.inner.is_empty() } /// Returns an iterator that yields the items in the [`IndexMap`]. #[inline] pub fn iter(&self) -> Iter<'_, K, V> { Iter { inner: self.inner.iter(), } } /// Returns an iterator that yields the mutable items in the [`IndexMap`]. #[inline] pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { IterMut { inner: self.inner.iter_mut(), } } /// Returns an iterator that yields the keys in the [`IndexMap`]. #[inline] pub fn keys(&self) -> Keys<'_, K, V> { Keys { inner: self.inner.keys(), } } /// Returns an iterator that yields the values in the [`IndexMap`]. #[inline] pub fn values(&self) -> Values<'_, K, V> { Values { inner: self.inner.values(), } } /// Returns a mutable iterator that yields the values in the [`IndexMap`]. #[inline] pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { ValuesMut { inner: self.inner.values_mut(), } } /// Returns the key-value entry at the given `index` if any. #[inline] pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { self.inner.get_index(index) } /// Returns the mutable key-value entry at the given `index` if any. #[inline] pub fn get_index_mut(&mut self, index: usize) -> Option<(&K, &mut V)> { self.inner.get_index_mut(index) } } impl IndexMap where K: Hash + Eq + Ord + Clone, { /// Reserves capacity for at least `additional` more elements to be inserted in the [`IndexMap`]. #[inline] pub fn reserve(&mut self, additional: usize) { #[cfg(not(feature = "no-hash-maps"))] self.inner.reserve(additional); #[cfg(feature = "no-hash-maps")] let _ = additional; } /// Returns true if `key` is contains in the [`IndexMap`]. #[inline] pub fn contains_key(&self, key: &Q) -> bool where K: Borrow, Q: Hash + Eq + Ord, { self.inner.contains_key(key) } /// Returns a reference to the value corresponding to the `key`. #[inline] pub fn get(&self, key: &Q) -> Option<&V> where K: Borrow, Q: Hash + Eq + Ord, { self.inner.get(key) } /// Return references to the key-value pair stored for `key`, /// if it is present, else `None`. #[inline] pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> where K: Borrow, Q: Hash + Eq + Ord, { self.inner.get_key_value(key) } /// Returns the key-value pair corresponding to the supplied key /// as well as the unique index of the returned key-value pair. /// /// The supplied key may be any borrowed form of the map's key type, /// but the ordering on the borrowed form *must* match the ordering /// on the key type. #[inline] pub fn get_full(&self, key: &Q) -> Option<(usize, &K, &V)> where K: Borrow + Ord, Q: Hash + Eq + Ord, { self.inner.get_full(key) } /// Returns a mutable reference to the value corresponding to the key. #[inline] pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where K: Borrow, Q: Hash + Eq + Ord, { self.inner.get_mut(key) } /// Inserts a key-value pair into the [`IndexMap`]. /// /// If the map did not have this key present, `None` is returned. /// /// If the map did have this key present, the value is updated, and the old /// value is returned. The key is not updated, though; this matters for /// types that can be `==` without being identical. #[inline] pub fn insert(&mut self, key: K, value: V) -> Option { self.inner.insert(key, value) } /// Remove the key-value pair equivalent to `key` and return its value. /// /// Like [`Vec::swap_remove`], the pair is removed by swapping it with the /// last element of the map and popping it off. **This perturbs /// the position of what used to be the last element!** /// /// Return `None` if `key` is not in map. /// /// [`Vec::swap_remove`]: alloc::vec::Vec::swap_remove #[inline] pub fn swap_remove(&mut self, key: &Q) -> Option where K: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.swap_remove(key) } /// Remove and return the key-value pair equivalent to `key`. /// /// Like [`Vec::swap_remove`], the pair is removed by swapping it with the /// last element of the map and popping it off. **This perturbs /// the position of what used to be the last element!** /// /// Return `None` if `key` is not in map. /// /// [`Vec::swap_remove`]: alloc::vec::Vec::swap_remove #[inline] pub fn swap_remove_entry(&mut self, key: &Q) -> Option<(K, V)> where K: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.swap_remove_entry(key) } /// Gets the given key's corresponding entry in the [`IndexMap`] for in-place manipulation. #[inline] pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { match self.inner.entry(key) { detail::EntryImpl::Occupied(entry) => Entry::Occupied(OccupiedEntry { inner: entry }), detail::EntryImpl::Vacant(entry) => Entry::Vacant(VacantEntry { inner: entry }), } } } impl Index<&Q> for IndexMap where K: Borrow + Hash + Eq + Ord, Q: ?Sized + Hash + Eq + Ord, { type Output = V; #[inline] fn index(&self, key: &Q) -> &V { &self.inner[key] } } impl Index for IndexMap where K: Hash + Eq + Ord, { type Output = V; #[inline] fn index(&self, key: usize) -> &V { &self.inner[key] } } impl Extend<(K, V)> for IndexMap where K: Eq + Hash + Ord + Clone, { #[inline] fn extend>(&mut self, iter: Iter) { self.inner.extend(iter) } } /// A view into a single entry in a [`IndexMap`], which may either be vacant or occupied. /// /// This enum is constructed from the entry method on [`IndexMap`]. #[derive(Debug)] pub enum Entry<'a, K: Ord, V> { /// An occupied entry. Occupied(OccupiedEntry<'a, K, V>), /// A vacant entry. Vacant(VacantEntry<'a, K, V>), } impl<'a, K, V> Entry<'a, K, V> where K: Hash + Eq + Ord + Clone, { /// Returns a reference to this entry's key. #[inline] pub fn key(&self) -> &K { match *self { Self::Occupied(ref entry) => entry.key(), Self::Vacant(ref entry) => entry.key(), } } } impl<'a, K, V> Entry<'a, K, V> where K: Hash + Eq + Ord + Clone, V: Default, { /// Ensures a value is in the entry by inserting the default value if empty, /// and returns a mutable reference to the value in the entry. #[inline] pub fn or_default(self) -> &'a mut V { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => entry.insert(Default::default()), } } } /// A view into an occupied entry in a [`IndexMap`]. /// /// It is part of the [`Entry`] enum. #[derive(Debug)] pub struct OccupiedEntry<'a, K: Ord, V> { inner: detail::OccupiedEntryImpl<'a, K, V>, } impl<'a, K: 'a, V: 'a> OccupiedEntry<'a, K, V> where K: Ord + Clone, { /// Gets a reference to the key in the entry. #[inline] pub fn key(&self) -> &K { self.inner.key() } /// Gets a reference to the value in the entry. #[inline] pub fn get(&self) -> &V { self.inner.get() } /// Gets a mutable reference to the value in the entry. #[inline] pub fn get_mut(&mut self) -> &mut V { self.inner.get_mut() } /// Sets the value of the entry with the [`OccupiedEntry`]'s key, and returns the entry's old value. #[inline] pub fn insert(&mut self, value: V) -> V { self.inner.insert(value) } /// Converts the [`OccupiedEntry`] into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. #[inline] pub fn into_mut(self) -> &'a mut V { self.inner.into_mut() } } /// A view into a vacant entry in a [`IndexMap`]. /// /// It is part of the [`Entry`] enum. #[derive(Debug)] pub struct VacantEntry<'a, K: Ord, V> { inner: detail::VacantEntryImpl<'a, K, V>, } impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> where K: Ord + Clone, { /// Gets a reference to the key in the entry. #[inline] pub fn key(&self) -> &K { self.inner.key() } /// Take ownership of the key. #[inline] pub fn into_key(self) -> K { self.inner.into_key() } /// Sets the value of the entry with the [`VacantEntry`]'s key, and returns a mutable reference to it. #[inline] pub fn insert(self, value: V) -> &'a mut V where K: Hash, { self.inner.insert(value) } } impl FromIterator<(K, V)> for IndexMap where K: Hash + Ord + Eq + Clone, { #[inline] fn from_iter(iter: I) -> Self where I: IntoIterator, { Self { inner: >::from_iter(iter), } } } impl<'a, K, V> IntoIterator for &'a IndexMap { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } /// An iterator over the items of a [`IndexMap`]. #[derive(Debug, Clone)] pub struct Iter<'a, K, V> { inner: detail::IterImpl<'a, K, V>, } impl<'a, K, V> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} impl<'a, K, V> IntoIterator for &'a mut IndexMap { type Item = (&'a K, &'a mut V); type IntoIter = IterMut<'a, K, V>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// An iterator over the mutable items of a [`IndexMap`]. #[derive(Debug)] pub struct IterMut<'a, K, V> { inner: detail::IterMutImpl<'a, K, V>, } impl<'a, K, V> Iterator for IterMut<'a, K, V> { type Item = (&'a K, &'a mut V); #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} impl IntoIterator for IndexMap { type Item = (K, V); type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.inner.into_iter(), } } } /// An iterator over the owned items of an [`IndexMap`]. #[derive(Debug)] pub struct IntoIter { inner: detail::IntoIterImpl, } impl<'a, K, V> Iterator for IntoIter { type Item = (K, V); #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K, V> ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K, V> FusedIterator for IntoIter {} /// An iterator over the keys of a [`IndexMap`]. #[derive(Debug, Clone)] pub struct Keys<'a, K, V> { inner: detail::KeysImpl<'a, K, V>, } impl<'a, K, V> Iterator for Keys<'a, K, V> { type Item = &'a K; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} /// An iterator over the values of a [`IndexMap`]. #[derive(Debug, Clone)] pub struct Values<'a, K, V> { inner: detail::ValuesImpl<'a, K, V>, } impl<'a, K, V> Iterator for Values<'a, K, V> { type Item = &'a V; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K, V> FusedIterator for Values<'a, K, V> {} /// An mutable iterator over the values of a [`IndexMap`]. #[derive(Debug)] pub struct ValuesMut<'a, K, V> { inner: detail::ValuesMutImpl<'a, K, V>, } impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} #[cfg(feature = "serde")] impl serde::Serialize for IndexMap where K: serde::Serialize + Eq + Hash + Ord, V: serde::Serialize, { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { serde::Serialize::serialize(&self.inner, serializer) } } #[cfg(feature = "serde")] impl<'a, K, V> serde::Deserialize<'a> for IndexMap where K: serde::Deserialize<'a> + Eq + Hash + Ord + Clone, V: serde::Deserialize<'a>, { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'a>, { Ok(IndexMap { inner: serde::Deserialize::deserialize(deserializer)?, }) } } impl PartialEq for IndexMap where K: PartialEq + Hash + Ord, V: PartialEq, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } fn ne(&self, other: &Self) -> bool { self.inner != other.inner } } impl Eq for IndexMap where K: Eq + Hash + Ord, V: Eq, { } wasmparser-0.217.0/src/collections/index_set.rs000064400000000000000000000177031046102023000176240ustar 00000000000000//! Type definitions for an ordered set. use crate::collections::IndexMap; use core::{borrow::Borrow, hash::Hash, iter::FusedIterator, ops::Index}; /// A default set of values. /// /// Provides an API compatible with both [`IndexSet`] and a custom implementation based on [`BTreeMap`]. /// /// [`IndexSet`]: indexmap::IndexSet /// [`BTreeMap`]: alloc::collections::BTreeMap #[derive(Debug, Clone)] pub struct IndexSet { inner: IndexMap, } impl Default for IndexSet { #[inline] fn default() -> Self { Self { inner: IndexMap::default(), } } } impl IndexSet { /// Clears the [`IndexSet`], removing all elements. #[inline] pub fn clear(&mut self) { self.inner.clear() } /// Returns the number of elements in the [`IndexSet`]. #[inline] pub fn len(&self) -> usize { self.inner.len() } /// Returns `true` if the [`IndexSet`] contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.inner.is_empty() } /// Returns an iterator that yields the items in the [`IndexSet`]. #[inline] pub fn iter(&self) -> Iter<'_, T> { Iter { inner: self.inner.iter(), } } } impl IndexSet where T: Eq + Hash + Ord + Clone, { /// Reserves capacity for at least `additional` more elements to be inserted in the [`IndexSet`]. #[inline] pub fn reserve(&mut self, additional: usize) { self.inner.reserve(additional); } /// Returns true if the [`IndexSet`] contains an element equal to the `value`. #[inline] pub fn contains(&self, value: &Q) -> bool where T: Borrow, Q: Hash + Eq + Ord, { self.inner.contains_key(value) } /// Returns a reference to the element in the [`IndexSet`], if any, that is equal to the `value`. #[inline] pub fn get(&self, value: &Q) -> Option<&T> where T: Borrow, Q: Hash + Eq + Ord, { self.inner.get_key_value(value).map(|(x, &())| x) } /// Return the index of the item provided, if it exists. pub fn get_index_of(&self, value: &Q) -> Option where T: Borrow, Q: Hash + Eq + Ord + ?Sized, { let (index, _, _) = self.inner.get_full(value)?; Some(index) } /// Adds `value` to the [`IndexSet`]. /// /// Returns whether the value was newly inserted: /// /// - Returns `true` if the set did not previously contain an equal value. /// - Returns `false` otherwise and the entry is not updated. #[inline] pub fn insert(&mut self, value: T) -> bool { self.inner.insert(value, ()).is_none() } /// Remove the value from the [`IndexSet`], and return `true` if it was present. /// /// Like [`Vec::swap_remove`], the value is removed by swapping it with the /// last element of the set and popping it off. **This perturbs /// the position of what used to be the last element!** /// /// Return `false` if `value` was not in the set. /// /// Computes in **O(1)** time (average). /// /// [`Vec::swap_remove`]: alloc::vec::Vec::swap_remove #[inline] pub fn swap_remove(&mut self, value: &Q) -> bool where T: Borrow, Q: Hash + Eq + Ord, { self.inner.swap_remove(value).is_some() } /// Adds a value to the [`IndexSet`], replacing the existing value, if any, that is equal to the given /// one. Returns the replaced value. pub fn replace(&mut self, value: T) -> Option { let removed = self.inner.swap_remove_entry(&value); self.inner.insert(value, ()); removed.map(|(key, _value)| key) } /// Returns `true` if `self` has no elements in common with `other`. /// This is equivalent to checking for an empty intersection. pub fn is_disjoint(&self, other: &Self) -> bool { if self.len() <= other.len() { self.iter().all(move |value| !other.contains(value)) } else { other.iter().all(move |value| !self.contains(value)) } } /// Returns `true` if the [`IndexSet`] is a subset of another, /// i.e., `other` contains at least all the values in `self`. pub fn is_subset(&self, other: &Self) -> bool { self.len() <= other.len() && self.iter().all(move |value| other.contains(value)) } /// Returns `true` if the [`IndexSet`] is a superset of another, /// i.e., `self` contains at least all the values in `other`. #[inline] pub fn is_superset(&self, other: &Self) -> bool { other.is_subset(self) } } impl Index for IndexSet where T: Hash + Eq + Ord, { type Output = T; #[inline] fn index(&self, index: usize) -> &T { let Some((value, _)) = self.inner.get_index(index) else { panic!("out of bounds index: {index}"); }; value } } impl FromIterator for IndexSet where T: Hash + Eq + Ord + Clone, { fn from_iter(iter: I) -> Self where I: IntoIterator, { Self { inner: >::from_iter(iter.into_iter().map(|value| (value, ()))), } } } impl<'a, T> IntoIterator for &'a IndexSet { type Item = &'a T; type IntoIter = Iter<'a, T>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl Extend for IndexSet where T: Hash + Eq + Ord + Clone, { fn extend>(&mut self, iter: Iter) { self.inner.extend(iter.into_iter().map(|value| (value, ()))) } } /// An iterator over the items of a [`IndexSet`]. #[derive(Debug, Clone)] pub struct Iter<'a, T> { inner: <&'a IndexMap as IntoIterator>::IntoIter, } impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next().map(|(key, _value)| key) } } impl<'a, T> ExactSizeIterator for Iter<'a, T> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, T> FusedIterator for Iter<'a, T> {} impl IntoIterator for IndexSet { type Item = T; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.inner.into_iter(), } } } /// An iterator over the owned items of an [`IndexSet`]. #[derive(Debug)] pub struct IntoIter { inner: as IntoIterator>::IntoIter, } impl Iterator for IntoIter { type Item = T; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next().map(|(key, _value)| key) } } impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.inner.len() } } impl FusedIterator for IntoIter {} #[cfg(feature = "serde")] impl serde::Serialize for IndexSet where T: serde::Serialize + Eq + Hash + Ord, { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { serde::Serialize::serialize(&self.inner, serializer) } } #[cfg(feature = "serde")] impl<'a, T> serde::Deserialize<'a> for IndexSet where T: serde::Deserialize<'a> + Eq + Hash + Ord + Clone, { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'a>, { Ok(IndexSet { inner: serde::Deserialize::deserialize(deserializer)?, }) } } impl PartialEq for IndexSet where T: PartialEq + Hash + Ord, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } fn ne(&self, other: &Self) -> bool { self.inner != other.inner } } impl Eq for IndexSet where T: Eq + Hash + Ord {} wasmparser-0.217.0/src/collections/map.rs000064400000000000000000000523061046102023000164150ustar 00000000000000//! Type definitions for a default map. use core::fmt::Debug; use core::{borrow::Borrow, hash::Hash, iter::FusedIterator, ops::Index}; #[cfg(not(feature = "no-hash-maps"))] mod detail { use crate::collections::hash; use hashbrown::hash_map; pub type MapImpl = hash_map::HashMap; pub type EntryImpl<'a, K, V> = hash_map::Entry<'a, K, V, hash::RandomState>; pub type OccupiedEntryImpl<'a, K, V> = hash_map::OccupiedEntry<'a, K, V, hash::RandomState>; pub type VacantEntryImpl<'a, K, V> = hash_map::VacantEntry<'a, K, V, hash::RandomState>; pub type IterImpl<'a, K, V> = hash_map::Iter<'a, K, V>; pub type IterMutImpl<'a, K, V> = hash_map::IterMut<'a, K, V>; pub type IntoIterImpl = hash_map::IntoIter; pub type KeysImpl<'a, K, V> = hash_map::Keys<'a, K, V>; pub type ValuesImpl<'a, K, V> = hash_map::Values<'a, K, V>; pub type ValuesMutImpl<'a, K, V> = hash_map::ValuesMut<'a, K, V>; pub type IntoKeysImpl = hash_map::IntoKeys; pub type IntoValuesImpl = hash_map::IntoValues; } #[cfg(feature = "no-hash-maps")] mod detail { use alloc::collections::btree_map; pub type MapImpl = btree_map::BTreeMap; pub type EntryImpl<'a, K, V> = btree_map::Entry<'a, K, V>; pub type OccupiedEntryImpl<'a, K, V> = btree_map::OccupiedEntry<'a, K, V>; pub type VacantEntryImpl<'a, K, V> = btree_map::VacantEntry<'a, K, V>; pub type IterImpl<'a, K, V> = btree_map::Iter<'a, K, V>; pub type IterMutImpl<'a, K, V> = btree_map::IterMut<'a, K, V>; pub type IntoIterImpl = btree_map::IntoIter; pub type KeysImpl<'a, K, V> = btree_map::Keys<'a, K, V>; pub type ValuesImpl<'a, K, V> = btree_map::Values<'a, K, V>; pub type ValuesMutImpl<'a, K, V> = btree_map::ValuesMut<'a, K, V>; pub type IntoKeysImpl = btree_map::IntoKeys; pub type IntoValuesImpl = btree_map::IntoValues; } /// A default key-value mapping. /// /// Provides an API compatible with both [`HashMap`] and [`BTreeMap`]. /// /// [`HashMap`]: hashbrown::HashMap /// [`BTreeMap`]: alloc::collections::BTreeMap #[derive(Debug, Clone)] pub struct Map { inner: detail::MapImpl, } impl Default for Map { #[inline] fn default() -> Self { Self { inner: detail::MapImpl::default(), } } } impl Map { /// Creates a new empty [`Map`]. #[inline] pub fn new() -> Self { Self::default() } /// Clears the [`Map`], removing all elements. #[inline] pub fn clear(&mut self) { self.inner.clear() } /// Returns the number of elements in the [`Map`]. #[inline] pub fn len(&self) -> usize { self.inner.len() } /// Returns `true` if the [`Map`] contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.inner.is_empty() } /// Returns an iterator that yields the items in the [`Map`]. #[inline] pub fn iter(&self) -> Iter<'_, K, V> { Iter { inner: self.inner.iter(), } } /// Returns a mutable iterator that yields the items in the [`Map`]. #[inline] pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { IterMut { inner: self.inner.iter_mut(), } } /// Returns an iterator that yields the keys in the [`Map`]. #[inline] pub fn keys(&self) -> Keys<'_, K, V> { Keys { inner: self.inner.keys(), } } /// Creates a consuming iterator visiting all the keys in arbitrary order. /// /// The [`Map`] cannot be used after calling this. /// The iterator element type is `K`. #[inline] pub fn into_keys(self) -> IntoKeys { IntoKeys { inner: self.inner.into_keys(), } } /// Returns an iterator that yields the values in the [`Map`]. #[inline] pub fn values(&self) -> Values<'_, K, V> { Values { inner: self.inner.values(), } } /// Creates a consuming iterator visiting all the values in arbitrary order. /// /// The [`Map`] cannot be used after calling this. /// The iterator element type is `V`. #[inline] pub fn into_values(self) -> IntoValues { IntoValues { inner: self.inner.into_values(), } } /// Returns a mutable iterator that yields the values in the [`Map`]. #[inline] pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { ValuesMut { inner: self.inner.values_mut(), } } } impl Map where K: Hash + Eq + Ord, { /// Reserves capacity for at least `additional` more elements to be inserted in the [`Map`]. #[inline] pub fn reserve(&mut self, additional: usize) { #[cfg(not(feature = "no-hash-maps"))] self.inner.reserve(additional); #[cfg(feature = "no-hash-maps")] let _ = additional; } /// Returns true if `key` is contains in the [`Map`]. #[inline] pub fn contains_key(&self, key: &Q) -> bool where K: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.contains_key(key) } /// Returns a reference to the value corresponding to the `key`. #[inline] pub fn get(&self, key: &Q) -> Option<&V> where K: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.get(key) } /// Returns the key-value pair corresponding to the supplied key. /// /// The supplied key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. #[inline] pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> where K: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.get_key_value(key) } /// Returns a mutable reference to the value corresponding to the key. #[inline] pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where K: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.get_mut(key) } /// Inserts a key-value pair into the [`Map`]. /// /// If the map did not have this key present, `None` is returned. /// /// If the map did have this key present, the value is updated, and the old /// value is returned. The key is not updated, though; this matters for /// types that can be `==` without being identical. #[inline] pub fn insert(&mut self, key: K, value: V) -> Option { self.inner.insert(key, value) } /// Removes a key from the [`Map`], returning the value at the key if the key was previously in the map. #[inline] pub fn remove(&mut self, key: &Q) -> Option where K: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.remove(key) } /// Removes a key from the [`Map`], returning the stored key and value if the key /// was previously in the map. /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. #[inline] pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> where K: Borrow, Q: ?Sized + Hash + Ord, { self.inner.remove_entry(key) } /// Gets the given key's corresponding entry in the [`Map`] for in-place manipulation. #[inline] pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { match self.inner.entry(key) { detail::EntryImpl::Occupied(entry) => Entry::Occupied(OccupiedEntry { inner: entry }), detail::EntryImpl::Vacant(entry) => Entry::Vacant(VacantEntry { inner: entry }), } } /// Retains only the elements specified by the predicate. /// /// In other words, remove all pairs `(k, v)` for which `f(&k, &mut v)` returns `false`. /// The elements are visited in ascending key order. #[inline] pub fn retain(&mut self, f: F) where F: FnMut(&K, &mut V) -> bool, { self.inner.retain(f) } } impl PartialEq for Map where K: Eq + Hash, V: Eq, { #[inline] fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } impl Eq for Map where K: Eq + Hash, V: Eq, { } impl Index<&Q> for Map where K: Borrow + Hash + Eq + Ord, Q: ?Sized + Hash + Eq + Ord, { type Output = V; #[inline] fn index(&self, key: &Q) -> &V { &self.inner[key] } } impl<'a, K, V> Extend<(&'a K, &'a V)> for Map where K: Eq + Hash + Ord + Copy, V: Copy, { #[inline] fn extend>(&mut self, iter: Iter) { self.inner.extend(iter) } } impl Extend<(K, V)> for Map where K: Eq + Hash + Ord, { #[inline] fn extend>(&mut self, iter: Iter) { self.inner.extend(iter) } } /// A view into a single entry in a [`Map`], which may either be vacant or occupied. /// /// This enum is constructed from the entry method on [`Map`]. #[derive(Debug)] pub enum Entry<'a, K: Ord, V> { /// An occupied entry. Occupied(OccupiedEntry<'a, K, V>), /// A vacant entry. Vacant(VacantEntry<'a, K, V>), } impl<'a, K, V> Entry<'a, K, V> where K: Hash + Ord, { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. #[inline] pub fn or_insert(self, default: V) -> &'a mut V { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => entry.insert(default), } } /// Ensures a value is in the [`Entry`] by inserting the result of the default function if empty, /// and returns a mutable reference to the value in the entry. #[inline] pub fn or_insert_with V>(self, default: F) -> &'a mut V { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => entry.insert(default()), } } /// Ensures a value is in the [`Entry`] by inserting, if empty, the result of the default function. /// This method allows for generating key-derived values for insertion by providing the default /// function a reference to the key that was moved during the `.entry(key)` method call. /// /// The reference to the moved key is provided so that cloning or copying the key is /// unnecessary, unlike with `.or_insert_with(|| ... )`. #[inline] pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => { let value = default(entry.key()); entry.insert(value) } } } /// Returns a reference to this [`Entry`]'s key. #[inline] pub fn key(&self) -> &K { match *self { Self::Occupied(ref entry) => entry.key(), Self::Vacant(ref entry) => entry.key(), } } /// Provides in-place mutable access to an occupied [`Entry`] before any /// potential inserts into the map. #[inline] pub fn and_modify(self, f: F) -> Self where F: FnOnce(&mut V), { match self { Self::Occupied(mut entry) => { f(entry.get_mut()); Self::Occupied(entry) } Self::Vacant(entry) => Self::Vacant(entry), } } } impl<'a, K, V> Entry<'a, K, V> where K: Hash + Ord, V: Default, { /// Ensures a value is in the [`Entry`] by inserting the default value if empty, /// and returns a mutable reference to the value in the entry. #[inline] pub fn or_default(self) -> &'a mut V { match self { Self::Occupied(entry) => entry.into_mut(), Self::Vacant(entry) => entry.insert(Default::default()), } } } /// A view into an occupied entry in a [`Map`]. /// /// It is part of the [`Entry`] enum. pub struct OccupiedEntry<'a, K, V> { inner: detail::OccupiedEntryImpl<'a, K, V>, } impl<'a, K, V> Debug for OccupiedEntry<'a, K, V> where K: Debug + Ord + 'a, V: Debug + 'a, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self.inner.fmt(f) } } impl<'a, K, V> OccupiedEntry<'a, K, V> where K: Ord + 'a, V: 'a, { /// Gets a reference to the key in the entry. #[inline] pub fn key(&self) -> &K { self.inner.key() } /// Gets a reference to the value in the entry. #[inline] pub fn get(&self) -> &V { self.inner.get() } /// Gets a mutable reference to the value in the entry. #[inline] pub fn get_mut(&mut self) -> &mut V { self.inner.get_mut() } /// Sets the value of the entry with the [`OccupiedEntry`]'s key, and returns the entry's old value. #[inline] pub fn insert(&mut self, value: V) -> V { self.inner.insert(value) } /// Converts the [`OccupiedEntry`] into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. #[inline] pub fn into_mut(self) -> &'a mut V { self.inner.into_mut() } /// Take ownership of the key and value from the [`Map`]. #[inline] pub fn remove_entry(self) -> (K, V) { self.inner.remove_entry() } /// Takes the value of the entry out of the [`Map`], and returns it. #[inline] pub fn remove(self) -> V { self.inner.remove() } } /// A view into a vacant entry in a [`Map`]. /// /// It is part of the [`Entry`] enum. pub struct VacantEntry<'a, K, V> { inner: detail::VacantEntryImpl<'a, K, V>, } impl<'a, K, V> Debug for VacantEntry<'a, K, V> where K: Debug + Ord + 'a, V: Debug + 'a, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self.inner.fmt(f) } } impl<'a, K, V> VacantEntry<'a, K, V> where K: Ord + 'a, V: 'a, { /// Gets a reference to the key in the entry. #[inline] pub fn key(&self) -> &K { self.inner.key() } /// Take ownership of the key. #[inline] pub fn into_key(self) -> K { self.inner.into_key() } /// Sets the value of the entry with the [`VacantEntry`]'s key, and returns a mutable reference to it. #[inline] pub fn insert(self, value: V) -> &'a mut V where K: Hash, { self.inner.insert(value) } } impl FromIterator<(K, V)> for Map where K: Hash + Eq + Ord, { #[inline] fn from_iter(iter: I) -> Self where I: IntoIterator, { Self { inner: >::from_iter(iter), } } } impl<'a, K, V> IntoIterator for &'a Map { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } /// An iterator over the items of a [`Map`]. #[derive(Debug, Clone)] pub struct Iter<'a, K, V> { inner: detail::IterImpl<'a, K, V>, } impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K: 'a, V: 'a> ExactSizeIterator for Iter<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K: 'a, V: 'a> FusedIterator for Iter<'a, K, V> where detail::IterImpl<'a, K, V>: FusedIterator { } impl<'a, K: 'a, V: 'a> IntoIterator for &'a mut Map { type Item = (&'a K, &'a mut V); type IntoIter = IterMut<'a, K, V>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// An iterator over the mutable items of a [`Map`]. #[derive(Debug)] pub struct IterMut<'a, K, V> { inner: detail::IterMutImpl<'a, K, V>, } impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> { type Item = (&'a K, &'a mut V); #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMut<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K: 'a, V: 'a> FusedIterator for IterMut<'a, K, V> where detail::IterMutImpl<'a, K, V>: FusedIterator { } impl IntoIterator for Map { type Item = (K, V); type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.inner.into_iter(), } } } /// An iterator over the owned items of an [`Map`]. #[derive(Debug)] pub struct IntoIter { inner: detail::IntoIterImpl, } impl Iterator for IntoIter { type Item = (K, V); #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.inner.len() } } impl FusedIterator for IntoIter where detail::IntoIterImpl: FusedIterator {} /// An iterator over the keys of a [`Map`]. #[derive(Debug, Clone)] pub struct Keys<'a, K, V> { inner: detail::KeysImpl<'a, K, V>, } impl<'a, K: 'a, V> Iterator for Keys<'a, K, V> { type Item = &'a K; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K: 'a, V> ExactSizeIterator for Keys<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K: 'a, V> FusedIterator for Keys<'a, K, V> where detail::KeysImpl<'a, K, V>: FusedIterator {} /// An iterator over the values of a [`Map`]. #[derive(Debug, Clone)] pub struct Values<'a, K, V> { inner: detail::ValuesImpl<'a, K, V>, } impl<'a, K, V: 'a> Iterator for Values<'a, K, V> { type Item = &'a V; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K, V: 'a> ExactSizeIterator for Values<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K, V: 'a> FusedIterator for Values<'a, K, V> where detail::ValuesImpl<'a, K, V>: FusedIterator { } /// An mutable iterator over the values of a [`Map`]. #[derive(Debug)] pub struct ValuesMut<'a, K, V> { inner: detail::ValuesMutImpl<'a, K, V>, } impl<'a, K, V: 'a> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, K, V: 'a> ExactSizeIterator for ValuesMut<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, K, V: 'a> FusedIterator for ValuesMut<'a, K, V> where detail::ValuesMutImpl<'a, K, V>: FusedIterator { } /// An iterator over the owned keys of a [`Map`]. #[derive(Debug)] pub struct IntoKeys { inner: detail::IntoKeysImpl, } impl Iterator for IntoKeys { type Item = K; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl ExactSizeIterator for IntoKeys { #[inline] fn len(&self) -> usize { self.inner.len() } } impl FusedIterator for IntoKeys where detail::IntoKeysImpl: FusedIterator {} /// An iterator over the owned values of a [`Map`]. #[derive(Debug)] pub struct IntoValues { inner: detail::IntoValuesImpl, } impl Iterator for IntoValues { type Item = V; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl ExactSizeIterator for IntoValues { #[inline] fn len(&self) -> usize { self.inner.len() } } impl FusedIterator for IntoValues where detail::IntoValuesImpl: FusedIterator {} #[cfg(feature = "serde")] impl serde::Serialize for Map where K: serde::Serialize + Eq + Hash + Ord, V: serde::Serialize, { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { serde::Serialize::serialize(&self.inner, serializer) } } #[cfg(feature = "serde")] impl<'a, K, V> serde::Deserialize<'a> for Map where K: serde::Deserialize<'a> + Eq + Hash + Ord, V: serde::Deserialize<'a>, { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'a>, { Ok(Map { inner: serde::Deserialize::deserialize(deserializer)?, }) } } wasmparser-0.217.0/src/collections/mod.rs000064400000000000000000000021561046102023000164150ustar 00000000000000//! Type definitions for maps and sets used by the `wasmparser` crate. //! //! This module contains type definitions for [`Map`], [`Set`], [`IndexMap`], and [`IndexSet`]. //! These types are thin-wrappers around either hash-map based or B-tree-map based data structures. //! Users can strictly use the `btree`-map based variants by enabling the `no-hash-maps` crate feature. //! //! - [`Map`]: Either backed by [`hashbrown::HashMap`] or Rust's [`BTreeMap`]. //! - [`Set`]: Either backed by [`hashbrown::HashSet`] or Rust's [`BTreeSet`]. //! - [`IndexMap`]: Either backed by [`indexmap::IndexMap`] or a custom implementation based on Rust's [`BTreeMap`]. //! - [`IndexSet`]: Either backed by [`indexmap::IndexSet`] or a custom implementation based on Rust's [`BTreeMap`]. //! //! For the hash-map based type definitions the hash algorithm type parameter is fixed. //! //! [`BTreeMap`]: alloc::collections::BTreeMap //! [`BTreeSet`]: alloc::collections::BTreeSet pub mod hash; pub mod index_map; pub mod index_set; pub mod map; pub mod set; #[doc(inline)] pub use self::{index_map::IndexMap, index_set::IndexSet, map::Map, set::Set}; wasmparser-0.217.0/src/collections/set.rs000064400000000000000000000372031046102023000164320ustar 00000000000000//! Type definitions for a default set. use core::{ borrow::Borrow, fmt::{self, Debug}, hash::Hash, iter::FusedIterator, ops::{BitAnd, BitOr, BitXor, Sub}, }; #[cfg(not(feature = "no-hash-maps"))] mod detail { use crate::collections::hash; use hashbrown::hash_set; pub type SetImpl = hash_set::HashSet; pub type IterImpl<'a, T> = hash_set::Iter<'a, T>; pub type IntoIterImpl = hash_set::IntoIter; pub type DifferenceImpl<'a, T> = hash_set::Difference<'a, T, hash::RandomState>; pub type IntersectionImpl<'a, T> = hash_set::Intersection<'a, T, hash::RandomState>; pub type SymmetricDifferenceImpl<'a, T> = hash_set::SymmetricDifference<'a, T, hash::RandomState>; pub type UnionImpl<'a, T> = hash_set::Union<'a, T, hash::RandomState>; } #[cfg(feature = "no-hash-maps")] mod detail { use alloc::collections::btree_set; pub type SetImpl = btree_set::BTreeSet; pub type IterImpl<'a, T> = btree_set::Iter<'a, T>; pub type IntoIterImpl = btree_set::IntoIter; pub type DifferenceImpl<'a, T> = btree_set::Difference<'a, T>; pub type IntersectionImpl<'a, T> = btree_set::Intersection<'a, T>; pub type SymmetricDifferenceImpl<'a, T> = btree_set::SymmetricDifference<'a, T>; pub type UnionImpl<'a, T> = btree_set::Union<'a, T>; } /// A default set of values. /// /// Provides an API compatible with both [`HashSet`] and [`BTreeSet`]. /// /// [`HashSet`]: hashbrown::HashSet /// [`BTreeSet`]: alloc::collections::BTreeSet #[derive(Debug, Clone)] pub struct Set { /// The underlying hash-set or btree-set data structure used. inner: detail::SetImpl, } impl Default for Set { #[inline] fn default() -> Self { Self { inner: detail::SetImpl::default(), } } } impl Set { /// Clears the [`Set`], removing all elements. #[inline] pub fn clear(&mut self) { self.inner.clear() } /// Retains only the elements specified by the predicate. /// /// In other words, remove all elements `e` for which `f(&e)` returns `false`. /// The elements are visited in unsorted (and unspecified) order. #[inline] pub fn retain(&mut self, f: F) where T: Ord, F: FnMut(&T) -> bool, { self.inner.retain(f) } /// Returns the number of elements in the [`Set`]. #[inline] pub fn len(&self) -> usize { self.inner.len() } /// Returns `true` if the [`Set`] contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.inner.is_empty() } /// Returns an iterator that yields the items in the [`Set`]. #[inline] pub fn iter(&self) -> Iter<'_, T> { Iter { inner: self.inner.iter(), } } } impl Set where T: Eq + Hash + Ord, { /// Reserves capacity for at least `additional` more elements to be inserted in the [`Set`]. #[inline] pub fn reserve(&mut self, additional: usize) { #[cfg(not(feature = "no-hash-maps"))] self.inner.reserve(additional); #[cfg(feature = "no-hash-maps")] let _ = additional; } /// Returns true if the [`Set`] contains an element equal to the `value`. #[inline] pub fn contains(&self, value: &Q) -> bool where T: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.contains(value) } /// Returns a reference to the element in the [`Set`], if any, that is equal to the `value`. #[inline] pub fn get(&self, value: &Q) -> Option<&T> where T: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.get(value) } /// Adds `value` to the [`Set`]. /// /// Returns whether the value was newly inserted: /// /// - Returns `true` if the set did not previously contain an equal value. /// - Returns `false` otherwise and the entry is not updated. #[inline] pub fn insert(&mut self, value: T) -> bool { self.inner.insert(value) } /// If the set contains an element equal to the value, removes it from the [`Set`] and drops it. /// /// Returns `true` if such an element was present, otherwise `false`. #[inline] pub fn remove(&mut self, value: &Q) -> bool where T: Borrow, Q: ?Sized + Hash + Eq + Ord, { self.inner.remove(value) } /// Removes and returns the element in the [`Set`], if any, that is equal to /// the value. /// /// The value may be any borrowed form of the set's element type, /// but the ordering on the borrowed form *must* match the /// ordering on the element type. #[inline] pub fn take(&mut self, value: &Q) -> Option where T: Borrow, Q: ?Sized + Hash + Ord, { self.inner.take(value) } /// Adds a value to the [`Set`], replacing the existing value, if any, that is equal to the given /// one. Returns the replaced value. #[inline] pub fn replace(&mut self, value: T) -> Option { self.inner.replace(value) } /// Returns `true` if `self` has no elements in common with `other`. /// This is equivalent to checking for an empty intersection. #[inline] pub fn is_disjoint(&self, other: &Self) -> bool { self.inner.is_disjoint(&other.inner) } /// Returns `true` if the [`Set`] is a subset of another, /// i.e., `other` contains at least all the values in `self`. #[inline] pub fn is_subset(&self, other: &Self) -> bool { self.inner.is_subset(&other.inner) } /// Returns `true` if the [`Set`] is a superset of another, /// i.e., `self` contains at least all the values in `other`. #[inline] pub fn is_superset(&self, other: &Self) -> bool { self.inner.is_superset(&other.inner) } /// Visits the values representing the difference, /// i.e., the values that are in `self` but not in `other`. #[inline] pub fn difference<'a>(&'a self, other: &'a Self) -> Difference<'a, T> { Difference { inner: self.inner.difference(&other.inner), } } /// Visits the values representing the symmetric difference, /// i.e., the values that are in `self` or in `other` but not in both. #[inline] pub fn symmetric_difference<'a>(&'a self, other: &'a Self) -> SymmetricDifference<'a, T> { SymmetricDifference { inner: self.inner.symmetric_difference(&other.inner), } } /// Visits the values representing the intersection, /// i.e., the values that are both in `self` and `other`. /// /// When an equal element is present in `self` and `other` /// then the resulting `Intersection` may yield references to /// one or the other. This can be relevant if `T` contains fields which /// are not compared by its `Eq` implementation, and may hold different /// value between the two equal copies of `T` in the two sets. #[inline] pub fn intersection<'a>(&'a self, other: &'a Self) -> Intersection<'a, T> { Intersection { inner: self.inner.intersection(&other.inner), } } /// Visits the values representing the union, /// i.e., all the values in `self` or `other`, without duplicates. #[inline] pub fn union<'a>(&'a self, other: &'a Self) -> Union<'a, T> { Union { inner: self.inner.union(&other.inner), } } } impl PartialEq for Set where T: Eq + Hash, { #[inline] fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } impl Eq for Set where T: Eq + Hash {} impl FromIterator for Set where T: Hash + Eq + Ord, { #[inline] fn from_iter(iter: I) -> Self where I: IntoIterator, { Self { inner: >::from_iter(iter), } } } impl<'a, T> IntoIterator for &'a Set { type Item = &'a T; type IntoIter = Iter<'a, T>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a, T> Extend<&'a T> for Set where T: Hash + Eq + Ord + Copy + 'a, { #[inline] fn extend>(&mut self, iter: Iter) { self.inner.extend(iter) } } impl Extend for Set where T: Hash + Eq + Ord, { #[inline] fn extend>(&mut self, iter: Iter) { self.inner.extend(iter) } } impl<'a, T> BitAnd for &'a Set where T: Eq + Hash + Ord + Clone + 'a, { type Output = Set; #[inline] fn bitand(self, rhs: Self) -> Set { Set { inner: BitAnd::bitand(&self.inner, &rhs.inner), } } } impl<'a, T> BitOr for &'a Set where T: Eq + Hash + Ord + Clone + 'a, { type Output = Set; #[inline] fn bitor(self, rhs: Self) -> Set { Set { inner: BitOr::bitor(&self.inner, &rhs.inner), } } } impl<'a, T> BitXor for &'a Set where T: Eq + Hash + Ord + Clone + 'a, { type Output = Set; #[inline] fn bitxor(self, rhs: Self) -> Set { Set { inner: BitXor::bitxor(&self.inner, &rhs.inner), } } } impl<'a, T> Sub for &'a Set where T: Eq + Hash + Ord + Clone + 'a, { type Output = Set; #[inline] fn sub(self, rhs: Self) -> Set { Set { inner: Sub::sub(&self.inner, &rhs.inner), } } } /// An iterator over the items of a [`Set`]. #[derive(Debug, Clone)] pub struct Iter<'a, T> { inner: detail::IterImpl<'a, T>, } impl<'a, T: 'a> Iterator for Iter<'a, T> { type Item = &'a T; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl<'a, T: 'a> ExactSizeIterator for Iter<'a, T> { #[inline] fn len(&self) -> usize { self.inner.len() } } impl<'a, T: 'a> FusedIterator for Iter<'a, T> where detail::IterImpl<'a, T>: FusedIterator {} impl IntoIterator for Set { type Item = T; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.inner.into_iter(), } } } /// An iterator over the owned items of an [`Set`]. #[derive(Debug)] pub struct IntoIter { inner: detail::IntoIterImpl, } impl Iterator for IntoIter { type Item = T; #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline] fn next(&mut self) -> Option { self.inner.next() } } impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.inner.len() } } impl FusedIterator for IntoIter where detail::IntoIterImpl: FusedIterator {} /// A lazy iterator producing elements in the difference of [`Set`]s. /// /// This `struct` is created by the [`difference`] method on [`Set`]. /// See its documentation for more. /// /// [`difference`]: Set::difference pub struct Difference<'a, T: 'a> { inner: detail::DifferenceImpl<'a, T>, } impl Debug for Difference<'_, T> where T: Debug + Hash + Eq, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } impl Clone for Difference<'_, T> { #[inline] fn clone(&self) -> Self { Self { inner: self.inner.clone(), } } } impl<'a, T> Iterator for Difference<'a, T> where T: Hash + Eq + Ord, { type Item = &'a T; #[inline] fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl<'a, T> FusedIterator for Difference<'a, T> where T: Hash + Eq + Ord, detail::DifferenceImpl<'a, T>: FusedIterator, { } /// A lazy iterator producing elements in the intersection of [`Set`]s. /// /// This `struct` is created by the [`intersection`] method on [`Set`]. /// See its documentation for more. /// /// [`intersection`]: Set::intersection pub struct Intersection<'a, T: 'a> { inner: detail::IntersectionImpl<'a, T>, } impl Debug for Intersection<'_, T> where T: Debug + Hash + Eq, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } impl Clone for Intersection<'_, T> { #[inline] fn clone(&self) -> Self { Self { inner: self.inner.clone(), } } } impl<'a, T> Iterator for Intersection<'a, T> where T: Hash + Eq + Ord, { type Item = &'a T; #[inline] fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl<'a, T> FusedIterator for Intersection<'a, T> where T: Hash + Eq + Ord, detail::IntersectionImpl<'a, T>: FusedIterator, { } /// A lazy iterator producing elements in the symmetric difference of [`Set`]s. /// /// This `struct` is created by the [`symmetric_difference`] method on /// [`Set`]. See its documentation for more. /// /// [`symmetric_difference`]: Set::symmetric_difference pub struct SymmetricDifference<'a, T: 'a> { inner: detail::SymmetricDifferenceImpl<'a, T>, } impl Debug for SymmetricDifference<'_, T> where T: Debug + Hash + Eq, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } impl Clone for SymmetricDifference<'_, T> { #[inline] fn clone(&self) -> Self { Self { inner: self.inner.clone(), } } } impl<'a, T> Iterator for SymmetricDifference<'a, T> where T: Hash + Eq + Ord, { type Item = &'a T; #[inline] fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl<'a, T> FusedIterator for SymmetricDifference<'a, T> where T: Hash + Eq + Ord, detail::SymmetricDifferenceImpl<'a, T>: FusedIterator, { } /// A lazy iterator producing elements in the union of [`Set`]s. /// /// This `struct` is created by the [`union`] method on /// [`Set`]. See its documentation for more. /// /// [`union`]: Set::union pub struct Union<'a, T: 'a> { inner: detail::UnionImpl<'a, T>, } impl Debug for Union<'_, T> where T: Debug + Hash + Eq, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } impl Clone for Union<'_, T> { #[inline] fn clone(&self) -> Self { Self { inner: self.inner.clone(), } } } impl<'a, T> Iterator for Union<'a, T> where T: Hash + Eq + Ord, { type Item = &'a T; #[inline] fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl<'a, T> FusedIterator for Union<'a, T> where T: Hash + Eq + Ord, detail::UnionImpl<'a, T>: FusedIterator, { } #[cfg(feature = "serde")] impl serde::Serialize for Set where T: serde::Serialize + Eq + Hash + Ord, { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { serde::Serialize::serialize(&self.inner, serializer) } } #[cfg(feature = "serde")] impl<'a, T> serde::Deserialize<'a> for Set where T: serde::Deserialize<'a> + Eq + Hash + Ord, { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'a>, { Ok(Set { inner: serde::Deserialize::deserialize(deserializer)?, }) } } wasmparser-0.217.0/src/features.rs000064400000000000000000000302151046102023000151330ustar 00000000000000macro_rules! define_wasm_features { ( $(#[$outer:meta])* pub struct WasmFeatures: $repr:ty { $( $(#[$inner:ident $($args:tt)*])* pub $field:ident: $const:ident($flag:expr) = $default:expr; )* } ) => { #[cfg(feature = "features")] bitflags::bitflags! { $(#[$outer])* pub struct WasmFeatures: $repr { $( $(#[$inner $($args)*])* #[doc = "\nDefaults to `"] #[doc = stringify!($default)] #[doc = "`.\n"] const $const = $flag; )* } } /// Enabled WebAssembly proposals and features. /// /// This is the disabled zero-size version of this structure because the /// `features` feature was disabled at compile time of this crate. #[cfg(not(feature = "features"))] #[derive(Clone, Debug, Default, Hash, Copy)] pub struct WasmFeatures { _priv: (), } #[cfg(feature = "features")] impl Default for WasmFeatures { #[inline] fn default() -> Self { let mut features = WasmFeatures::empty(); $( features.set(WasmFeatures::$const, $default); )* features } } impl WasmFeatures { /// Construct a bit-packed `WasmFeatures` from the inflated struct version. #[inline] #[cfg(feature = "features")] pub fn from_inflated(inflated: WasmFeaturesInflated) -> Self { let mut features = WasmFeatures::empty(); $( features.set(WasmFeatures::$const, inflated.$field); )* features } /// Inflate these bit-packed features into a struct with a field per /// feature. /// /// Although the inflated struct takes up much more memory than the /// bit-packed version, its fields can be exhaustively matched /// upon. This makes it useful for temporarily checking against, /// while keeping the bit-packed version as the method of storing /// the features for longer periods of time. #[inline] #[cfg(feature = "features")] pub fn inflate(&self) -> WasmFeaturesInflated { WasmFeaturesInflated { $( $field: self.$field(), )* } } $( /// Returns whether this feature is enabled in this feature set. #[inline] pub fn $field(&self) -> bool { #[cfg(feature = "features")] { self.contains(WasmFeatures::$const) } #[cfg(not(feature = "features"))] { $default } } )* } /// Inflated version of [`WasmFeatures`][crate::WasmFeatures] that /// allows for exhaustive matching on fields. #[cfg(feature = "features")] pub struct WasmFeaturesInflated { $( $(#[$inner $($args)*])* #[doc = "\nDefaults to `"] #[doc = stringify!($default)] #[doc = "`.\n"] pub $field: bool, )* } macro_rules! foreach_wasm_feature { ($f:ident) => { $($f!($field = $default);)* } } pub(crate) use foreach_wasm_feature; }; } define_wasm_features! { /// Flags for features that are enabled for validation. /// /// This type controls the set of WebAssembly proposals and features that /// are active during validation and parsing of WebAssembly binaries. This /// is used in conjunction with /// [`Validator::new_with_features`](crate::Validator::new_with_features) /// for example. /// /// The [`Default`] implementation for this structure returns the set of /// supported WebAssembly proposals this crate implements. All features are /// required to be in Phase 4 or above in the WebAssembly standardization /// process. /// /// Enabled/disabled features can affect both parsing and validation at this /// time. When decoding a WebAssembly binary it's generally recommended if /// possible to enable all features, as is the default with /// [`BinaryReader::new`](crate::BinaryReader::new). If strict conformance /// with historical versions of the specification are required then /// [`BinaryReader::new_features`](crate::BinaryReader::new_features) or /// [`BinaryReader::set_features`](crate::BinaryReader::set_features) can be /// used. /// /// This crate additionally has a compile-time Cargo feature called /// `features` which can be used to enable/disable most of this type at /// compile time. This crate feature is turned on by default and enables /// this bitflags-representation of this structure. When the `features` /// feature is disabled then this is a zero-sized structure and no longer /// has any associated constants. When `features` are disabled all values /// for proposals are fixed at compile time to their defaults. #[derive(Hash, Debug, Copy, Clone, Eq, PartialEq)] pub struct WasmFeatures: u32 { /// The WebAssembly `mutable-global` proposal. pub mutable_global: MUTABLE_GLOBAL(1) = true; /// The WebAssembly `saturating-float-to-int` proposal. pub saturating_float_to_int: SATURATING_FLOAT_TO_INT(1 << 1) = true; /// The WebAssembly `sign-extension-ops` proposal. pub sign_extension: SIGN_EXTENSION(1 << 2) = true; /// The WebAssembly reference types proposal. pub reference_types: REFERENCE_TYPES(1 << 3) = true; /// The WebAssembly multi-value proposal. pub multi_value: MULTI_VALUE(1 << 4) = true; /// The WebAssembly bulk memory operations proposal. pub bulk_memory: BULK_MEMORY(1 << 5) = true; /// The WebAssembly SIMD proposal. pub simd: SIMD(1 << 6) = true; /// The WebAssembly Relaxed SIMD proposal. pub relaxed_simd: RELAXED_SIMD(1 << 7) = true; /// The WebAssembly threads proposal. pub threads: THREADS(1 << 8) = true; /// The WebAssembly shared-everything-threads proposal; includes new /// component model built-ins. pub shared_everything_threads: SHARED_EVERYTHING_THREADS(1 << 9) = false; /// The WebAssembly tail-call proposal. pub tail_call: TAIL_CALL(1 << 10) = true; /// Whether or not floating-point instructions are enabled. /// /// This is enabled by default can be used to disallow floating-point /// operators and types. /// /// This does not correspond to a WebAssembly proposal but is instead /// intended for embeddings which have stricter-than-usual requirements /// about execution. Floats in WebAssembly can have different NaN patterns /// across hosts which can lead to host-dependent execution which some /// runtimes may not desire. pub floats: FLOATS(1 << 11) = true; /// The WebAssembly multi memory proposal. pub multi_memory: MULTI_MEMORY(1 << 12) = true; /// The WebAssembly exception handling proposal. pub exceptions: EXCEPTIONS(1 << 13) = true; /// The WebAssembly memory64 proposal. pub memory64: MEMORY64(1 << 14) = false; /// The WebAssembly extended_const proposal. pub extended_const: EXTENDED_CONST(1 << 15) = true; /// The WebAssembly component model proposal. pub component_model: COMPONENT_MODEL(1 << 16) = true; /// The WebAssembly typed function references proposal. pub function_references: FUNCTION_REFERENCES(1 << 17) = true; /// The WebAssembly memory control proposal. pub memory_control: MEMORY_CONTROL(1 << 18) = false; /// The WebAssembly gc proposal. pub gc: GC(1 << 19) = true; /// The WebAssembly [custom-page-sizes /// proposal](https://github.com/WebAssembly/custom-page-sizes). pub custom_page_sizes: CUSTOM_PAGE_SIZES(1 << 20) = false; /// Support for the `value` type in the component model proposal. pub component_model_values: COMPONENT_MODEL_VALUES(1 << 21) = false; /// Support for the nested namespaces and projects in component model names. pub component_model_nested_names: COMPONENT_MODEL_NESTED_NAMES(1 << 22) = false; /// Support for more than 32 flags per-type in the component model. pub component_model_more_flags: COMPONENT_MODEL_MORE_FLAGS(1 << 23) = false; /// Support for multiple return values in a component model function. pub component_model_multiple_returns: COMPONENT_MODEL_MULTIPLE_RETURNS(1 << 24) = false; /// The WebAssembly legacy exception handling proposal (phase 1) /// /// # Note /// /// Support this feature as long as all leading browsers also support it /// pub legacy_exceptions: LEGACY_EXCEPTIONS(1 << 25) = false; /// Whether or not gc types are enabled. /// /// This feature does not correspond to any WebAssembly proposal nor /// concept in the specification itself. This is intended to assist /// embedders in disabling support for GC types at validation time. For /// example if an engine wants to support all of WebAssembly except /// a runtime garbage collector it could disable this feature. /// /// This features is enabled by default and is used to gate types such /// as `externref` or `anyref`. Note that the requisite WebAssembly /// proposal must also be enabled for types like `externref`, meaning /// that it requires both `REFERENCE_TYPES` and `GC_TYPE` to be enabled. /// /// Note that the `funcref` and `exnref` types are not gated by this /// feature. Those are expected to not require a full garbage collector /// so are not gated by this. pub gc_types: GC_TYPES(1 << 26) = true; } } impl WasmFeatures { /// The feature set associated with the 1.0 version of the /// WebAssembly specification or the "MVP" feature set. #[cfg(feature = "features")] pub const WASM1: WasmFeatures = WasmFeatures::FLOATS.union(WasmFeatures::GC_TYPES); /// The feature set associated with the 2.0 version of the /// WebAssembly specification. #[cfg(feature = "features")] pub const WASM2: WasmFeatures = WasmFeatures::WASM1 .union(WasmFeatures::BULK_MEMORY) .union(WasmFeatures::REFERENCE_TYPES) .union(WasmFeatures::SIGN_EXTENSION) .union(WasmFeatures::MUTABLE_GLOBAL) .union(WasmFeatures::SATURATING_FLOAT_TO_INT) .union(WasmFeatures::MULTI_VALUE) .union(WasmFeatures::SIMD); /// The feature set associated with the 3.0 version of the /// WebAssembly specification. /// /// Note that as of the time of this writing the 3.0 version of the /// specification is not yet published. The precise set of features set /// here may change as that continues to evolve. #[cfg(feature = "features")] pub const WASM3: WasmFeatures = WasmFeatures::WASM2 .union(WasmFeatures::GC) .union(WasmFeatures::TAIL_CALL) .union(WasmFeatures::EXTENDED_CONST) .union(WasmFeatures::FUNCTION_REFERENCES) .union(WasmFeatures::MULTI_MEMORY) .union(WasmFeatures::RELAXED_SIMD) .union(WasmFeatures::THREADS) .union(WasmFeatures::EXCEPTIONS); } #[cfg(feature = "features")] impl From for WasmFeatures { #[inline] fn from(inflated: WasmFeaturesInflated) -> Self { Self::from_inflated(inflated) } } #[cfg(feature = "features")] impl From for WasmFeaturesInflated { #[inline] fn from(features: WasmFeatures) -> Self { features.inflate() } } wasmparser-0.217.0/src/lib.rs000064400000000000000000001413331046102023000140670ustar 00000000000000/* Copyright 2017 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! A simple event-driven library for parsing WebAssembly binary files //! (or streams). //! //! The parser library reports events as they happen and only stores //! parsing information for a brief period of time, making it very fast //! and memory-efficient. The event-driven model, however, has some drawbacks. //! If you need random access to the entire WebAssembly data-structure, //! this is not the right library for you. You could however, build such //! a data-structure using this library. //! //! To get started, create a [`Parser`] using [`Parser::new`] and then follow //! the examples documented for [`Parser::parse`] or [`Parser::parse_all`]. #![deny(missing_docs)] #![no_std] #![cfg_attr(docsrs, feature(doc_auto_cfg))] extern crate alloc; #[cfg(feature = "std")] #[macro_use] extern crate std; /// A small "prelude" to use throughout this crate. /// /// This crate is tagged with `#![no_std]` meaning that we get libcore's prelude /// by default. This crate also uses `alloc`, however, and common types there /// like `String`. This custom prelude helps bring those types into scope to /// avoid having to import each of them manually. mod prelude { pub use alloc::borrow::ToOwned; pub use alloc::boxed::Box; pub use alloc::format; pub use alloc::string::{String, ToString}; pub use alloc::vec; pub use alloc::vec::Vec; #[cfg(feature = "validate")] pub use crate::collections::{IndexMap, IndexSet, Map, Set}; } /// A helper macro to conveniently iterate over all opcodes recognized by this /// crate. This can be used to work with either the [`Operator`] enumeration or /// the [`VisitOperator`] trait if your use case uniformly handles all operators /// the same way. /// /// It is also possible to specialize handling of operators depending on the /// Wasm proposal from which they are originating. /// /// This is an "iterator macro" where this macro is invoked with the name of /// another macro, and then that macro is invoked with the list of all /// operators. An example invocation of this looks like: /// /// The list of specializable Wasm proposals is as follows: /// /// - `@mvp`: Denoting a Wasm operator from the initial Wasm MVP version. /// - `@exceptions`: [Wasm `exception-handling` proposal] /// - `@tail_call`: [Wasm `tail-calls` proposal] /// - `@reference_types`: [Wasm `reference-types` proposal] /// - `@sign_extension`: [Wasm `sign-extension-ops` proposal] /// - `@saturating_float_to_int`: [Wasm `non_trapping_float-to-int-conversions` proposal] /// - `@bulk_memory `:[Wasm `bulk-memory` proposal] /// - `@threads`: [Wasm `threads` proposal] /// - `@simd`: [Wasm `simd` proposal] /// - `@relaxed_simd`: [Wasm `relaxed-simd` proposal] /// - `@gc`: [Wasm `gc` proposal] /// /// [Wasm `exception-handling` proposal]: /// https://github.com/WebAssembly/exception-handling /// /// [Wasm `tail-calls` proposal]: /// https://github.com/WebAssembly/tail-call /// /// [Wasm `reference-types` proposal]: /// https://github.com/WebAssembly/reference-types /// /// [Wasm `sign-extension-ops` proposal]: /// https://github.com/WebAssembly/sign-extension-ops /// /// [Wasm `non_trapping_float-to-int-conversions` proposal]: /// https://github.com/WebAssembly/nontrapping-float-to-int-conversions /// /// [Wasm `bulk-memory` proposal]: /// https://github.com/WebAssembly/bulk-memory-operations /// /// [Wasm `threads` proposal]: /// https://github.com/webassembly/threads /// /// [Wasm `simd` proposal]: /// https://github.com/webassembly/simd /// /// [Wasm `relaxed-simd` proposal]: /// https://github.com/WebAssembly/relaxed-simd /// /// [Wasm `gc` proposal]: /// https://github.com/WebAssembly/gc /// /// ``` /// macro_rules! define_visit_operator { /// // The outer layer of repetition represents how all operators are /// // provided to the macro at the same time. /// // /// // The `$proposal` identifier indicates the Wasm proposals from which /// // the Wasm operator is originating. /// // For example to specialize the macro match arm for Wasm SIMD proposal /// // operators you could write `@simd` instead of `@$proposal:ident` to /// // only catch those operators. /// // /// // The `$op` name is bound to the `Operator` variant name. The /// // payload of the operator is optionally specified (the `$(...)?` /// // clause) since not all instructions have payloads. Within the payload /// // each argument is named and has its type specified. /// // /// // The `$visit` name is bound to the corresponding name in the /// // `VisitOperator` trait that this corresponds to. /// ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { /// $( /// fn $visit(&mut self $($(,$arg: $argty)*)?) { /// // do nothing for this example /// } /// )* /// } /// } /// /// pub struct VisitAndDoNothing; /// /// impl<'a> wasmparser::VisitOperator<'a> for VisitAndDoNothing { /// type Output = (); /// /// wasmparser::for_each_operator!(define_visit_operator); /// } /// ``` #[macro_export] macro_rules! for_each_operator { ($mac:ident) => { $mac! { @mvp Unreachable => visit_unreachable @mvp Nop => visit_nop @mvp Block { blockty: $crate::BlockType } => visit_block @mvp Loop { blockty: $crate::BlockType } => visit_loop @mvp If { blockty: $crate::BlockType } => visit_if @mvp Else => visit_else @exceptions TryTable { try_table: $crate::TryTable } => visit_try_table @exceptions Throw { tag_index: u32 } => visit_throw @exceptions ThrowRef => visit_throw_ref // Deprecated old instructions from the exceptions proposal @legacy_exceptions Try { blockty: $crate::BlockType } => visit_try @legacy_exceptions Catch { tag_index: u32 } => visit_catch @legacy_exceptions Rethrow { relative_depth: u32 } => visit_rethrow @legacy_exceptions Delegate { relative_depth: u32 } => visit_delegate @legacy_exceptions CatchAll => visit_catch_all @mvp End => visit_end @mvp Br { relative_depth: u32 } => visit_br @mvp BrIf { relative_depth: u32 } => visit_br_if @mvp BrTable { targets: $crate::BrTable<'a> } => visit_br_table @mvp Return => visit_return @mvp Call { function_index: u32 } => visit_call @mvp CallIndirect { type_index: u32, table_index: u32 } => visit_call_indirect @tail_call ReturnCall { function_index: u32 } => visit_return_call @tail_call ReturnCallIndirect { type_index: u32, table_index: u32 } => visit_return_call_indirect @mvp Drop => visit_drop @mvp Select => visit_select @reference_types TypedSelect { ty: $crate::ValType } => visit_typed_select @mvp LocalGet { local_index: u32 } => visit_local_get @mvp LocalSet { local_index: u32 } => visit_local_set @mvp LocalTee { local_index: u32 } => visit_local_tee @mvp GlobalGet { global_index: u32 } => visit_global_get @mvp GlobalSet { global_index: u32 } => visit_global_set @mvp I32Load { memarg: $crate::MemArg } => visit_i32_load @mvp I64Load { memarg: $crate::MemArg } => visit_i64_load @mvp F32Load { memarg: $crate::MemArg } => visit_f32_load @mvp F64Load { memarg: $crate::MemArg } => visit_f64_load @mvp I32Load8S { memarg: $crate::MemArg } => visit_i32_load8_s @mvp I32Load8U { memarg: $crate::MemArg } => visit_i32_load8_u @mvp I32Load16S { memarg: $crate::MemArg } => visit_i32_load16_s @mvp I32Load16U { memarg: $crate::MemArg } => visit_i32_load16_u @mvp I64Load8S { memarg: $crate::MemArg } => visit_i64_load8_s @mvp I64Load8U { memarg: $crate::MemArg } => visit_i64_load8_u @mvp I64Load16S { memarg: $crate::MemArg } => visit_i64_load16_s @mvp I64Load16U { memarg: $crate::MemArg } => visit_i64_load16_u @mvp I64Load32S { memarg: $crate::MemArg } => visit_i64_load32_s @mvp I64Load32U { memarg: $crate::MemArg } => visit_i64_load32_u @mvp I32Store { memarg: $crate::MemArg } => visit_i32_store @mvp I64Store { memarg: $crate::MemArg } => visit_i64_store @mvp F32Store { memarg: $crate::MemArg } => visit_f32_store @mvp F64Store { memarg: $crate::MemArg } => visit_f64_store @mvp I32Store8 { memarg: $crate::MemArg } => visit_i32_store8 @mvp I32Store16 { memarg: $crate::MemArg } => visit_i32_store16 @mvp I64Store8 { memarg: $crate::MemArg } => visit_i64_store8 @mvp I64Store16 { memarg: $crate::MemArg } => visit_i64_store16 @mvp I64Store32 { memarg: $crate::MemArg } => visit_i64_store32 @mvp MemorySize { mem: u32 } => visit_memory_size @mvp MemoryGrow { mem: u32 } => visit_memory_grow @mvp I32Const { value: i32 } => visit_i32_const @mvp I64Const { value: i64 } => visit_i64_const @mvp F32Const { value: $crate::Ieee32 } => visit_f32_const @mvp F64Const { value: $crate::Ieee64 } => visit_f64_const @reference_types RefNull { hty: $crate::HeapType } => visit_ref_null @reference_types RefIsNull => visit_ref_is_null @reference_types RefFunc { function_index: u32 } => visit_ref_func @gc RefEq => visit_ref_eq @mvp I32Eqz => visit_i32_eqz @mvp I32Eq => visit_i32_eq @mvp I32Ne => visit_i32_ne @mvp I32LtS => visit_i32_lt_s @mvp I32LtU => visit_i32_lt_u @mvp I32GtS => visit_i32_gt_s @mvp I32GtU => visit_i32_gt_u @mvp I32LeS => visit_i32_le_s @mvp I32LeU => visit_i32_le_u @mvp I32GeS => visit_i32_ge_s @mvp I32GeU => visit_i32_ge_u @mvp I64Eqz => visit_i64_eqz @mvp I64Eq => visit_i64_eq @mvp I64Ne => visit_i64_ne @mvp I64LtS => visit_i64_lt_s @mvp I64LtU => visit_i64_lt_u @mvp I64GtS => visit_i64_gt_s @mvp I64GtU => visit_i64_gt_u @mvp I64LeS => visit_i64_le_s @mvp I64LeU => visit_i64_le_u @mvp I64GeS => visit_i64_ge_s @mvp I64GeU => visit_i64_ge_u @mvp F32Eq => visit_f32_eq @mvp F32Ne => visit_f32_ne @mvp F32Lt => visit_f32_lt @mvp F32Gt => visit_f32_gt @mvp F32Le => visit_f32_le @mvp F32Ge => visit_f32_ge @mvp F64Eq => visit_f64_eq @mvp F64Ne => visit_f64_ne @mvp F64Lt => visit_f64_lt @mvp F64Gt => visit_f64_gt @mvp F64Le => visit_f64_le @mvp F64Ge => visit_f64_ge @mvp I32Clz => visit_i32_clz @mvp I32Ctz => visit_i32_ctz @mvp I32Popcnt => visit_i32_popcnt @mvp I32Add => visit_i32_add @mvp I32Sub => visit_i32_sub @mvp I32Mul => visit_i32_mul @mvp I32DivS => visit_i32_div_s @mvp I32DivU => visit_i32_div_u @mvp I32RemS => visit_i32_rem_s @mvp I32RemU => visit_i32_rem_u @mvp I32And => visit_i32_and @mvp I32Or => visit_i32_or @mvp I32Xor => visit_i32_xor @mvp I32Shl => visit_i32_shl @mvp I32ShrS => visit_i32_shr_s @mvp I32ShrU => visit_i32_shr_u @mvp I32Rotl => visit_i32_rotl @mvp I32Rotr => visit_i32_rotr @mvp I64Clz => visit_i64_clz @mvp I64Ctz => visit_i64_ctz @mvp I64Popcnt => visit_i64_popcnt @mvp I64Add => visit_i64_add @mvp I64Sub => visit_i64_sub @mvp I64Mul => visit_i64_mul @mvp I64DivS => visit_i64_div_s @mvp I64DivU => visit_i64_div_u @mvp I64RemS => visit_i64_rem_s @mvp I64RemU => visit_i64_rem_u @mvp I64And => visit_i64_and @mvp I64Or => visit_i64_or @mvp I64Xor => visit_i64_xor @mvp I64Shl => visit_i64_shl @mvp I64ShrS => visit_i64_shr_s @mvp I64ShrU => visit_i64_shr_u @mvp I64Rotl => visit_i64_rotl @mvp I64Rotr => visit_i64_rotr @mvp F32Abs => visit_f32_abs @mvp F32Neg => visit_f32_neg @mvp F32Ceil => visit_f32_ceil @mvp F32Floor => visit_f32_floor @mvp F32Trunc => visit_f32_trunc @mvp F32Nearest => visit_f32_nearest @mvp F32Sqrt => visit_f32_sqrt @mvp F32Add => visit_f32_add @mvp F32Sub => visit_f32_sub @mvp F32Mul => visit_f32_mul @mvp F32Div => visit_f32_div @mvp F32Min => visit_f32_min @mvp F32Max => visit_f32_max @mvp F32Copysign => visit_f32_copysign @mvp F64Abs => visit_f64_abs @mvp F64Neg => visit_f64_neg @mvp F64Ceil => visit_f64_ceil @mvp F64Floor => visit_f64_floor @mvp F64Trunc => visit_f64_trunc @mvp F64Nearest => visit_f64_nearest @mvp F64Sqrt => visit_f64_sqrt @mvp F64Add => visit_f64_add @mvp F64Sub => visit_f64_sub @mvp F64Mul => visit_f64_mul @mvp F64Div => visit_f64_div @mvp F64Min => visit_f64_min @mvp F64Max => visit_f64_max @mvp F64Copysign => visit_f64_copysign @mvp I32WrapI64 => visit_i32_wrap_i64 @mvp I32TruncF32S => visit_i32_trunc_f32_s @mvp I32TruncF32U => visit_i32_trunc_f32_u @mvp I32TruncF64S => visit_i32_trunc_f64_s @mvp I32TruncF64U => visit_i32_trunc_f64_u @mvp I64ExtendI32S => visit_i64_extend_i32_s @mvp I64ExtendI32U => visit_i64_extend_i32_u @mvp I64TruncF32S => visit_i64_trunc_f32_s @mvp I64TruncF32U => visit_i64_trunc_f32_u @mvp I64TruncF64S => visit_i64_trunc_f64_s @mvp I64TruncF64U => visit_i64_trunc_f64_u @mvp F32ConvertI32S => visit_f32_convert_i32_s @mvp F32ConvertI32U => visit_f32_convert_i32_u @mvp F32ConvertI64S => visit_f32_convert_i64_s @mvp F32ConvertI64U => visit_f32_convert_i64_u @mvp F32DemoteF64 => visit_f32_demote_f64 @mvp F64ConvertI32S => visit_f64_convert_i32_s @mvp F64ConvertI32U => visit_f64_convert_i32_u @mvp F64ConvertI64S => visit_f64_convert_i64_s @mvp F64ConvertI64U => visit_f64_convert_i64_u @mvp F64PromoteF32 => visit_f64_promote_f32 @mvp I32ReinterpretF32 => visit_i32_reinterpret_f32 @mvp I64ReinterpretF64 => visit_i64_reinterpret_f64 @mvp F32ReinterpretI32 => visit_f32_reinterpret_i32 @mvp F64ReinterpretI64 => visit_f64_reinterpret_i64 @sign_extension I32Extend8S => visit_i32_extend8_s @sign_extension I32Extend16S => visit_i32_extend16_s @sign_extension I64Extend8S => visit_i64_extend8_s @sign_extension I64Extend16S => visit_i64_extend16_s @sign_extension I64Extend32S => visit_i64_extend32_s // 0xFB prefixed operators // Garbage Collection // http://github.com/WebAssembly/gc @gc StructNew { struct_type_index: u32 } => visit_struct_new @gc StructNewDefault { struct_type_index: u32 } => visit_struct_new_default @gc StructGet { struct_type_index: u32, field_index: u32 } => visit_struct_get @gc StructGetS { struct_type_index: u32, field_index: u32 } => visit_struct_get_s @gc StructGetU { struct_type_index: u32, field_index: u32 } => visit_struct_get_u @gc StructSet { struct_type_index: u32, field_index: u32 } => visit_struct_set @gc ArrayNew { array_type_index: u32 } => visit_array_new @gc ArrayNewDefault { array_type_index: u32 } => visit_array_new_default @gc ArrayNewFixed { array_type_index: u32, array_size: u32 } => visit_array_new_fixed @gc ArrayNewData { array_type_index: u32, array_data_index: u32 } => visit_array_new_data @gc ArrayNewElem { array_type_index: u32, array_elem_index: u32 } => visit_array_new_elem @gc ArrayGet { array_type_index: u32 } => visit_array_get @gc ArrayGetS { array_type_index: u32 } => visit_array_get_s @gc ArrayGetU { array_type_index: u32 } => visit_array_get_u @gc ArraySet { array_type_index: u32 } => visit_array_set @gc ArrayLen => visit_array_len @gc ArrayFill { array_type_index: u32 } => visit_array_fill @gc ArrayCopy { array_type_index_dst: u32, array_type_index_src: u32 } => visit_array_copy @gc ArrayInitData { array_type_index: u32, array_data_index: u32 } => visit_array_init_data @gc ArrayInitElem { array_type_index: u32, array_elem_index: u32 } => visit_array_init_elem @gc RefTestNonNull { hty: $crate::HeapType } => visit_ref_test_non_null @gc RefTestNullable { hty: $crate::HeapType } => visit_ref_test_nullable @gc RefCastNonNull { hty: $crate::HeapType } => visit_ref_cast_non_null @gc RefCastNullable { hty: $crate::HeapType } => visit_ref_cast_nullable @gc BrOnCast { relative_depth: u32, from_ref_type: $crate::RefType, to_ref_type: $crate::RefType } => visit_br_on_cast @gc BrOnCastFail { relative_depth: u32, from_ref_type: $crate::RefType, to_ref_type: $crate::RefType } => visit_br_on_cast_fail @gc AnyConvertExtern => visit_any_convert_extern @gc ExternConvertAny => visit_extern_convert_any @gc RefI31 => visit_ref_i31 @gc I31GetS => visit_i31_get_s @gc I31GetU => visit_i31_get_u // 0xFC operators // Non-trapping Float-to-int Conversions // https://github.com/WebAssembly/nontrapping-float-to-int-conversions @saturating_float_to_int I32TruncSatF32S => visit_i32_trunc_sat_f32_s @saturating_float_to_int I32TruncSatF32U => visit_i32_trunc_sat_f32_u @saturating_float_to_int I32TruncSatF64S => visit_i32_trunc_sat_f64_s @saturating_float_to_int I32TruncSatF64U => visit_i32_trunc_sat_f64_u @saturating_float_to_int I64TruncSatF32S => visit_i64_trunc_sat_f32_s @saturating_float_to_int I64TruncSatF32U => visit_i64_trunc_sat_f32_u @saturating_float_to_int I64TruncSatF64S => visit_i64_trunc_sat_f64_s @saturating_float_to_int I64TruncSatF64U => visit_i64_trunc_sat_f64_u // 0xFC prefixed operators // bulk memory operations // https://github.com/WebAssembly/bulk-memory-operations @bulk_memory MemoryInit { data_index: u32, mem: u32 } => visit_memory_init @bulk_memory DataDrop { data_index: u32 } => visit_data_drop @bulk_memory MemoryCopy { dst_mem: u32, src_mem: u32 } => visit_memory_copy @bulk_memory MemoryFill { mem: u32 } => visit_memory_fill @bulk_memory TableInit { elem_index: u32, table: u32 } => visit_table_init @bulk_memory ElemDrop { elem_index: u32 } => visit_elem_drop @bulk_memory TableCopy { dst_table: u32, src_table: u32 } => visit_table_copy // 0xFC prefixed operators // reference-types // https://github.com/WebAssembly/reference-types @reference_types TableFill { table: u32 } => visit_table_fill @reference_types TableGet { table: u32 } => visit_table_get @reference_types TableSet { table: u32 } => visit_table_set @reference_types TableGrow { table: u32 } => visit_table_grow @reference_types TableSize { table: u32 } => visit_table_size // OxFC prefixed operators // memory control (experimental) // https://github.com/WebAssembly/design/issues/1439 @memory_control MemoryDiscard { mem: u32 } => visit_memory_discard // 0xFE prefixed operators // threads // https://github.com/WebAssembly/threads @threads MemoryAtomicNotify { memarg: $crate::MemArg } => visit_memory_atomic_notify @threads MemoryAtomicWait32 { memarg: $crate::MemArg } => visit_memory_atomic_wait32 @threads MemoryAtomicWait64 { memarg: $crate::MemArg } => visit_memory_atomic_wait64 @threads AtomicFence => visit_atomic_fence @threads I32AtomicLoad { memarg: $crate::MemArg } => visit_i32_atomic_load @threads I64AtomicLoad { memarg: $crate::MemArg } => visit_i64_atomic_load @threads I32AtomicLoad8U { memarg: $crate::MemArg } => visit_i32_atomic_load8_u @threads I32AtomicLoad16U { memarg: $crate::MemArg } => visit_i32_atomic_load16_u @threads I64AtomicLoad8U { memarg: $crate::MemArg } => visit_i64_atomic_load8_u @threads I64AtomicLoad16U { memarg: $crate::MemArg } => visit_i64_atomic_load16_u @threads I64AtomicLoad32U { memarg: $crate::MemArg } => visit_i64_atomic_load32_u @threads I32AtomicStore { memarg: $crate::MemArg } => visit_i32_atomic_store @threads I64AtomicStore { memarg: $crate::MemArg } => visit_i64_atomic_store @threads I32AtomicStore8 { memarg: $crate::MemArg } => visit_i32_atomic_store8 @threads I32AtomicStore16 { memarg: $crate::MemArg } => visit_i32_atomic_store16 @threads I64AtomicStore8 { memarg: $crate::MemArg } => visit_i64_atomic_store8 @threads I64AtomicStore16 { memarg: $crate::MemArg } => visit_i64_atomic_store16 @threads I64AtomicStore32 { memarg: $crate::MemArg } => visit_i64_atomic_store32 @threads I32AtomicRmwAdd { memarg: $crate::MemArg } => visit_i32_atomic_rmw_add @threads I64AtomicRmwAdd { memarg: $crate::MemArg } => visit_i64_atomic_rmw_add @threads I32AtomicRmw8AddU { memarg: $crate::MemArg } => visit_i32_atomic_rmw8_add_u @threads I32AtomicRmw16AddU { memarg: $crate::MemArg } => visit_i32_atomic_rmw16_add_u @threads I64AtomicRmw8AddU { memarg: $crate::MemArg } => visit_i64_atomic_rmw8_add_u @threads I64AtomicRmw16AddU { memarg: $crate::MemArg } => visit_i64_atomic_rmw16_add_u @threads I64AtomicRmw32AddU { memarg: $crate::MemArg } => visit_i64_atomic_rmw32_add_u @threads I32AtomicRmwSub { memarg: $crate::MemArg } => visit_i32_atomic_rmw_sub @threads I64AtomicRmwSub { memarg: $crate::MemArg } => visit_i64_atomic_rmw_sub @threads I32AtomicRmw8SubU { memarg: $crate::MemArg } => visit_i32_atomic_rmw8_sub_u @threads I32AtomicRmw16SubU { memarg: $crate::MemArg } => visit_i32_atomic_rmw16_sub_u @threads I64AtomicRmw8SubU { memarg: $crate::MemArg } => visit_i64_atomic_rmw8_sub_u @threads I64AtomicRmw16SubU { memarg: $crate::MemArg } => visit_i64_atomic_rmw16_sub_u @threads I64AtomicRmw32SubU { memarg: $crate::MemArg } => visit_i64_atomic_rmw32_sub_u @threads I32AtomicRmwAnd { memarg: $crate::MemArg } => visit_i32_atomic_rmw_and @threads I64AtomicRmwAnd { memarg: $crate::MemArg } => visit_i64_atomic_rmw_and @threads I32AtomicRmw8AndU { memarg: $crate::MemArg } => visit_i32_atomic_rmw8_and_u @threads I32AtomicRmw16AndU { memarg: $crate::MemArg } => visit_i32_atomic_rmw16_and_u @threads I64AtomicRmw8AndU { memarg: $crate::MemArg } => visit_i64_atomic_rmw8_and_u @threads I64AtomicRmw16AndU { memarg: $crate::MemArg } => visit_i64_atomic_rmw16_and_u @threads I64AtomicRmw32AndU { memarg: $crate::MemArg } => visit_i64_atomic_rmw32_and_u @threads I32AtomicRmwOr { memarg: $crate::MemArg } => visit_i32_atomic_rmw_or @threads I64AtomicRmwOr { memarg: $crate::MemArg } => visit_i64_atomic_rmw_or @threads I32AtomicRmw8OrU { memarg: $crate::MemArg } => visit_i32_atomic_rmw8_or_u @threads I32AtomicRmw16OrU { memarg: $crate::MemArg } => visit_i32_atomic_rmw16_or_u @threads I64AtomicRmw8OrU { memarg: $crate::MemArg } => visit_i64_atomic_rmw8_or_u @threads I64AtomicRmw16OrU { memarg: $crate::MemArg } => visit_i64_atomic_rmw16_or_u @threads I64AtomicRmw32OrU { memarg: $crate::MemArg } => visit_i64_atomic_rmw32_or_u @threads I32AtomicRmwXor { memarg: $crate::MemArg } => visit_i32_atomic_rmw_xor @threads I64AtomicRmwXor { memarg: $crate::MemArg } => visit_i64_atomic_rmw_xor @threads I32AtomicRmw8XorU { memarg: $crate::MemArg } => visit_i32_atomic_rmw8_xor_u @threads I32AtomicRmw16XorU { memarg: $crate::MemArg } => visit_i32_atomic_rmw16_xor_u @threads I64AtomicRmw8XorU { memarg: $crate::MemArg } => visit_i64_atomic_rmw8_xor_u @threads I64AtomicRmw16XorU { memarg: $crate::MemArg } => visit_i64_atomic_rmw16_xor_u @threads I64AtomicRmw32XorU { memarg: $crate::MemArg } => visit_i64_atomic_rmw32_xor_u @threads I32AtomicRmwXchg { memarg: $crate::MemArg } => visit_i32_atomic_rmw_xchg @threads I64AtomicRmwXchg { memarg: $crate::MemArg } => visit_i64_atomic_rmw_xchg @threads I32AtomicRmw8XchgU { memarg: $crate::MemArg } => visit_i32_atomic_rmw8_xchg_u @threads I32AtomicRmw16XchgU { memarg: $crate::MemArg } => visit_i32_atomic_rmw16_xchg_u @threads I64AtomicRmw8XchgU { memarg: $crate::MemArg } => visit_i64_atomic_rmw8_xchg_u @threads I64AtomicRmw16XchgU { memarg: $crate::MemArg } => visit_i64_atomic_rmw16_xchg_u @threads I64AtomicRmw32XchgU { memarg: $crate::MemArg } => visit_i64_atomic_rmw32_xchg_u @threads I32AtomicRmwCmpxchg { memarg: $crate::MemArg } => visit_i32_atomic_rmw_cmpxchg @threads I64AtomicRmwCmpxchg { memarg: $crate::MemArg } => visit_i64_atomic_rmw_cmpxchg @threads I32AtomicRmw8CmpxchgU { memarg: $crate::MemArg } => visit_i32_atomic_rmw8_cmpxchg_u @threads I32AtomicRmw16CmpxchgU { memarg: $crate::MemArg } => visit_i32_atomic_rmw16_cmpxchg_u @threads I64AtomicRmw8CmpxchgU { memarg: $crate::MemArg } => visit_i64_atomic_rmw8_cmpxchg_u @threads I64AtomicRmw16CmpxchgU { memarg: $crate::MemArg } => visit_i64_atomic_rmw16_cmpxchg_u @threads I64AtomicRmw32CmpxchgU { memarg: $crate::MemArg } => visit_i64_atomic_rmw32_cmpxchg_u // Also 0xFE prefixed operators // shared-everything threads // https://github.com/WebAssembly/shared-everything-threads @shared_everything_threads GlobalAtomicGet { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_get @shared_everything_threads GlobalAtomicSet { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_set @shared_everything_threads GlobalAtomicRmwAdd { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_add @shared_everything_threads GlobalAtomicRmwSub { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_sub @shared_everything_threads GlobalAtomicRmwAnd { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_and @shared_everything_threads GlobalAtomicRmwOr { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_or @shared_everything_threads GlobalAtomicRmwXor { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_xor @shared_everything_threads GlobalAtomicRmwXchg { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_xchg @shared_everything_threads GlobalAtomicRmwCmpxchg { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_cmpxchg @shared_everything_threads TableAtomicGet { ordering: $crate::Ordering, table_index: u32 } => visit_table_atomic_get @shared_everything_threads TableAtomicSet { ordering: $crate::Ordering, table_index: u32 } => visit_table_atomic_set @shared_everything_threads TableAtomicRmwXchg { ordering: $crate::Ordering, table_index: u32 } => visit_table_atomic_rmw_xchg @shared_everything_threads TableAtomicRmwCmpxchg { ordering: $crate::Ordering, table_index: u32 } => visit_table_atomic_rmw_cmpxchg @shared_everything_threads StructAtomicGet { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_get @shared_everything_threads StructAtomicGetS { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_get_s @shared_everything_threads StructAtomicGetU { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_get_u @shared_everything_threads StructAtomicSet { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_set @shared_everything_threads StructAtomicRmwAdd { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_rmw_add @shared_everything_threads StructAtomicRmwSub { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_rmw_sub @shared_everything_threads StructAtomicRmwAnd { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_rmw_and @shared_everything_threads StructAtomicRmwOr { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_rmw_or @shared_everything_threads StructAtomicRmwXor { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_rmw_xor @shared_everything_threads StructAtomicRmwXchg { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_rmw_xchg @shared_everything_threads StructAtomicRmwCmpxchg { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32 } => visit_struct_atomic_rmw_cmpxchg @shared_everything_threads ArrayAtomicGet { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_get @shared_everything_threads ArrayAtomicGetS { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_get_s @shared_everything_threads ArrayAtomicGetU { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_get_u @shared_everything_threads ArrayAtomicSet { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_set @shared_everything_threads ArrayAtomicRmwAdd { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_add @shared_everything_threads ArrayAtomicRmwSub { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_sub @shared_everything_threads ArrayAtomicRmwAnd { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_and @shared_everything_threads ArrayAtomicRmwOr { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_or @shared_everything_threads ArrayAtomicRmwXor { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_xor @shared_everything_threads ArrayAtomicRmwXchg { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_xchg @shared_everything_threads ArrayAtomicRmwCmpxchg { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_cmpxchg @shared_everything_threads RefI31Shared => visit_ref_i31_shared // 0xFD operators // 128-bit SIMD // - https://github.com/webassembly/simd // - https://webassembly.github.io/simd/core/binary/instructions.html @simd V128Load { memarg: $crate::MemArg } => visit_v128_load @simd V128Load8x8S { memarg: $crate::MemArg } => visit_v128_load8x8_s @simd V128Load8x8U { memarg: $crate::MemArg } => visit_v128_load8x8_u @simd V128Load16x4S { memarg: $crate::MemArg } => visit_v128_load16x4_s @simd V128Load16x4U { memarg: $crate::MemArg } => visit_v128_load16x4_u @simd V128Load32x2S { memarg: $crate::MemArg } => visit_v128_load32x2_s @simd V128Load32x2U { memarg: $crate::MemArg } => visit_v128_load32x2_u @simd V128Load8Splat { memarg: $crate::MemArg } => visit_v128_load8_splat @simd V128Load16Splat { memarg: $crate::MemArg } => visit_v128_load16_splat @simd V128Load32Splat { memarg: $crate::MemArg } => visit_v128_load32_splat @simd V128Load64Splat { memarg: $crate::MemArg } => visit_v128_load64_splat @simd V128Load32Zero { memarg: $crate::MemArg } => visit_v128_load32_zero @simd V128Load64Zero { memarg: $crate::MemArg } => visit_v128_load64_zero @simd V128Store { memarg: $crate::MemArg } => visit_v128_store @simd V128Load8Lane { memarg: $crate::MemArg, lane: u8 } => visit_v128_load8_lane @simd V128Load16Lane { memarg: $crate::MemArg, lane: u8 } => visit_v128_load16_lane @simd V128Load32Lane { memarg: $crate::MemArg, lane: u8 } => visit_v128_load32_lane @simd V128Load64Lane { memarg: $crate::MemArg, lane: u8 } => visit_v128_load64_lane @simd V128Store8Lane { memarg: $crate::MemArg, lane: u8 } => visit_v128_store8_lane @simd V128Store16Lane { memarg: $crate::MemArg, lane: u8 } => visit_v128_store16_lane @simd V128Store32Lane { memarg: $crate::MemArg, lane: u8 } => visit_v128_store32_lane @simd V128Store64Lane { memarg: $crate::MemArg, lane: u8 } => visit_v128_store64_lane @simd V128Const { value: $crate::V128 } => visit_v128_const @simd I8x16Shuffle { lanes: [u8; 16] } => visit_i8x16_shuffle @simd I8x16ExtractLaneS { lane: u8 } => visit_i8x16_extract_lane_s @simd I8x16ExtractLaneU { lane: u8 } => visit_i8x16_extract_lane_u @simd I8x16ReplaceLane { lane: u8 } => visit_i8x16_replace_lane @simd I16x8ExtractLaneS { lane: u8 } => visit_i16x8_extract_lane_s @simd I16x8ExtractLaneU { lane: u8 } => visit_i16x8_extract_lane_u @simd I16x8ReplaceLane { lane: u8 } => visit_i16x8_replace_lane @simd I32x4ExtractLane { lane: u8 } => visit_i32x4_extract_lane @simd I32x4ReplaceLane { lane: u8 } => visit_i32x4_replace_lane @simd I64x2ExtractLane { lane: u8 } => visit_i64x2_extract_lane @simd I64x2ReplaceLane { lane: u8 } => visit_i64x2_replace_lane @simd F32x4ExtractLane { lane: u8 } => visit_f32x4_extract_lane @simd F32x4ReplaceLane { lane: u8 } => visit_f32x4_replace_lane @simd F64x2ExtractLane { lane: u8 } => visit_f64x2_extract_lane @simd F64x2ReplaceLane { lane: u8 } => visit_f64x2_replace_lane @simd I8x16Swizzle => visit_i8x16_swizzle @simd I8x16Splat => visit_i8x16_splat @simd I16x8Splat => visit_i16x8_splat @simd I32x4Splat => visit_i32x4_splat @simd I64x2Splat => visit_i64x2_splat @simd F32x4Splat => visit_f32x4_splat @simd F64x2Splat => visit_f64x2_splat @simd I8x16Eq => visit_i8x16_eq @simd I8x16Ne => visit_i8x16_ne @simd I8x16LtS => visit_i8x16_lt_s @simd I8x16LtU => visit_i8x16_lt_u @simd I8x16GtS => visit_i8x16_gt_s @simd I8x16GtU => visit_i8x16_gt_u @simd I8x16LeS => visit_i8x16_le_s @simd I8x16LeU => visit_i8x16_le_u @simd I8x16GeS => visit_i8x16_ge_s @simd I8x16GeU => visit_i8x16_ge_u @simd I16x8Eq => visit_i16x8_eq @simd I16x8Ne => visit_i16x8_ne @simd I16x8LtS => visit_i16x8_lt_s @simd I16x8LtU => visit_i16x8_lt_u @simd I16x8GtS => visit_i16x8_gt_s @simd I16x8GtU => visit_i16x8_gt_u @simd I16x8LeS => visit_i16x8_le_s @simd I16x8LeU => visit_i16x8_le_u @simd I16x8GeS => visit_i16x8_ge_s @simd I16x8GeU => visit_i16x8_ge_u @simd I32x4Eq => visit_i32x4_eq @simd I32x4Ne => visit_i32x4_ne @simd I32x4LtS => visit_i32x4_lt_s @simd I32x4LtU => visit_i32x4_lt_u @simd I32x4GtS => visit_i32x4_gt_s @simd I32x4GtU => visit_i32x4_gt_u @simd I32x4LeS => visit_i32x4_le_s @simd I32x4LeU => visit_i32x4_le_u @simd I32x4GeS => visit_i32x4_ge_s @simd I32x4GeU => visit_i32x4_ge_u @simd I64x2Eq => visit_i64x2_eq @simd I64x2Ne => visit_i64x2_ne @simd I64x2LtS => visit_i64x2_lt_s @simd I64x2GtS => visit_i64x2_gt_s @simd I64x2LeS => visit_i64x2_le_s @simd I64x2GeS => visit_i64x2_ge_s @simd F32x4Eq => visit_f32x4_eq @simd F32x4Ne => visit_f32x4_ne @simd F32x4Lt => visit_f32x4_lt @simd F32x4Gt => visit_f32x4_gt @simd F32x4Le => visit_f32x4_le @simd F32x4Ge => visit_f32x4_ge @simd F64x2Eq => visit_f64x2_eq @simd F64x2Ne => visit_f64x2_ne @simd F64x2Lt => visit_f64x2_lt @simd F64x2Gt => visit_f64x2_gt @simd F64x2Le => visit_f64x2_le @simd F64x2Ge => visit_f64x2_ge @simd V128Not => visit_v128_not @simd V128And => visit_v128_and @simd V128AndNot => visit_v128_andnot @simd V128Or => visit_v128_or @simd V128Xor => visit_v128_xor @simd V128Bitselect => visit_v128_bitselect @simd V128AnyTrue => visit_v128_any_true @simd I8x16Abs => visit_i8x16_abs @simd I8x16Neg => visit_i8x16_neg @simd I8x16Popcnt => visit_i8x16_popcnt @simd I8x16AllTrue => visit_i8x16_all_true @simd I8x16Bitmask => visit_i8x16_bitmask @simd I8x16NarrowI16x8S => visit_i8x16_narrow_i16x8_s @simd I8x16NarrowI16x8U => visit_i8x16_narrow_i16x8_u @simd I8x16Shl => visit_i8x16_shl @simd I8x16ShrS => visit_i8x16_shr_s @simd I8x16ShrU => visit_i8x16_shr_u @simd I8x16Add => visit_i8x16_add @simd I8x16AddSatS => visit_i8x16_add_sat_s @simd I8x16AddSatU => visit_i8x16_add_sat_u @simd I8x16Sub => visit_i8x16_sub @simd I8x16SubSatS => visit_i8x16_sub_sat_s @simd I8x16SubSatU => visit_i8x16_sub_sat_u @simd I8x16MinS => visit_i8x16_min_s @simd I8x16MinU => visit_i8x16_min_u @simd I8x16MaxS => visit_i8x16_max_s @simd I8x16MaxU => visit_i8x16_max_u @simd I8x16AvgrU => visit_i8x16_avgr_u @simd I16x8ExtAddPairwiseI8x16S => visit_i16x8_extadd_pairwise_i8x16_s @simd I16x8ExtAddPairwiseI8x16U => visit_i16x8_extadd_pairwise_i8x16_u @simd I16x8Abs => visit_i16x8_abs @simd I16x8Neg => visit_i16x8_neg @simd I16x8Q15MulrSatS => visit_i16x8_q15mulr_sat_s @simd I16x8AllTrue => visit_i16x8_all_true @simd I16x8Bitmask => visit_i16x8_bitmask @simd I16x8NarrowI32x4S => visit_i16x8_narrow_i32x4_s @simd I16x8NarrowI32x4U => visit_i16x8_narrow_i32x4_u @simd I16x8ExtendLowI8x16S => visit_i16x8_extend_low_i8x16_s @simd I16x8ExtendHighI8x16S => visit_i16x8_extend_high_i8x16_s @simd I16x8ExtendLowI8x16U => visit_i16x8_extend_low_i8x16_u @simd I16x8ExtendHighI8x16U => visit_i16x8_extend_high_i8x16_u @simd I16x8Shl => visit_i16x8_shl @simd I16x8ShrS => visit_i16x8_shr_s @simd I16x8ShrU => visit_i16x8_shr_u @simd I16x8Add => visit_i16x8_add @simd I16x8AddSatS => visit_i16x8_add_sat_s @simd I16x8AddSatU => visit_i16x8_add_sat_u @simd I16x8Sub => visit_i16x8_sub @simd I16x8SubSatS => visit_i16x8_sub_sat_s @simd I16x8SubSatU => visit_i16x8_sub_sat_u @simd I16x8Mul => visit_i16x8_mul @simd I16x8MinS => visit_i16x8_min_s @simd I16x8MinU => visit_i16x8_min_u @simd I16x8MaxS => visit_i16x8_max_s @simd I16x8MaxU => visit_i16x8_max_u @simd I16x8AvgrU => visit_i16x8_avgr_u @simd I16x8ExtMulLowI8x16S => visit_i16x8_extmul_low_i8x16_s @simd I16x8ExtMulHighI8x16S => visit_i16x8_extmul_high_i8x16_s @simd I16x8ExtMulLowI8x16U => visit_i16x8_extmul_low_i8x16_u @simd I16x8ExtMulHighI8x16U => visit_i16x8_extmul_high_i8x16_u @simd I32x4ExtAddPairwiseI16x8S => visit_i32x4_extadd_pairwise_i16x8_s @simd I32x4ExtAddPairwiseI16x8U => visit_i32x4_extadd_pairwise_i16x8_u @simd I32x4Abs => visit_i32x4_abs @simd I32x4Neg => visit_i32x4_neg @simd I32x4AllTrue => visit_i32x4_all_true @simd I32x4Bitmask => visit_i32x4_bitmask @simd I32x4ExtendLowI16x8S => visit_i32x4_extend_low_i16x8_s @simd I32x4ExtendHighI16x8S => visit_i32x4_extend_high_i16x8_s @simd I32x4ExtendLowI16x8U => visit_i32x4_extend_low_i16x8_u @simd I32x4ExtendHighI16x8U => visit_i32x4_extend_high_i16x8_u @simd I32x4Shl => visit_i32x4_shl @simd I32x4ShrS => visit_i32x4_shr_s @simd I32x4ShrU => visit_i32x4_shr_u @simd I32x4Add => visit_i32x4_add @simd I32x4Sub => visit_i32x4_sub @simd I32x4Mul => visit_i32x4_mul @simd I32x4MinS => visit_i32x4_min_s @simd I32x4MinU => visit_i32x4_min_u @simd I32x4MaxS => visit_i32x4_max_s @simd I32x4MaxU => visit_i32x4_max_u @simd I32x4DotI16x8S => visit_i32x4_dot_i16x8_s @simd I32x4ExtMulLowI16x8S => visit_i32x4_extmul_low_i16x8_s @simd I32x4ExtMulHighI16x8S => visit_i32x4_extmul_high_i16x8_s @simd I32x4ExtMulLowI16x8U => visit_i32x4_extmul_low_i16x8_u @simd I32x4ExtMulHighI16x8U => visit_i32x4_extmul_high_i16x8_u @simd I64x2Abs => visit_i64x2_abs @simd I64x2Neg => visit_i64x2_neg @simd I64x2AllTrue => visit_i64x2_all_true @simd I64x2Bitmask => visit_i64x2_bitmask @simd I64x2ExtendLowI32x4S => visit_i64x2_extend_low_i32x4_s @simd I64x2ExtendHighI32x4S => visit_i64x2_extend_high_i32x4_s @simd I64x2ExtendLowI32x4U => visit_i64x2_extend_low_i32x4_u @simd I64x2ExtendHighI32x4U => visit_i64x2_extend_high_i32x4_u @simd I64x2Shl => visit_i64x2_shl @simd I64x2ShrS => visit_i64x2_shr_s @simd I64x2ShrU => visit_i64x2_shr_u @simd I64x2Add => visit_i64x2_add @simd I64x2Sub => visit_i64x2_sub @simd I64x2Mul => visit_i64x2_mul @simd I64x2ExtMulLowI32x4S => visit_i64x2_extmul_low_i32x4_s @simd I64x2ExtMulHighI32x4S => visit_i64x2_extmul_high_i32x4_s @simd I64x2ExtMulLowI32x4U => visit_i64x2_extmul_low_i32x4_u @simd I64x2ExtMulHighI32x4U => visit_i64x2_extmul_high_i32x4_u @simd F32x4Ceil => visit_f32x4_ceil @simd F32x4Floor => visit_f32x4_floor @simd F32x4Trunc => visit_f32x4_trunc @simd F32x4Nearest => visit_f32x4_nearest @simd F32x4Abs => visit_f32x4_abs @simd F32x4Neg => visit_f32x4_neg @simd F32x4Sqrt => visit_f32x4_sqrt @simd F32x4Add => visit_f32x4_add @simd F32x4Sub => visit_f32x4_sub @simd F32x4Mul => visit_f32x4_mul @simd F32x4Div => visit_f32x4_div @simd F32x4Min => visit_f32x4_min @simd F32x4Max => visit_f32x4_max @simd F32x4PMin => visit_f32x4_pmin @simd F32x4PMax => visit_f32x4_pmax @simd F64x2Ceil => visit_f64x2_ceil @simd F64x2Floor => visit_f64x2_floor @simd F64x2Trunc => visit_f64x2_trunc @simd F64x2Nearest => visit_f64x2_nearest @simd F64x2Abs => visit_f64x2_abs @simd F64x2Neg => visit_f64x2_neg @simd F64x2Sqrt => visit_f64x2_sqrt @simd F64x2Add => visit_f64x2_add @simd F64x2Sub => visit_f64x2_sub @simd F64x2Mul => visit_f64x2_mul @simd F64x2Div => visit_f64x2_div @simd F64x2Min => visit_f64x2_min @simd F64x2Max => visit_f64x2_max @simd F64x2PMin => visit_f64x2_pmin @simd F64x2PMax => visit_f64x2_pmax @simd I32x4TruncSatF32x4S => visit_i32x4_trunc_sat_f32x4_s @simd I32x4TruncSatF32x4U => visit_i32x4_trunc_sat_f32x4_u @simd F32x4ConvertI32x4S => visit_f32x4_convert_i32x4_s @simd F32x4ConvertI32x4U => visit_f32x4_convert_i32x4_u @simd I32x4TruncSatF64x2SZero => visit_i32x4_trunc_sat_f64x2_s_zero @simd I32x4TruncSatF64x2UZero => visit_i32x4_trunc_sat_f64x2_u_zero @simd F64x2ConvertLowI32x4S => visit_f64x2_convert_low_i32x4_s @simd F64x2ConvertLowI32x4U => visit_f64x2_convert_low_i32x4_u @simd F32x4DemoteF64x2Zero => visit_f32x4_demote_f64x2_zero @simd F64x2PromoteLowF32x4 => visit_f64x2_promote_low_f32x4 // Relaxed SIMD operators // https://github.com/WebAssembly/relaxed-simd @relaxed_simd I8x16RelaxedSwizzle => visit_i8x16_relaxed_swizzle @relaxed_simd I32x4RelaxedTruncF32x4S => visit_i32x4_relaxed_trunc_f32x4_s @relaxed_simd I32x4RelaxedTruncF32x4U => visit_i32x4_relaxed_trunc_f32x4_u @relaxed_simd I32x4RelaxedTruncF64x2SZero => visit_i32x4_relaxed_trunc_f64x2_s_zero @relaxed_simd I32x4RelaxedTruncF64x2UZero => visit_i32x4_relaxed_trunc_f64x2_u_zero @relaxed_simd F32x4RelaxedMadd => visit_f32x4_relaxed_madd @relaxed_simd F32x4RelaxedNmadd => visit_f32x4_relaxed_nmadd @relaxed_simd F64x2RelaxedMadd => visit_f64x2_relaxed_madd @relaxed_simd F64x2RelaxedNmadd => visit_f64x2_relaxed_nmadd @relaxed_simd I8x16RelaxedLaneselect => visit_i8x16_relaxed_laneselect @relaxed_simd I16x8RelaxedLaneselect => visit_i16x8_relaxed_laneselect @relaxed_simd I32x4RelaxedLaneselect => visit_i32x4_relaxed_laneselect @relaxed_simd I64x2RelaxedLaneselect => visit_i64x2_relaxed_laneselect @relaxed_simd F32x4RelaxedMin => visit_f32x4_relaxed_min @relaxed_simd F32x4RelaxedMax => visit_f32x4_relaxed_max @relaxed_simd F64x2RelaxedMin => visit_f64x2_relaxed_min @relaxed_simd F64x2RelaxedMax => visit_f64x2_relaxed_max @relaxed_simd I16x8RelaxedQ15mulrS => visit_i16x8_relaxed_q15mulr_s @relaxed_simd I16x8RelaxedDotI8x16I7x16S => visit_i16x8_relaxed_dot_i8x16_i7x16_s @relaxed_simd I32x4RelaxedDotI8x16I7x16AddS => visit_i32x4_relaxed_dot_i8x16_i7x16_add_s // Typed Function references @function_references CallRef { type_index: u32 } => visit_call_ref @function_references ReturnCallRef { type_index: u32 } => visit_return_call_ref @function_references RefAsNonNull => visit_ref_as_non_null @function_references BrOnNull { relative_depth: u32 } => visit_br_on_null @function_references BrOnNonNull { relative_depth: u32 } => visit_br_on_non_null } }; } macro_rules! format_err { ($offset:expr, $($arg:tt)*) => { crate::BinaryReaderError::fmt(format_args!($($arg)*), $offset) } } macro_rules! bail { ($($arg:tt)*) => {return Err(format_err!($($arg)*))} } pub use crate::binary_reader::{BinaryReader, BinaryReaderError, Result}; pub use crate::features::*; pub use crate::parser::*; pub use crate::readers::*; mod binary_reader; mod features; mod limits; mod parser; mod readers; #[cfg(feature = "validate")] mod resources; #[cfg(feature = "validate")] mod validator; #[cfg(feature = "validate")] pub use crate::resources::*; #[cfg(feature = "validate")] pub use crate::validator::*; #[cfg(feature = "validate")] pub mod collections; wasmparser-0.217.0/src/limits.rs000064400000000000000000000067531046102023000146300ustar 00000000000000/* Copyright 2017 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #![cfg_attr(not(feature = "validate"), allow(dead_code))] // The following limits are imposed by wasmparser on WebAssembly modules. // The limits are agreed upon with other engines for consistency. pub const MAX_WASM_TYPES: usize = 1_000_000; pub const MAX_WASM_SUPERTYPES: usize = 1; pub const MAX_WASM_FUNCTIONS: usize = 1_000_000; pub const MAX_WASM_IMPORTS: usize = 1_000_000; pub const MAX_WASM_EXPORTS: usize = 1_000_000; pub const MAX_WASM_GLOBALS: usize = 1_000_000; pub const MAX_WASM_ELEMENT_SEGMENTS: usize = 100_000; pub const MAX_WASM_DATA_SEGMENTS: usize = 100_000; pub const MAX_WASM_STRING_SIZE: usize = 100_000; pub const MAX_WASM_FUNCTION_SIZE: usize = 128 * 1024; pub const MAX_WASM_FUNCTION_LOCALS: usize = 50000; pub const MAX_WASM_FUNCTION_PARAMS: usize = 1000; pub const MAX_WASM_FUNCTION_RETURNS: usize = 1000; pub const _MAX_WASM_TABLE_SIZE: usize = 10_000_000; pub const MAX_WASM_TABLE_ENTRIES: usize = 10_000_000; pub const MAX_WASM_TABLES: usize = 100; pub const MAX_WASM_MEMORIES: usize = 100; pub const MAX_WASM_TAGS: usize = 1_000_000; pub const MAX_WASM_BR_TABLE_SIZE: usize = MAX_WASM_FUNCTION_SIZE; pub const MAX_WASM_STRUCT_FIELDS: usize = 10_000; pub const MAX_WASM_CATCHES: usize = 10_000; pub const MAX_WASM_SUBTYPING_DEPTH: usize = 63; pub const DEFAULT_WASM_PAGE_SIZE: u64 = 1 << 16; pub fn max_wasm_memory32_pages(page_size: u64) -> u64 { assert!(page_size.is_power_of_two()); assert!(page_size <= DEFAULT_WASM_PAGE_SIZE); (1 << 32) / page_size } pub fn max_wasm_memory64_pages(page_size: u64) -> u64 { assert!(page_size.is_power_of_two()); assert!(page_size <= DEFAULT_WASM_PAGE_SIZE); u64::try_from((1_u128 << 64) / u128::from(page_size)).unwrap_or(u64::MAX) } // Component-related limits pub const MAX_WASM_MODULE_SIZE: usize = 1024 * 1024 * 1024; //= 1 GiB pub const MAX_WASM_MODULE_TYPE_DECLS: usize = 100_000; pub const MAX_WASM_COMPONENT_TYPE_DECLS: usize = 100_000; pub const MAX_WASM_INSTANCE_TYPE_DECLS: usize = 100_000; pub const MAX_WASM_RECORD_FIELDS: usize = 10_000; pub const MAX_WASM_VARIANT_CASES: usize = 10_000; pub const MAX_WASM_TUPLE_TYPES: usize = 10_000; pub const MAX_WASM_FLAG_NAMES: usize = 1_000; pub const MAX_WASM_ENUM_CASES: usize = 10_000; pub const MAX_WASM_INSTANTIATION_EXPORTS: usize = 100_000; pub const MAX_WASM_CANONICAL_OPTIONS: usize = 10; pub const MAX_WASM_INSTANTIATION_ARGS: usize = 100_000; pub const MAX_WASM_START_ARGS: usize = 1000; pub const MAX_WASM_TYPE_SIZE: u32 = 1_000_000; pub const MAX_WASM_MODULES: usize = 1_000; pub const MAX_WASM_COMPONENTS: usize = 1_000; pub const MAX_WASM_INSTANCES: usize = 1_000; pub const MAX_WASM_VALUES: usize = 1_000; /// Core items in components such as globals/memories/tables don't actually /// create new definitions but are instead just aliases to preexisting items. /// This means they have a different limit than the core wasm based limits. pub const MAX_CORE_INDEX_SPACE_ITEMS: usize = 1_000_000; wasmparser-0.217.0/src/parser.rs000064400000000000000000002053301046102023000146130ustar 00000000000000use crate::binary_reader::WASM_MAGIC_NUMBER; use crate::prelude::*; use crate::CoreTypeSectionReader; #[cfg(feature = "features")] use crate::WasmFeatures; use crate::{ limits::MAX_WASM_MODULE_SIZE, BinaryReader, BinaryReaderError, ComponentCanonicalSectionReader, ComponentExportSectionReader, ComponentImportSectionReader, ComponentInstanceSectionReader, ComponentStartFunction, ComponentTypeSectionReader, CustomSectionReader, DataSectionReader, ElementSectionReader, ExportSectionReader, FromReader, FunctionBody, FunctionSectionReader, GlobalSectionReader, ImportSectionReader, InstanceSectionReader, MemorySectionReader, Result, SectionLimited, TableSectionReader, TagSectionReader, TypeSectionReader, }; use core::fmt; use core::iter; use core::ops::Range; pub(crate) const WASM_MODULE_VERSION: u16 = 0x1; // Note that this started at `0xa` and we're incrementing up from there. When // the component model is stabilized this will become 0x1. The changes here are: // // * [????-??-??] 0xa - original version // * [2023-01-05] 0xb - `export` introduces an alias // * [2023-02-06] 0xc - `export` has an optional type ascribed to it // * [2023-05-10] 0xd - imports/exports drop URLs, new discriminator byte which // allows for `(import (interface "...") ...)` syntax. pub(crate) const WASM_COMPONENT_VERSION: u16 = 0xd; const KIND_MODULE: u16 = 0x00; const KIND_COMPONENT: u16 = 0x01; /// The supported encoding formats for the parser. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Encoding { /// The encoding format is a WebAssembly module. Module, /// The encoding format is a WebAssembly component. Component, } /// An incremental parser of a binary WebAssembly module or component. /// /// This type is intended to be used to incrementally parse a WebAssembly module /// or component as bytes become available for the module. This can also be used /// to parse modules or components that are already entirely resident within memory. /// /// This primary function for a parser is the [`Parser::parse`] function which /// will incrementally consume input. You can also use the [`Parser::parse_all`] /// function to parse a module or component that is entirely resident in memory. #[derive(Debug, Clone)] pub struct Parser { state: State, offset: u64, max_size: u64, encoding: Encoding, #[cfg(feature = "features")] features: WasmFeatures, } #[derive(Debug, Clone)] enum State { Header, SectionStart, FunctionBody { remaining: u32, len: u32 }, } /// A successful return payload from [`Parser::parse`]. /// /// On success one of two possible values can be returned, either that more data /// is needed to continue parsing or a chunk of the input was parsed, indicating /// how much of it was parsed. #[derive(Debug)] pub enum Chunk<'a> { /// This can be returned at any time and indicates that more data is needed /// to proceed with parsing. Zero bytes were consumed from the input to /// [`Parser::parse`]. The `u64` value here is a hint as to how many more /// bytes are needed to continue parsing. NeedMoreData(u64), /// A chunk was successfully parsed. Parsed { /// This many bytes of the `data` input to [`Parser::parse`] were /// consumed to produce `payload`. consumed: usize, /// The value that we actually parsed. payload: Payload<'a>, }, } /// Values that can be parsed from a WebAssembly module or component. /// /// This enumeration is all possible chunks of pieces that can be parsed by a /// [`Parser`] from a binary WebAssembly module or component. Note that for many /// sections the entire section is parsed all at once, whereas other functions, /// like the code section, are parsed incrementally. This is a distinction where some /// sections, like the type section, are required to be fully resident in memory /// (fully downloaded) before proceeding. Other sections, like the code section, /// can be processed in a streaming fashion where each function is extracted /// individually so it can possibly be shipped to another thread while you wait /// for more functions to get downloaded. /// /// Note that payloads, when returned, do not indicate that the module or component /// is valid. For example when you receive a `Payload::TypeSection` the type /// section itself has not yet actually been parsed. The reader returned will be /// able to parse it, but you'll have to actually iterate the reader to do the /// full parse. Each payload returned is intended to be a *window* into the /// original `data` passed to [`Parser::parse`] which can be further processed /// if necessary. pub enum Payload<'a> { /// Indicates the header of a WebAssembly module or component. Version { /// The version number found in the header. num: u16, /// The encoding format being parsed. encoding: Encoding, /// The range of bytes that were parsed to consume the header of the /// module or component. Note that this range is relative to the start /// of the byte stream. range: Range, }, /// A module type section was received and the provided reader can be /// used to parse the contents of the type section. TypeSection(TypeSectionReader<'a>), /// A module import section was received and the provided reader can be /// used to parse the contents of the import section. ImportSection(ImportSectionReader<'a>), /// A module function section was received and the provided reader can be /// used to parse the contents of the function section. FunctionSection(FunctionSectionReader<'a>), /// A module table section was received and the provided reader can be /// used to parse the contents of the table section. TableSection(TableSectionReader<'a>), /// A module memory section was received and the provided reader can be /// used to parse the contents of the memory section. MemorySection(MemorySectionReader<'a>), /// A module tag section was received, and the provided reader can be /// used to parse the contents of the tag section. TagSection(TagSectionReader<'a>), /// A module global section was received and the provided reader can be /// used to parse the contents of the global section. GlobalSection(GlobalSectionReader<'a>), /// A module export section was received, and the provided reader can be /// used to parse the contents of the export section. ExportSection(ExportSectionReader<'a>), /// A module start section was received. StartSection { /// The start function index func: u32, /// The range of bytes that specify the `func` field, specified in /// offsets relative to the start of the byte stream. range: Range, }, /// A module element section was received and the provided reader can be /// used to parse the contents of the element section. ElementSection(ElementSectionReader<'a>), /// A module data count section was received. DataCountSection { /// The number of data segments. count: u32, /// The range of bytes that specify the `count` field, specified in /// offsets relative to the start of the byte stream. range: Range, }, /// A module data section was received and the provided reader can be /// used to parse the contents of the data section. DataSection(DataSectionReader<'a>), /// Indicator of the start of the code section of a WebAssembly module. /// /// This entry is returned whenever the code section starts. The `count` /// field indicates how many entries are in this code section. After /// receiving this start marker you're guaranteed that the next `count` /// items will be either `CodeSectionEntry` or an error will be returned. /// /// This, unlike other sections, is intended to be used for streaming the /// contents of the code section. The code section is not required to be /// fully resident in memory when we parse it. Instead a [`Parser`] is /// capable of parsing piece-by-piece of a code section. CodeSectionStart { /// The number of functions in this section. count: u32, /// The range of bytes that represent this section, specified in /// offsets relative to the start of the byte stream. range: Range, /// The size, in bytes, of the remaining contents of this section. /// /// This can be used in combination with [`Parser::skip_section`] /// where the caller will know how many bytes to skip before feeding /// bytes into `Parser` again. size: u32, }, /// An entry of the code section, a function, was parsed from a WebAssembly /// module. /// /// This entry indicates that a function was successfully received from the /// code section, and the payload here is the window into the original input /// where the function resides. Note that the function itself has not been /// parsed, it's only been outlined. You'll need to process the /// `FunctionBody` provided to test whether it parses and/or is valid. CodeSectionEntry(FunctionBody<'a>), /// A core module section was received and the provided parser can be /// used to parse the nested module. /// /// This variant is special in that it returns a sub-`Parser`. Upon /// receiving a `ModuleSection` it is expected that the returned /// `Parser` will be used instead of the parent `Parser` until the parse has /// finished. You'll need to feed data into the `Parser` returned until it /// returns `Payload::End`. After that you'll switch back to the parent /// parser to resume parsing the rest of the current component. /// /// Note that binaries will not be parsed correctly if you feed the data for /// a nested module into the parent [`Parser`]. ModuleSection { /// The parser for the nested module. parser: Parser, /// The range of bytes that represent the nested module in the /// original byte stream. /// /// Note that, to better support streaming parsing and validation, the /// validator does *not* check that this range is in bounds. unchecked_range: Range, }, /// A core instance section was received and the provided parser can be /// used to parse the contents of the core instance section. /// /// Currently this section is only parsed in a component. InstanceSection(InstanceSectionReader<'a>), /// A core type section was received and the provided parser can be /// used to parse the contents of the core type section. /// /// Currently this section is only parsed in a component. CoreTypeSection(CoreTypeSectionReader<'a>), /// A component section from a WebAssembly component was received and the /// provided parser can be used to parse the nested component. /// /// This variant is special in that it returns a sub-`Parser`. Upon /// receiving a `ComponentSection` it is expected that the returned /// `Parser` will be used instead of the parent `Parser` until the parse has /// finished. You'll need to feed data into the `Parser` returned until it /// returns `Payload::End`. After that you'll switch back to the parent /// parser to resume parsing the rest of the current component. /// /// Note that binaries will not be parsed correctly if you feed the data for /// a nested component into the parent [`Parser`]. ComponentSection { /// The parser for the nested component. parser: Parser, /// The range of bytes that represent the nested component in the /// original byte stream. /// /// Note that, to better support streaming parsing and validation, the /// validator does *not* check that this range is in bounds. unchecked_range: Range, }, /// A component instance section was received and the provided reader can be /// used to parse the contents of the component instance section. ComponentInstanceSection(ComponentInstanceSectionReader<'a>), /// A component alias section was received and the provided reader can be /// used to parse the contents of the component alias section. ComponentAliasSection(SectionLimited<'a, crate::ComponentAlias<'a>>), /// A component type section was received and the provided reader can be /// used to parse the contents of the component type section. ComponentTypeSection(ComponentTypeSectionReader<'a>), /// A component canonical section was received and the provided reader can be /// used to parse the contents of the component canonical section. ComponentCanonicalSection(ComponentCanonicalSectionReader<'a>), /// A component start section was received. ComponentStartSection { /// The start function description. start: ComponentStartFunction, /// The range of bytes that specify the `start` field. range: Range, }, /// A component import section was received and the provided reader can be /// used to parse the contents of the component import section. ComponentImportSection(ComponentImportSectionReader<'a>), /// A component export section was received, and the provided reader can be /// used to parse the contents of the component export section. ComponentExportSection(ComponentExportSectionReader<'a>), /// A module or component custom section was received. CustomSection(CustomSectionReader<'a>), /// An unknown section was found. /// /// This variant is returned for all unknown sections encountered. This /// likely wants to be interpreted as an error by consumers of the parser, /// but this can also be used to parse sections currently unsupported by /// the parser. UnknownSection { /// The 8-bit identifier for this section. id: u8, /// The contents of this section. contents: &'a [u8], /// The range of bytes, relative to the start of the original data /// stream, that the contents of this section reside in. range: Range, }, /// The end of the WebAssembly module or component was reached. /// /// The value is the offset in the input byte stream where the end /// was reached. End(usize), } const CUSTOM_SECTION: u8 = 0; const TYPE_SECTION: u8 = 1; const IMPORT_SECTION: u8 = 2; const FUNCTION_SECTION: u8 = 3; const TABLE_SECTION: u8 = 4; const MEMORY_SECTION: u8 = 5; const GLOBAL_SECTION: u8 = 6; const EXPORT_SECTION: u8 = 7; const START_SECTION: u8 = 8; const ELEMENT_SECTION: u8 = 9; const CODE_SECTION: u8 = 10; const DATA_SECTION: u8 = 11; const DATA_COUNT_SECTION: u8 = 12; const TAG_SECTION: u8 = 13; const COMPONENT_MODULE_SECTION: u8 = 1; const COMPONENT_CORE_INSTANCE_SECTION: u8 = 2; const COMPONENT_CORE_TYPE_SECTION: u8 = 3; const COMPONENT_SECTION: u8 = 4; const COMPONENT_INSTANCE_SECTION: u8 = 5; const COMPONENT_ALIAS_SECTION: u8 = 6; const COMPONENT_TYPE_SECTION: u8 = 7; const COMPONENT_CANONICAL_SECTION: u8 = 8; const COMPONENT_START_SECTION: u8 = 9; const COMPONENT_IMPORT_SECTION: u8 = 10; const COMPONENT_EXPORT_SECTION: u8 = 11; impl Parser { /// Creates a new parser. /// /// Reports errors and ranges relative to `offset` provided, where `offset` /// is some logical offset within the input stream that we're parsing. pub fn new(offset: u64) -> Parser { Parser { state: State::Header, offset, max_size: u64::MAX, // Assume the encoding is a module until we know otherwise encoding: Encoding::Module, #[cfg(feature = "features")] features: WasmFeatures::all(), } } /// Tests whether `bytes` looks like a core WebAssembly module. /// /// This will inspect the first 8 bytes of `bytes` and return `true` if it /// starts with the standard core WebAssembly header. pub fn is_core_wasm(bytes: &[u8]) -> bool { const HEADER: [u8; 8] = [ WASM_MAGIC_NUMBER[0], WASM_MAGIC_NUMBER[1], WASM_MAGIC_NUMBER[2], WASM_MAGIC_NUMBER[3], WASM_MODULE_VERSION.to_le_bytes()[0], WASM_MODULE_VERSION.to_le_bytes()[1], KIND_MODULE.to_le_bytes()[0], KIND_MODULE.to_le_bytes()[1], ]; bytes.starts_with(&HEADER) } /// Tests whether `bytes` looks like a WebAssembly component. /// /// This will inspect the first 8 bytes of `bytes` and return `true` if it /// starts with the standard WebAssembly component header. pub fn is_component(bytes: &[u8]) -> bool { const HEADER: [u8; 8] = [ WASM_MAGIC_NUMBER[0], WASM_MAGIC_NUMBER[1], WASM_MAGIC_NUMBER[2], WASM_MAGIC_NUMBER[3], WASM_COMPONENT_VERSION.to_le_bytes()[0], WASM_COMPONENT_VERSION.to_le_bytes()[1], KIND_COMPONENT.to_le_bytes()[0], KIND_COMPONENT.to_le_bytes()[1], ]; bytes.starts_with(&HEADER) } /// Returns the currently active set of wasm features that this parser is /// using while parsing. /// /// The default set of features is [`WasmFeatures::all()`] for new parsers. /// /// For more information see [`BinaryReader::new`]. #[cfg(feature = "features")] pub fn features(&self) -> WasmFeatures { self.features } /// Sets the wasm features active while parsing to the `features` specified. /// /// The default set of features is [`WasmFeatures::all()`] for new parsers. /// /// For more information see [`BinaryReader::new`]. #[cfg(feature = "features")] pub fn set_features(&mut self, features: WasmFeatures) { self.features = features; } /// Attempts to parse a chunk of data. /// /// This method will attempt to parse the next incremental portion of a /// WebAssembly binary. Data available for the module or component is /// provided as `data`, and the data can be incomplete if more data has yet /// to arrive. The `eof` flag indicates whether more data will ever be received. /// /// There are two ways parsing can succeed with this method: /// /// * `Chunk::NeedMoreData` - this indicates that there is not enough bytes /// in `data` to parse a payload. The caller needs to wait for more data to /// be available in this situation before calling this method again. It is /// guaranteed that this is only returned if `eof` is `false`. /// /// * `Chunk::Parsed` - this indicates that a chunk of the input was /// successfully parsed. The payload is available in this variant of what /// was parsed, and this also indicates how many bytes of `data` was /// consumed. It's expected that the caller will not provide these bytes /// back to the [`Parser`] again. /// /// Note that all `Chunk` return values are connected, with a lifetime, to /// the input buffer. Each parsed chunk borrows the input buffer and is a /// view into it for successfully parsed chunks. /// /// It is expected that you'll call this method until `Payload::End` is /// reached, at which point you're guaranteed that the parse has completed. /// Note that complete parsing, for the top-level module or component, /// implies that `data` is empty and `eof` is `true`. /// /// # Errors /// /// Parse errors are returned as an `Err`. Errors can happen when the /// structure of the data is unexpected or if sections are too large for /// example. Note that errors are not returned for malformed *contents* of /// sections here. Sections are generally not individually parsed and each /// returned [`Payload`] needs to be iterated over further to detect all /// errors. /// /// # Examples /// /// An example of reading a wasm file from a stream (`std::io::Read`) and /// incrementally parsing it. /// /// ``` /// use std::io::Read; /// use anyhow::Result; /// use wasmparser::{Parser, Chunk, Payload::*}; /// /// fn parse(mut reader: impl Read) -> Result<()> { /// let mut buf = Vec::new(); /// let mut cur = Parser::new(0); /// let mut eof = false; /// let mut stack = Vec::new(); /// /// loop { /// let (payload, consumed) = match cur.parse(&buf, eof)? { /// Chunk::NeedMoreData(hint) => { /// assert!(!eof); // otherwise an error would be returned /// /// // Use the hint to preallocate more space, then read /// // some more data into our buffer. /// // /// // Note that the buffer management here is not ideal, /// // but it's compact enough to fit in an example! /// let len = buf.len(); /// buf.extend((0..hint).map(|_| 0u8)); /// let n = reader.read(&mut buf[len..])?; /// buf.truncate(len + n); /// eof = n == 0; /// continue; /// } /// /// Chunk::Parsed { consumed, payload } => (payload, consumed), /// }; /// /// match payload { /// // Sections for WebAssembly modules /// Version { .. } => { /* ... */ } /// TypeSection(_) => { /* ... */ } /// ImportSection(_) => { /* ... */ } /// FunctionSection(_) => { /* ... */ } /// TableSection(_) => { /* ... */ } /// MemorySection(_) => { /* ... */ } /// TagSection(_) => { /* ... */ } /// GlobalSection(_) => { /* ... */ } /// ExportSection(_) => { /* ... */ } /// StartSection { .. } => { /* ... */ } /// ElementSection(_) => { /* ... */ } /// DataCountSection { .. } => { /* ... */ } /// DataSection(_) => { /* ... */ } /// /// // Here we know how many functions we'll be receiving as /// // `CodeSectionEntry`, so we can prepare for that, and /// // afterwards we can parse and handle each function /// // individually. /// CodeSectionStart { .. } => { /* ... */ } /// CodeSectionEntry(body) => { /// // here we can iterate over `body` to parse the function /// // and its locals /// } /// /// // Sections for WebAssembly components /// InstanceSection(_) => { /* ... */ } /// CoreTypeSection(_) => { /* ... */ } /// ComponentInstanceSection(_) => { /* ... */ } /// ComponentAliasSection(_) => { /* ... */ } /// ComponentTypeSection(_) => { /* ... */ } /// ComponentCanonicalSection(_) => { /* ... */ } /// ComponentStartSection { .. } => { /* ... */ } /// ComponentImportSection(_) => { /* ... */ } /// ComponentExportSection(_) => { /* ... */ } /// /// ModuleSection { parser, .. } /// | ComponentSection { parser, .. } => { /// stack.push(cur.clone()); /// cur = parser.clone(); /// } /// /// CustomSection(_) => { /* ... */ } /// /// // most likely you'd return an error here /// UnknownSection { id, .. } => { /* ... */ } /// /// // Once we've reached the end of a parser we either resume /// // at the parent parser or we break out of the loop because /// // we're done. /// End(_) => { /// if let Some(parent_parser) = stack.pop() { /// cur = parent_parser; /// } else { /// break; /// } /// } /// } /// /// // once we're done processing the payload we can forget the /// // original. /// buf.drain(..consumed); /// } /// /// Ok(()) /// } /// /// # parse(&b"\0asm\x01\0\0\0"[..]).unwrap(); /// ``` pub fn parse<'a>(&mut self, data: &'a [u8], eof: bool) -> Result> { let (data, eof) = if usize_to_u64(data.len()) > self.max_size { (&data[..(self.max_size as usize)], true) } else { (data, eof) }; // TODO: thread through `offset: u64` to `BinaryReader`, remove // the cast here. let starting_offset = self.offset as usize; let mut reader = BinaryReader::new(data, starting_offset); #[cfg(feature = "features")] { reader.set_features(self.features); } match self.parse_reader(&mut reader, eof) { Ok(payload) => { // Be sure to update our offset with how far we got in the // reader let consumed = reader.original_position() - starting_offset; self.offset += usize_to_u64(consumed); self.max_size -= usize_to_u64(consumed); Ok(Chunk::Parsed { consumed: consumed, payload, }) } Err(e) => { // If we're at EOF then there's no way we can recover from any // error, so continue to propagate it. if eof { return Err(e); } // If our error doesn't look like it can be resolved with more // data being pulled down, then propagate it, otherwise switch // the error to "feed me please" match e.inner.needed_hint { Some(hint) => Ok(Chunk::NeedMoreData(usize_to_u64(hint))), None => Err(e), } } } } fn parse_reader<'a>( &mut self, reader: &mut BinaryReader<'a>, eof: bool, ) -> Result> { use Payload::*; match self.state { State::Header => { let start = reader.original_position(); let header_version = reader.read_header_version()?; self.encoding = match (header_version >> 16) as u16 { KIND_MODULE => Encoding::Module, KIND_COMPONENT => Encoding::Component, _ => bail!(start + 4, "unknown binary version: {header_version:#10x}"), }; let num = header_version as u16; self.state = State::SectionStart; Ok(Version { num, encoding: self.encoding, range: start..reader.original_position(), }) } State::SectionStart => { // If we're at eof and there are no bytes in our buffer, then // that means we reached the end of the data since it's // just a bunch of sections concatenated after the header. if eof && reader.bytes_remaining() == 0 { return Ok(Payload::End(reader.original_position())); } let id_pos = reader.original_position(); let id = reader.read_u8()?; if id & 0x80 != 0 { return Err(BinaryReaderError::new("malformed section id", id_pos)); } let len_pos = reader.original_position(); let mut len = reader.read_var_u32()?; // Test to make sure that this section actually fits within // `Parser::max_size`. This doesn't matter for top-level modules // but it is required for nested modules/components to correctly ensure // that all sections live entirely within their section of the // file. let consumed = reader.original_position() - id_pos; let section_overflow = self .max_size .checked_sub(usize_to_u64(consumed)) .and_then(|s| s.checked_sub(len.into())) .is_none(); if section_overflow { return Err(BinaryReaderError::new("section too large", len_pos)); } match (self.encoding, id) { // Sections for both modules and components. (_, 0) => section(reader, len, CustomSectionReader::new, CustomSection), // Module sections (Encoding::Module, TYPE_SECTION) => { section(reader, len, TypeSectionReader::new, TypeSection) } (Encoding::Module, IMPORT_SECTION) => { section(reader, len, ImportSectionReader::new, ImportSection) } (Encoding::Module, FUNCTION_SECTION) => { section(reader, len, FunctionSectionReader::new, FunctionSection) } (Encoding::Module, TABLE_SECTION) => { section(reader, len, TableSectionReader::new, TableSection) } (Encoding::Module, MEMORY_SECTION) => { section(reader, len, MemorySectionReader::new, MemorySection) } (Encoding::Module, GLOBAL_SECTION) => { section(reader, len, GlobalSectionReader::new, GlobalSection) } (Encoding::Module, EXPORT_SECTION) => { section(reader, len, ExportSectionReader::new, ExportSection) } (Encoding::Module, START_SECTION) => { let (func, range) = single_item(reader, len, "start")?; Ok(StartSection { func, range }) } (Encoding::Module, ELEMENT_SECTION) => { section(reader, len, ElementSectionReader::new, ElementSection) } (Encoding::Module, CODE_SECTION) => { let start = reader.original_position(); let count = delimited(reader, &mut len, |r| r.read_var_u32())?; let range = start..reader.original_position() + len as usize; self.state = State::FunctionBody { remaining: count, len, }; Ok(CodeSectionStart { count, range, size: len, }) } (Encoding::Module, DATA_SECTION) => { section(reader, len, DataSectionReader::new, DataSection) } (Encoding::Module, DATA_COUNT_SECTION) => { let (count, range) = single_item(reader, len, "data count")?; Ok(DataCountSection { count, range }) } (Encoding::Module, TAG_SECTION) => { section(reader, len, TagSectionReader::new, TagSection) } // Component sections (Encoding::Component, COMPONENT_MODULE_SECTION) | (Encoding::Component, COMPONENT_SECTION) => { if len as usize > MAX_WASM_MODULE_SIZE { bail!( len_pos, "{} section is too large", if id == 1 { "module" } else { "component " } ); } let range = reader.original_position() ..reader.original_position() + usize::try_from(len).unwrap(); self.max_size -= u64::from(len); self.offset += u64::from(len); let mut parser = Parser::new(usize_to_u64(reader.original_position())); #[cfg(feature = "features")] { parser.features = self.features; } parser.max_size = u64::from(len); Ok(match id { 1 => ModuleSection { parser, unchecked_range: range, }, 4 => ComponentSection { parser, unchecked_range: range, }, _ => unreachable!(), }) } (Encoding::Component, COMPONENT_CORE_INSTANCE_SECTION) => { section(reader, len, InstanceSectionReader::new, InstanceSection) } (Encoding::Component, COMPONENT_CORE_TYPE_SECTION) => { section(reader, len, CoreTypeSectionReader::new, CoreTypeSection) } (Encoding::Component, COMPONENT_INSTANCE_SECTION) => section( reader, len, ComponentInstanceSectionReader::new, ComponentInstanceSection, ), (Encoding::Component, COMPONENT_ALIAS_SECTION) => { section(reader, len, SectionLimited::new, ComponentAliasSection) } (Encoding::Component, COMPONENT_TYPE_SECTION) => section( reader, len, ComponentTypeSectionReader::new, ComponentTypeSection, ), (Encoding::Component, COMPONENT_CANONICAL_SECTION) => section( reader, len, ComponentCanonicalSectionReader::new, ComponentCanonicalSection, ), (Encoding::Component, COMPONENT_START_SECTION) => { let (start, range) = single_item(reader, len, "component start")?; Ok(ComponentStartSection { start, range }) } (Encoding::Component, COMPONENT_IMPORT_SECTION) => section( reader, len, ComponentImportSectionReader::new, ComponentImportSection, ), (Encoding::Component, COMPONENT_EXPORT_SECTION) => section( reader, len, ComponentExportSectionReader::new, ComponentExportSection, ), (_, id) => { let offset = reader.original_position(); let contents = reader.read_bytes(len as usize)?; let range = offset..offset + len as usize; Ok(UnknownSection { id, contents, range, }) } } } // Once we hit 0 remaining incrementally parsed items, with 0 // remaining bytes in each section, we're done and can switch back // to parsing sections. State::FunctionBody { remaining: 0, len: 0, } => { self.state = State::SectionStart; self.parse_reader(reader, eof) } // ... otherwise trailing bytes with no remaining entries in these // sections indicates an error. State::FunctionBody { remaining: 0, len } => { debug_assert!(len > 0); let offset = reader.original_position(); Err(BinaryReaderError::new( "trailing bytes at end of section", offset, )) } // Functions are relatively easy to parse when we know there's at // least one remaining and at least one byte available to read // things. // // We use the remaining length try to read a u32 size of the // function, and using that size we require the entire function be // resident in memory. This means that we're reading whole chunks of // functions at a time. // // Limiting via `Parser::max_size` (nested parsing) happens above in // `fn parse`, and limiting by our section size happens via // `delimited`. Actual parsing of the function body is delegated to // the caller to iterate over the `FunctionBody` structure. State::FunctionBody { remaining, mut len } => { let body = delimited(reader, &mut len, |r| { Ok(FunctionBody::new(r.read_reader()?)) })?; self.state = State::FunctionBody { remaining: remaining - 1, len, }; Ok(CodeSectionEntry(body)) } } } /// Convenience function that can be used to parse a module or component /// that is entirely resident in memory. /// /// This function will parse the `data` provided as a WebAssembly module /// or component. /// /// Note that when this function yields sections that provide parsers, /// no further action is required for those sections as payloads from /// those parsers will be automatically returned. /// /// # Examples /// /// An example of reading a wasm file from a stream (`std::io::Read`) into /// a buffer and then parsing it. /// /// ``` /// use std::io::Read; /// use anyhow::Result; /// use wasmparser::{Parser, Chunk, Payload::*}; /// /// fn parse(mut reader: impl Read) -> Result<()> { /// let mut buf = Vec::new(); /// reader.read_to_end(&mut buf)?; /// let parser = Parser::new(0); /// /// for payload in parser.parse_all(&buf) { /// match payload? { /// // Sections for WebAssembly modules /// Version { .. } => { /* ... */ } /// TypeSection(_) => { /* ... */ } /// ImportSection(_) => { /* ... */ } /// FunctionSection(_) => { /* ... */ } /// TableSection(_) => { /* ... */ } /// MemorySection(_) => { /* ... */ } /// TagSection(_) => { /* ... */ } /// GlobalSection(_) => { /* ... */ } /// ExportSection(_) => { /* ... */ } /// StartSection { .. } => { /* ... */ } /// ElementSection(_) => { /* ... */ } /// DataCountSection { .. } => { /* ... */ } /// DataSection(_) => { /* ... */ } /// /// // Here we know how many functions we'll be receiving as /// // `CodeSectionEntry`, so we can prepare for that, and /// // afterwards we can parse and handle each function /// // individually. /// CodeSectionStart { .. } => { /* ... */ } /// CodeSectionEntry(body) => { /// // here we can iterate over `body` to parse the function /// // and its locals /// } /// /// // Sections for WebAssembly components /// ModuleSection { .. } => { /* ... */ } /// InstanceSection(_) => { /* ... */ } /// CoreTypeSection(_) => { /* ... */ } /// ComponentSection { .. } => { /* ... */ } /// ComponentInstanceSection(_) => { /* ... */ } /// ComponentAliasSection(_) => { /* ... */ } /// ComponentTypeSection(_) => { /* ... */ } /// ComponentCanonicalSection(_) => { /* ... */ } /// ComponentStartSection { .. } => { /* ... */ } /// ComponentImportSection(_) => { /* ... */ } /// ComponentExportSection(_) => { /* ... */ } /// /// CustomSection(_) => { /* ... */ } /// /// // most likely you'd return an error here /// UnknownSection { id, .. } => { /* ... */ } /// /// // Once we've reached the end of a parser we either resume /// // at the parent parser or the payload iterator is at its /// // end and we're done. /// End(_) => {} /// } /// } /// /// Ok(()) /// } /// /// # parse(&b"\0asm\x01\0\0\0"[..]).unwrap(); /// ``` pub fn parse_all(self, mut data: &[u8]) -> impl Iterator> { let mut stack = Vec::new(); let mut cur = self; let mut done = false; iter::from_fn(move || { if done { return None; } let payload = match cur.parse(data, true) { // Propagate all errors Err(e) => { done = true; return Some(Err(e)); } // This isn't possible because `eof` is always true. Ok(Chunk::NeedMoreData(_)) => unreachable!(), Ok(Chunk::Parsed { payload, consumed }) => { data = &data[consumed..]; payload } }; match &payload { Payload::ModuleSection { parser, .. } | Payload::ComponentSection { parser, .. } => { stack.push(cur.clone()); cur = parser.clone(); } Payload::End(_) => match stack.pop() { Some(p) => cur = p, None => done = true, }, _ => {} } Some(Ok(payload)) }) } /// Skip parsing the code section entirely. /// /// This function can be used to indicate, after receiving /// `CodeSectionStart`, that the section will not be parsed. /// /// The caller will be responsible for skipping `size` bytes (found in the /// `CodeSectionStart` payload). Bytes should only be fed into `parse` /// after the `size` bytes have been skipped. /// /// # Panics /// /// This function will panic if the parser is not in a state where it's /// parsing the code section. /// /// # Examples /// /// ``` /// use wasmparser::{Result, Parser, Chunk, Payload::*}; /// use core::ops::Range; /// /// fn objdump_headers(mut wasm: &[u8]) -> Result<()> { /// let mut parser = Parser::new(0); /// loop { /// let payload = match parser.parse(wasm, true)? { /// Chunk::Parsed { consumed, payload } => { /// wasm = &wasm[consumed..]; /// payload /// } /// // this state isn't possible with `eof = true` /// Chunk::NeedMoreData(_) => unreachable!(), /// }; /// match payload { /// TypeSection(s) => print_range("type section", &s.range()), /// ImportSection(s) => print_range("import section", &s.range()), /// // .. other sections /// /// // Print the range of the code section we see, but don't /// // actually iterate over each individual function. /// CodeSectionStart { range, size, .. } => { /// print_range("code section", &range); /// parser.skip_section(); /// wasm = &wasm[size as usize..]; /// } /// End(_) => break, /// _ => {} /// } /// } /// Ok(()) /// } /// /// fn print_range(section: &str, range: &Range) { /// println!("{:>40}: {:#010x} - {:#010x}", section, range.start, range.end); /// } /// ``` pub fn skip_section(&mut self) { let skip = match self.state { State::FunctionBody { remaining: _, len } => len, _ => panic!("wrong state to call `skip_section`"), }; self.offset += u64::from(skip); self.max_size -= u64::from(skip); self.state = State::SectionStart; } } fn usize_to_u64(a: usize) -> u64 { a.try_into().unwrap() } /// Parses an entire section resident in memory into a `Payload`. /// /// Requires that `len` bytes are resident in `reader` and uses `ctor`/`variant` /// to construct the section to return. fn section<'a, T>( reader: &mut BinaryReader<'a>, len: u32, ctor: fn(BinaryReader<'a>) -> Result, variant: fn(T) -> Payload<'a>, ) -> Result> { let reader = reader.skip(|r| { r.read_bytes(len as usize)?; Ok(()) })?; // clear the hint for "need this many more bytes" here because we already // read all the bytes, so it's not possible to read more bytes if this // fails. let reader = ctor(reader).map_err(clear_hint)?; Ok(variant(reader)) } /// Reads a section that is represented by a single uleb-encoded `u32`. fn single_item<'a, T>( reader: &mut BinaryReader<'a>, len: u32, desc: &str, ) -> Result<(T, Range)> where T: FromReader<'a>, { let range = reader.original_position()..reader.original_position() + len as usize; let mut content = reader.skip(|r| { r.read_bytes(len as usize)?; Ok(()) })?; // We can't recover from "unexpected eof" here because our entire section is // already resident in memory, so clear the hint for how many more bytes are // expected. let ret = content.read().map_err(clear_hint)?; if !content.eof() { bail!( content.original_position(), "unexpected content in the {desc} section", ); } Ok((ret, range)) } /// Attempts to parse using `f`. /// /// This will update `*len` with the number of bytes consumed, and it will cause /// a failure to be returned instead of the number of bytes consumed exceeds /// what `*len` currently is. fn delimited<'a, T>( reader: &mut BinaryReader<'a>, len: &mut u32, f: impl FnOnce(&mut BinaryReader<'a>) -> Result, ) -> Result { let start = reader.original_position(); let ret = f(reader)?; *len = match (reader.original_position() - start) .try_into() .ok() .and_then(|i| len.checked_sub(i)) { Some(i) => i, None => return Err(BinaryReaderError::new("unexpected end-of-file", start)), }; Ok(ret) } impl Default for Parser { fn default() -> Parser { Parser::new(0) } } impl Payload<'_> { /// If this `Payload` represents a section in the original wasm module then /// the section's id and range within the original wasm binary are returned. /// /// Not all payloads refer to entire sections, such as the `Version` and /// `CodeSectionEntry` variants. These variants will return `None` from this /// function. /// /// Otherwise this function will return `Some` where the first element is /// the byte identifier for the section and the second element is the range /// of the contents of the section within the original wasm binary. /// /// The purpose of this method is to enable tools to easily iterate over /// entire sections if necessary and handle sections uniformly, for example /// dropping custom sections while preserving all other sections. pub fn as_section(&self) -> Option<(u8, Range)> { use Payload::*; match self { Version { .. } => None, TypeSection(s) => Some((TYPE_SECTION, s.range())), ImportSection(s) => Some((IMPORT_SECTION, s.range())), FunctionSection(s) => Some((FUNCTION_SECTION, s.range())), TableSection(s) => Some((TABLE_SECTION, s.range())), MemorySection(s) => Some((MEMORY_SECTION, s.range())), TagSection(s) => Some((TAG_SECTION, s.range())), GlobalSection(s) => Some((GLOBAL_SECTION, s.range())), ExportSection(s) => Some((EXPORT_SECTION, s.range())), ElementSection(s) => Some((ELEMENT_SECTION, s.range())), DataSection(s) => Some((DATA_SECTION, s.range())), StartSection { range, .. } => Some((START_SECTION, range.clone())), DataCountSection { range, .. } => Some((DATA_COUNT_SECTION, range.clone())), CodeSectionStart { range, .. } => Some((CODE_SECTION, range.clone())), CodeSectionEntry(_) => None, ModuleSection { unchecked_range: range, .. } => Some((COMPONENT_MODULE_SECTION, range.clone())), InstanceSection(s) => Some((COMPONENT_CORE_INSTANCE_SECTION, s.range())), CoreTypeSection(s) => Some((COMPONENT_CORE_TYPE_SECTION, s.range())), ComponentSection { unchecked_range: range, .. } => Some((COMPONENT_SECTION, range.clone())), ComponentInstanceSection(s) => Some((COMPONENT_INSTANCE_SECTION, s.range())), ComponentAliasSection(s) => Some((COMPONENT_ALIAS_SECTION, s.range())), ComponentTypeSection(s) => Some((COMPONENT_TYPE_SECTION, s.range())), ComponentCanonicalSection(s) => Some((COMPONENT_CANONICAL_SECTION, s.range())), ComponentStartSection { range, .. } => Some((COMPONENT_START_SECTION, range.clone())), ComponentImportSection(s) => Some((COMPONENT_IMPORT_SECTION, s.range())), ComponentExportSection(s) => Some((COMPONENT_EXPORT_SECTION, s.range())), CustomSection(c) => Some((CUSTOM_SECTION, c.range())), UnknownSection { id, range, .. } => Some((*id, range.clone())), End(_) => None, } } } impl fmt::Debug for Payload<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use Payload::*; match self { Version { num, encoding, range, } => f .debug_struct("Version") .field("num", num) .field("encoding", encoding) .field("range", range) .finish(), // Module sections TypeSection(_) => f.debug_tuple("TypeSection").field(&"...").finish(), ImportSection(_) => f.debug_tuple("ImportSection").field(&"...").finish(), FunctionSection(_) => f.debug_tuple("FunctionSection").field(&"...").finish(), TableSection(_) => f.debug_tuple("TableSection").field(&"...").finish(), MemorySection(_) => f.debug_tuple("MemorySection").field(&"...").finish(), TagSection(_) => f.debug_tuple("TagSection").field(&"...").finish(), GlobalSection(_) => f.debug_tuple("GlobalSection").field(&"...").finish(), ExportSection(_) => f.debug_tuple("ExportSection").field(&"...").finish(), ElementSection(_) => f.debug_tuple("ElementSection").field(&"...").finish(), DataSection(_) => f.debug_tuple("DataSection").field(&"...").finish(), StartSection { func, range } => f .debug_struct("StartSection") .field("func", func) .field("range", range) .finish(), DataCountSection { count, range } => f .debug_struct("DataCountSection") .field("count", count) .field("range", range) .finish(), CodeSectionStart { count, range, size } => f .debug_struct("CodeSectionStart") .field("count", count) .field("range", range) .field("size", size) .finish(), CodeSectionEntry(_) => f.debug_tuple("CodeSectionEntry").field(&"...").finish(), // Component sections ModuleSection { parser: _, unchecked_range: range, } => f .debug_struct("ModuleSection") .field("range", range) .finish(), InstanceSection(_) => f.debug_tuple("InstanceSection").field(&"...").finish(), CoreTypeSection(_) => f.debug_tuple("CoreTypeSection").field(&"...").finish(), ComponentSection { parser: _, unchecked_range: range, } => f .debug_struct("ComponentSection") .field("range", range) .finish(), ComponentInstanceSection(_) => f .debug_tuple("ComponentInstanceSection") .field(&"...") .finish(), ComponentAliasSection(_) => f .debug_tuple("ComponentAliasSection") .field(&"...") .finish(), ComponentTypeSection(_) => f.debug_tuple("ComponentTypeSection").field(&"...").finish(), ComponentCanonicalSection(_) => f .debug_tuple("ComponentCanonicalSection") .field(&"...") .finish(), ComponentStartSection { .. } => f .debug_tuple("ComponentStartSection") .field(&"...") .finish(), ComponentImportSection(_) => f .debug_tuple("ComponentImportSection") .field(&"...") .finish(), ComponentExportSection(_) => f .debug_tuple("ComponentExportSection") .field(&"...") .finish(), CustomSection(c) => f.debug_tuple("CustomSection").field(c).finish(), UnknownSection { id, range, .. } => f .debug_struct("UnknownSection") .field("id", id) .field("range", range) .finish(), End(offset) => f.debug_tuple("End").field(offset).finish(), } } } fn clear_hint(mut err: BinaryReaderError) -> BinaryReaderError { err.inner.needed_hint = None; err } #[cfg(test)] mod tests { use super::*; macro_rules! assert_matches { ($a:expr, $b:pat $(,)?) => { match $a { $b => {} a => panic!("`{:?}` doesn't match `{}`", a, stringify!($b)), } }; } #[test] fn header() { assert!(Parser::default().parse(&[], true).is_err()); assert_matches!( Parser::default().parse(&[], false), Ok(Chunk::NeedMoreData(4)), ); assert_matches!( Parser::default().parse(b"\0", false), Ok(Chunk::NeedMoreData(3)), ); assert_matches!( Parser::default().parse(b"\0asm", false), Ok(Chunk::NeedMoreData(4)), ); assert_matches!( Parser::default().parse(b"\0asm\x01\0\0\0", false), Ok(Chunk::Parsed { consumed: 8, payload: Payload::Version { num: 1, .. }, }), ); } #[test] fn header_iter() { for _ in Parser::default().parse_all(&[]) {} for _ in Parser::default().parse_all(b"\0") {} for _ in Parser::default().parse_all(b"\0asm") {} for _ in Parser::default().parse_all(b"\0asm\x01\x01\x01\x01") {} } fn parser_after_header() -> Parser { let mut p = Parser::default(); assert_matches!( p.parse(b"\0asm\x01\0\0\0", false), Ok(Chunk::Parsed { consumed: 8, payload: Payload::Version { num: WASM_MODULE_VERSION, encoding: Encoding::Module, .. }, }), ); p } fn parser_after_component_header() -> Parser { let mut p = Parser::default(); assert_matches!( p.parse(b"\0asm\x0d\0\x01\0", false), Ok(Chunk::Parsed { consumed: 8, payload: Payload::Version { num: WASM_COMPONENT_VERSION, encoding: Encoding::Component, .. }, }), ); p } #[test] fn start_section() { assert_matches!( parser_after_header().parse(&[], false), Ok(Chunk::NeedMoreData(1)), ); assert!(parser_after_header().parse(&[8], true).is_err()); assert!(parser_after_header().parse(&[8, 1], true).is_err()); assert!(parser_after_header().parse(&[8, 2], true).is_err()); assert_matches!( parser_after_header().parse(&[8], false), Ok(Chunk::NeedMoreData(1)), ); assert_matches!( parser_after_header().parse(&[8, 1], false), Ok(Chunk::NeedMoreData(1)), ); assert_matches!( parser_after_header().parse(&[8, 2], false), Ok(Chunk::NeedMoreData(2)), ); assert_matches!( parser_after_header().parse(&[8, 1, 1], false), Ok(Chunk::Parsed { consumed: 3, payload: Payload::StartSection { func: 1, .. }, }), ); assert!(parser_after_header().parse(&[8, 2, 1, 1], false).is_err()); assert!(parser_after_header().parse(&[8, 0], false).is_err()); } #[test] fn end_works() { assert_matches!( parser_after_header().parse(&[], true), Ok(Chunk::Parsed { consumed: 0, payload: Payload::End(8), }), ); } #[test] fn type_section() { assert!(parser_after_header().parse(&[1], true).is_err()); assert!(parser_after_header().parse(&[1, 0], false).is_err()); assert!(parser_after_header().parse(&[8, 2], true).is_err()); assert_matches!( parser_after_header().parse(&[1], false), Ok(Chunk::NeedMoreData(1)), ); assert_matches!( parser_after_header().parse(&[1, 1], false), Ok(Chunk::NeedMoreData(1)), ); assert_matches!( parser_after_header().parse(&[1, 1, 1], false), Ok(Chunk::Parsed { consumed: 3, payload: Payload::TypeSection(_), }), ); assert_matches!( parser_after_header().parse(&[1, 1, 1, 2, 3, 4], false), Ok(Chunk::Parsed { consumed: 3, payload: Payload::TypeSection(_), }), ); } #[test] fn custom_section() { assert!(parser_after_header().parse(&[0], true).is_err()); assert!(parser_after_header().parse(&[0, 0], false).is_err()); assert!(parser_after_header().parse(&[0, 1, 1], false).is_err()); assert_matches!( parser_after_header().parse(&[0, 2, 1], false), Ok(Chunk::NeedMoreData(1)), ); assert_custom( parser_after_header().parse(&[0, 1, 0], false).unwrap(), 3, "", 11, b"", Range { start: 10, end: 11 }, ); assert_custom( parser_after_header() .parse(&[0, 2, 1, b'a'], false) .unwrap(), 4, "a", 12, b"", Range { start: 10, end: 12 }, ); assert_custom( parser_after_header() .parse(&[0, 2, 0, b'a'], false) .unwrap(), 4, "", 11, b"a", Range { start: 10, end: 12 }, ); } fn assert_custom( chunk: Chunk<'_>, expected_consumed: usize, expected_name: &str, expected_data_offset: usize, expected_data: &[u8], expected_range: Range, ) { let (consumed, s) = match chunk { Chunk::Parsed { consumed, payload: Payload::CustomSection(s), } => (consumed, s), _ => panic!("not a custom section payload"), }; assert_eq!(consumed, expected_consumed); assert_eq!(s.name(), expected_name); assert_eq!(s.data_offset(), expected_data_offset); assert_eq!(s.data(), expected_data); assert_eq!(s.range(), expected_range); } #[test] fn function_section() { assert!(parser_after_header().parse(&[10], true).is_err()); assert!(parser_after_header().parse(&[10, 0], true).is_err()); assert!(parser_after_header().parse(&[10, 1], true).is_err()); assert_matches!( parser_after_header().parse(&[10], false), Ok(Chunk::NeedMoreData(1)) ); assert_matches!( parser_after_header().parse(&[10, 1], false), Ok(Chunk::NeedMoreData(1)) ); let mut p = parser_after_header(); assert_matches!( p.parse(&[10, 1, 0], false), Ok(Chunk::Parsed { consumed: 3, payload: Payload::CodeSectionStart { count: 0, .. }, }), ); assert_matches!( p.parse(&[], true), Ok(Chunk::Parsed { consumed: 0, payload: Payload::End(11), }), ); let mut p = parser_after_header(); assert_matches!( p.parse(&[10, 2, 1, 0], false), Ok(Chunk::Parsed { consumed: 3, payload: Payload::CodeSectionStart { count: 1, .. }, }), ); assert_matches!( p.parse(&[0], false), Ok(Chunk::Parsed { consumed: 1, payload: Payload::CodeSectionEntry(_), }), ); assert_matches!( p.parse(&[], true), Ok(Chunk::Parsed { consumed: 0, payload: Payload::End(12), }), ); // 1 byte section with 1 function can't read the function body because // the section is too small let mut p = parser_after_header(); assert_matches!( p.parse(&[10, 1, 1], false), Ok(Chunk::Parsed { consumed: 3, payload: Payload::CodeSectionStart { count: 1, .. }, }), ); assert_eq!( p.parse(&[0], false).unwrap_err().message(), "unexpected end-of-file" ); // section with 2 functions but section is cut off let mut p = parser_after_header(); assert_matches!( p.parse(&[10, 2, 2], false), Ok(Chunk::Parsed { consumed: 3, payload: Payload::CodeSectionStart { count: 2, .. }, }), ); assert_matches!( p.parse(&[0], false), Ok(Chunk::Parsed { consumed: 1, payload: Payload::CodeSectionEntry(_), }), ); assert_matches!(p.parse(&[], false), Ok(Chunk::NeedMoreData(1))); assert_eq!( p.parse(&[0], false).unwrap_err().message(), "unexpected end-of-file", ); // trailing data is bad let mut p = parser_after_header(); assert_matches!( p.parse(&[10, 3, 1], false), Ok(Chunk::Parsed { consumed: 3, payload: Payload::CodeSectionStart { count: 1, .. }, }), ); assert_matches!( p.parse(&[0], false), Ok(Chunk::Parsed { consumed: 1, payload: Payload::CodeSectionEntry(_), }), ); assert_eq!( p.parse(&[0], false).unwrap_err().message(), "trailing bytes at end of section", ); } #[test] fn single_module() { let mut p = parser_after_component_header(); assert_matches!(p.parse(&[4], false), Ok(Chunk::NeedMoreData(1))); // A module that's 8 bytes in length let mut sub = match p.parse(&[1, 8], false) { Ok(Chunk::Parsed { consumed: 2, payload: Payload::ModuleSection { parser, .. }, }) => parser, other => panic!("bad parse {:?}", other), }; // Parse the header of the submodule with the sub-parser. assert_matches!(sub.parse(&[], false), Ok(Chunk::NeedMoreData(4))); assert_matches!(sub.parse(b"\0asm", false), Ok(Chunk::NeedMoreData(4))); assert_matches!( sub.parse(b"\0asm\x01\0\0\0", false), Ok(Chunk::Parsed { consumed: 8, payload: Payload::Version { num: 1, encoding: Encoding::Module, .. }, }), ); // The sub-parser should be byte-limited so the next byte shouldn't get // consumed, it's intended for the parent parser. assert_matches!( sub.parse(&[10], false), Ok(Chunk::Parsed { consumed: 0, payload: Payload::End(18), }), ); // The parent parser should now be back to resuming, and we simulate it // being done with bytes to ensure that it's safely at the end, // completing the module code section. assert_matches!(p.parse(&[], false), Ok(Chunk::NeedMoreData(1))); assert_matches!( p.parse(&[], true), Ok(Chunk::Parsed { consumed: 0, payload: Payload::End(18), }), ); } #[test] fn nested_section_too_big() { let mut p = parser_after_component_header(); // A module that's 10 bytes in length let mut sub = match p.parse(&[1, 10], false) { Ok(Chunk::Parsed { consumed: 2, payload: Payload::ModuleSection { parser, .. }, }) => parser, other => panic!("bad parse {:?}", other), }; // use 8 bytes to parse the header, leaving 2 remaining bytes in our // module. assert_matches!( sub.parse(b"\0asm\x01\0\0\0", false), Ok(Chunk::Parsed { consumed: 8, payload: Payload::Version { num: 1, .. }, }), ); // We can't parse a section which declares its bigger than the outer // module. This is a custom section, one byte big, with one content byte. The // content byte, however, lives outside of the parent's module code // section. assert_eq!( sub.parse(&[0, 1, 0], false).unwrap_err().message(), "section too large", ); } } wasmparser-0.217.0/src/readers/component/aliases.rs000064400000000000000000000075771046102023000204040ustar 00000000000000use crate::{BinaryReader, ComponentExternalKind, ExternalKind, FromReader, Result}; /// Represents the kind of an outer alias in a WebAssembly component. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ComponentOuterAliasKind { /// The alias is to a core module. CoreModule, /// The alias is to a core type. CoreType, /// The alias is to a component type. Type, /// The alias is to a component. Component, } /// Represents an alias in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub enum ComponentAlias<'a> { /// The alias is to an export of a component instance. InstanceExport { /// The alias kind. kind: ComponentExternalKind, /// The instance index. instance_index: u32, /// The export name. name: &'a str, }, /// The alias is to an export of a module instance. CoreInstanceExport { /// The alias kind. kind: ExternalKind, /// The instance index. instance_index: u32, /// The export name. name: &'a str, }, /// The alias is to an outer item. Outer { /// The alias kind. kind: ComponentOuterAliasKind, /// The outward count, starting at zero for the current component. count: u32, /// The index of the item within the outer component. index: u32, }, } /// Section reader for the component alias section pub type ComponentAliasSectionReader<'a> = crate::SectionLimited<'a, ComponentAlias<'a>>; impl<'a> FromReader<'a> for ComponentAlias<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // We don't know what type of alias it is yet, so just read the sort bytes let offset = reader.original_position(); let byte1 = reader.read_u8()?; let byte2 = if byte1 == 0x00 { Some(reader.read_u8()?) } else { None }; Ok(match reader.read_u8()? { 0x00 => ComponentAlias::InstanceExport { kind: ComponentExternalKind::from_bytes(byte1, byte2, offset)?, instance_index: reader.read_var_u32()?, name: reader.read_string()?, }, 0x01 => ComponentAlias::CoreInstanceExport { kind: BinaryReader::external_kind_from_byte( byte2.ok_or_else(|| { BinaryReader::invalid_leading_byte_error( byte1, "core instance export kind", offset, ) })?, offset, )?, instance_index: reader.read_var_u32()?, name: reader.read_string()?, }, 0x02 => ComponentAlias::Outer { kind: component_outer_alias_kind_from_bytes(byte1, byte2, offset)?, count: reader.read_var_u32()?, index: reader.read_var_u32()?, }, x => reader.invalid_leading_byte(x, "alias")?, }) } } fn component_outer_alias_kind_from_bytes( byte1: u8, byte2: Option, offset: usize, ) -> Result { Ok(match byte1 { 0x00 => match byte2.unwrap() { 0x10 => ComponentOuterAliasKind::CoreType, 0x11 => ComponentOuterAliasKind::CoreModule, x => { return Err(BinaryReader::invalid_leading_byte_error( x, "component outer alias kind", offset + 1, )) } }, 0x03 => ComponentOuterAliasKind::Type, 0x04 => ComponentOuterAliasKind::Component, x => { return Err(BinaryReader::invalid_leading_byte_error( x, "component outer alias kind", offset, )) } }) } wasmparser-0.217.0/src/readers/component/canonicals.rs000064400000000000000000000114341046102023000210600ustar 00000000000000use crate::limits::MAX_WASM_CANONICAL_OPTIONS; use crate::prelude::*; use crate::{BinaryReader, FromReader, Result, SectionLimited}; /// Represents options for component functions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CanonicalOption { /// The string types in the function signature are UTF-8 encoded. UTF8, /// The string types in the function signature are UTF-16 encoded. UTF16, /// The string types in the function signature are compact UTF-16 encoded. CompactUTF16, /// The memory to use if the lifting or lowering of a function requires memory access. /// /// The value is an index to a core memory. Memory(u32), /// The realloc function to use if the lifting or lowering of a function requires memory /// allocation. /// /// The value is an index to a core function of type `(func (param i32 i32 i32 i32) (result i32))`. Realloc(u32), /// The post-return function to use if the lifting of a function requires /// cleanup after the function returns. PostReturn(u32), } /// Represents a canonical function in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub enum CanonicalFunction { /// The function lifts a core WebAssembly function to the canonical ABI. Lift { /// The index of the core WebAssembly function to lift. core_func_index: u32, /// The index of the lifted function's type. type_index: u32, /// The canonical options for the function. options: Box<[CanonicalOption]>, }, /// The function lowers a canonical ABI function to a core WebAssembly function. Lower { /// The index of the function to lower. func_index: u32, /// The canonical options for the function. options: Box<[CanonicalOption]>, }, /// A function which creates a new owned handle to a resource. ResourceNew { /// The type index of the resource that's being created. resource: u32, }, /// A function which is used to drop resource handles of the specified type. ResourceDrop { /// The type index of the resource that's being dropped. resource: u32, }, /// A function which returns the underlying i32-based representation of the /// specified resource. ResourceRep { /// The type index of the resource that's being accessed. resource: u32, }, } /// A reader for the canonical section of a WebAssembly component. pub type ComponentCanonicalSectionReader<'a> = SectionLimited<'a, CanonicalFunction>; impl<'a> FromReader<'a> for CanonicalFunction { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => match reader.read_u8()? { 0x00 => { let core_func_index = reader.read_var_u32()?; let options = reader .read_iter(MAX_WASM_CANONICAL_OPTIONS, "canonical options")? .collect::>()?; let type_index = reader.read_var_u32()?; CanonicalFunction::Lift { core_func_index, options, type_index, } } x => return reader.invalid_leading_byte(x, "canonical function lift"), }, 0x01 => match reader.read_u8()? { 0x00 => CanonicalFunction::Lower { func_index: reader.read_var_u32()?, options: reader .read_iter(MAX_WASM_CANONICAL_OPTIONS, "canonical options")? .collect::>()?, }, x => return reader.invalid_leading_byte(x, "canonical function lower"), }, 0x02 => CanonicalFunction::ResourceNew { resource: reader.read()?, }, 0x03 => CanonicalFunction::ResourceDrop { resource: reader.read()?, }, 0x04 => CanonicalFunction::ResourceRep { resource: reader.read()?, }, x => return reader.invalid_leading_byte(x, "canonical function"), }) } } impl<'a> FromReader<'a> for CanonicalOption { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => CanonicalOption::UTF8, 0x01 => CanonicalOption::UTF16, 0x02 => CanonicalOption::CompactUTF16, 0x03 => CanonicalOption::Memory(reader.read_var_u32()?), 0x04 => CanonicalOption::Realloc(reader.read_var_u32()?), 0x05 => CanonicalOption::PostReturn(reader.read_var_u32()?), x => return reader.invalid_leading_byte(x, "canonical option"), }) } } wasmparser-0.217.0/src/readers/component/exports.rs000064400000000000000000000105471046102023000204560ustar 00000000000000use crate::{BinaryReader, ComponentTypeRef, FromReader, Result, SectionLimited}; /// Represents the kind of an external items of a WebAssembly component. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ComponentExternalKind { /// The external kind is a core module. Module, /// The external kind is a function. Func, /// The external kind is a value. Value, /// The external kind is a type. Type, /// The external kind is an instance. Instance, /// The external kind is a component. Component, } impl ComponentExternalKind { pub(crate) fn from_bytes( byte1: u8, byte2: Option, offset: usize, ) -> Result { Ok(match byte1 { 0x00 => match byte2.unwrap() { 0x11 => ComponentExternalKind::Module, x => { return Err(BinaryReader::invalid_leading_byte_error( x, "component external kind", offset + 1, )) } }, 0x01 => ComponentExternalKind::Func, 0x02 => ComponentExternalKind::Value, 0x03 => ComponentExternalKind::Type, 0x04 => ComponentExternalKind::Component, 0x05 => ComponentExternalKind::Instance, x => { return Err(BinaryReader::invalid_leading_byte_error( x, "component external kind", offset, )) } }) } /// Returns a simple string description of this kind. pub fn desc(&self) -> &'static str { use ComponentExternalKind::*; match self { Module => "module", Func => "func", Value => "value", Type => "type", Instance => "instance", Component => "component", } } } /// Represents an export in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub struct ComponentExport<'a> { /// The name of the exported item. pub name: ComponentExportName<'a>, /// The kind of the export. pub kind: ComponentExternalKind, /// The index of the exported item. pub index: u32, /// An optionally specified type ascribed to this export. pub ty: Option, } /// A reader for the export section of a WebAssembly component. pub type ComponentExportSectionReader<'a> = SectionLimited<'a, ComponentExport<'a>>; impl<'a> FromReader<'a> for ComponentExport<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(ComponentExport { name: reader.read()?, kind: reader.read()?, index: reader.read()?, ty: match reader.read_u8()? { 0x00 => None, 0x01 => Some(reader.read()?), other => { return Err(BinaryReader::invalid_leading_byte_error( other, "optional component export type", reader.original_position() - 1, )) } }, }) } } impl<'a> FromReader<'a> for ComponentExternalKind { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let offset = reader.original_position(); let byte1 = reader.read_u8()?; let byte2 = if byte1 == 0x00 { Some(reader.read_u8()?) } else { None }; ComponentExternalKind::from_bytes(byte1, byte2, offset) } } /// Represents the name of a component export. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[allow(missing_docs)] pub struct ComponentExportName<'a>(pub &'a str); impl<'a> FromReader<'a> for ComponentExportName<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { match reader.read_u8()? { 0x00 => {} // Historically export names used a discriminator byte of 0x01 to // indicate an "interface" of the form `a:b/c` but nowadays that's // inferred from string syntax. Ignore 0-vs-1 to continue to parse // older binaries. Eventually this will go away. 0x01 => {} x => return reader.invalid_leading_byte(x, "export name"), } Ok(ComponentExportName(reader.read_string()?)) } } wasmparser-0.217.0/src/readers/component/imports.rs000064400000000000000000000126041046102023000204430ustar 00000000000000use crate::{ BinaryReader, ComponentExternalKind, ComponentValType, FromReader, Result, SectionLimited, }; /// Represents the type bounds for imports and exports. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum TypeBounds { /// The type is bounded by equality. Eq(u32), /// A fresh resource type, SubResource, } impl<'a> FromReader<'a> for TypeBounds { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => TypeBounds::Eq(reader.read()?), 0x01 => TypeBounds::SubResource, x => return reader.invalid_leading_byte(x, "type bound"), }) } } /// Represents a reference to a component type. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ComponentTypeRef { /// The reference is to a core module type. /// /// The index is expected to be core type index to a core module type. Module(u32), /// The reference is to a function type. /// /// The index is expected to be a type index to a function type. Func(u32), /// The reference is to a value type. Value(ComponentValType), /// The reference is to a bounded type. /// /// The index is expected to be a type index. Type(TypeBounds), /// The reference is to an instance type. /// /// The index is a type index to an instance type. Instance(u32), /// The reference is to a component type. /// /// The index is a type index to a component type. Component(u32), } impl ComponentTypeRef { /// Returns the corresponding [`ComponentExternalKind`] for this reference. pub fn kind(&self) -> ComponentExternalKind { match self { ComponentTypeRef::Module(_) => ComponentExternalKind::Module, ComponentTypeRef::Func(_) => ComponentExternalKind::Func, ComponentTypeRef::Value(_) => ComponentExternalKind::Value, ComponentTypeRef::Type(..) => ComponentExternalKind::Type, ComponentTypeRef::Instance(_) => ComponentExternalKind::Instance, ComponentTypeRef::Component(_) => ComponentExternalKind::Component, } } } impl<'a> FromReader<'a> for ComponentTypeRef { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read()? { ComponentExternalKind::Module => ComponentTypeRef::Module(reader.read()?), ComponentExternalKind::Func => ComponentTypeRef::Func(reader.read()?), ComponentExternalKind::Value => ComponentTypeRef::Value(reader.read()?), ComponentExternalKind::Type => ComponentTypeRef::Type(reader.read()?), ComponentExternalKind::Instance => ComponentTypeRef::Instance(reader.read()?), ComponentExternalKind::Component => ComponentTypeRef::Component(reader.read()?), }) } } /// Represents an import in a WebAssembly component #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct ComponentImport<'a> { /// The name of the imported item. pub name: ComponentImportName<'a>, /// The type reference for the import. pub ty: ComponentTypeRef, } impl<'a> FromReader<'a> for ComponentImport<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(ComponentImport { name: reader.read()?, ty: reader.read()?, }) } } /// A reader for the import section of a WebAssembly component. /// /// # Examples /// /// ``` /// use wasmparser::{ComponentImportSectionReader, BinaryReader}; /// let data: &[u8] = &[0x01, 0x00, 0x01, 0x41, 0x01, 0x66]; /// let reader = BinaryReader::new(data, 0); /// let reader = ComponentImportSectionReader::new(reader).unwrap(); /// for import in reader { /// let import = import.expect("import"); /// println!("Import: {:?}", import); /// } /// ``` pub type ComponentImportSectionReader<'a> = SectionLimited<'a, ComponentImport<'a>>; /// Represents the name of a component import. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[allow(missing_docs)] pub struct ComponentImportName<'a>(pub &'a str); impl<'a> FromReader<'a> for ComponentImportName<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { match reader.read_u8()? { // This is the spec-required byte as of this time. 0x00 => {} // Prior to WebAssembly/component-model#263 export names used a // discriminator byte of 0x01 to indicate an "interface" of the // form `a:b/c` but nowadays that's inferred from string syntax. // Ignore 0-vs-1 to continue to parse older binaries. Eventually // this will go away. // // This logic to ignore 0x01 was landed on 2023-10-28 in // bytecodealliance/wasm-tools#1262 and the encoder at the time // still emitted 0x01 to have better compatibility with prior // validators. // // On 2024-09-03 in bytecodealliance/wasm-tools#TODO the encoder // was updated to always emit 0x00 as a leading byte. After enough // time has passed this case may be able to be removed. When // removing this it's probably best to do it with a `WasmFeatures` // flag first to ensure there's an opt-in way of fixing things. 0x01 => {} x => return reader.invalid_leading_byte(x, "import name"), } Ok(ComponentImportName(reader.read_string()?)) } } wasmparser-0.217.0/src/readers/component/instances.rs000064400000000000000000000133501046102023000207340ustar 00000000000000use crate::limits::{MAX_WASM_INSTANTIATION_ARGS, MAX_WASM_INSTANTIATION_EXPORTS}; use crate::prelude::*; use crate::{ BinaryReader, ComponentExport, ComponentExternalKind, Export, FromReader, Result, SectionLimited, }; /// Represents the kind of an instantiation argument for a core instance. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum InstantiationArgKind { /// The instantiation argument is a core instance. Instance, } /// Represents an argument to instantiating a WebAssembly module. #[derive(Debug, Clone, Eq, PartialEq)] pub struct InstantiationArg<'a> { /// The name of the module argument. pub name: &'a str, /// The kind of the module argument. pub kind: InstantiationArgKind, /// The index of the argument item. pub index: u32, } /// Represents an instance of a WebAssembly module. #[derive(Debug, Clone, Eq, PartialEq)] pub enum Instance<'a> { /// The instance is from instantiating a WebAssembly module. Instantiate { /// The module index. module_index: u32, /// The module's instantiation arguments. args: Box<[InstantiationArg<'a>]>, }, /// The instance is a from exporting local items. FromExports(Box<[Export<'a>]>), } /// A reader for the core instance section of a WebAssembly component. /// /// # Examples /// /// ``` /// use wasmparser::{InstanceSectionReader, BinaryReader}; /// # let data: &[u8] = &[0x01, 0x00, 0x00, 0x01, 0x03, b'f', b'o', b'o', 0x12, 0x00]; /// let reader = BinaryReader::new(data, 0); /// let mut reader = InstanceSectionReader::new(reader).unwrap(); /// for inst in reader { /// println!("Instance {:?}", inst.expect("instance")); /// } /// ``` pub type InstanceSectionReader<'a> = SectionLimited<'a, Instance<'a>>; impl<'a> FromReader<'a> for Instance<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => Instance::Instantiate { module_index: reader.read_var_u32()?, args: reader .read_iter(MAX_WASM_INSTANTIATION_ARGS, "core instantiation arguments")? .collect::>()?, }, 0x01 => Instance::FromExports( reader .read_iter(MAX_WASM_INSTANTIATION_ARGS, "core instantiation arguments")? .collect::>()?, ), x => return reader.invalid_leading_byte(x, "core instance"), }) } } impl<'a> FromReader<'a> for InstantiationArg<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(InstantiationArg { name: reader.read()?, kind: reader.read()?, index: reader.read()?, }) } } impl<'a> FromReader<'a> for InstantiationArgKind { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x12 => InstantiationArgKind::Instance, x => return reader.invalid_leading_byte(x, "instantiation arg kind"), }) } } /// Represents an argument to instantiating a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub struct ComponentInstantiationArg<'a> { /// The name of the component argument. pub name: &'a str, /// The kind of the component argument. pub kind: ComponentExternalKind, /// The index of the argument item. pub index: u32, } /// Represents an instance in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub enum ComponentInstance<'a> { /// The instance is from instantiating a WebAssembly component. Instantiate { /// The component index. component_index: u32, /// The component's instantiation arguments. args: Box<[ComponentInstantiationArg<'a>]>, }, /// The instance is a from exporting local items. FromExports(Box<[ComponentExport<'a>]>), } /// A reader for the component instance section of a WebAssembly component. /// /// # Examples /// /// ``` /// use wasmparser::{ComponentInstanceSectionReader, BinaryReader}; /// # let data: &[u8] = &[0x01, 0x00, 0x00, 0x01, 0x03, b'f', b'o', b'o', 0x01, 0x00]; /// let reader = BinaryReader::new(data, 0); /// let mut reader = ComponentInstanceSectionReader::new(reader).unwrap(); /// for inst in reader { /// println!("Instance {:?}", inst.expect("instance")); /// } /// ``` pub type ComponentInstanceSectionReader<'a> = SectionLimited<'a, ComponentInstance<'a>>; impl<'a> FromReader<'a> for ComponentInstance<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => ComponentInstance::Instantiate { component_index: reader.read_var_u32()?, args: reader .read_iter(MAX_WASM_INSTANTIATION_ARGS, "instantiation arguments")? .collect::>()?, }, 0x01 => ComponentInstance::FromExports( (0..reader.read_size(MAX_WASM_INSTANTIATION_EXPORTS, "instantiation exports")?) .map(|_| { Ok(ComponentExport { name: reader.read()?, kind: reader.read()?, index: reader.read()?, ty: None, }) }) .collect::>()?, ), x => return reader.invalid_leading_byte(x, "instance"), }) } } impl<'a> FromReader<'a> for ComponentInstantiationArg<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(ComponentInstantiationArg { name: reader.read()?, kind: reader.read()?, index: reader.read()?, }) } } wasmparser-0.217.0/src/readers/component/names.rs000064400000000000000000000072351046102023000200550ustar 00000000000000use crate::{BinaryReader, BinaryReaderError, NameMap, Result, Subsection, Subsections}; use core::ops::Range; /// Type used to iterate and parse the contents of the `component-name` custom /// section in compnents, similar to the `name` section of core modules. pub type ComponentNameSectionReader<'a> = Subsections<'a, ComponentName<'a>>; /// Represents a name read from the names custom section. #[derive(Clone)] #[allow(missing_docs)] pub enum ComponentName<'a> { Component { name: &'a str, name_range: Range, }, CoreFuncs(NameMap<'a>), CoreGlobals(NameMap<'a>), CoreMemories(NameMap<'a>), CoreTables(NameMap<'a>), CoreModules(NameMap<'a>), CoreInstances(NameMap<'a>), CoreTypes(NameMap<'a>), Types(NameMap<'a>), Instances(NameMap<'a>), Components(NameMap<'a>), Funcs(NameMap<'a>), Values(NameMap<'a>), /// An unknown [name subsection](https://webassembly.github.io/spec/core/appendix/custom.html#subsections). Unknown { /// The identifier for this subsection. ty: u8, /// The contents of this subsection. data: &'a [u8], /// The range of bytes, relative to the start of the original data /// stream, that the contents of this subsection reside in. range: Range, }, } impl<'a> Subsection<'a> for ComponentName<'a> { fn from_reader(id: u8, mut reader: BinaryReader<'a>) -> Result { let data = reader.remaining_buffer(); let offset = reader.original_position(); Ok(match id { 0 => { let name = reader.read_string()?; if !reader.eof() { return Err(BinaryReaderError::new( "trailing data at the end of a name", reader.original_position(), )); } ComponentName::Component { name, name_range: offset..reader.original_position(), } } 1 => { let ctor: fn(NameMap<'a>) -> ComponentName<'a> = match reader.read_u8()? { 0x00 => match reader.read_u8()? { 0x00 => ComponentName::CoreFuncs, 0x01 => ComponentName::CoreTables, 0x02 => ComponentName::CoreMemories, 0x03 => ComponentName::CoreGlobals, 0x10 => ComponentName::CoreTypes, 0x11 => ComponentName::CoreModules, 0x12 => ComponentName::CoreInstances, _ => { return Ok(ComponentName::Unknown { ty: 1, data, range: offset..offset + data.len(), }); } }, 0x01 => ComponentName::Funcs, 0x02 => ComponentName::Values, 0x03 => ComponentName::Types, 0x04 => ComponentName::Components, 0x05 => ComponentName::Instances, _ => { return Ok(ComponentName::Unknown { ty: 1, data, range: offset..offset + data.len(), }); } }; ctor(NameMap::new(reader.shrink())?) } ty => ComponentName::Unknown { ty, data, range: offset..offset + data.len(), }, }) } } wasmparser-0.217.0/src/readers/component/start.rs000064400000000000000000000020711046102023000201000ustar 00000000000000use crate::limits::{MAX_WASM_FUNCTION_RETURNS, MAX_WASM_START_ARGS}; use crate::prelude::*; use crate::{BinaryReader, FromReader, Result}; /// Represents the start function in a WebAssembly component. #[derive(Debug, Clone)] pub struct ComponentStartFunction { /// The index to the start function. pub func_index: u32, /// The start function arguments. /// /// The arguments are specified by value index. pub arguments: Box<[u32]>, /// The number of expected results for the start function. pub results: u32, } impl<'a> FromReader<'a> for ComponentStartFunction { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let func_index = reader.read_var_u32()?; let arguments = reader .read_iter(MAX_WASM_START_ARGS, "start function arguments")? .collect::>()?; let results = reader.read_size(MAX_WASM_FUNCTION_RETURNS, "start function results")? as u32; Ok(ComponentStartFunction { func_index, arguments, results, }) } } wasmparser-0.217.0/src/readers/component/types.rs000064400000000000000000000456031046102023000201170ustar 00000000000000use crate::limits::*; use crate::prelude::*; use crate::{ BinaryReader, ComponentAlias, ComponentExportName, ComponentImport, ComponentTypeRef, FromReader, Import, Result, SectionLimited, SubType, TypeRef, ValType, }; use core::fmt; /// Represents the kind of an outer core alias in a WebAssembly component. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum OuterAliasKind { /// The alias is to a core type. Type, } /// Represents a core type in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub enum CoreType<'a> { /// The type is for a core subtype. Sub(SubType), /// The type is for a core module. Module(Box<[ModuleTypeDeclaration<'a>]>), } impl<'a> FromReader<'a> for CoreType<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.peek()? { 0x60 => CoreType::Sub(reader.read()?), 0x5e | 0x5f => bail!( reader.current_position(), "no support for GC types in the component model yet" ), 0x50 => { reader.read_u8()?; CoreType::Module( reader .read_iter(MAX_WASM_MODULE_TYPE_DECLS, "module type declaration")? .collect::>()?, ) } x => return reader.invalid_leading_byte(x, "core type"), }) } } /// Represents a module type declaration in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub enum ModuleTypeDeclaration<'a> { /// The module type definition is for a type. Type(SubType), /// The module type definition is for an export. Export { /// The name of the exported item. name: &'a str, /// The type reference of the export. ty: TypeRef, }, /// The module type declaration is for an outer alias. OuterAlias { /// The alias kind. kind: OuterAliasKind, /// The outward count, starting at zero for the current type. count: u32, /// The index of the item within the outer type. index: u32, }, /// The module type definition is for an import. Import(Import<'a>), } impl<'a> FromReader<'a> for ModuleTypeDeclaration<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => ModuleTypeDeclaration::Import(reader.read()?), 0x01 => ModuleTypeDeclaration::Type(reader.read()?), 0x02 => { let kind = match reader.read_u8()? { 0x10 => OuterAliasKind::Type, x => { return reader.invalid_leading_byte(x, "outer alias kind"); } }; match reader.read_u8()? { 0x01 => ModuleTypeDeclaration::OuterAlias { kind, count: reader.read()?, index: reader.read()?, }, x => { return reader.invalid_leading_byte(x, "outer alias target"); } } } 0x03 => ModuleTypeDeclaration::Export { name: reader.read()?, ty: reader.read()?, }, x => return reader.invalid_leading_byte(x, "type definition"), }) } } /// A reader for the core type section of a WebAssembly component. /// /// # Examples /// ``` /// use wasmparser::{CoreTypeSectionReader, BinaryReader}; /// # let data: &[u8] = &[0x01, 0x60, 0x00, 0x00]; /// let reader = BinaryReader::new(data, 0); /// let mut reader = CoreTypeSectionReader::new(reader).unwrap(); /// for ty in reader { /// println!("Type {:?}", ty.expect("type")); /// } /// ``` pub type CoreTypeSectionReader<'a> = SectionLimited<'a, CoreType<'a>>; /// Represents a value type in a WebAssembly component. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ComponentValType { /// The value type is a primitive type. Primitive(PrimitiveValType), /// The value type is a reference to a defined type. Type(u32), } impl<'a> FromReader<'a> for ComponentValType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { if let Some(ty) = PrimitiveValType::from_byte(reader.peek()?) { reader.read_u8()?; return Ok(ComponentValType::Primitive(ty)); } Ok(ComponentValType::Type(reader.read_var_s33()? as u32)) } } impl<'a> FromReader<'a> for Option { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { match reader.read_u8()? { 0x0 => Ok(None), 0x1 => Ok(Some(reader.read()?)), x => reader.invalid_leading_byte(x, "optional component value type"), } } } /// Represents a primitive value type. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PrimitiveValType { /// The type is a boolean. Bool, /// The type is a signed 8-bit integer. S8, /// The type is an unsigned 8-bit integer. U8, /// The type is a signed 16-bit integer. S16, /// The type is an unsigned 16-bit integer. U16, /// The type is a signed 32-bit integer. S32, /// The type is an unsigned 32-bit integer. U32, /// The type is a signed 64-bit integer. S64, /// The type is an unsigned 64-bit integer. U64, /// The type is a 32-bit floating point number with only one NaN. F32, /// The type is a 64-bit floating point number with only one NaN. F64, /// The type is a Unicode character. Char, /// The type is a string. String, } impl PrimitiveValType { fn from_byte(byte: u8) -> Option { Some(match byte { 0x7f => PrimitiveValType::Bool, 0x7e => PrimitiveValType::S8, 0x7d => PrimitiveValType::U8, 0x7c => PrimitiveValType::S16, 0x7b => PrimitiveValType::U16, 0x7a => PrimitiveValType::S32, 0x79 => PrimitiveValType::U32, 0x78 => PrimitiveValType::S64, 0x77 => PrimitiveValType::U64, 0x76 => PrimitiveValType::F32, 0x75 => PrimitiveValType::F64, 0x74 => PrimitiveValType::Char, 0x73 => PrimitiveValType::String, _ => return None, }) } #[cfg(feature = "validate")] pub(crate) fn contains_ptr(&self) -> bool { matches!(self, Self::String) } /// Determines if primitive value type `a` is a subtype of `b`. pub fn is_subtype_of(a: Self, b: Self) -> bool { // Note that this intentionally diverges from the upstream specification // at this time and only considers exact equality for subtyping // relationships. // // More information can be found in the subtyping implementation for // component functions. a == b } } impl fmt::Display for PrimitiveValType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use PrimitiveValType::*; let s = match self { Bool => "bool", S8 => "s8", U8 => "u8", S16 => "s16", U16 => "u16", S32 => "s32", U32 => "u32", S64 => "s64", U64 => "u64", F32 => "f32", F64 => "f64", Char => "char", String => "string", }; s.fmt(f) } } /// Represents a type in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub enum ComponentType<'a> { /// The type is a component defined type. Defined(ComponentDefinedType<'a>), /// The type is a function type. Func(ComponentFuncType<'a>), /// The type is a component type. Component(Box<[ComponentTypeDeclaration<'a>]>), /// The type is an instance type. Instance(Box<[InstanceTypeDeclaration<'a>]>), /// The type is a fresh new resource type. Resource { /// The representation of this resource type in core WebAssembly. rep: ValType, /// An optionally-specified destructor to use for when this resource is /// no longer needed. dtor: Option, }, } impl<'a> FromReader<'a> for ComponentType<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x3f => ComponentType::Resource { rep: reader.read()?, dtor: match reader.read_u8()? { 0x00 => None, 0x01 => Some(reader.read()?), b => return reader.invalid_leading_byte(b, "resource destructor"), }, }, 0x40 => { let params = reader .read_iter(MAX_WASM_FUNCTION_PARAMS, "component function parameters")? .collect::>()?; let results = reader.read()?; ComponentType::Func(ComponentFuncType { params, results }) } 0x41 => ComponentType::Component( reader .read_iter(MAX_WASM_COMPONENT_TYPE_DECLS, "component type declaration")? .collect::>()?, ), 0x42 => ComponentType::Instance( reader .read_iter(MAX_WASM_INSTANCE_TYPE_DECLS, "instance type declaration")? .collect::>()?, ), x => { if let Some(ty) = PrimitiveValType::from_byte(x) { ComponentType::Defined(ComponentDefinedType::Primitive(ty)) } else { ComponentType::Defined(ComponentDefinedType::read(reader, x)?) } } }) } } /// Represents part of a component type declaration in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub enum ComponentTypeDeclaration<'a> { /// The component type declaration is for a core type. CoreType(CoreType<'a>), /// The component type declaration is for a type. Type(ComponentType<'a>), /// The component type declaration is for an alias. Alias(ComponentAlias<'a>), /// The component type declaration is for an export. Export { /// The name of the export. name: ComponentExportName<'a>, /// The type reference for the export. ty: ComponentTypeRef, }, /// The component type declaration is for an import. Import(ComponentImport<'a>), } impl<'a> FromReader<'a> for ComponentTypeDeclaration<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // Component types are effectively instance types with the additional // variant of imports; check for imports here or delegate to // `InstanceTypeDeclaration` with the appropriate conversions. if reader.peek()? == 0x03 { reader.read_u8()?; return Ok(ComponentTypeDeclaration::Import(reader.read()?)); } Ok(match reader.read()? { InstanceTypeDeclaration::CoreType(t) => ComponentTypeDeclaration::CoreType(t), InstanceTypeDeclaration::Type(t) => ComponentTypeDeclaration::Type(t), InstanceTypeDeclaration::Alias(a) => ComponentTypeDeclaration::Alias(a), InstanceTypeDeclaration::Export { name, ty } => { ComponentTypeDeclaration::Export { name, ty } } }) } } /// Represents an instance type declaration in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub enum InstanceTypeDeclaration<'a> { /// The component type declaration is for a core type. CoreType(CoreType<'a>), /// The instance type declaration is for a type. Type(ComponentType<'a>), /// The instance type declaration is for an alias. Alias(ComponentAlias<'a>), /// The instance type declaration is for an export. Export { /// The name of the export. name: ComponentExportName<'a>, /// The type reference for the export. ty: ComponentTypeRef, }, } impl<'a> FromReader<'a> for InstanceTypeDeclaration<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => InstanceTypeDeclaration::CoreType(reader.read()?), 0x01 => InstanceTypeDeclaration::Type(reader.read()?), 0x02 => InstanceTypeDeclaration::Alias(reader.read()?), 0x04 => InstanceTypeDeclaration::Export { name: reader.read()?, ty: reader.read()?, }, x => return reader.invalid_leading_byte(x, "component or instance type declaration"), }) } } /// Represents the result type of a component function. #[derive(Debug, Clone, Eq, PartialEq)] pub enum ComponentFuncResult<'a> { /// The function returns a singular, unnamed type. Unnamed(ComponentValType), /// The function returns zero or more named types. Named(Box<[(&'a str, ComponentValType)]>), } impl<'a> FromReader<'a> for ComponentFuncResult<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => ComponentFuncResult::Unnamed(reader.read()?), 0x01 => ComponentFuncResult::Named( reader .read_iter(MAX_WASM_FUNCTION_RETURNS, "component function results")? .collect::>()?, ), x => return reader.invalid_leading_byte(x, "component function results"), }) } } impl ComponentFuncResult<'_> { /// Gets the count of types returned by the function. pub fn type_count(&self) -> usize { match self { Self::Unnamed(_) => 1, Self::Named(vec) => vec.len(), } } /// Iterates over the types returned by the function. pub fn iter(&self) -> impl Iterator, &ComponentValType)> { enum Either { Left(L), Right(R), } impl Iterator for Either where L: Iterator, R: Iterator, { type Item = L::Item; fn next(&mut self) -> Option { match self { Either::Left(l) => l.next(), Either::Right(r) => r.next(), } } } match self { Self::Unnamed(ty) => Either::Left(core::iter::once(ty).map(|ty| (None, ty))), Self::Named(vec) => Either::Right(vec.iter().map(|(n, ty)| (Some(*n), ty))), } } } /// Represents a type of a function in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub struct ComponentFuncType<'a> { /// The function parameters. pub params: Box<[(&'a str, ComponentValType)]>, /// The function result. pub results: ComponentFuncResult<'a>, } /// Represents a case in a variant type. #[derive(Debug, Clone, PartialEq, Eq)] pub struct VariantCase<'a> { /// The name of the variant case. pub name: &'a str, /// The value type of the variant case. pub ty: Option, /// The index of the variant case that is refined by this one. pub refines: Option, } impl<'a> FromReader<'a> for VariantCase<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(VariantCase { name: reader.read()?, ty: reader.read()?, refines: match reader.read_u8()? { 0x0 => None, 0x1 => Some(reader.read_var_u32()?), x => return reader.invalid_leading_byte(x, "variant case refines"), }, }) } } /// Represents a defined type in a WebAssembly component. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ComponentDefinedType<'a> { /// The type is one of the primitive value types. Primitive(PrimitiveValType), /// The type is a record with the given fields. Record(Box<[(&'a str, ComponentValType)]>), /// The type is a variant with the given cases. Variant(Box<[VariantCase<'a>]>), /// The type is a list of the given value type. List(ComponentValType), /// The type is a tuple of the given value types. Tuple(Box<[ComponentValType]>), /// The type is flags with the given names. Flags(Box<[&'a str]>), /// The type is an enum with the given tags. Enum(Box<[&'a str]>), /// The type is an option of the given value type. Option(ComponentValType), /// The type is a result type. Result { /// The type returned for success. ok: Option, /// The type returned for failure. err: Option, }, /// An owned handle to a resource. Own(u32), /// A borrowed handle to a resource. Borrow(u32), } impl<'a> ComponentDefinedType<'a> { fn read(reader: &mut BinaryReader<'a>, byte: u8) -> Result> { Ok(match byte { 0x72 => ComponentDefinedType::Record( reader .read_iter(MAX_WASM_RECORD_FIELDS, "record field")? .collect::>()?, ), 0x71 => ComponentDefinedType::Variant( reader .read_iter(MAX_WASM_VARIANT_CASES, "variant cases")? .collect::>()?, ), 0x70 => ComponentDefinedType::List(reader.read()?), 0x6f => ComponentDefinedType::Tuple( reader .read_iter(MAX_WASM_TUPLE_TYPES, "tuple types")? .collect::>()?, ), 0x6e => ComponentDefinedType::Flags( reader .read_iter(MAX_WASM_FLAG_NAMES, "flag names")? .collect::>()?, ), 0x6d => ComponentDefinedType::Enum( reader .read_iter(MAX_WASM_ENUM_CASES, "enum cases")? .collect::>()?, ), // NOTE: 0x6c (union) removed 0x6b => ComponentDefinedType::Option(reader.read()?), 0x6a => ComponentDefinedType::Result { ok: reader.read()?, err: reader.read()?, }, 0x69 => ComponentDefinedType::Own(reader.read()?), 0x68 => ComponentDefinedType::Borrow(reader.read()?), x => return reader.invalid_leading_byte(x, "component defined type"), }) } } /// A reader for the type section of a WebAssembly component. /// /// # Examples /// /// ``` /// use wasmparser::{ComponentTypeSectionReader, BinaryReader}; /// let data: &[u8] = &[0x01, 0x40, 0x01, 0x03, b'f', b'o', b'o', 0x73, 0x00, 0x73]; /// let reader = BinaryReader::new(data, 0); /// let mut reader = ComponentTypeSectionReader::new(reader).unwrap(); /// for ty in reader { /// println!("Type {:?}", ty.expect("type")); /// } /// ``` pub type ComponentTypeSectionReader<'a> = SectionLimited<'a, ComponentType<'a>>; wasmparser-0.217.0/src/readers/component.rs000064400000000000000000000004671046102023000167520ustar 00000000000000mod aliases; mod canonicals; mod exports; mod imports; mod instances; mod names; mod start; mod types; pub use self::aliases::*; pub use self::canonicals::*; pub use self::exports::*; pub use self::imports::*; pub use self::instances::*; pub use self::names::*; pub use self::start::*; pub use self::types::*; wasmparser-0.217.0/src/readers/core/branch_hinting.rs000064400000000000000000000036571046102023000206610ustar 00000000000000use crate::{BinaryReader, FromReader, Result, SectionLimited}; /// A reader for the `metadata.code.branch_hint` custom section. pub type BranchHintSectionReader<'a> = SectionLimited<'a, BranchHintFunction<'a>>; /// Branch hints for a single function. /// /// Produced from [`BranchHintSectionReader`]. #[derive(Debug, Clone)] pub struct BranchHintFunction<'a> { /// The function that these branch hints apply to. pub func: u32, /// The branch hints available for this function. pub hints: SectionLimited<'a, BranchHint>, } impl<'a> FromReader<'a> for BranchHintFunction<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let func = reader.read_var_u32()?; // FIXME(#188) ideally wouldn't have to do skips here let hints = reader.skip(|reader| { let items_count = reader.read_var_u32()?; for _ in 0..items_count { reader.read::()?; } Ok(()) })?; Ok(BranchHintFunction { func, hints: SectionLimited::new(hints)?, }) } } /// A hint for a single branch. #[derive(Debug, Copy, Clone)] pub struct BranchHint { /// The byte offset, from the start of the function's body, of where the /// hinted instruction lives. pub func_offset: u32, /// Whether or not the branch is hinted to be taken or not. pub taken: bool, } impl<'a> FromReader<'a> for BranchHint { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let func_offset = reader.read_var_u32()?; match reader.read_u8()? { 1 => {} n => reader.invalid_leading_byte(n, "invalid branch hint byte")?, } let taken = match reader.read_u8()? { 0 => false, 1 => true, n => reader.invalid_leading_byte(n, "invalid branch hint taken byte")?, }; Ok(BranchHint { func_offset, taken }) } } wasmparser-0.217.0/src/readers/core/code.rs000064400000000000000000000101561046102023000166060ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, FromReader, OperatorsReader, Result, SectionLimited, ValType}; use core::ops::Range; /// A reader for the code section of a WebAssembly module. pub type CodeSectionReader<'a> = SectionLimited<'a, FunctionBody<'a>>; /// Represents a WebAssembly function body. #[derive(Debug, Clone)] pub struct FunctionBody<'a> { reader: BinaryReader<'a>, } impl<'a> FunctionBody<'a> { /// Constructs a new `FunctionBody` for the given data and offset. pub fn new(reader: BinaryReader<'a>) -> Self { Self { reader } } /// Gets a binary reader for this function body. pub fn get_binary_reader(&self) -> BinaryReader<'a> { self.reader.clone() } fn skip_locals(reader: &mut BinaryReader) -> Result<()> { let count = reader.read_var_u32()?; for _ in 0..count { reader.read_var_u32()?; reader.read::()?; } Ok(()) } /// Gets the locals reader for this function body. pub fn get_locals_reader(&self) -> Result> { let mut reader = self.reader.clone(); let count = reader.read_var_u32()?; Ok(LocalsReader { reader, count }) } /// Gets the operators reader for this function body. pub fn get_operators_reader(&self) -> Result> { let mut reader = self.reader.clone(); Self::skip_locals(&mut reader)?; Ok(OperatorsReader::new(reader)) } /// Gets the range of the function body. pub fn range(&self) -> Range { self.reader.range() } /// Returns the body of this function as a list of bytes. /// /// Note that the returned bytes start with the function locals declaration. pub fn as_bytes(&self) -> &'a [u8] { self.reader.remaining_buffer() } } impl<'a> FromReader<'a> for FunctionBody<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let reader = reader.read_reader()?; Ok(FunctionBody { reader }) } } /// A reader for a function body's locals. pub struct LocalsReader<'a> { reader: BinaryReader<'a>, count: u32, } impl<'a> LocalsReader<'a> { /// Gets the count of locals in the function body. pub fn get_count(&self) -> u32 { self.count } /// Gets the original position of the reader. pub fn original_position(&self) -> usize { self.reader.original_position() } /// Reads an item from the reader. pub fn read(&mut self) -> Result<(u32, ValType)> { let count = self.reader.read()?; let value_type = self.reader.read()?; Ok((count, value_type)) } } impl<'a> IntoIterator for LocalsReader<'a> { type Item = Result<(u32, ValType)>; type IntoIter = LocalsIterator<'a>; fn into_iter(self) -> Self::IntoIter { let count = self.count; LocalsIterator { reader: self, left: count, err: false, } } } /// An iterator over locals in a function body. pub struct LocalsIterator<'a> { reader: LocalsReader<'a>, left: u32, err: bool, } impl<'a> Iterator for LocalsIterator<'a> { type Item = Result<(u32, ValType)>; fn next(&mut self) -> Option { if self.err || self.left == 0 { return None; } let result = self.reader.read(); self.err = result.is_err(); self.left -= 1; Some(result) } fn size_hint(&self) -> (usize, Option) { let count = self.reader.get_count() as usize; (count, Some(count)) } } wasmparser-0.217.0/src/readers/core/coredumps.rs000064400000000000000000000222171046102023000176760ustar 00000000000000use crate::prelude::*; use crate::{BinaryReader, FromReader, Result}; /// The data portion of a custom section representing a core dump. Per the /// tool-conventions repo, this section just specifies the executable name that /// the core dump came from while the rest of the core dump information is /// contained in a corestack custom section /// /// # Examples /// /// ``` /// use wasmparser::{BinaryReader, CoreDumpSection, FromReader, Result}; /// let data: &[u8] = &[0x00, 0x09, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x77, 0x61, /// 0x73, 0x6d]; /// let mut reader = BinaryReader::new(data, 0); /// let core = CoreDumpSection::new(reader).unwrap(); /// assert!(core.name == "test.wasm") /// ``` pub struct CoreDumpSection<'a> { /// The name of the process that created the core dump pub name: &'a str, } impl<'a> CoreDumpSection<'a> { /// Parses this section from the provided `reader`, derived from a custom /// section. pub fn new(mut reader: BinaryReader<'a>) -> Result> { let pos = reader.original_position(); if reader.read_u8()? != 0 { bail!(pos, "invalid start byte for core dump name"); } let name = reader.read_string()?; if !reader.eof() { bail!( reader.original_position(), "trailing bytes at end of custom section" ); } Ok(CoreDumpSection { name }) } } /// The data portion of a "coremodules" custom section. This contains a vec of /// module names that will be referenced by index by other coredump sections. /// /// # Example /// /// ``` /// use wasmparser::{BinaryReader, CoreDumpModulesSection, FromReader, Result}; /// let data: &[u8] = &[0x01, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74]; /// let reader = BinaryReader::new(data, 0); /// let modules_section = CoreDumpModulesSection::new(reader).unwrap(); /// assert!(modules_section.modules[0] == "test") /// ``` #[derive(Debug)] pub struct CoreDumpModulesSection<'a> { /// A list of module names, which may be URLs, file paths, or other /// identifiers for the module. pub modules: Vec<&'a str>, } impl<'a> CoreDumpModulesSection<'a> { /// Parses this section from the provided `reader`, derived from a custom /// section. pub fn new(mut reader: BinaryReader<'a>) -> Result> { let pos = reader.original_position(); let mut modules = vec![]; for _ in 0..reader.read_var_u32()? { if reader.read_u8()? != 0 { bail!(pos, "invalid start byte for coremodule"); } modules.push(reader.read_string()?); } if !reader.eof() { bail!( reader.original_position(), "trailing bytes at end of custom section" ); } Ok(CoreDumpModulesSection { modules }) } } /// A custom section representing the instances involved in a given coredump pub struct CoreDumpInstancesSection { /// The instances for the coredump pub instances: Vec, } impl CoreDumpInstancesSection { /// Parses this section from the provided `reader`, derived from a custom /// section. pub fn new(mut reader: BinaryReader<'_>) -> Result { let mut instances = vec![]; for _ in 0..reader.read_var_u32()? { instances.push(CoreDumpInstance::from_reader(&mut reader)?); } if !reader.eof() { bail!( reader.original_position(), "trailing bytes at end of custom section" ); } Ok(CoreDumpInstancesSection { instances }) } } /// A single instance from a coredump instances section #[derive(Debug)] pub struct CoreDumpInstance { /// The module that this is an instance of, as an index into a "coremodules" /// section. pub module_index: u32, /// Which of the coredump's memories are this instance's memories, via /// indexing into the memory index space. pub memories: Vec, /// Which of the coredump's globals are this instance's globals, via /// indexing into the global index space. pub globals: Vec, } impl<'a> FromReader<'a> for CoreDumpInstance { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let pos = reader.original_position(); if reader.read_u8()? != 0 { bail!(pos, "invalid start byte for core dump instance"); } let module_index = reader.read_var_u32()?; let mut memories = vec![]; for _ in 0..reader.read_var_u32()? { memories.push(reader.read_var_u32()?); } let mut globals = vec![]; for _ in 0..reader.read_var_u32()? { globals.push(reader.read_var_u32()?); } Ok(CoreDumpInstance { module_index, memories, globals, }) } } /// The data portion of a custom section representing a core dump stack. The /// structure of this follows the coredump spec in the tool-conventions repo /// /// # Examples /// /// ``` /// use wasmparser::{BinaryReader, CoreDumpStackSection, FromReader}; /// /// let data: &[u8] = &[0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x01, 0x00, 0x04, /// 0x2a, 0x33, 0x01, 0x7f, 0x01, 0x01, 0x7f, 0x02]; /// let reader = BinaryReader::new(data, 0); /// let corestack = CoreDumpStackSection::new(reader).unwrap(); /// assert!(corestack.name == "main"); /// assert!(corestack.frames.len() == 1); /// let frame = &corestack.frames[0]; /// assert!(frame.instanceidx == 4); /// assert!(frame.funcidx == 42); /// assert!(frame.codeoffset == 51); /// assert!(frame.locals.len() == 1); /// assert!(frame.stack.len() == 1); /// ``` pub struct CoreDumpStackSection<'a> { /// The thread name pub name: &'a str, /// The stack frames for the core dump pub frames: Vec, } impl<'a> CoreDumpStackSection<'a> { /// Parses this section from the provided `reader`, derived from a custom /// section. pub fn new(mut reader: BinaryReader<'a>) -> Result> { let pos = reader.original_position(); if reader.read_u8()? != 0 { bail!(pos, "invalid start byte for core dump stack name"); } let name = reader.read_string()?; let mut frames = vec![]; for _ in 0..reader.read_var_u32()? { frames.push(CoreDumpStackFrame::from_reader(&mut reader)?); } if !reader.eof() { bail!( reader.original_position(), "trailing bytes at end of custom section" ); } Ok(CoreDumpStackSection { name: name, frames: frames, }) } } /// A single stack frame from a core dump #[derive(Debug)] pub struct CoreDumpStackFrame { /// The instance that this stack frame belongs to. pub instanceidx: u32, /// The function index in the module pub funcidx: u32, /// The instruction's offset relative to the function's start pub codeoffset: u32, /// The locals for this stack frame (including function parameters) pub locals: Vec, /// The values on the stack pub stack: Vec, } impl<'a> FromReader<'a> for CoreDumpStackFrame { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let pos = reader.original_position(); if reader.read_u8()? != 0 { bail!(pos, "invalid start byte for core dump stack frame"); } let instanceidx = reader.read_var_u32()?; let funcidx = reader.read_var_u32()?; let codeoffset = reader.read_var_u32()?; let mut locals = vec![]; for _ in 0..reader.read_var_u32()? { locals.push(CoreDumpValue::from_reader(reader)?); } let mut stack = vec![]; for _ in 0..reader.read_var_u32()? { stack.push(CoreDumpValue::from_reader(reader)?); } Ok(CoreDumpStackFrame { instanceidx, funcidx, codeoffset, locals, stack, }) } } /// Local and stack values are encoded using one byte for the type (similar to /// Wasm's Number Types) followed by bytes representing the actual value /// See the tool-conventions repo for more details. #[derive(Clone, Debug)] pub enum CoreDumpValue { /// A missing value (usually missing because it was optimized out) Missing, /// An i32 value I32(i32), /// An i64 value I64(i64), /// An f32 value F32(f32), /// An f64 value F64(f64), } impl<'a> FromReader<'a> for CoreDumpValue { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let pos = reader.original_position(); match reader.read_u8()? { 0x01 => Ok(CoreDumpValue::Missing), 0x7F => Ok(CoreDumpValue::I32(reader.read_var_i32()?)), 0x7E => Ok(CoreDumpValue::I64(reader.read_var_i64()?)), 0x7D => Ok(CoreDumpValue::F32(f32::from_bits( reader.read_f32()?.bits(), ))), 0x7C => Ok(CoreDumpValue::F64(f64::from_bits( reader.read_f64()?.bits(), ))), _ => bail!(pos, "invalid CoreDumpValue type"), } } } wasmparser-0.217.0/src/readers/core/custom.rs000064400000000000000000000120151046102023000172020ustar 00000000000000use crate::{BinaryReader, Result}; use core::fmt; use core::ops::Range; /// A reader for custom sections of a WebAssembly module. #[derive(Clone)] pub struct CustomSectionReader<'a> { name: &'a str, reader: BinaryReader<'a>, } impl<'a> CustomSectionReader<'a> { /// Constructs a new `CustomSectionReader` for the given data and offset. pub fn new(mut reader: BinaryReader<'a>) -> Result> { let name = reader.read_string()?; Ok(CustomSectionReader { name, reader }) } /// The name of the custom section. pub fn name(&self) -> &'a str { self.name } /// The offset, relative to the start of the original module or component, /// that the `data` payload for this custom section starts at. pub fn data_offset(&self) -> usize { self.reader.original_position() } /// The actual contents of the custom section. pub fn data(&self) -> &'a [u8] { self.reader.remaining_buffer() } /// The range of bytes that specify this whole custom section (including /// both the name of this custom section and its data) specified in /// offsets relative to the start of the byte stream. pub fn range(&self) -> Range { self.reader.range() } /// Attempts to match and see if this custom section is statically known to /// `wasmparser` with any known section reader. /// /// This will inspect `self.name()` and return a [`KnownCustom`] if the name /// matches a known custom section where there is a parser available for it. /// This can also be used as a convenience function for creating such /// parsers. /// /// If the custom section name is not known, or if a reader could not be /// created, then `KnownCustom::Unknown` is returned. pub fn as_known(&self) -> KnownCustom<'a> { match self.name() { "name" => KnownCustom::Name(crate::NameSectionReader::new(self.reader.shrink())), "component-name" => KnownCustom::ComponentName(crate::ComponentNameSectionReader::new( self.reader.shrink(), )), "metadata.code.branch_hint" => { match crate::BranchHintSectionReader::new(self.reader.shrink()) { Ok(s) => KnownCustom::BranchHints(s), Err(_) => KnownCustom::Unknown, } } "producers" => match crate::ProducersSectionReader::new(self.reader.shrink()) { Ok(s) => KnownCustom::Producers(s), Err(_) => KnownCustom::Unknown, }, "dylink.0" => { KnownCustom::Dylink0(crate::Dylink0SectionReader::new(self.reader.shrink())) } "core" => match crate::CoreDumpSection::new(self.reader.shrink()) { Ok(s) => KnownCustom::CoreDump(s), Err(_) => KnownCustom::Unknown, }, "coremodules" => match crate::CoreDumpModulesSection::new(self.reader.shrink()) { Ok(s) => KnownCustom::CoreDumpModules(s), Err(_) => KnownCustom::Unknown, }, "coreinstances" => match crate::CoreDumpInstancesSection::new(self.reader.shrink()) { Ok(s) => KnownCustom::CoreDumpInstances(s), Err(_) => KnownCustom::Unknown, }, "corestack" => match crate::CoreDumpStackSection::new(self.reader.shrink()) { Ok(s) => KnownCustom::CoreDumpStack(s), Err(_) => KnownCustom::Unknown, }, "linking" => match crate::LinkingSectionReader::new(self.reader.shrink()) { Ok(s) => KnownCustom::Linking(s), Err(_) => KnownCustom::Unknown, }, s if s.starts_with("reloc.") => { match crate::RelocSectionReader::new(self.reader.shrink()) { Ok(s) => KnownCustom::Reloc(s), Err(_) => KnownCustom::Unknown, } } _ => KnownCustom::Unknown, } } } /// Return value of [`CustomSectionReader::as_known`]. #[allow(missing_docs)] pub enum KnownCustom<'a> { Name(crate::NameSectionReader<'a>), ComponentName(crate::ComponentNameSectionReader<'a>), BranchHints(crate::BranchHintSectionReader<'a>), Producers(crate::ProducersSectionReader<'a>), Dylink0(crate::Dylink0SectionReader<'a>), CoreDump(crate::CoreDumpSection<'a>), CoreDumpStack(crate::CoreDumpStackSection<'a>), CoreDumpInstances(crate::CoreDumpInstancesSection), CoreDumpModules(crate::CoreDumpModulesSection<'a>), Linking(crate::LinkingSectionReader<'a>), Reloc(crate::RelocSectionReader<'a>), Unknown, } impl<'a> fmt::Debug for CustomSectionReader<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CustomSectionReader") .field("name", &self.name) .field("data_offset", &self.data_offset()) .field("data", &"...") .field("range", &self.range()) .finish() } } wasmparser-0.217.0/src/readers/core/data.rs000064400000000000000000000064701046102023000166110ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, BinaryReaderError, ConstExpr, FromReader, Result, SectionLimited}; use core::ops::Range; /// Represents a data segment in a core WebAssembly module. #[derive(Debug, Clone)] pub struct Data<'a> { /// The kind of data segment. pub kind: DataKind<'a>, /// The data of the data segment. pub data: &'a [u8], /// The range of the data segment. pub range: Range, } /// The kind of data segment. #[derive(Debug, Clone)] pub enum DataKind<'a> { /// The data segment is passive. Passive, /// The data segment is active. Active { /// The memory index for the data segment. memory_index: u32, /// The initialization expression for the data segment. offset_expr: ConstExpr<'a>, }, } /// A reader for the data section of a WebAssembly module. pub type DataSectionReader<'a> = SectionLimited<'a, Data<'a>>; impl<'a> FromReader<'a> for Data<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let segment_start = reader.original_position(); // The current handling of the flags is largely specified in the `bulk-memory` proposal, // which at the time this comment is written has been merged to the main specification // draft. // // Notably, this proposal allows multiple different encodings of the memory index 0. `00` // and `02 00` are both valid ways to specify the 0-th memory. However it also makes // another encoding of the 0-th memory `80 00` no longer valid. // // We, however maintain this by parsing `flags` as a LEB128 integer. In that case, `80 00` // encoding is parsed out as `0` and is therefore assigned a `memidx` 0, even though the // current specification draft does not allow for this. // // See also https://github.com/WebAssembly/spec/issues/1439 let flags = reader.read_var_u32()?; let kind = match flags { 1 => DataKind::Passive, 0 | 2 => { let memory_index = if flags == 0 { 0 } else { reader.read_var_u32()? }; let offset_expr = reader.read()?; DataKind::Active { memory_index, offset_expr, } } _ => { return Err(BinaryReaderError::new( "invalid flags byte in data segment", segment_start, )); } }; let data = reader.read_reader()?; Ok(Data { kind, data: data.remaining_buffer(), range: segment_start..data.range().end, }) } } wasmparser-0.217.0/src/readers/core/dylink0.rs000064400000000000000000000067561046102023000172610ustar 00000000000000use crate::prelude::*; use crate::{BinaryReader, Result, Subsection, Subsections, SymbolFlags}; use core::ops::Range; /// Parser for the dynamic linking `dylink.0` custom section. /// /// This format is currently defined upstream at /// . pub type Dylink0SectionReader<'a> = Subsections<'a, Dylink0Subsection<'a>>; const WASM_DYLINK_MEM_INFO: u8 = 1; const WASM_DYLINK_NEEDED: u8 = 2; const WASM_DYLINK_EXPORT_INFO: u8 = 3; const WASM_DYLINK_IMPORT_INFO: u8 = 4; /// Represents a `WASM_DYLINK_MEM_INFO` field #[derive(Debug, Copy, Clone)] pub struct MemInfo { /// Size of the memory area the loader should reserve for the module, which /// will begin at `env.__memory_base`. pub memory_size: u32, /// The required alignment of the memory area, in bytes, encoded as a power /// of 2. pub memory_alignment: u32, /// Size of the table area the loader should reserve for the module, which /// will begin at `env.__table_base`. pub table_size: u32, /// The required alignment of the table area, in elements, encoded as a /// power of 2. pub table_alignment: u32, } #[allow(missing_docs)] #[derive(Debug)] pub struct ExportInfo<'a> { pub name: &'a str, pub flags: SymbolFlags, } #[allow(missing_docs)] #[derive(Debug)] pub struct ImportInfo<'a> { pub module: &'a str, pub field: &'a str, pub flags: SymbolFlags, } /// Possible subsections of the `dylink.0` custom section. #[derive(Debug)] #[allow(missing_docs)] pub enum Dylink0Subsection<'a> { MemInfo(MemInfo), Needed(Vec<&'a str>), ExportInfo(Vec>), ImportInfo(Vec>), Unknown { ty: u8, data: &'a [u8], range: Range, }, } impl<'a> Subsection<'a> for Dylink0Subsection<'a> { fn from_reader(id: u8, mut reader: BinaryReader<'a>) -> Result { let data = reader.remaining_buffer(); let offset = reader.original_position(); Ok(match id { WASM_DYLINK_MEM_INFO => Self::MemInfo(MemInfo { memory_size: reader.read_var_u32()?, memory_alignment: reader.read_var_u32()?, table_size: reader.read_var_u32()?, table_alignment: reader.read_var_u32()?, }), WASM_DYLINK_NEEDED => Self::Needed( (0..reader.read_var_u32()?) .map(|_| reader.read_string()) .collect::>()?, ), WASM_DYLINK_EXPORT_INFO => Self::ExportInfo( (0..reader.read_var_u32()?) .map(|_| { Ok(ExportInfo { name: reader.read_string()?, flags: reader.read()?, }) }) .collect::>()?, ), WASM_DYLINK_IMPORT_INFO => Self::ImportInfo( (0..reader.read_var_u32()?) .map(|_| { Ok(ImportInfo { module: reader.read_string()?, field: reader.read_string()?, flags: reader.read()?, }) }) .collect::>()?, ), ty => Self::Unknown { ty, data, range: offset..offset + data.len(), }, }) } } wasmparser-0.217.0/src/readers/core/elements.rs000064400000000000000000000121551046102023000175110ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{ BinaryReader, BinaryReaderError, ConstExpr, ExternalKind, FromReader, RefType, Result, SectionLimited, }; use core::ops::Range; /// Represents a core WebAssembly element segment. #[derive(Clone)] pub struct Element<'a> { /// The kind of the element segment. pub kind: ElementKind<'a>, /// The initial elements of the element segment. pub items: ElementItems<'a>, /// The range of the the element segment. pub range: Range, } /// The kind of element segment. #[derive(Clone)] pub enum ElementKind<'a> { /// The element segment is passive. Passive, /// The element segment is active. Active { /// The index of the table being initialized. table_index: Option, /// The initial expression of the element segment. offset_expr: ConstExpr<'a>, }, /// The element segment is declared. Declared, } /// Represents the items of an element segment. #[derive(Clone)] pub enum ElementItems<'a> { /// This element contains function indices. Functions(SectionLimited<'a, u32>), /// This element contains constant expressions used to initialize the table. Expressions(RefType, SectionLimited<'a, ConstExpr<'a>>), } /// A reader for the element section of a WebAssembly module. pub type ElementSectionReader<'a> = SectionLimited<'a, Element<'a>>; impl<'a> FromReader<'a> for Element<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let elem_start = reader.original_position(); // The current handling of the flags is largely specified in the `bulk-memory` proposal, // which at the time this commend is written has been merged to the main specification // draft. // // Notably, this proposal allows multiple different encodings of the table index 0. `00` // and `02 00` are both valid ways to specify the 0-th table. However it also makes // another encoding of the 0-th memory `80 00` no longer valid. // // We, however maintain this support by parsing `flags` as a LEB128 integer. In that case, // `80 00` encoding is parsed out as `0` and is therefore assigned a `tableidx` 0, even // though the current specification draft does not allow for this. // // See also https://github.com/WebAssembly/spec/issues/1439 let flags = reader.read_var_u32()?; if (flags & !0b111) != 0 { return Err(BinaryReaderError::new( "invalid flags byte in element segment", reader.original_position() - 1, )); } let kind = if flags & 0b001 != 0 { if flags & 0b010 != 0 { ElementKind::Declared } else { ElementKind::Passive } } else { let table_index = if flags & 0b010 == 0 { None } else { Some(reader.read_var_u32()?) }; let offset_expr = reader.read()?; ElementKind::Active { table_index, offset_expr, } }; let exprs = flags & 0b100 != 0; let ty = if flags & 0b011 != 0 { if exprs { Some(reader.read()?) } else { match reader.read()? { ExternalKind::Func => None, _ => { return Err(BinaryReaderError::new( "only the function external type is supported in elem segment", reader.original_position() - 1, )); } } } } else { None }; // FIXME(#188) ideally wouldn't have to do skips here let data = reader.skip(|reader| { let items_count = reader.read_var_u32()?; if exprs { for _ in 0..items_count { reader.skip_const_expr()?; } } else { for _ in 0..items_count { reader.read_var_u32()?; } } Ok(()) })?; let items = if exprs { ElementItems::Expressions(ty.unwrap_or(RefType::FUNCREF), SectionLimited::new(data)?) } else { assert!(ty.is_none()); ElementItems::Functions(SectionLimited::new(data)?) }; let elem_end = reader.original_position(); let range = elem_start..elem_end; Ok(Element { kind, items, range }) } } wasmparser-0.217.0/src/readers/core/exports.rs000064400000000000000000000040341046102023000173760ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, FromReader, Result, SectionLimited}; /// A reader for the export section of a WebAssembly module. pub type ExportSectionReader<'a> = SectionLimited<'a, Export<'a>>; /// External types as defined [here]. /// /// [here]: https://webassembly.github.io/spec/core/syntax/types.html#external-types #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ExternalKind { /// The external kind is a function. Func, /// The external kind if a table. Table, /// The external kind is a memory. Memory, /// The external kind is a global. Global, /// The external kind is a tag. Tag, } /// Represents an export in a WebAssembly module. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Export<'a> { /// The name of the exported item. pub name: &'a str, /// The kind of the export. pub kind: ExternalKind, /// The index of the exported item. pub index: u32, } impl<'a> FromReader<'a> for Export<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(Export { name: reader.read_string()?, kind: reader.read()?, index: reader.read_var_u32()?, }) } } impl<'a> FromReader<'a> for ExternalKind { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let offset = reader.original_position(); let byte = reader.read_u8()?; BinaryReader::external_kind_from_byte(byte, offset) } } wasmparser-0.217.0/src/readers/core/functions.rs000064400000000000000000000013341046102023000177020ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /// A reader for the function section of a WebAssembly module. pub type FunctionSectionReader<'a> = crate::SectionLimited<'a, u32>; wasmparser-0.217.0/src/readers/core/globals.rs000064400000000000000000000040401046102023000173120ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, ConstExpr, FromReader, GlobalType, Result, SectionLimited}; /// Represents a core WebAssembly global. #[derive(Debug, Clone)] pub struct Global<'a> { /// The global's type. pub ty: GlobalType, /// The global's initialization expression. pub init_expr: ConstExpr<'a>, } /// A reader for the global section of a WebAssembly module. pub type GlobalSectionReader<'a> = SectionLimited<'a, Global<'a>>; impl<'a> FromReader<'a> for Global<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let ty = reader.read()?; let init_expr = reader.read()?; Ok(Global { ty, init_expr }) } } impl<'a> FromReader<'a> for GlobalType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let content_type = reader.read()?; let flags = reader.read_u8()?; if reader.shared_everything_threads() { if flags > 0b11 { bail!(reader.original_position() - 1, "malformed global flags") } } else { if flags > 0b1 { bail!( reader.original_position() - 1, "malformed mutability -- or shared globals \ require the shared-everything-threads proposal" ) } } Ok(GlobalType { content_type, mutable: (flags & 0b01) > 0, shared: (flags & 0b10) > 0, }) } } wasmparser-0.217.0/src/readers/core/imports.rs000064400000000000000000000047251046102023000173760ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{ BinaryReader, ExternalKind, FromReader, GlobalType, MemoryType, Result, SectionLimited, TableType, TagType, }; /// Represents a reference to a type definition in a WebAssembly module. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum TypeRef { /// The type is a function. /// /// The value is an index into the type section. Func(u32), /// The type is a table. Table(TableType), /// The type is a memory. Memory(MemoryType), /// The type is a global. Global(GlobalType), /// The type is a tag. /// /// This variant is only used for the exception handling proposal. /// /// The value is an index in the types index space. Tag(TagType), } /// Represents an import in a WebAssembly module. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Import<'a> { /// The module being imported from. pub module: &'a str, /// The name of the imported item. pub name: &'a str, /// The type of the imported item. pub ty: TypeRef, } /// A reader for the import section of a WebAssembly module. pub type ImportSectionReader<'a> = SectionLimited<'a, Import<'a>>; impl<'a> FromReader<'a> for Import<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(Import { module: reader.read()?, name: reader.read()?, ty: reader.read()?, }) } } impl<'a> FromReader<'a> for TypeRef { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read()? { ExternalKind::Func => TypeRef::Func(reader.read_var_u32()?), ExternalKind::Table => TypeRef::Table(reader.read()?), ExternalKind::Memory => TypeRef::Memory(reader.read()?), ExternalKind::Global => TypeRef::Global(reader.read()?), ExternalKind::Tag => TypeRef::Tag(reader.read()?), }) } } wasmparser-0.217.0/src/readers/core/init.rs000064400000000000000000000040571046102023000166420ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, FromReader, OperatorsReader, Result}; use core::fmt; /// Represents an initialization expression. #[derive(Clone)] pub struct ConstExpr<'a> { reader: BinaryReader<'a>, } impl PartialEq for ConstExpr<'_> { fn eq(&self, other: &Self) -> bool { self.reader.remaining_buffer() == other.reader.remaining_buffer() } } impl Eq for ConstExpr<'_> {} impl<'a> ConstExpr<'a> { /// Constructs a new `ConstExpr` from the given data and offset. pub fn new(reader: BinaryReader<'a>) -> ConstExpr<'a> { ConstExpr { reader } } /// Gets a binary reader for the initialization expression. pub fn get_binary_reader(&self) -> BinaryReader<'a> { self.reader.clone() } /// Gets an operators reader for the initialization expression. pub fn get_operators_reader(&self) -> OperatorsReader<'a> { OperatorsReader::new(self.get_binary_reader()) } } impl<'a> FromReader<'a> for ConstExpr<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // FIXME(#188) ideally shouldn't need to skip here let reader = reader.skip(|r| r.skip_const_expr())?; Ok(ConstExpr { reader }) } } impl fmt::Debug for ConstExpr<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ConstExpr") .field("offset", &self.reader.original_position()) .field("data", &self.reader.remaining_buffer()) .finish() } } wasmparser-0.217.0/src/readers/core/linking.rs000064400000000000000000000361751046102023000173400ustar 00000000000000use crate::prelude::*; use crate::{ BinaryReader, BinaryReaderError, FromReader, Result, SectionLimited, Subsection, Subsections, }; use core::ops::Range; bitflags::bitflags! { /// Flags for WebAssembly symbols. /// /// These flags correspond to those described in /// /// with the `WASM_SYM_*` prefix. #[repr(transparent)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SymbolFlags: u32 { /* N.B.: Newly added flags should be keep in sync with `print_dylink0_flags` in `crates/wasmprinter/src/lib.rs`. */ /// This is a weak symbol. const BINDING_WEAK = 1 << 0; /// This is a local symbol (this is exclusive with [BINDING_WEAK]). const BINDING_LOCAL = 1 << 1; /// This is a hidden symbol. const VISIBILITY_HIDDEN = 1 << 2; /// This symbol is not defined. const UNDEFINED = 1 << 4; /// This symbol is intended to be exported from the wasm module to the host environment. const EXPORTED = 1 << 5; /// This symbol uses an explicit symbol name, rather than reusing the name from a wasm import. const EXPLICIT_NAME = 1 << 6; /// This symbol is intended to be included in the linker output, regardless of whether it is used by the program. const NO_STRIP = 1 << 7; /// This symbol resides in thread local storage. const TLS = 1 << 8; /// This symbol represents an absolute address. const ABSOLUTE = 1 << 9; } /// Flags for WebAssembly segments. /// /// These flags are defined by implementation at the time of writing: /// #[repr(transparent)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] pub struct SegmentFlags: u32 { /// The segment contains only null-terminated strings, which allows the linker to perform merging. const STRINGS = 0x1; /// The segment contains thread-local data. const TLS = 0x2; } } impl<'a> FromReader<'a> for SymbolFlags { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(Self::from_bits_retain(reader.read_var_u32()?)) } } impl<'a> FromReader<'a> for SegmentFlags { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(Self::from_bits_retain(reader.read_var_u32()?)) } } /// A reader for the `linking` custom section of a WebAssembly module. /// /// This format is currently defined upstream at /// . #[derive(Debug, Clone)] pub struct LinkingSectionReader<'a> { /// The version of linking metadata contained in this section. version: u32, /// The subsections in this section. subsections: Subsections<'a, Linking<'a>>, /// The range of the entire section, including the version. range: Range, } /// Represents a reader for segments from the linking custom section. pub type SegmentMap<'a> = SectionLimited<'a, Segment<'a>>; /// Represents extra metadata about the data segments. #[derive(Debug, Copy, Clone)] pub struct Segment<'a> { /// The name for the segment. pub name: &'a str, /// The required alignment of the segment, encoded as a power of 2. pub alignment: u32, /// The flags for the segment. pub flags: SegmentFlags, } impl<'a> FromReader<'a> for Segment<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let name = reader.read_string()?; let alignment = reader.read_var_u32()?; let flags = reader.read()?; Ok(Self { name, alignment, flags, }) } } /// Represents a reader for init functions from the linking custom section. pub type InitFuncMap<'a> = SectionLimited<'a, InitFunc>; /// Represents an init function in the linking custom section. #[derive(Debug, Copy, Clone)] pub struct InitFunc { /// The priority of the init function. pub priority: u32, /// The symbol index of init function (*not* the function index). pub symbol_index: u32, } impl<'a> FromReader<'a> for InitFunc { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let priority = reader.read_var_u32()?; let symbol_index = reader.read_var_u32()?; Ok(Self { priority, symbol_index, }) } } /// Represents a reader for COMDAT data from the linking custom section. pub type ComdatMap<'a> = SectionLimited<'a, Comdat<'a>>; /// Represents [COMDAT](https://llvm.org/docs/LangRef.html#comdats) data in the linking custom section. #[derive(Debug, Clone)] pub struct Comdat<'a> { /// The name of this comdat. pub name: &'a str, /// The flags. pub flags: u32, /// The member symbols of this comdat. pub symbols: SectionLimited<'a, ComdatSymbol>, } impl<'a> FromReader<'a> for Comdat<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let name = reader.read_string()?; let flags = reader.read_var_u32()?; // FIXME(#188) ideally shouldn't need to skip here let symbols = reader.skip(|reader| { let count = reader.read_var_u32()?; for _ in 0..count { reader.read::()?; } Ok(()) })?; Ok(Self { name, flags, symbols: SectionLimited::new(symbols)?, }) } } /// Represents a symbol that is part of a comdat. #[derive(Debug, Copy, Clone)] pub struct ComdatSymbol { /// The kind of the symbol. pub kind: ComdatSymbolKind, /// The index of the symbol. Must not be an import. pub index: u32, } impl<'a> FromReader<'a> for ComdatSymbol { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let kind = reader.read()?; let index = reader.read_var_u32()?; Ok(Self { kind, index }) } } /// Represents a symbol kind. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ComdatSymbolKind { /// The symbol is a data segment. Data, /// The symbol is a function. Func, /// The symbol is a global. Global, /// The symbol is an event. Event, /// The symbol is a table. Table, /// The symbol is a section. Section, } impl<'a> FromReader<'a> for ComdatSymbolKind { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let offset = reader.original_position(); match reader.read_u8()? { 0 => Ok(Self::Data), 1 => Ok(Self::Func), 2 => Ok(Self::Global), 3 => Ok(Self::Event), 4 => Ok(Self::Table), 5 => Ok(Self::Section), k => Err(BinaryReader::invalid_leading_byte_error( k, "comdat symbol kind", offset, )), } } } /// Represents a reader for symbol info from the linking custom section. pub type SymbolInfoMap<'a> = SectionLimited<'a, SymbolInfo<'a>>; /// Represents extra information about symbols in the linking custom section. /// /// The symbol flags correspond to those described in /// /// with the `WASM_SYM_*` prefix. #[derive(Debug, Copy, Clone)] pub enum SymbolInfo<'a> { /// The symbol is a function. Func { /// The flags for the symbol. flags: SymbolFlags, /// The index of the function corresponding to this symbol. index: u32, /// The name for the function, if it is defined or uses an explicit name. name: Option<&'a str>, }, /// The symbol is a data symbol. Data { /// The flags for the symbol. flags: SymbolFlags, /// The name for the symbol. name: &'a str, /// The definition of the data symbol, if it is defined. symbol: Option, }, /// The symbol is a global. Global { /// The flags for the symbol. flags: SymbolFlags, /// The index of the global corresponding to this symbol. index: u32, /// The name for the global, if it is defined or uses an explicit name. name: Option<&'a str>, }, /// The symbol is a section. Section { /// The flags for the symbol. flags: SymbolFlags, /// The index of the function corresponding to this symbol. section: u32, }, /// The symbol is an event. Event { /// The flags for the symbol. flags: SymbolFlags, /// The index of the event corresponding to this symbol. index: u32, /// The name for the event, if it is defined or uses an explicit name. name: Option<&'a str>, }, /// The symbol is a table. Table { /// The flags for the symbol. flags: SymbolFlags, /// The index of the table corresponding to this symbol. index: u32, /// The name for the table, if it is defined or uses an explicit name. name: Option<&'a str>, }, } impl<'a> FromReader<'a> for SymbolInfo<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let offset = reader.original_position(); let kind = reader.read_u8()?; let flags: SymbolFlags = reader.read()?; let defined = !flags.contains(SymbolFlags::UNDEFINED); let explicit_name = flags.contains(SymbolFlags::EXPLICIT_NAME); const SYMTAB_FUNCTION: u8 = 0; const SYMTAB_DATA: u8 = 1; const SYMTAB_GLOBAL: u8 = 2; const SYMTAB_SECTION: u8 = 3; const SYMTAB_EVENT: u8 = 4; const SYMTAB_TABLE: u8 = 5; // https://github.com/WebAssembly/wabt/blob/1.0.34/src/binary-writer.cc#L1226 match kind { SYMTAB_FUNCTION | SYMTAB_GLOBAL | SYMTAB_EVENT | SYMTAB_TABLE => { let index = reader.read_var_u32()?; let name = match defined || explicit_name { true => Some(reader.read_string()?), false => None, }; Ok(match kind { SYMTAB_FUNCTION => Self::Func { flags, index, name }, SYMTAB_GLOBAL => Self::Global { flags, index, name }, SYMTAB_EVENT => Self::Event { flags, index, name }, SYMTAB_TABLE => Self::Table { flags, index, name }, _ => unreachable!(), }) } SYMTAB_DATA => { let name = reader.read_string()?; let data = match defined { true => Some(reader.read()?), false => None, }; Ok(Self::Data { flags, name, symbol: data, }) } SYMTAB_SECTION => { let section = reader.read_var_u32()?; Ok(Self::Section { flags, section }) } k => Err(BinaryReader::invalid_leading_byte_error( k, "symbol kind", offset, )), } } } /// Represents the metadata about a data symbol defined in the wasm file. #[derive(Debug, Copy, Clone)] pub struct DefinedDataSymbol { /// The index of the data segment. pub index: u32, /// The offset within the segment. Must be <= the segment's size. pub offset: u32, /// The size of the data, which can be zero. `offset + size` must be <= the segment's size. pub size: u32, } impl<'a> FromReader<'a> for DefinedDataSymbol { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let index = reader.read_var_u32()?; let offset = reader.read_var_u32()?; let size = reader.read_var_u32()?; Ok(Self { index, offset, size, }) } } /// Represents a subsection read from the linking custom section. #[derive(Debug, Clone)] pub enum Linking<'a> { /// Extra metadata about the data segments. SegmentInfo(SegmentMap<'a>), /// A list of constructor functions to be called at startup. InitFuncs(InitFuncMap<'a>), /// The [COMDAT](https://llvm.org/docs/LangRef.html#comdats) groups of associated linking objects. ComdatInfo(ComdatMap<'a>), /// Extra information about the symbols present in the module. SymbolTable(SymbolInfoMap<'a>), /// An unknown [linking subsection](https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#linking-metadata-section). Unknown { /// The identifier for this subsection. ty: u8, /// The contents of this subsection. data: &'a [u8], /// The range of bytes, relative to the start of the original data /// stream, that the contents of this subsection reside in. range: Range, }, } impl<'a> Subsection<'a> for Linking<'a> { fn from_reader(id: u8, reader: BinaryReader<'a>) -> Result { let data = reader.remaining_buffer(); let offset = reader.original_position(); Ok(match id { 5 => Self::SegmentInfo(SegmentMap::new(reader)?), 6 => Self::InitFuncs(InitFuncMap::new(reader)?), 7 => Self::ComdatInfo(ComdatMap::new(reader)?), 8 => Self::SymbolTable(SymbolInfoMap::new(reader)?), ty => Self::Unknown { ty, data, range: offset..offset + data.len(), }, }) } } impl<'a> LinkingSectionReader<'a> { /// Creates a new reader for the linking section contents starting at /// `offset` within the original wasm file. pub fn new(mut reader: BinaryReader<'a>) -> Result { let range = reader.range(); let offset = reader.original_position(); let version = reader.read_var_u32()?; if version != 2 { return Err(BinaryReaderError::new( format!("unsupported linking section version: {}", version), offset, )); } let subsections = Subsections::new(reader.shrink()); Ok(Self { version, subsections, range, }) } /// Returns the version of linking metadata contained in this section. pub fn version(&self) -> u32 { self.version } /// Returns the original byte offset of this section. pub fn original_position(&self) -> usize { self.subsections.original_position() } /// Returns the range, as byte offsets, of this section within the original /// wasm binary. pub fn range(&self) -> Range { self.range.clone() } /// Returns the iterator for advancing through the subsections. /// /// You can also use [`IntoIterator::into_iter`] directly on this type. pub fn subsections(&self) -> Subsections<'a, Linking<'a>> { self.subsections.clone() } } impl<'a> IntoIterator for LinkingSectionReader<'a> { type Item = Result>; type IntoIter = Subsections<'a, Linking<'a>>; fn into_iter(self) -> Self::IntoIter { self.subsections } } wasmparser-0.217.0/src/readers/core/memories.rs000064400000000000000000000035451046102023000175200ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, FromReader, MemoryType, Result, SectionLimited}; /// A reader for the memory section of a WebAssembly module. pub type MemorySectionReader<'a> = SectionLimited<'a, MemoryType>; impl<'a> FromReader<'a> for MemoryType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let pos = reader.original_position(); let flags = reader.read_u8()?; if (flags & !0b1111) != 0 { bail!(pos, "invalid memory limits flags"); } let memory64 = flags & 0b0100 != 0; let shared = flags & 0b0010 != 0; let has_max = flags & 0b0001 != 0; let has_page_size = flags & 0b1000 != 0; Ok(MemoryType { memory64, shared, initial: if memory64 { reader.read_var_u64()? } else { reader.read_var_u32()?.into() }, maximum: if !has_max { None } else if memory64 { Some(reader.read_var_u64()?) } else { Some(reader.read_var_u32()?.into()) }, page_size_log2: if has_page_size { Some(reader.read_var_u32()?) } else { None }, }) } } wasmparser-0.217.0/src/readers/core/names.rs000064400000000000000000000130051046102023000167730ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{ BinaryReader, BinaryReaderError, FromReader, Result, SectionLimited, Subsection, Subsections, }; use core::ops::Range; /// Represents a name map from the names custom section. pub type NameMap<'a> = SectionLimited<'a, Naming<'a>>; /// Represents a name for an index from the names section. #[derive(Debug, Copy, Clone)] pub struct Naming<'a> { /// The index being named. pub index: u32, /// The name for the index. pub name: &'a str, } impl<'a> FromReader<'a> for Naming<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let index = reader.read_var_u32()?; // This seems to match what browsers do where they don't limit the // length of names in the `name` section while they do limit the names // in the import and export section for example. let name = reader.read_unlimited_string()?; Ok(Naming { index, name }) } } /// Represents a reader for indirect names from the names custom section. pub type IndirectNameMap<'a> = SectionLimited<'a, IndirectNaming<'a>>; /// Represents an indirect name in the names custom section. #[derive(Debug, Clone)] pub struct IndirectNaming<'a> { /// The indirect index of the name. pub index: u32, /// The map of names within the `index` prior. pub names: NameMap<'a>, } impl<'a> FromReader<'a> for IndirectNaming<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let index = reader.read_var_u32()?; // Skip the `NameMap` manually here. // // FIXME(#188) shouldn't need to skip here let names = reader.skip(|reader| { let count = reader.read_var_u32()?; for _ in 0..count { reader.read_var_u32()?; reader.skip_string()?; } Ok(()) })?; Ok(IndirectNaming { index, names: NameMap::new(names)?, }) } } /// Represents a name read from the names custom section. #[derive(Clone)] pub enum Name<'a> { /// The name is for the module. Module { /// The specified name. name: &'a str, /// The byte range that `name` occupies in the original binary. name_range: Range, }, /// The name is for the functions. Function(NameMap<'a>), /// The name is for the function locals. Local(IndirectNameMap<'a>), /// The name is for the function labels. Label(IndirectNameMap<'a>), /// The name is for the types. Type(NameMap<'a>), /// The name is for the tables. Table(NameMap<'a>), /// The name is for the memories. Memory(NameMap<'a>), /// The name is for the globals. Global(NameMap<'a>), /// The name is for the element segments. Element(NameMap<'a>), /// The name is for the data segments. Data(NameMap<'a>), /// The name is for fields. Field(IndirectNameMap<'a>), /// The name is for tags. Tag(NameMap<'a>), /// An unknown [name subsection](https://webassembly.github.io/spec/core/appendix/custom.html#subsections). Unknown { /// The identifier for this subsection. ty: u8, /// The contents of this subsection. data: &'a [u8], /// The range of bytes, relative to the start of the original data /// stream, that the contents of this subsection reside in. range: Range, }, } /// A reader for the name custom section of a WebAssembly module. pub type NameSectionReader<'a> = Subsections<'a, Name<'a>>; impl<'a> Subsection<'a> for Name<'a> { fn from_reader(id: u8, mut reader: BinaryReader<'a>) -> Result { let data = reader.remaining_buffer(); let offset = reader.original_position(); Ok(match id { 0 => { let name = reader.read_string()?; if !reader.eof() { return Err(BinaryReaderError::new( "trailing data at the end of a name", reader.original_position(), )); } Name::Module { name, name_range: offset..reader.original_position(), } } 1 => Name::Function(NameMap::new(reader)?), 2 => Name::Local(IndirectNameMap::new(reader)?), 3 => Name::Label(IndirectNameMap::new(reader)?), 4 => Name::Type(NameMap::new(reader)?), 5 => Name::Table(NameMap::new(reader)?), 6 => Name::Memory(NameMap::new(reader)?), 7 => Name::Global(NameMap::new(reader)?), 8 => Name::Element(NameMap::new(reader)?), 9 => Name::Data(NameMap::new(reader)?), 10 => Name::Field(IndirectNameMap::new(reader)?), 11 => Name::Tag(NameMap::new(reader)?), ty => Name::Unknown { ty, data, range: offset..offset + data.len(), }, }) } } wasmparser-0.217.0/src/readers/core/operators.rs000064400000000000000000000350571046102023000177210ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::limits::MAX_WASM_CATCHES; use crate::prelude::*; use crate::{BinaryReader, BinaryReaderError, FromReader, Result, ValType}; /// Represents a block type. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum BlockType { /// The block produces consumes nor produces any values. Empty, /// The block produces a singular value of the given type ([] -> \[t]). Type(ValType), /// The block is described by a function type. /// /// The index is to a function type in the types section. FuncType(u32), } /// Represents a memory immediate in a WebAssembly memory instruction. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct MemArg { /// Alignment, stored as `n` where the actual alignment is `2^n` pub align: u8, /// Maximum alignment, stored as `n` where the actual alignment is `2^n`. /// /// Note that this field is not actually read from the binary format, it /// will be a constant depending on which instruction this `MemArg` is a /// payload for. pub max_align: u8, /// A fixed byte-offset that this memory immediate specifies. /// /// Note that the memory64 proposal can specify a full 64-bit byte offset /// while otherwise only 32-bit offsets are allowed. Once validated /// memory immediates for 32-bit memories are guaranteed to be at most /// `u32::MAX` whereas 64-bit memories can use the full 64-bits. pub offset: u64, /// The index of the memory this immediate points to. /// /// Note that this points within the module's own memory index space, and /// is always zero unless the multi-memory proposal of WebAssembly is /// enabled. pub memory: u32, } /// A br_table entries representation. #[derive(Clone)] pub struct BrTable<'a> { pub(crate) reader: crate::BinaryReader<'a>, pub(crate) cnt: u32, pub(crate) default: u32, } impl PartialEq for BrTable<'_> { fn eq(&self, other: &Self) -> bool { self.cnt == other.cnt && self.default == other.default && self.reader.remaining_buffer() == other.reader.remaining_buffer() } } impl Eq for BrTable<'_> {} /// An IEEE binary32 immediate floating point value, represented as a u32 /// containing the bit pattern. /// /// All bit patterns are allowed. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct Ieee32(pub(crate) u32); impl Ieee32 { /// Gets the underlying bits of the 32-bit float. pub fn bits(self) -> u32 { self.0 } } impl From for Ieee32 { fn from(value: f32) -> Self { Ieee32 { 0: u32::from_le_bytes(value.to_le_bytes()), } } } impl From for f32 { fn from(bits: Ieee32) -> f32 { f32::from_bits(bits.bits()) } } /// An IEEE binary64 immediate floating point value, represented as a u64 /// containing the bit pattern. /// /// All bit patterns are allowed. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct Ieee64(pub(crate) u64); impl Ieee64 { /// Gets the underlying bits of the 64-bit float. pub fn bits(self) -> u64 { self.0 } } impl From for Ieee64 { fn from(value: f64) -> Self { Ieee64 { 0: u64::from_le_bytes(value.to_le_bytes()), } } } impl From for f64 { fn from(bits: Ieee64) -> f64 { f64::from_bits(bits.bits()) } } /// Represents a 128-bit vector value. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct V128(pub(crate) [u8; 16]); impl V128 { /// Gets the bytes of the vector value. pub fn bytes(&self) -> &[u8; 16] { &self.0 } /// Gets a signed 128-bit integer value from the vector's bytes. pub fn i128(&self) -> i128 { i128::from_le_bytes(self.0) } } impl From for i128 { fn from(bits: V128) -> i128 { bits.i128() } } impl From for u128 { fn from(bits: V128) -> u128 { u128::from_le_bytes(bits.0) } } /// Represents the memory ordering for atomic instructions. /// /// For an in-depth explanation of memory orderings, see the C++ documentation /// for [`memory_order`] or the Rust documentation for [`atomic::Ordering`]. /// /// [`memory_order`]: https://en.cppreference.com/w/cpp/atomic/memory_order /// [`atomic::Ordering`]: https://doc.rust-lang.org/std/sync/atomic/enum.Ordering.html #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum Ordering { /// For a load, it acquires; this orders all operations before the last /// "releasing" store. For a store, it releases; this orders all operations /// before it at the next "acquiring" load. AcqRel, /// Like `AcqRel` but all threads see all sequentially consistent operations /// in the same order. SeqCst, } macro_rules! define_operator { ($(@$proposal:ident $op:ident $({ $($payload:tt)* })? => $visit:ident)*) => { /// Instructions as defined [here]. /// /// [here]: https://webassembly.github.io/spec/core/binary/instructions.html #[derive(Debug, Clone, Eq, PartialEq)] #[allow(missing_docs)] pub enum Operator<'a> { $( $op $({ $($payload)* })?, )* } } } for_each_operator!(define_operator); /// A reader for a core WebAssembly function's operators. #[derive(Clone)] pub struct OperatorsReader<'a> { reader: BinaryReader<'a>, } impl<'a> OperatorsReader<'a> { pub(crate) fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> { OperatorsReader { reader } } /// Determines if the reader is at the end of the operators. pub fn eof(&self) -> bool { self.reader.eof() } /// Gets the original position of the reader. pub fn original_position(&self) -> usize { self.reader.original_position() } /// Ensures the reader is at the end. /// /// This function returns an error if there is extra data after the operators. pub fn ensure_end(&self) -> Result<()> { if self.eof() { return Ok(()); } Err(BinaryReaderError::new( "unexpected data at the end of operators", self.reader.original_position(), )) } /// Reads an operator from the reader. pub fn read(&mut self) -> Result> { self.reader.read_operator() } /// Converts to an iterator of operators paired with offsets. pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> { OperatorsIteratorWithOffsets { reader: self, err: false, } } /// Reads an operator with its offset. pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> { let pos = self.reader.original_position(); Ok((self.read()?, pos)) } /// Visit a single operator with the specified [`VisitOperator`] instance. /// /// See [`BinaryReader::visit_operator`] for more information. pub fn visit_operator(&mut self, visitor: &mut T) -> Result<>::Output> where T: VisitOperator<'a>, { self.reader.visit_operator(visitor) } /// Gets a binary reader from this operators reader. pub fn get_binary_reader(&self) -> BinaryReader<'a> { self.reader.clone() } /// Returns whether there is an `end` opcode followed by eof remaining in /// this reader. pub fn is_end_then_eof(&self) -> bool { self.reader.is_end_then_eof() } } impl<'a> IntoIterator for OperatorsReader<'a> { type Item = Result>; type IntoIter = OperatorsIterator<'a>; /// Reads content of the code section. /// /// # Examples /// ``` /// # use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader}; /// # let data: &[u8] = &[ /// # 0x01, 0x03, 0x00, 0x01, 0x0b]; /// let reader = BinaryReader::new(data, 0); /// let code_reader = CodeSectionReader::new(reader).unwrap(); /// for body in code_reader { /// let body = body.expect("function body"); /// let mut op_reader = body.get_operators_reader().expect("op reader"); /// let ops = op_reader.into_iter().collect::>>().expect("ops"); /// assert!( /// if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false }, /// "found {:?}", /// ops /// ); /// } /// ``` fn into_iter(self) -> Self::IntoIter { OperatorsIterator { reader: self, err: false, } } } /// An iterator over a function's operators. pub struct OperatorsIterator<'a> { reader: OperatorsReader<'a>, err: bool, } impl<'a> Iterator for OperatorsIterator<'a> { type Item = Result>; fn next(&mut self) -> Option { if self.err || self.reader.eof() { return None; } let result = self.reader.read(); self.err = result.is_err(); Some(result) } } /// An iterator over a function's operators with offsets. pub struct OperatorsIteratorWithOffsets<'a> { reader: OperatorsReader<'a>, err: bool, } impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> { type Item = Result<(Operator<'a>, usize)>; /// Reads content of the code section with offsets. /// /// # Examples /// ``` /// use wasmparser::{Operator, CodeSectionReader, Result, BinaryReader}; /// # let data: &[u8] = &[ /// # 0x01, 0x03, 0x00, /* offset = 23 */ 0x01, 0x0b]; /// let reader = BinaryReader::new(data, 20); /// let code_reader = CodeSectionReader::new(reader).unwrap(); /// for body in code_reader { /// let body = body.expect("function body"); /// let mut op_reader = body.get_operators_reader().expect("op reader"); /// let ops = op_reader.into_iter_with_offsets().collect::>>().expect("ops"); /// assert!( /// if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false }, /// "found {:?}", /// ops /// ); /// } /// ``` fn next(&mut self) -> Option { if self.err || self.reader.eof() { return None; } let result = self.reader.read_with_offset(); self.err = result.is_err(); Some(result) } } macro_rules! define_visit_operator { ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output; )* } } /// Trait implemented by types that can visit all [`Operator`] variants. #[allow(missing_docs)] pub trait VisitOperator<'a> { /// The result type of the visitor. type Output: 'a; /// Visits the [`Operator`] `op` using the given `offset`. /// /// # Note /// /// This is a convenience method that is intended for non-performance /// critical use cases. For performance critical implementations users /// are recommended to directly use the respective `visit` methods or /// implement [`VisitOperator`] on their own. fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { macro_rules! visit_operator { ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { match op { $( Operator::$op $({ $($arg),* })? => self.$visit($($($arg.clone()),*)?), )* } } } for_each_operator!(visit_operator) } for_each_operator!(define_visit_operator); } macro_rules! define_visit_operator_delegate { ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { V::$visit(&mut *self, $($($arg),*)?) } )* } } impl<'a, 'b, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for &'b mut V { type Output = V::Output; fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { V::visit_operator(*self, op) } for_each_operator!(define_visit_operator_delegate); } impl<'a, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for Box { type Output = V::Output; fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { V::visit_operator(&mut *self, op) } for_each_operator!(define_visit_operator_delegate); } /// A `try_table` entries representation. #[derive(Clone, Debug, Eq, PartialEq)] pub struct TryTable { /// The block type describing the try block itself. pub ty: BlockType, /// Outer blocks which will receive exceptions. pub catches: Vec, } /// Catch clauses that can be specified in [`TryTable`]. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[allow(missing_docs)] pub enum Catch { /// Equivalent of `catch` One { tag: u32, label: u32 }, /// Equivalent of `catch_ref` OneRef { tag: u32, label: u32 }, /// Equivalent of `catch_all` All { label: u32 }, /// Equivalent of `catch_all_ref` AllRef { label: u32 }, } impl<'a> FromReader<'a> for TryTable { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let ty = reader.read_block_type()?; let catches = reader .read_iter(MAX_WASM_CATCHES, "catches")? .collect::>()?; Ok(TryTable { ty, catches }) } } impl<'a> FromReader<'a> for Catch { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(match reader.read_u8()? { 0x00 => Catch::One { tag: reader.read_var_u32()?, label: reader.read_var_u32()?, }, 0x01 => Catch::OneRef { tag: reader.read_var_u32()?, label: reader.read_var_u32()?, }, 0x02 => Catch::All { label: reader.read_var_u32()?, }, 0x03 => Catch::AllRef { label: reader.read_var_u32()?, }, x => return reader.invalid_leading_byte(x, "catch"), }) } } wasmparser-0.217.0/src/readers/core/producers.rs000064400000000000000000000060771046102023000177110ustar 00000000000000/* Copyright 2019 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, FromReader, Result, SectionLimited}; /// A reader for the producers custom section of a WebAssembly module. /// /// # Examples /// /// ``` /// # let data: &[u8] = &[0x01, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, /// # 0x02, 0x03, 0x77, 0x61, 0x74, 0x01, 0x31, 0x01, 0x43, 0x03, 0x39, 0x2e, 0x30]; /// # use wasmparser::{ProducersSectionReader, ProducersFieldValue, Result, BinaryReader}; /// let reader = BinaryReader::new(data, 0); /// let reader = ProducersSectionReader::new(reader).expect("producers reader"); /// let field = reader.into_iter().next().unwrap().expect("producers field"); /// assert!(field.name == "language"); /// let value = field.values.into_iter().collect::>>().expect("values"); /// assert!(value.len() == 2); /// assert!(value[0].name == "wat" && value[0].version == "1"); /// assert!(value[1].name == "C" && value[1].version == "9.0"); /// ``` pub type ProducersSectionReader<'a> = SectionLimited<'a, ProducersField<'a>>; /// A field from the producers custom section. #[derive(Debug, Clone)] pub struct ProducersField<'a> { /// The name of the field. pub name: &'a str, /// The values specified for this field pub values: SectionLimited<'a, ProducersFieldValue<'a>>, } impl<'a> FromReader<'a> for ProducersField<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let offset = reader.original_position(); let name = reader.read_string()?; match name { "language" | "sdk" | "processed-by" => {} _ => bail!(offset, "invalid producers field name: `{name}`"), } let values = reader.skip(|reader| { // FIXME(#188) ideally shouldn't need to skip here for _ in 0..reader.read_var_u32()? { reader.skip_string()?; reader.skip_string()?; } Ok(()) })?; Ok(ProducersField { name, values: SectionLimited::new(values)?, }) } } /// Represents a field value in the producers custom section. #[derive(Debug, Copy, Clone)] pub struct ProducersFieldValue<'a> { /// The field name. pub name: &'a str, /// The field version. pub version: &'a str, } impl<'a> FromReader<'a> for ProducersFieldValue<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let name = reader.read_string()?; let version = reader.read_string()?; Ok(ProducersFieldValue { name, version }) } } wasmparser-0.217.0/src/readers/core/reloc.rs000064400000000000000000000266671046102023000170160ustar 00000000000000use crate::{BinaryReader, FromReader, Result, SectionLimited}; use core::ops::Range; /// Reader for relocation entries within a `reloc.*` section. pub type RelocationEntryReader<'a> = SectionLimited<'a, RelocationEntry>; /// Reader for reloc.* sections as defined by /// . #[derive(Debug, Clone)] pub struct RelocSectionReader<'a> { section: u32, range: Range, entries: SectionLimited<'a, RelocationEntry>, } impl<'a> RelocSectionReader<'a> { /// Creates a new reader for a `reloc.*` section starting at /// `original_position` within the wasm file. pub fn new(mut reader: BinaryReader<'a>) -> Result { let range = reader.range().clone(); let section = reader.read_var_u32()?; Ok(Self { section, range, entries: SectionLimited::new(reader.shrink())?, }) } /// Index of section to which the relocations apply. pub fn section_index(&self) -> u32 { self.section } /// The byte range of the entire section. pub fn range(&self) -> Range { self.range.clone() } /// The relocation entries. pub fn entries(&self) -> SectionLimited<'a, RelocationEntry> { self.entries.clone() } } macro_rules! back_to_enum { ($(#[$meta:meta])+ $vis:vis enum $name:ident { $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)* }) => { $(#[$meta])* $vis enum $name { $($(#[$vmeta])* $vname $(= $val)?,)* } impl TryFrom for $name { type Error = (); fn try_from(v: u8) -> Result { match v { $(x if x == $name::$vname as u8 => Ok($name::$vname),)* _ => Err(()), } } } } } back_to_enum! { /// Relocation entry type. Each entry type corresponds to one of the /// `R_WASM_*` constants defined at /// /// and /// . #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] #[repr(u8)] pub enum RelocationType { /// A function index encoded as a 5-byte varuint32. Used for the /// immediate argument of a call instruction. (since LLVM 10.0) FunctionIndexLeb = 0, /// A function table index encoded as a 5-byte varint32. Used to refer /// to the immediate argument of a i32.const instruction, e.g. taking /// the address of a function. (since LLVM 10.0) TableIndexSleb = 1, /// A function table index encoded as a uint32, e.g. taking the address /// of a function in a static data initializer. (since LLVM 10.0) TableIndexI32 = 2, /// A linear memory index encoded as a 5-byte varuint32. Used for the /// immediate argument of a load or store instruction, e.g. directly /// loading from or storing to a C++ global. (since LLVM 10.0) MemoryAddrLeb = 3, /// A linear memory index encoded as a 5-byte varint32. Used for the /// immediate argument of a i32.const instruction, e.g. taking the /// address of a C++ global. (since LLVM 10.0) MemoryAddrSleb = 4, /// A linear memory index encoded as a uint32, e.g. taking the address /// of a C++ global in a static data initializer. (since LLVM 10.0) MemoryAddrI32 = 5, /// A type index encoded as a 5-byte varuint32, e.g. the type immediate /// in a call_indirect. (since LLVM 10.0) TypeIndexLeb = 6, /// A global index encoded as a 5-byte varuint32, e.g. the index /// immediate in a get_global. (since LLVM 10.0) GlobalIndexLeb = 7, /// A byte offset within code section for the specific function encoded /// as a uint32. The offsets start at the actual function code excluding /// its size field. (since LLVM 10.0) FunctionOffsetI32 = 8, /// A byte offset from start of the specified section encoded as a /// uint32. (since LLVM 10.0) SectionOffsetI32 = 9, /// An event index encoded as a 5-byte varuint32. Used for the immediate /// argument of a throw and if_except instruction. (since LLVM 10.0) EventIndexLeb = 10, /// A memory address relative to the __memory_base wasm global. Used in /// position independent code (-fPIC) where absolute memory addresses /// are not known at link time. MemoryAddrRelSleb = 11, /// A function address (table index) relative to the __table_base wasm /// global. Used in position indepenent code (-fPIC) where absolute /// function addresses are not known at link time. TableIndexRelSleb = 12, /// A global index encoded as uint32. (since LLVM 11.0) GlobalIndexI32 = 13, /// The 64-bit counterpart of `MemoryAddrLeb`. A 64-bit linear memory /// index encoded as a 10-byte varuint64, Used for the immediate /// argument of a load or store instruction on a 64-bit linear memory /// array. (since LLVM 11.0) MemoryAddrLeb64 = 14, /// The 64-bit counterpart of `MemoryAddrSleb`. A 64-bit linear memory /// index encoded as a 10-byte varint64. Used for the immediate argument /// of a i64.const instruction. (since LLVM 11.0) MemoryAddrSleb64 = 15, /// The 64-bit counterpart of `MemoryAddrI32`. A 64-bit linear memory /// index encoded as a uint64, e.g. taking the 64-bit address of a C++ /// global in a static data initializer. (since LLVM 11.0) MemoryAddrI64 = 16, /// The 64-bit counterpart of `MemoryAddrRelSleb`. MemoryAddrRelSleb64 = 17, /// The 64-bit counterpart of `TableIndexSleb`. A function table index /// encoded as a 10-byte varint64. Used to refer to the immediate /// argument of a i64.const instruction, e.g. taking the address of a /// function in Wasm64. (in LLVM 12.0) TableIndexSleb64 = 18, /// The 64-bit counterpart of `TableIndexI32`. A function table index /// encoded as a uint64, e.g. taking the address of a function in a /// static data initializer. (in LLVM 12.0) TableIndexI64 = 19, /// A table number encoded as a 5-byte varuint32. Used for the table /// immediate argument in the table.* instructions. (in LLVM 12.0) TableNumberLeb = 20, /// An offset from the __tls_base symbol encoded as a 5-byte varint32. /// Used for PIC case to avoid absolute relocation. (in LLVM 12.0) MemoryAddrTlsSleb = 21, /// The 64-bit counterpart of `FunctionOffsetI32`. A byte offset within /// code section for the specific function encoded as a uint64. (in LLVM /// 12.0) FunctionOffsetI64 = 22, /// A byte offset between the relocating address and a linear memory /// index encoded as a uint32. Used for pointer-relative addressing. (in /// LLVM 13.0) MemoryAddrLocrelI32 = 23, /// The 64-bit counterpart of `TableIndexRelSleb`. A function table /// index encoded as a 10-byte varint64. (in LLVM 13.0) TableIndexRelSleb64 = 24, /// The 64-bit counterpart of `MemoryAddrTlsSleb`. (in LLVM 13.0) MemoryAddrTlsSleb64 = 25, /// A function index encoded as a uint32. Used in custom sections for /// function annotations (`__attribute__((annotate()))`) (in LLVM /// 17.0) FunctionIndexI32 = 26, } } impl<'a> FromReader<'a> for RelocationType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let num = reader.read_u8()?; num.try_into().or_else(|_| { Err(BinaryReader::invalid_leading_byte_error( num, "RelocEntryType", reader.original_position() - 1, )) }) } } /// Indicates the kind of addend that applies to a relocation entry. #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum RelocAddendKind { /// Relocation entry does not include an addend. None, /// Relocation entry includes a 32-bit addend. Addend32, /// Relocation entry includes a 64-bit addend. Addend64, } /// Single relocation entry within a `reloc.*` section, as defined at /// . #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub struct RelocationEntry { /// Relocation entry type. pub ty: RelocationType, /// Offset in bytes from the start of the section indicated by /// `RelocSectionReader::section` targetted by this relocation. pub offset: u32, /// Index in the symbol table contained in the linking section that /// corresponds to the value at `offset`. pub index: u32, /// Addend to add to the address, or `0` if not applicable. The value must /// be consistent with the `self.ty.addend_kind()`. pub addend: i64, } impl RelocationEntry { /// Byte range relative to the start of the section indicated by /// `RelocSectionReader::section` targetted by this relocation. pub fn relocation_range(&self) -> Range { (self.offset as usize)..(self.offset as usize + self.ty.extent()) } } impl RelocationType { /// Indicates if this relocation type has an associated `RelocEntry::addend`. pub const fn addend_kind(self: Self) -> RelocAddendKind { use RelocationType::*; match self { MemoryAddrLeb | MemoryAddrSleb | MemoryAddrI32 | FunctionOffsetI32 | SectionOffsetI32 | MemoryAddrLocrelI32 | MemoryAddrRelSleb | MemoryAddrTlsSleb => { RelocAddendKind::Addend32 } MemoryAddrRelSleb64 | MemoryAddrTlsSleb64 | MemoryAddrLeb64 | MemoryAddrSleb64 | MemoryAddrI64 | FunctionOffsetI64 => RelocAddendKind::Addend64, _ => RelocAddendKind::None, } } /// Indicates the number of bytes that this relocation type targets. pub const fn extent(self) -> usize { use RelocationType::*; match self { FunctionIndexLeb | TableIndexSleb | MemoryAddrLeb | MemoryAddrSleb | TypeIndexLeb | GlobalIndexLeb | EventIndexLeb | MemoryAddrRelSleb | TableIndexRelSleb | TableNumberLeb | MemoryAddrTlsSleb => 5, MemoryAddrLeb64 | MemoryAddrSleb64 | TableIndexSleb64 | TableIndexRelSleb64 | MemoryAddrRelSleb64 | MemoryAddrTlsSleb64 => 10, TableIndexI32 | MemoryAddrI32 | FunctionOffsetI32 | SectionOffsetI32 | GlobalIndexI32 | MemoryAddrLocrelI32 | FunctionIndexI32 => 4, MemoryAddrI64 | TableIndexI64 | FunctionOffsetI64 => 8, } } } impl<'a> FromReader<'a> for RelocationEntry { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let ty = RelocationType::from_reader(reader)?; let offset = reader.read_var_u32()?; let index = reader.read_var_u32()?; let addend = match ty.addend_kind() { RelocAddendKind::None => 0, RelocAddendKind::Addend32 => reader.read_var_i32()? as i64, RelocAddendKind::Addend64 => reader.read_var_i64()?, }; Ok(RelocationEntry { ty, offset, index, addend, }) } } wasmparser-0.217.0/src/readers/core/tables.rs000064400000000000000000000057451046102023000171560ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, ConstExpr, FromReader, Result, SectionLimited, TableType}; /// A reader for the table section of a WebAssembly module. pub type TableSectionReader<'a> = SectionLimited<'a, Table<'a>>; /// Type information about a table defined in the table section of a WebAssembly /// module. #[derive(Clone, Debug)] pub struct Table<'a> { /// The type of this table, including its element type and its limits. pub ty: TableType, /// The initialization expression for the table. pub init: TableInit<'a>, } /// Different modes of initializing a table. #[derive(Clone, Debug)] pub enum TableInit<'a> { /// The table is initialized to all null elements. RefNull, /// Each element in the table is initialized with the specified constant /// expression. Expr(ConstExpr<'a>), } impl<'a> FromReader<'a> for Table<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let has_init_expr = if reader.peek()? == 0x40 { reader.read_u8()?; true } else { false }; if has_init_expr { if reader.read_u8()? != 0x00 { bail!(reader.original_position() - 1, "invalid table encoding"); } } let ty = reader.read::()?; let init = if has_init_expr { TableInit::Expr(reader.read()?) } else { TableInit::RefNull }; Ok(Table { ty, init }) } } impl<'a> FromReader<'a> for TableType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let element_type = reader.read()?; let pos = reader.original_position(); let flags = reader.read_u8()?; if (flags & !0b111) != 0 { bail!(pos, "invalid table resizable limits flags"); } let has_max = (flags & 0b001) != 0; let shared = (flags & 0b010) != 0; let table64 = (flags & 0b100) != 0; Ok(TableType { element_type, table64, initial: if table64 { reader.read_var_u64()? } else { reader.read_var_u32()?.into() }, maximum: if !has_max { None } else if table64 { Some(reader.read_var_u64()?) } else { Some(reader.read_var_u32()?.into()) }, shared, }) } } wasmparser-0.217.0/src/readers/core/tags.rs000064400000000000000000000022541046102023000166320ustar 00000000000000/* Copyright 2020 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, FromReader, Result, SectionLimited, TagKind, TagType}; /// A reader for the tags section of a WebAssembly module. pub type TagSectionReader<'a> = SectionLimited<'a, TagType>; impl<'a> FromReader<'a> for TagType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let attribute = reader.read_u8()?; if attribute != 0 { bail!(reader.original_position() - 1, "invalid tag attributes"); } Ok(TagType { kind: TagKind::Exception, func_type_idx: reader.read_var_u32()?, }) } } wasmparser-0.217.0/src/readers/core/types/matches.rs000064400000000000000000000226611046102023000204700ustar 00000000000000//! Implementation of matching (structural subtyping) for core Wasm types. //! //! We only ever do structural matching for one link at a time in a subtype //! chain. That is, we never recurse on new `SubType`s. This is because //! subtyping relations are required to be declared, so the earlier links in the //! chain were already checked when we processed those declarations. //! //! Note that while we don't recursively *match* on each sub- and supertype field //! when checking whether a struct type matches another struct type, we do check //! that either `field_type_a == field_type b` or that it was previously //! declared that `field_type a <: field_type b`. The latter case means that we //! previously checked that they matched when we saw the declaration, and we //! don't need to match again; we just look at the declarations from now on. use crate::{ types::{CoreTypeId, RecGroupId, TypeList}, ArrayType, CompositeInnerType, CompositeType, FieldType, FuncType, RefType, StorageType, StructType, SubType, ValType, }; /// Wasm type matching. pub trait Matches { /// Does `a` structurally match `b`? /// /// Both `a` and `b` must be canonicalized already. /// /// This is expected to recursively break down and inspect the *parts* of /// `Self` but should always bottom out in subtype checks, rather than /// looping back to new match calls on a *whole* new `Self`. This is what /// maintains the "single-link-in-the-chain" property mentioned in the /// module comment above. fn matches(types: &TypeList, a: Self, b: Self) -> bool; } /// A `T` with its containing `RecGroupId`. /// /// The `RecGroupId` can be used to resolve canonicalized type references that /// are indices into the local rec group. #[derive(Debug, Copy, Clone)] pub(crate) struct WithRecGroup { inner: T, rec_group_id: RecGroupId, } impl WithRecGroup { #[inline] fn rec_group(x: Self) -> RecGroupId { x.rec_group_id } } impl core::ops::Deref for WithRecGroup { type Target = T; #[inline] fn deref(&self) -> &T { &self.inner } } impl core::ops::DerefMut for WithRecGroup { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl WithRecGroup { /// Construct a new `WithRecGroup` by looking up the /// `CoreTypeId`'s rec group id in the `TypeList`. pub(crate) fn new(types: &TypeList, id: CoreTypeId) -> Self { let rec_group_id = types.rec_group_id_of(id); WithRecGroup { inner: id, rec_group_id, } } } impl WithRecGroup { /// Project into a field of the inner value, while maintaining the /// `RecGroupId` context. pub(crate) fn map(x: Self, f: impl FnOnce(T) -> U) -> WithRecGroup { WithRecGroup { inner: f(x.inner), rec_group_id: x.rec_group_id, } } } impl<'a> Matches for WithRecGroup<&'a SubType> { fn matches(types: &TypeList, a: Self, b: Self) -> bool { // NB: matching does not check finality and supertypes. That is checked // once when we define types, not repeatedly every time we check // matches. Matches::matches( types, WithRecGroup::map(a, |a| &a.composite_type), WithRecGroup::map(b, |b| &b.composite_type), ) } } impl<'a> Matches for WithRecGroup<&'a CompositeType> { fn matches(types: &TypeList, a: Self, b: Self) -> bool { use CompositeInnerType::*; if (*a).shared != (*b).shared { return false; } match (&(*a).inner, &(*b).inner) { (Func(fa), Func(fb)) => Matches::matches( types, WithRecGroup::map(a, |_| fa), WithRecGroup::map(b, |_| fb), ), (Func(_), _) => false, (Array(aa), Array(ab)) => Matches::matches( types, WithRecGroup::map(a, |_| *aa), WithRecGroup::map(b, |_| *ab), ), (Array(_), _) => false, (Struct(sa), Struct(sb)) => Matches::matches( types, WithRecGroup::map(a, |_| sa), WithRecGroup::map(b, |_| sb), ), (Struct(_), _) => false, } } } impl<'a> Matches for WithRecGroup<&'a FuncType> { fn matches(types: &TypeList, a: Self, b: Self) -> bool { if a.params().len() != b.params().len() || a.results().len() != b.results().len() { return false; } // A quick recap of covariance, contravariance, and how it applies to // subtyping function types, because I (and almost everyone else, it // seems) always need a reminder: // // *Covariance* is when `a <: b` and `a' <: b'`. That is, the subtyping // checks on the nested things (`a'` and `b'`) goes the same way as the // outer things (`a` and `b`). // // *Contravariance* is when `a <: b` and `b' <: a'`. That is, the // subtyping on the nested things is reversed compared to the outer // things. // // Now, when we are checking subtyping for function types, we have the // following variance: // // * Parameters are contravariant: `(a -> c) <: (b -> c)` when `b <: a`. // // For example, we can substitute a `Cat -> Cat` function with a // `Animal -> Cat` function because `Cat <: Animal` and so all // arguments that could be given to the function are still valid. // // We can't do the opposite and replace an `Animal -> Cat` function // with a `Cat -> Cat` function. What would the `Cat -> Cat` function // do when given a `Dog`? It is unsound. // // * Results are covariant: `(a -> b) <: (a -> c)` when `b <: c`. // // For example, we can substitute a `Cat -> Animal` function with a // `Cat -> Cat` function because callers expect to be returned an // `Animal` and all `Cat`s are `Animal`s. (Also: all `Cat`s are // `Beautiful`!) // // We cannot do the opposite and substitute a `Cat -> Cat` function // with a `Cat -> Animal` function, since callers expect a `Cat` but // the new function could return a `Pig`. // // As always, Wikipedia is also helpful: // https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) let params_match = a.params().iter().zip(b.params()).all(|(pa, pb)| { // Parameters are contravariant. Matches::matches( types, WithRecGroup::map(b, |_| *pb), WithRecGroup::map(a, |_| *pa), ) }); if !params_match { return false; } a.results().iter().zip(b.results()).all(|(ra, rb)| { // Results are covariant. Matches::matches( types, WithRecGroup::map(a, |_| *ra), WithRecGroup::map(b, |_| *rb), ) }) } } impl Matches for WithRecGroup { fn matches(types: &TypeList, a: Self, b: Self) -> bool { Matches::matches( types, WithRecGroup::map(a, |a| a.0), WithRecGroup::map(b, |b| b.0), ) } } impl<'a> Matches for WithRecGroup<&'a StructType> { fn matches(types: &TypeList, a: Self, b: Self) -> bool { // Note: Struct types support width and depth subtyping. a.fields.len() >= b.fields.len() && a.fields.iter().zip(b.fields.iter()).all(|(fa, fb)| { Matches::matches( types, WithRecGroup::map(a, |_| *fa), WithRecGroup::map(b, |_| *fb), ) }) } } impl Matches for WithRecGroup { fn matches(types: &TypeList, a: Self, b: Self) -> bool { (b.mutable || !a.mutable) && Matches::matches( types, WithRecGroup::map(a, |a| a.element_type), WithRecGroup::map(b, |b| b.element_type), ) } } impl Matches for WithRecGroup { fn matches(types: &TypeList, a: Self, b: Self) -> bool { use StorageType as ST; match (*a, *b) { (ST::I8, ST::I8) | (ST::I16, ST::I16) => true, (ST::I8 | ST::I16, _) => false, (ST::Val(va), ST::Val(vb)) => Matches::matches( types, WithRecGroup::map(a, |_| va), WithRecGroup::map(b, |_| vb), ), (ST::Val(_), _) => false, } } } impl Matches for WithRecGroup { fn matches(types: &TypeList, a: Self, b: Self) -> bool { match (*a, *b) { (ValType::Ref(ra), ValType::Ref(rb)) => Matches::matches( types, WithRecGroup::map(a, |_| ra), WithRecGroup::map(b, |_| rb), ), (ValType::Ref(_), _) => false, (ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128, _) => { *a == *b } } } } impl Matches for WithRecGroup { fn matches(types: &TypeList, a: Self, b: Self) -> bool { types.reftype_is_subtype_impl( *a, Some(WithRecGroup::rec_group(a)), *b, Some(WithRecGroup::rec_group(b)), ) } } wasmparser-0.217.0/src/readers/core/types.rs000064400000000000000000002051301046102023000170360ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::limits::{ MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS, MAX_WASM_STRUCT_FIELDS, MAX_WASM_SUPERTYPES, MAX_WASM_TYPES, }; use crate::prelude::*; #[cfg(feature = "validate")] use crate::types::CoreTypeId; use crate::{BinaryReader, BinaryReaderError, FromReader, Result, SectionLimited}; use core::cmp::Ordering; use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; #[cfg(feature = "validate")] mod matches; #[cfg(feature = "validate")] pub(crate) use self::matches::{Matches, WithRecGroup}; /// A packed representation of a type index. /// /// This type is morally an `enum` of either: /// /// 1. An index into a Wasm module's type space. /// /// 2. A `CoreTypeId` identifier. /// /// 3. An index into a recursion group's elements. /// /// The latter two variants are *canonical* while the first is not. Reading raw /// types will produce (1), while working with types after validation will /// produce (2) and (3). // // This is a bit-packed `u32` with the following layout: // // [ unused:u10 kind:u2 index:u20 ] // // It must fit in 22 bits to keep `RefType` in 24 bits and `ValType` in 32 bits, // so the top ten bits are unused. // // The `index` field's interpretation depends on the `kind` field, which may be // one of the following: // // * `00`: The `index` is an index into the module's type space. // // * `01`: The `index` is an index into the containing type's recursion group. // // * `10`: The `index` is a `CoreTypeId`. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PackedIndex(u32); // Assert that we can fit indices up to `MAX_WASM_TYPES` inside `RefType`. #[test] fn can_fit_max_wasm_types_in_packed_index() { assert!(PackedIndex::can_represent_index( crate::limits::MAX_WASM_TYPES as u32 )); assert!(PackedIndex::can_represent_index( 0b00000000_00001111_00000000_00000000 )); assert!(PackedIndex::can_represent_index( 0b00000000_00000000_11111111_00000000 )); assert!(PackedIndex::can_represent_index( 0b00000000_00000000_00000000_11111111 )); assert!(PackedIndex::can_represent_index(0)); } impl PackedIndex { const UNUSED_MASK: u32 = u32::MAX & !(Self::KIND_MASK | Self::INDEX_MASK); const KIND_MASK: u32 = 0b11 << 20; const INDEX_MASK: u32 = (1 << 20) - 1; const MODULE_KIND: u32 = 0b00 << 20; const REC_GROUP_KIND: u32 = 0b01 << 20; #[cfg(feature = "validate")] const ID_KIND: u32 = 0b10 << 20; #[inline] pub(crate) fn unchecked_from_u32(x: u32) -> Self { debug_assert_eq!(Self::UNUSED_MASK & x, 0); Self(x) } #[inline] pub(crate) fn to_u32(id: Self) -> u32 { let x = id.0; debug_assert_eq!(Self::UNUSED_MASK & x, 0); x } #[inline] fn can_represent_index(index: u32) -> bool { index & Self::INDEX_MASK == index } #[inline] fn kind(&self) -> u32 { self.0 & Self::KIND_MASK } #[inline] fn index(&self) -> u32 { self.0 & Self::INDEX_MASK } /// Construct a `PackedIndex` from an index into a module's types space. #[inline] pub fn from_module_index(index: u32) -> Option { if PackedIndex::can_represent_index(index) { Some(PackedIndex(PackedIndex::MODULE_KIND | index)) } else { None } } /// Construct a `PackedIndex` from an index into the index's containing /// recursion group. #[inline] pub fn from_rec_group_index(index: u32) -> Option { if PackedIndex::can_represent_index(index) { Some(PackedIndex(PackedIndex::REC_GROUP_KIND | index)) } else { None } } /// Construct a `PackedIndex` from the given `CoreTypeId`. #[inline] #[cfg(feature = "validate")] pub fn from_id(id: CoreTypeId) -> Option { let index = u32::try_from(crate::types::TypeIdentifier::index(&id)).unwrap(); if PackedIndex::can_represent_index(index) { Some(PackedIndex(PackedIndex::ID_KIND | index)) } else { None } } /// Is this index in canonical form? #[inline] #[cfg(feature = "validate")] pub fn is_canonical(&self) -> bool { match self.kind() { Self::REC_GROUP_KIND | Self::ID_KIND => true, Self::MODULE_KIND => false, _ => unreachable!(), } } /// Uncompress this packed index into an actual `enum` that can be matched /// on. #[inline] pub fn unpack(&self) -> UnpackedIndex { match self.kind() { Self::MODULE_KIND => UnpackedIndex::Module(self.index()), Self::REC_GROUP_KIND => UnpackedIndex::RecGroup(self.index()), #[cfg(feature = "validate")] Self::ID_KIND => UnpackedIndex::Id( ::from_index(self.index()), ), _ => unreachable!(), } } /// Get the underlying index into a module's types space, if any. #[inline] pub fn as_module_index(&self) -> Option { if self.kind() == Self::MODULE_KIND { Some(self.index()) } else { None } } /// Get the underlying index into the containing recursion group, if any. #[inline] pub fn as_rec_group_index(&self) -> Option { if self.kind() == Self::REC_GROUP_KIND { Some(self.index()) } else { None } } /// Get the underlying `CoreTypeId`, if any. #[inline] #[cfg(feature = "validate")] pub fn as_core_type_id(&self) -> Option { if self.kind() == Self::ID_KIND { Some(::from_index( self.index(), )) } else { None } } } impl fmt::Debug for PackedIndex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("CoreTypeIndex") .field( "kind", match self.kind() { Self::MODULE_KIND => &"module", Self::REC_GROUP_KIND => &"recgroup", #[cfg(feature = "validate")] Self::ID_KIND => &"id", _ => unreachable!(), }, ) .field("index", &self.index()) .finish() } } impl fmt::Display for PackedIndex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result { fmt::Display::fmt(&self.unpack(), f) } } /// The uncompressed form of a `PackedIndex`. /// /// Can be used for `match` statements. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum UnpackedIndex { /// An index into a Wasm module's types space. Module(u32), /// An index into the containing recursion group's elements. RecGroup(u32), /// A type identifier. #[cfg(feature = "validate")] Id(CoreTypeId), } impl UnpackedIndex { /// Compress this index into its packed form. /// /// Returns `None` if an index is beyond implementation limits. pub fn pack(&self) -> Option { match self { UnpackedIndex::Module(i) => PackedIndex::from_module_index(*i), UnpackedIndex::RecGroup(i) => PackedIndex::from_rec_group_index(*i), #[cfg(feature = "validate")] UnpackedIndex::Id(id) => PackedIndex::from_id(*id), } } /// Is this index in canonical form? #[inline] #[cfg(feature = "validate")] pub fn is_canonical(&self) -> bool { matches!(self, UnpackedIndex::RecGroup(_) | UnpackedIndex::Id(_)) } /// Get the underlying index into a module's types space, if any. #[inline] pub fn as_module_index(&self) -> Option { if let Self::Module(i) = *self { Some(i) } else { None } } /// Get the underlying index into the containing recursion group, if any. #[inline] pub fn as_rec_group_index(&self) -> Option { if let Self::RecGroup(i) = *self { Some(i) } else { None } } /// Get the underlying `CoreTypeId`, if any. #[inline] #[cfg(feature = "validate")] pub fn as_core_type_id(&self) -> Option { if let Self::Id(id) = *self { Some(id) } else { None } } } impl fmt::Display for UnpackedIndex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result { match self { UnpackedIndex::Module(i) => write!(f, "(module {i})"), UnpackedIndex::RecGroup(i) => write!(f, "(recgroup {i})"), #[cfg(feature = "validate")] UnpackedIndex::Id(id) => write!(f, "(id {})", crate::types::TypeIdentifier::index(id)), } } } /// Represents a recursive type group in a WebAssembly module. #[derive(Debug, Clone)] pub struct RecGroup { inner: RecGroupInner, } #[derive(Debug, Clone)] enum RecGroupInner { Implicit((usize, SubType)), Explicit(Vec<(usize, SubType)>), } impl RecGroup { /// Create an explicit `RecGroup` for the given types. pub(crate) fn explicit(types: Vec<(usize, SubType)>) -> Self { RecGroup { inner: RecGroupInner::Explicit(types), } } /// Create an implicit `RecGroup` for a type that was not contained /// in a `(rec ...)`. pub(crate) fn implicit(offset: usize, ty: SubType) -> Self { RecGroup { inner: RecGroupInner::Implicit((offset, ty)), } } /// Is this an explicit recursion group? pub fn is_explicit_rec_group(&self) -> bool { matches!(self.inner, RecGroupInner::Explicit(..)) } /// Returns the list of subtypes in the recursive type group. pub fn types(&self) -> impl ExactSizeIterator + '_ { let types = match &self.inner { RecGroupInner::Implicit(ty) => core::slice::from_ref(ty), RecGroupInner::Explicit(types) => types, }; types.iter().map(|(_, ty)| ty) } /// Return a mutable borrow of the list of subtypes in this /// recursive type group. #[cfg(feature = "validate")] pub(crate) fn types_mut(&mut self) -> impl ExactSizeIterator + '_ { let types = match &mut self.inner { RecGroupInner::Implicit(ty) => core::slice::from_mut(ty), RecGroupInner::Explicit(types) => types, }; types.iter_mut().map(|(_, ty)| ty) } /// Returns an owning iterator of all subtypes in this recursion /// group. pub fn into_types(self) -> impl ExactSizeIterator { self.into_types_and_offsets().map(|(_, ty)| ty) } /// Returns an owning iterator of all subtypes in this recursion /// group, along with their offset. pub fn into_types_and_offsets(self) -> impl ExactSizeIterator { return match self.inner { RecGroupInner::Implicit(tup) => Iter::Implicit(Some(tup)), RecGroupInner::Explicit(types) => Iter::Explicit(types.into_iter()), }; enum Iter { Implicit(Option<(usize, SubType)>), Explicit(alloc::vec::IntoIter<(usize, SubType)>), } impl Iterator for Iter { type Item = (usize, SubType); fn next(&mut self) -> Option<(usize, SubType)> { match self { Self::Implicit(ty) => ty.take(), Self::Explicit(types) => types.next(), } } fn size_hint(&self) -> (usize, Option) { match self { Self::Implicit(None) => (0, Some(0)), Self::Implicit(Some(_)) => (1, Some(1)), Self::Explicit(types) => types.size_hint(), } } } impl ExactSizeIterator for Iter {} } } impl Hash for RecGroup { fn hash(&self, hasher: &mut H) { let types = self.types(); types.len().hash(hasher); for ty in types { ty.hash(hasher); } } } impl PartialEq for RecGroup { fn eq(&self, other: &RecGroup) -> bool { let self_tys = self.types(); let other_tys = other.types(); self_tys.len() == other_tys.len() && self_tys.zip(other_tys).all(|(a, b)| a == b) } } impl Eq for RecGroup {} impl Ord for RecGroup { fn cmp(&self, other: &Self) -> Ordering { let self_tys = self.types(); let other_tys = other.types(); self_tys.cmp(other_tys) } } impl PartialOrd for RecGroup { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } /// Represents a subtype of possible other types in a WebAssembly module. #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct SubType { /// Is the subtype final. pub is_final: bool, /// The list of supertype indexes. As of GC MVP, there can be at most one supertype. pub supertype_idx: Option, /// The composite type of the subtype. pub composite_type: CompositeType, } impl fmt::Display for SubType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_final && self.supertype_idx.is_none() { fmt::Display::fmt(&self.composite_type, f) } else { write!(f, "(sub ")?; if self.is_final { write!(f, "final ")?; } if let Some(idx) = self.supertype_idx { write!(f, "{idx} ")?; } fmt::Display::fmt(&self.composite_type, f)?; write!(f, ")") } } } impl SubType { /// Unwrap an `ArrayType` or panic. /// /// Does not check finality or whether there is a supertype. pub fn unwrap_array(&self) -> &ArrayType { self.composite_type.unwrap_array() } /// Unwrap an `FuncType` or panic. /// /// Does not check finality or whether there is a supertype. pub fn unwrap_func(&self) -> &FuncType { self.composite_type.unwrap_func() } /// Unwrap an `StructType` or panic. /// /// Does not check finality or whether there is a supertype. pub fn unwrap_struct(&self) -> &StructType { self.composite_type.unwrap_struct() } /// Maps any `UnpackedIndex` via the specified closure. #[cfg(feature = "validate")] pub(crate) fn remap_indices( &mut self, f: &mut dyn FnMut(&mut PackedIndex) -> Result<()>, ) -> Result<()> { if let Some(idx) = &mut self.supertype_idx { f(idx)?; } match &mut self.composite_type.inner { CompositeInnerType::Func(ty) => { for ty in ty.params_mut() { ty.remap_indices(f)?; } for ty in ty.results_mut() { ty.remap_indices(f)?; } } CompositeInnerType::Array(ty) => { ty.0.remap_indices(f)?; } CompositeInnerType::Struct(ty) => { for field in ty.fields.iter_mut() { field.remap_indices(f)?; } } } Ok(()) } } /// Represents a composite type in a WebAssembly module. #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct CompositeType { /// The type defined inside the composite type. pub inner: CompositeInnerType, /// Is the composite type shared? This is part of the /// shared-everything-threads proposal. pub shared: bool, } /// A [`CompositeType`] can contain one of these types. #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum CompositeInnerType { /// The type is for a function. Func(FuncType), /// The type is for an array. Array(ArrayType), /// The type is for a struct. Struct(StructType), } impl fmt::Display for CompositeType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use CompositeInnerType::*; if self.shared { write!(f, "(shared ")?; } match self.inner { Array(_) => write!(f, "(array ...)"), Func(_) => write!(f, "(func ...)"), Struct(_) => write!(f, "(struct ...)"), }?; if self.shared { write!(f, ")")?; } Ok(()) } } impl CompositeType { /// Unwrap a `FuncType` or panic. pub fn unwrap_func(&self) -> &FuncType { match &self.inner { CompositeInnerType::Func(f) => f, _ => panic!("not a func"), } } /// Unwrap a `ArrayType` or panic. pub fn unwrap_array(&self) -> &ArrayType { match &self.inner { CompositeInnerType::Array(a) => a, _ => panic!("not a array"), } } /// Unwrap a `StructType` or panic. pub fn unwrap_struct(&self) -> &StructType { match &self.inner { CompositeInnerType::Struct(s) => s, _ => panic!("not a struct"), } } } /// Represents a type of a function in a WebAssembly module. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct FuncType { /// The combined parameters and result types. params_results: Box<[ValType]>, /// The number of parameter types. len_params: usize, } impl fmt::Debug for FuncType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FuncType") .field("params", &self.params()) .field("results", &self.results()) .finish() } } impl FuncType { /// Creates a new [`FuncType`] from the given `params` and `results`. pub fn new(params: P, results: R) -> Self where P: IntoIterator, R: IntoIterator, { let mut buffer = params.into_iter().collect::>(); let len_params = buffer.len(); buffer.extend(results); Self { params_results: buffer.into(), len_params, } } /// Creates a new [`FuncType`] fom its raw parts. /// /// # Panics /// /// If `len_params` is greater than the length of `params_results` combined. pub(crate) fn from_raw_parts(params_results: Box<[ValType]>, len_params: usize) -> Self { assert!(len_params <= params_results.len()); Self { params_results, len_params, } } /// Returns a shared slice to the parameter types of the [`FuncType`]. #[inline] pub fn params(&self) -> &[ValType] { &self.params_results[..self.len_params] } /// Returns an exclusive slice to the parameter types of the /// [`FuncType`]. #[inline] #[cfg(feature = "validate")] pub(crate) fn params_mut(&mut self) -> &mut [ValType] { &mut self.params_results[..self.len_params] } /// Returns a shared slice to the result types of the [`FuncType`]. #[inline] pub fn results(&self) -> &[ValType] { &self.params_results[self.len_params..] } /// Returns an exclusive slice to the result types of the /// [`FuncType`]. #[inline] #[cfg(feature = "validate")] pub(crate) fn results_mut(&mut self) -> &mut [ValType] { &mut self.params_results[self.len_params..] } #[cfg(feature = "validate")] pub(crate) fn desc(&self) -> String { use core::fmt::Write; let mut s = String::new(); s.push_str("["); for (i, param) in self.params().iter().enumerate() { if i > 0 { s.push_str(" "); } write!(s, "{param}").unwrap(); } s.push_str("] -> ["); for (i, result) in self.results().iter().enumerate() { if i > 0 { s.push_str(" "); } write!(s, "{result}").unwrap(); } s.push_str("]"); s } } /// Represents a type of an array in a WebAssembly module. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct ArrayType(pub FieldType); /// Represents a field type of an array or a struct. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct FieldType { /// Array element type. pub element_type: StorageType, /// Are elements mutable. pub mutable: bool, } impl FieldType { /// Maps any `UnpackedIndex` via the specified closure. #[cfg(feature = "validate")] pub(crate) fn remap_indices( &mut self, f: &mut dyn FnMut(&mut PackedIndex) -> Result<()>, ) -> Result<()> { match &mut self.element_type { StorageType::I8 | StorageType::I16 => Ok(()), StorageType::Val(ty) => ty.remap_indices(f), } } } /// Represents storage types introduced in the GC spec for array and struct fields. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum StorageType { /// The storage type is i8. I8, /// The storage type is i16. I16, /// The storage type is a value type. Val(ValType), } impl fmt::Display for StorageType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::I8 => write!(f, "i8"), Self::I16 => write!(f, "i16"), Self::Val(v) => fmt::Display::fmt(v, f), } } } impl StorageType { /// Is this a packed storage type, i.e. one that must be sign- or /// zero-extended when converted to a `ValType`? pub fn is_packed(&self) -> bool { match self { Self::I8 | Self::I16 => true, Self::Val(_) => false, } } /// Unpack this storage type into the valtype that it is represented as on /// the operand stack. pub fn unpack(&self) -> ValType { match *self { Self::Val(ty) => ty, Self::I8 | Self::I16 => ValType::I32, } } } /// Represents a type of a struct in a WebAssembly module. #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct StructType { /// Struct fields. pub fields: Box<[FieldType]>, } /// Represents the types of values in a WebAssembly module. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ValType { /// The value type is i32. I32, /// The value type is i64. I64, /// The value type is f32. F32, /// The value type is f64. F64, /// The value type is v128. V128, /// The value type is a reference. Ref(RefType), } impl From for ValType { #[inline] fn from(ty: RefType) -> ValType { ValType::Ref(ty) } } impl fmt::Display for ValType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ValType::I32 => f.write_str("i32"), ValType::I64 => f.write_str("i64"), ValType::F32 => f.write_str("f32"), ValType::F64 => f.write_str("f64"), ValType::V128 => f.write_str("v128"), ValType::Ref(r) => fmt::Display::fmt(r, f), } } } impl ValType { /// Alias for the wasm `funcref` type. pub const FUNCREF: ValType = ValType::Ref(RefType::FUNCREF); /// Alias for the wasm `externref` type. pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF); /// Alias for the wasm `exnref` type. pub const EXNREF: ValType = ValType::Ref(RefType::EXNREF); /// Returns whether this value type is a "reference type". /// /// Only reference types are allowed in tables, for example, and with some /// instructions. Current reference types include `funcref` and `externref`. pub fn is_reference_type(&self) -> bool { matches!(self, ValType::Ref(_)) } /// Get the underlying reference type, if any. pub fn as_reference_type(&self) -> Option { match *self { ValType::Ref(r) => Some(r), ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => None, } } /// Whether the type is defaultable, i.e. it is not a non-nullable reference /// type. pub fn is_defaultable(&self) -> bool { match *self { Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128 => true, Self::Ref(rt) => rt.is_nullable(), } } /// Maps any `UnpackedIndex` via the specified closure. #[cfg(feature = "validate")] pub(crate) fn remap_indices( &mut self, map: &mut dyn FnMut(&mut PackedIndex) -> Result<()>, ) -> Result<()> { match self { ValType::Ref(r) => { if let Some(mut idx) = r.type_index() { map(&mut idx)?; *r = RefType::concrete(r.is_nullable(), idx); } } ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} } Ok(()) } } /// A reference type. /// /// The reference types proposal first introduced `externref` and /// `funcref`. /// /// The function references proposal introduced typed function /// references. /// /// The GC proposal introduces heap types: any, eq, i31, struct, array, /// nofunc, noextern, none. // // RefType is a bit-packed enum that fits in a `u24` aka `[u8; 3]`. Note that // its content is opaque (and subject to change), but its API is stable. // // It has the following internal structure: // // ``` // [nullable:u1 concrete==1:u1 index:u22] // [nullable:u1 concrete==0:u1 shared:u1 abstype:u4 (unused):u17] // ``` // // Where // // - `nullable` determines nullability of the ref, // // - `concrete` determines if the ref is of a dynamically defined type with an // index (encoded in a following bit-packing section) or of a known fixed // type, // // - `index` is the type index, // // - `shared` determines if the ref is shared, but only if it is not concrete in // which case we would need to examine the type at the concrete index, // // - `abstype` is an enumeration of abstract types: // // ``` // 1111 = any // // 1101 = eq // 1000 = i31 // 1001 = struct // 1100 = array // // 0101 = func // 0100 = nofunc // // 0011 = extern // 0010 = noextern // // 0001 = exn // // 0000 = none // ``` #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RefType([u8; 3]); impl fmt::Debug for RefType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let heap_type = self.heap_type(); match heap_type { // All abstract types follow the same general pattern. HeapType::Abstract { shared, ty } => { let nullable = self.is_nullable(); let name = ty.as_str(nullable); match (nullable, shared) { // Print the shortened form if nullable; i.e., `*ref`. (true, true) => write!(f, "(shared {}ref)", name), (true, false) => write!(f, "{}ref", name), // Print the long form otherwise; i.e., `(ref *)`. (false, true) => write!(f, "(ref (shared {}))", name), (false, false) => write!(f, "(ref {})", name), } } // Handle concrete types separately; they always use the long form // and don't show `shared`-ness. HeapType::Concrete(index) => { if self.is_nullable() { write!(f, "(ref null {})", index) } else { write!(f, "(ref {})", index) } } } } } impl fmt::Display for RefType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self, f) } } // Assert that we can fit indices up to `MAX_WASM_TYPES` inside `RefType`. #[test] fn can_fit_max_wasm_types_in_ref_type() { fn can_roundtrip_index(index: u32) -> bool { assert!(RefType::can_represent_type_index(index)); let rt = RefType::concrete(true, PackedIndex::from_module_index(index).unwrap()); assert!(rt.is_nullable()); let actual_index = match rt.type_index() { Some(i) => i, None => panic!(), }; actual_index.as_module_index() == Some(index) } assert!(can_roundtrip_index(crate::limits::MAX_WASM_TYPES as u32)); assert!(can_roundtrip_index(0b00000000_00001111_00000000_00000000)); assert!(can_roundtrip_index(0b00000000_00000000_11111111_00000000)); assert!(can_roundtrip_index(0b00000000_00000000_00000000_11111111)); assert!(can_roundtrip_index(0)); } impl RefType { // These bits are valid for all `RefType`s. const NULLABLE_BIT: u32 = 1 << 23; const CONCRETE_BIT: u32 = 1 << 22; // The `abstype` field is valid only when `concrete == 0`. const SHARED_BIT: u32 = 1 << 21; const ABSTYPE_MASK: u32 = 0b1111 << 17; const ANY_ABSTYPE: u32 = 0b1111 << 17; const EQ_ABSTYPE: u32 = 0b1101 << 17; const I31_ABSTYPE: u32 = 0b1000 << 17; const STRUCT_ABSTYPE: u32 = 0b1001 << 17; const ARRAY_ABSTYPE: u32 = 0b1100 << 17; const FUNC_ABSTYPE: u32 = 0b0101 << 17; const NOFUNC_ABSTYPE: u32 = 0b0100 << 17; const EXTERN_ABSTYPE: u32 = 0b0011 << 17; const NOEXTERN_ABSTYPE: u32 = 0b0010 << 17; const EXN_ABSTYPE: u32 = 0b0001 << 17; const NOEXN_ABSTYPE: u32 = 0b1110 << 17; const NONE_ABSTYPE: u32 = 0b0000 << 17; // The `index` is valid only when `concrete == 1`. const INDEX_MASK: u32 = (1 << 22) - 1; /// A nullable untyped function reference aka `(ref null func)` aka /// `funcref` aka `anyfunc`. pub const FUNCREF: Self = RefType::FUNC.nullable(); /// A nullable reference to an extern object aka `(ref null extern)` aka /// `externref`. pub const EXTERNREF: Self = RefType::EXTERN.nullable(); /// A nullable reference to any object aka `(ref null any)` aka `anyref`. pub const ANYREF: Self = RefType::ANY.nullable(); /// A nullable reference to no object aka `(ref null none)` aka `nullref`. pub const NULLREF: Self = RefType::NONE.nullable(); /// A nullable reference to a noextern object aka `(ref null noextern)` aka /// `nullexternref`. pub const NULLEXTERNREF: Self = RefType::NOEXTERN.nullable(); /// A nullable reference to a nofunc object aka `(ref null nofunc)` aka /// `nullfuncref`. pub const NULLFUNCREF: Self = RefType::NOFUNC.nullable(); /// A nullable reference to an eq object aka `(ref null eq)` aka `eqref`. pub const EQREF: Self = RefType::EQ.nullable(); /// A nullable reference to a struct aka `(ref null struct)` aka /// `structref`. pub const STRUCTREF: Self = RefType::STRUCT.nullable(); /// A nullable reference to an array aka `(ref null array)` aka `arrayref`. pub const ARRAYREF: Self = RefType::ARRAY.nullable(); /// A nullable reference to an i31 object aka `(ref null i31)` aka `i31ref`. pub const I31REF: Self = RefType::I31.nullable(); /// A nullable reference to an exception object aka `(ref null exn)` aka /// `exnref`. pub const EXNREF: Self = RefType::EXN.nullable(); /// A nullable reference to a noexn object aka `(ref null noexn)` aka /// `nullexnref`. pub const NULLEXNREF: Self = RefType::NOEXN.nullable(); /// A non-nullable untyped function reference aka `(ref func)`. pub const FUNC: Self = RefType::from_u32(Self::FUNC_ABSTYPE); /// A non-nullable reference to an extern object aka `(ref extern)`. pub const EXTERN: Self = RefType::from_u32(Self::EXTERN_ABSTYPE); /// A non-nullable reference to any object aka `(ref any)`. pub const ANY: Self = RefType::from_u32(Self::ANY_ABSTYPE); /// A non-nullable reference to no object aka `(ref none)`. pub const NONE: Self = RefType::from_u32(Self::NONE_ABSTYPE); /// A non-nullable reference to a noextern object aka `(ref noextern)`. pub const NOEXTERN: Self = RefType::from_u32(Self::NOEXTERN_ABSTYPE); /// A non-nullable reference to a nofunc object aka `(ref nofunc)`. pub const NOFUNC: Self = RefType::from_u32(Self::NOFUNC_ABSTYPE); /// A non-nullable reference to an eq object aka `(ref eq)`. pub const EQ: Self = RefType::from_u32(Self::EQ_ABSTYPE); /// A non-nullable reference to a struct aka `(ref struct)`. pub const STRUCT: Self = RefType::from_u32(Self::STRUCT_ABSTYPE); /// A non-nullable reference to an array aka `(ref array)`. pub const ARRAY: Self = RefType::from_u32(Self::ARRAY_ABSTYPE); /// A non-nullable reference to an i31 object aka `(ref i31)`. pub const I31: Self = RefType::from_u32(Self::I31_ABSTYPE); /// A non-nullable reference to an exn object aka `(ref exn)`. pub const EXN: Self = RefType::from_u32(Self::EXN_ABSTYPE); /// A non-nullable reference to a noexn object aka `(ref noexn)`. pub const NOEXN: Self = RefType::from_u32(Self::NOEXN_ABSTYPE); const fn can_represent_type_index(index: u32) -> bool { index & Self::INDEX_MASK == index } const fn u24_to_u32(bytes: [u8; 3]) -> u32 { let expanded_bytes = [bytes[0], bytes[1], bytes[2], 0]; u32::from_le_bytes(expanded_bytes) } const fn u32_to_u24(x: u32) -> [u8; 3] { let bytes = x.to_le_bytes(); debug_assert!(bytes[3] == 0); [bytes[0], bytes[1], bytes[2]] } #[inline] const fn as_u32(&self) -> u32 { Self::u24_to_u32(self.0) } #[inline] const fn from_u32(x: u32) -> Self { debug_assert!(x & (0b11111111 << 24) == 0); // Either concrete or it must be a known abstract type. debug_assert!( x & Self::CONCRETE_BIT != 0 || matches!( x & Self::ABSTYPE_MASK, Self::ANY_ABSTYPE | Self::EQ_ABSTYPE | Self::I31_ABSTYPE | Self::STRUCT_ABSTYPE | Self::ARRAY_ABSTYPE | Self::FUNC_ABSTYPE | Self::NOFUNC_ABSTYPE | Self::EXTERN_ABSTYPE | Self::NOEXTERN_ABSTYPE | Self::NONE_ABSTYPE | Self::EXN_ABSTYPE | Self::NOEXN_ABSTYPE ) ); RefType(Self::u32_to_u24(x)) } /// Create a reference to a concrete Wasm-defined type at the given /// index. /// /// Returns `None` when the type index is beyond this crate's /// implementation limits and therefore is not representable. pub fn concrete(nullable: bool, index: PackedIndex) -> Self { let index: u32 = PackedIndex::to_u32(index); debug_assert!(Self::can_represent_type_index(index)); let nullable32 = Self::NULLABLE_BIT * nullable as u32; RefType::from_u32(nullable32 | Self::CONCRETE_BIT | index) } /// Create a new `RefType`. /// /// Returns `None` when the heap type's type index (if any) is beyond this /// crate's implementation limits and therefore is not representable. pub fn new(nullable: bool, heap_type: HeapType) -> Option { let base32 = Self::NULLABLE_BIT * (nullable as u32); match heap_type { HeapType::Concrete(index) => Some(RefType::concrete(nullable, index.pack()?)), HeapType::Abstract { shared, ty } => { use AbstractHeapType::*; let base32 = base32 | (Self::SHARED_BIT * (shared as u32)); match ty { Func => Some(Self::from_u32(base32 | Self::FUNC_ABSTYPE)), Extern => Some(Self::from_u32(base32 | Self::EXTERN_ABSTYPE)), Any => Some(Self::from_u32(base32 | Self::ANY_ABSTYPE)), None => Some(Self::from_u32(base32 | Self::NONE_ABSTYPE)), NoExtern => Some(Self::from_u32(base32 | Self::NOEXTERN_ABSTYPE)), NoFunc => Some(Self::from_u32(base32 | Self::NOFUNC_ABSTYPE)), Eq => Some(Self::from_u32(base32 | Self::EQ_ABSTYPE)), Struct => Some(Self::from_u32(base32 | Self::STRUCT_ABSTYPE)), Array => Some(Self::from_u32(base32 | Self::ARRAY_ABSTYPE)), I31 => Some(Self::from_u32(base32 | Self::I31_ABSTYPE)), Exn => Some(Self::from_u32(base32 | Self::EXN_ABSTYPE)), NoExn => Some(Self::from_u32(base32 | Self::NOEXN_ABSTYPE)), } } } } /// Compute the [type difference] between the two given ref types. /// /// [type difference]: https://webassembly.github.io/gc/core/valid/conventions.html#aux-reftypediff pub fn difference(a: RefType, b: RefType) -> RefType { RefType::new( if b.is_nullable() { false } else { a.is_nullable() }, a.heap_type(), ) .unwrap() } /// Is this a reference to an concrete type? pub const fn is_concrete_type_ref(&self) -> bool { self.as_u32() & Self::CONCRETE_BIT != 0 } /// If this is a reference to a concrete Wasm-defined type, get its /// type index. pub fn type_index(&self) -> Option { if self.is_concrete_type_ref() { let index = self.as_u32() & Self::INDEX_MASK; Some(PackedIndex::unchecked_from_u32(index)) } else { None } } const fn abstype(&self) -> u32 { debug_assert!(!self.is_concrete_type_ref()); self.as_u32() & Self::ABSTYPE_MASK } /// Is this the abstract untyped function reference type aka `(ref /// null func)` aka `funcref` aka `anyfunc`? pub const fn is_func_ref(&self) -> bool { !self.is_concrete_type_ref() && self.abstype() == Self::FUNC_ABSTYPE } /// Is this the abstract external reference type aka `(ref null /// extern)` aka `externref`? pub const fn is_extern_ref(&self) -> bool { !self.is_concrete_type_ref() && self.abstype() == Self::EXTERN_ABSTYPE } /// Is this the abstract untyped array reference type aka `(ref null /// array)` aka `arrayref`? pub const fn is_array_ref(&self) -> bool { !self.is_concrete_type_ref() && self.abstype() == Self::ARRAY_ABSTYPE } /// Is this the abstract untyped struct reference type aka `(ref /// null struct)` aka `structref`? pub const fn is_struct_ref(&self) -> bool { !self.is_concrete_type_ref() && self.abstype() == Self::STRUCT_ABSTYPE } /// Is this ref type nullable? pub const fn is_nullable(&self) -> bool { self.as_u32() & Self::NULLABLE_BIT != 0 } /// Get the non-nullable version of this ref type. pub const fn as_non_null(&self) -> Self { Self::from_u32(self.as_u32() & !Self::NULLABLE_BIT) } /// Get the nullable version of this ref type. pub const fn nullable(&self) -> Self { Self::from_u32(self.as_u32() | Self::NULLABLE_BIT) } /// Get the shared version of this ref type as long as it is abstract. pub const fn shared(&self) -> Option { if self.is_concrete_type_ref() { None } else { Some(Self::from_u32(self.as_u32() | Self::SHARED_BIT)) } } /// Get the heap type that this is a reference to. pub fn heap_type(&self) -> HeapType { let s = self.as_u32(); if self.is_concrete_type_ref() { HeapType::Concrete(self.type_index().unwrap().unpack()) } else { use AbstractHeapType::*; let shared = s & Self::SHARED_BIT != 0; let ty = match s & Self::ABSTYPE_MASK { Self::FUNC_ABSTYPE => Func, Self::EXTERN_ABSTYPE => Extern, Self::ANY_ABSTYPE => Any, Self::NONE_ABSTYPE => None, Self::NOEXTERN_ABSTYPE => NoExtern, Self::NOFUNC_ABSTYPE => NoFunc, Self::EQ_ABSTYPE => Eq, Self::STRUCT_ABSTYPE => Struct, Self::ARRAY_ABSTYPE => Array, Self::I31_ABSTYPE => I31, Self::EXN_ABSTYPE => Exn, Self::NOEXN_ABSTYPE => NoExn, _ => unreachable!(), }; HeapType::Abstract { shared, ty } } } // Note that this is similar to `Display for RefType` except that it has // the indexes stubbed out. #[cfg(feature = "validate")] pub(crate) fn wat(&self) -> &'static str { let nullable = self.is_nullable(); match self.heap_type() { HeapType::Abstract { shared, ty } => { use AbstractHeapType::*; match (shared, nullable, ty) { // Shared and nullable. (true, true, Func) => "(shared funcref)", (true, true, Extern) => "(shared externref)", (true, true, Any) => "(shared anyref)", (true, true, None) => "(shared nullref)", (true, true, NoExtern) => "(shared nullexternref)", (true, true, NoFunc) => "(shared nullfuncref)", (true, true, Eq) => "(shared eqref)", (true, true, Struct) => "(shared structref)", (true, true, Array) => "(shared arrayref)", (true, true, I31) => "(shared i31ref)", (true, true, Exn) => "(shared exnref)", (true, true, NoExn) => "(shared nullexnref)", // Unshared but nullable. (false, true, Func) => "funcref", (false, true, Extern) => "externref", (false, true, Any) => "anyref", (false, true, None) => "nullref", (false, true, NoExtern) => "nullexternref", (false, true, NoFunc) => "nullfuncref", (false, true, Eq) => "eqref", (false, true, Struct) => "structref", (false, true, Array) => "arrayref", (false, true, I31) => "i31ref", (false, true, Exn) => "exnref", (false, true, NoExn) => "nullexnref", // Shared but not nullable. (true, false, Func) => "(ref (shared func))", (true, false, Extern) => "(ref (shared extern))", (true, false, Any) => "(ref (shared any))", (true, false, None) => "(ref (shared none))", (true, false, NoExtern) => "(ref (shared noextern))", (true, false, NoFunc) => "(ref (shared nofunc))", (true, false, Eq) => "(ref (shared eq))", (true, false, Struct) => "(ref (shared struct))", (true, false, Array) => "(ref (shared array))", (true, false, I31) => "(ref (shared i31))", (true, false, Exn) => "(ref (shared exn))", (true, false, NoExn) => "(ref (shared noexn))", // Neither shared nor nullable. (false, false, Func) => "(ref func)", (false, false, Extern) => "(ref extern)", (false, false, Any) => "(ref any)", (false, false, None) => "(ref none)", (false, false, NoExtern) => "(ref noextern)", (false, false, NoFunc) => "(ref nofunc)", (false, false, Eq) => "(ref eq)", (false, false, Struct) => "(ref struct)", (false, false, Array) => "(ref array)", (false, false, I31) => "(ref i31)", (false, false, Exn) => "(ref exn)", (false, false, NoExn) => "(ref noexn)", } } HeapType::Concrete(_) => { if nullable { "(ref null $type)" } else { "(ref $type)" } } } } } /// A heap type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum HeapType { /// An abstract heap type; e.g., `anyref`. Abstract { /// Whether the type is shared. /// /// Introduced in the shared-everything-threads proposal. shared: bool, /// The actual heap type. ty: AbstractHeapType, }, /// A concrete, user-defined type. /// /// Introduced in the function-references proposal. Concrete(UnpackedIndex), } impl HeapType { /// Alias for an unshared `func` heap type. pub const FUNC: Self = Self::Abstract { shared: false, ty: AbstractHeapType::Func, }; /// Alias for an unshared `extern` heap type. pub const EXTERN: Self = Self::Abstract { shared: false, ty: AbstractHeapType::Extern, }; } /// An abstract heap type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum AbstractHeapType { /// The abstract, untyped (any) function. /// /// Introduced in the references-types proposal. Func, /// The abstract, external heap type. /// /// Introduced in the references-types proposal. Extern, /// The abstract `any` heap type. /// /// The common supertype (a.k.a. top) of all internal types. /// /// Introduced in the GC proposal. Any, /// The abstract `none` heap type. /// /// The common subtype (a.k.a. bottom) of all internal types. /// /// Introduced in the GC proposal. None, /// The abstract `noextern` heap type. /// /// The common subtype (a.k.a. bottom) of all external types. /// /// Introduced in the GC proposal. NoExtern, /// The abstract `nofunc` heap type. /// /// The common subtype (a.k.a. bottom) of all function types. /// /// Introduced in the GC proposal. NoFunc, /// The abstract `eq` heap type. /// /// The common supertype of all heap types on which the `ref.eq` /// instruction is allowed. /// /// Introduced in the GC proposal. Eq, /// The abstract `struct` heap type. /// /// The common supertype of all struct types. /// /// Introduced in the GC proposal. Struct, /// The abstract `array` heap type. /// /// The common supertype of all array types. /// /// Introduced in the GC proposal. Array, /// The abstract `i31` heap type. /// /// It is not expected that Wasm runtimes actually store these /// values on the heap, but unbox them inline into the `i31ref`s /// themselves instead. /// /// Introduced in the GC proposal. I31, /// The abstraction `exception` heap type. /// /// Introduced in the exception-handling proposal. Exn, /// The abstract `noexn` heap type. /// /// The common subtype (a.k.a. bottom) of all exception types. /// /// Introduced in the exception-handling proposal. NoExn, } impl AbstractHeapType { pub(crate) const fn as_str(&self, nullable: bool) -> &str { use AbstractHeapType::*; match (nullable, self) { (_, Any) => "any", (true, None) => "null", (false, None) => "none", (true, NoExtern) => "nullextern", (false, NoExtern) => "noextern", (true, NoFunc) => "nullfunc", (false, NoFunc) => "nofunc", (_, Eq) => "eq", (_, Struct) => "struct", (_, Array) => "array", (_, I31) => "i31", (_, Extern) => "extern", (_, Func) => "func", (_, Exn) => "exn", (true, NoExn) => "nullexn", (false, NoExn) => "noexn", } } #[cfg(feature = "validate")] pub(crate) fn is_subtype_of(&self, other: AbstractHeapType) -> bool { use AbstractHeapType::*; match (self, other) { (a, b) if *a == b => true, (Eq | I31 | Struct | Array | None, Any) => true, (I31 | Struct | Array | None, Eq) => true, (NoExtern, Extern) => true, (NoFunc, Func) => true, (None, I31 | Array | Struct) => true, (NoExn, Exn) => true, // Nothing else matches. (Avoid full wildcard matches so // that adding/modifying variants is easier in the // future.) ( Func | Extern | Exn | Any | Eq | Array | I31 | Struct | None | NoFunc | NoExtern | NoExn, _, ) => false, } } } impl<'a> FromReader<'a> for StorageType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // NB: See `FromReader<'a> for ValType` for a table of how this // interacts with other value encodings. match reader.peek()? { 0x78 => { reader.read_u8()?; Ok(StorageType::I8) } 0x77 => { reader.read_u8()?; Ok(StorageType::I16) } _ => Ok(StorageType::Val(reader.read()?)), } } } impl<'a> FromReader<'a> for ValType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // Decoding value types is sort of subtle because the space of what's // being decoded here is actually spread out across an number of // locations. This comment here is intended to serve as a bit of a // reference to what's being decoded here and how it interacts with // other locations. // // Note that all value types are encoded as canonical-form negative // numbers in the sleb128 encoding scheme. Currently in the wasm spec // sleb128 isn't actually used but it looks to be modelled to allow it // one day. In the meantime the current values used are: // // | sleb128 | decimal | type | notes | // |---------|---------|--------------|------------------------------| // | 0x7F | -1 | i32 | | // | 0x7E | -2 | i64 | | // | 0x7D | -3 | f32 | | // | 0x7C | -4 | f64 | | // | 0x7B | -5 | v128 | simd proposal | // | 0x78 | -8 | i8 | gc proposal, in `FieldType` | // | 0x77 | -9 | i16 | gc proposal, in `FieldType` | // | 0x74 | -12 | noexn | gc + exceptions proposal | // | 0x73 | -13 | nofunc | gc proposal | // | 0x72 | -14 | noextern | gc proposal | // | 0x71 | -15 | nullref | gc proposal | // | 0x70 | -16 | func | reference types proposal | // | 0x6F | -17 | extern | reference types proposal | // | 0x6E | -18 | any | gc proposal | // | 0x6D | -19 | eq | gc proposal | // | 0x6C | -20 | i31 | gc proposal | // | 0x6B | -21 | struct | gc proposal | // | 0x6A | -22 | array | gc proposal | // | 0x69 | -23 | exnref | gc + exceptions proposal | // | 0x65 | -27 | shared $t | shared-everything proposal | // | 0x64 | -28 | ref $t | gc proposal, prefix byte | // | 0x63 | -29 | ref null $t | gc proposal, prefix byte | // | 0x60 | -32 | func $t | prefix byte | // | 0x5f | -33 | struct $t | gc proposal, prefix byte | // | 0x5e | -34 | array $t | gc proposal, prefix byte | // | 0x50 | -48 | sub $t | gc proposal, prefix byte | // | 0x4F | -49 | sub final $t | gc proposal, prefix byte | // | 0x4E | -50 | rec $t | gc proposal, prefix byte | // | 0x40 | -64 | ε | empty block type | // // Note that not all of these encodings are parsed here, for example // 0x78 as the encoding for `i8` is parsed only in `FieldType`. The // parsing of `FieldType` will delegate here without actually consuming // anything though so the encoding 0x78 still must be disjoint and not // read here otherwise. match reader.peek()? { 0x7F => { reader.read_u8()?; Ok(ValType::I32) } 0x7E => { reader.read_u8()?; Ok(ValType::I64) } 0x7D => { reader.read_u8()?; Ok(ValType::F32) } 0x7C => { reader.read_u8()?; Ok(ValType::F64) } 0x7B => { reader.read_u8()?; Ok(ValType::V128) } 0x70 | 0x6F | 0x65 | 0x64 | 0x63 | 0x6E | 0x71 | 0x72 | 0x73 | 0x74 | 0x6D | 0x6B | 0x6A | 0x6C | 0x69 => Ok(ValType::Ref(reader.read()?)), _ => bail!(reader.original_position(), "invalid value type"), } } } impl<'a> FromReader<'a> for RefType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let absheapty = |byte, pos| match byte { 0x70 => Ok(RefType::FUNC.nullable()), 0x6F => Ok(RefType::EXTERN.nullable()), 0x6E => Ok(RefType::ANY.nullable()), 0x71 => Ok(RefType::NONE.nullable()), 0x72 => Ok(RefType::NOEXTERN.nullable()), 0x73 => Ok(RefType::NOFUNC.nullable()), 0x6D => Ok(RefType::EQ.nullable()), 0x6B => Ok(RefType::STRUCT.nullable()), 0x6A => Ok(RefType::ARRAY.nullable()), 0x6C => Ok(RefType::I31.nullable()), 0x69 => Ok(RefType::EXN.nullable()), 0x74 => Ok(RefType::NOEXN.nullable()), _ => bail!(pos, "invalid abstract heap type"), }; // NB: See `FromReader<'a> for ValType` for a table of how this // interacts with other value encodings. match reader.read()? { byte @ (0x70 | 0x6F | 0x6E | 0x71 | 0x72 | 0x73 | 0x6D | 0x6B | 0x6A | 0x6C | 0x69 | 0x74) => { let pos = reader.original_position(); absheapty(byte, pos) } 0x65 => { let byte = reader.read()?; let pos = reader.original_position(); Ok(absheapty(byte, pos)?.shared().expect("must be abstract")) } byte @ (0x63 | 0x64) => { let nullable = byte == 0x63; let pos = reader.original_position(); RefType::new(nullable, reader.read()?) .ok_or_else(|| crate::BinaryReaderError::new("type index too large", pos)) } _ => bail!(reader.original_position(), "malformed reference type"), } } } impl<'a> FromReader<'a> for HeapType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // NB: See `FromReader<'a> for ValType` for a table of how this // interacts with other value encodings. match reader.peek()? { 0x65 => { reader.read_u8()?; let ty = reader.read()?; Ok(HeapType::Abstract { shared: true, ty }) } 0x70 | 0x6F | 0x6E | 0x71 | 0x72 | 0x73 | 0x6D | 0x6B | 0x6A | 0x6C | 0x69 | 0x74 => { let ty = reader.read()?; Ok(HeapType::Abstract { shared: false, ty }) } _ => { let idx = match u32::try_from(reader.read_var_s33()?) { Ok(idx) => idx, Err(_) => { bail!(reader.original_position(), "invalid indexed ref heap type"); } }; let idx = PackedIndex::from_module_index(idx).ok_or_else(|| { BinaryReaderError::new( "type index greater than implementation limits", reader.original_position(), ) })?; Ok(HeapType::Concrete(idx.unpack())) } } } } impl<'a> FromReader<'a> for AbstractHeapType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { use AbstractHeapType::*; match reader.read_u8()? { 0x70 => Ok(Func), 0x6F => Ok(Extern), 0x6E => Ok(Any), 0x71 => Ok(None), 0x72 => Ok(NoExtern), 0x73 => Ok(NoFunc), 0x6D => Ok(Eq), 0x6B => Ok(Struct), 0x6A => Ok(Array), 0x6C => Ok(I31), 0x69 => Ok(Exn), 0x74 => Ok(NoExn), _ => { bail!(reader.original_position(), "invalid abstract heap type"); } } } } /// Represents a table's type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TableType { /// The table's element type. pub element_type: RefType, /// Whether or not this is a 64-bit table. /// /// This is part of the memory64 proposal in WebAssembly. pub table64: bool, /// Initial size of this table, in elements. /// /// For 32-bit tables (when `table64` is `false`) this is guaranteed to /// be at most `u32::MAX` for valid types. pub initial: u64, /// Optional maximum size of the table, in elements. /// /// For 32-bit tables (when `table64` is `false`) this is guaranteed to /// be at most `u32::MAX` for valid types. pub maximum: Option, /// Whether this table is shared or not. /// /// This is included the shared-everything-threads proposal. pub shared: bool, } impl TableType { /// Gets the index type for the table. pub fn index_type(&self) -> ValType { if self.table64 { ValType::I64 } else { ValType::I32 } } } /// Represents a memory's type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct MemoryType { /// Whether or not this is a 64-bit memory, using i64 as an index. If this /// is false it's a 32-bit memory using i32 as an index. /// /// This is part of the memory64 proposal in WebAssembly. pub memory64: bool, /// Whether or not this is a "shared" memory, indicating that it should be /// send-able across threads and the `maximum` field is always present for /// valid types. /// /// This is part of the threads proposal in WebAssembly. pub shared: bool, /// Initial size of this memory, in wasm pages. /// /// For 32-bit memories (when `memory64` is `false`) this is guaranteed to /// be at most `u32::MAX` for valid types. pub initial: u64, /// Optional maximum size of this memory, in wasm pages. /// /// For 32-bit memories (when `memory64` is `false`) this is guaranteed to /// be at most `u32::MAX` for valid types. This field is always present for /// valid wasm memories when `shared` is `true`. pub maximum: Option, /// The log base 2 of the memory's custom page size. /// /// Memory pages are, by default, 64KiB large (i.e. 216 or /// `65536`). /// /// [The custom-page-sizes proposal] allows changing it to other values. /// /// [The custom-page-sizes proposal]: https://github.com/WebAssembly/custom-page-sizes pub page_size_log2: Option, } impl MemoryType { /// Gets the index type for the memory. pub fn index_type(&self) -> ValType { if self.memory64 { ValType::I64 } else { ValType::I32 } } } /// Represents a global's type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct GlobalType { /// The global's type. pub content_type: ValType, /// Whether or not the global is mutable. pub mutable: bool, /// Whether or not the global is shared. pub shared: bool, } /// Represents a tag kind. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum TagKind { /// The tag is an exception type. Exception, } /// A tag's type. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TagType { /// The kind of tag pub kind: TagKind, /// The function type this tag uses. pub func_type_idx: u32, } /// A reader for the type section of a WebAssembly module. pub type TypeSectionReader<'a> = SectionLimited<'a, RecGroup>; impl<'a> TypeSectionReader<'a> { /// Returns an iterator over this type section which will only yield /// function types and any usage of GC types from the GC proposal will /// be translated into an error. pub fn into_iter_err_on_gc_types(self) -> impl Iterator> + 'a { self.into_iter_with_offsets().map(|item| { let (offset, group) = item?; let mut types = group.into_types(); let ty = match (types.next(), types.next()) { (Some(ty), None) => ty, _ => bail!(offset, "gc proposal not supported"), }; if !ty.is_final || ty.supertype_idx.is_some() { bail!(offset, "gc proposal not supported"); } match ty.composite_type.inner { CompositeInnerType::Func(f) => Ok(f), CompositeInnerType::Array(_) | CompositeInnerType::Struct(_) => { bail!(offset, "gc proposal not supported"); } } }) } } impl<'a> FromReader<'a> for CompositeType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { read_composite_type(reader.read_u8()?, reader) } } fn read_composite_type( opcode: u8, reader: &mut BinaryReader, ) -> Result { // NB: See `FromReader<'a> for ValType` for a table of how this // interacts with other value encodings. let (shared, opcode) = if opcode == 0x65 { (true, reader.read_u8()?) } else { (false, opcode) }; let inner = match opcode { 0x60 => CompositeInnerType::Func(reader.read()?), 0x5e => CompositeInnerType::Array(reader.read()?), 0x5f => CompositeInnerType::Struct(reader.read()?), x => return reader.invalid_leading_byte(x, "type"), }; Ok(CompositeType { shared, inner }) } impl<'a> FromReader<'a> for RecGroup { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // NB: See `FromReader<'a> for ValType` for a table of how this // interacts with other value encodings. match reader.peek()? { 0x4e => { reader.read_u8()?; let mut iter = reader.read_iter(MAX_WASM_TYPES, "rec group types")?; let mut types = Vec::with_capacity(iter.size_hint().0); let mut offset = iter.reader.original_position(); while let Some(ty) = iter.next() { types.push((offset, ty?)); offset = iter.reader.original_position(); } Ok(RecGroup::explicit(types)) } _ => Ok(RecGroup::implicit( reader.original_position(), reader.read()?, )), } } } impl<'a> FromReader<'a> for SubType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let pos = reader.original_position(); // NB: See `FromReader<'a> for ValType` for a table of how this // interacts with other value encodings. Ok(match reader.read_u8()? { opcode @ (0x4f | 0x50) => { let idx_iter = reader.read_iter(MAX_WASM_SUPERTYPES, "supertype idxs")?; let idxs = idx_iter.collect::>>()?; if idxs.len() > 1 { return Err(BinaryReaderError::new( "multiple supertypes not supported", pos, )); } let supertype_idx = idxs .first() .copied() .map(|idx| { PackedIndex::from_module_index(idx).ok_or_else(|| { BinaryReaderError::new( "type index greater than implementation limits", reader.original_position(), ) }) }) .transpose()?; SubType { is_final: opcode == 0x4f, supertype_idx, composite_type: read_composite_type(reader.read_u8()?, reader)?, } } opcode => SubType { is_final: true, supertype_idx: None, composite_type: read_composite_type(opcode, reader)?, }, }) } } impl<'a> FromReader<'a> for FuncType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let mut params_results = reader .read_iter(MAX_WASM_FUNCTION_PARAMS, "function params")? .collect::>>()?; let len_params = params_results.len(); let results = reader.read_iter(MAX_WASM_FUNCTION_RETURNS, "function returns")?; params_results.reserve(results.size_hint().0); for result in results { params_results.push(result?); } Ok(FuncType::from_raw_parts(params_results.into(), len_params)) } } impl<'a> FromReader<'a> for FieldType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let element_type = reader.read()?; let mutable = reader.read_u8()?; Ok(FieldType { element_type, mutable: match mutable { 0 => false, 1 => true, _ => bail!( reader.original_position(), "malformed mutability byte for field type" ), }, }) } } impl<'a> FromReader<'a> for ArrayType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok(ArrayType(FieldType::from_reader(reader)?)) } } impl<'a> FromReader<'a> for StructType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let fields = reader.read_iter(MAX_WASM_STRUCT_FIELDS, "struct fields")?; Ok(StructType { fields: fields.collect::>()?, }) } } wasmparser-0.217.0/src/readers/core.rs000064400000000000000000000014561046102023000156770ustar 00000000000000mod branch_hinting; mod code; mod coredumps; mod custom; mod data; mod dylink0; mod elements; mod exports; mod functions; mod globals; mod imports; mod init; mod linking; mod memories; mod names; mod operators; mod producers; mod reloc; mod tables; mod tags; mod types; pub use self::branch_hinting::*; pub use self::code::*; pub use self::coredumps::*; pub use self::custom::*; pub use self::data::*; pub use self::dylink0::*; pub use self::elements::*; pub use self::exports::*; pub use self::functions::*; pub use self::globals::*; pub use self::imports::*; pub use self::init::*; pub use self::linking::*; pub use self::memories::*; pub use self::names::*; pub use self::operators::*; pub use self::producers::*; pub use self::reloc::*; pub use self::tables::*; pub use self::tags::*; pub use self::types::*; wasmparser-0.217.0/src/readers.rs000064400000000000000000000217051046102023000147460ustar 00000000000000/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{BinaryReader, BinaryReaderError, Result}; use ::core::fmt; use ::core::marker; use ::core::ops::Range; mod component; mod core; pub use self::component::*; pub use self::core::*; /// A trait implemented for items that can be decoded directly from a /// `BinaryReader`, or that which can be parsed from the WebAssembly binary /// format. /// /// Note that this is also accessible as a [`BinaryReader::read`] method. pub trait FromReader<'a>: Sized { /// Attempts to read `Self` from the provided binary reader, returning an /// error if it is unable to do so. fn from_reader(reader: &mut BinaryReader<'a>) -> Result; } impl<'a> FromReader<'a> for u32 { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { reader.read_var_u32() } } impl<'a> FromReader<'a> for &'a str { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { reader.read_string() } } impl<'a, T, U> FromReader<'a> for (T, U) where T: FromReader<'a>, U: FromReader<'a>, { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { Ok((reader.read()?, reader.read()?)) } } /// A generic structure for reading a section of a WebAssembly binary which has /// a limited number of items within it. /// /// Many WebAssembly sections are a count of items followed by that many items. /// This helper structure can be used to parse these sections and provides /// an iteration-based API for reading the contents. /// /// Note that this always implements the [`Clone`] trait to represent the /// ability to parse the section multiple times. pub struct SectionLimited<'a, T> { reader: BinaryReader<'a>, count: u32, _marker: marker::PhantomData, } impl<'a, T> SectionLimited<'a, T> { /// Creates a new section reader from the provided contents. /// /// The `data` provided here is the data of the section itself that will be /// parsed. The `offset` argument is the byte offset, in the original wasm /// binary, that the section was found. The `offset` argument is used /// for error reporting. /// /// # Errors /// /// Returns an error if a 32-bit count couldn't be read from the `data`. pub fn new(mut reader: BinaryReader<'a>) -> Result { let count = reader.read_var_u32()?; Ok(SectionLimited { reader, count, _marker: marker::PhantomData, }) } /// Returns the count of total items within this section. pub fn count(&self) -> u32 { self.count } /// Returns whether the original byte offset of this section. pub fn original_position(&self) -> usize { self.reader.original_position() } /// Returns the range, as byte offsets, of this section within the original /// wasm binary. pub fn range(&self) -> Range { self.reader.range() } /// Returns an iterator which yields not only each item in this section but /// additionally the offset of each item within the section. pub fn into_iter_with_offsets(self) -> SectionLimitedIntoIterWithOffsets<'a, T> where T: FromReader<'a>, { SectionLimitedIntoIterWithOffsets { iter: self.into_iter(), } } } impl Clone for SectionLimited<'_, T> { fn clone(&self) -> Self { SectionLimited { reader: self.reader.clone(), count: self.count, _marker: self._marker, } } } impl fmt::Debug for SectionLimited<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SectionLimited") .field("count", &self.count) .field("range", &self.range()) .finish() } } impl<'a, T> IntoIterator for SectionLimited<'a, T> where T: FromReader<'a>, { type Item = Result; type IntoIter = SectionLimitedIntoIter<'a, T>; fn into_iter(self) -> Self::IntoIter { SectionLimitedIntoIter { remaining: self.count, section: self, end: false, } } } /// A consuming iterator of a [`SectionLimited`]. /// /// This is created via the [`IntoIterator`] `impl` for the [`SectionLimited`] /// type. pub struct SectionLimitedIntoIter<'a, T> { section: SectionLimited<'a, T>, remaining: u32, end: bool, } impl SectionLimitedIntoIter<'_, T> { /// Returns the current byte offset of the section within this iterator. pub fn original_position(&self) -> usize { self.section.reader.original_position() } } impl<'a, T> Iterator for SectionLimitedIntoIter<'a, T> where T: FromReader<'a>, { type Item = Result; fn next(&mut self) -> Option> { if self.end { return None; } if self.remaining == 0 { self.end = true; if self.section.reader.eof() { return None; } return Some(Err(BinaryReaderError::new( "section size mismatch: unexpected data at the end of the section", self.section.reader.original_position(), ))); } let result = self.section.reader.read(); self.end = result.is_err(); self.remaining -= 1; Some(result) } fn size_hint(&self) -> (usize, Option) { let remaining = self.remaining as usize; (remaining, Some(remaining)) } } impl<'a, T> ExactSizeIterator for SectionLimitedIntoIter<'a, T> where T: FromReader<'a> {} /// An iterator over a limited section iterator. pub struct SectionLimitedIntoIterWithOffsets<'a, T> { iter: SectionLimitedIntoIter<'a, T>, } impl<'a, T> Iterator for SectionLimitedIntoIterWithOffsets<'a, T> where T: FromReader<'a>, { type Item = Result<(usize, T)>; fn next(&mut self) -> Option { let pos = self.iter.section.reader.original_position(); Some(self.iter.next()?.map(|item| (pos, item))) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, T> ExactSizeIterator for SectionLimitedIntoIterWithOffsets<'a, T> where T: FromReader<'a> {} /// A trait implemented for subsections of another outer section. /// /// This is currently only used for subsections within custom sections, such as /// the `name` section of core wasm. /// /// This is used in conjunction with [`Subsections`]. pub trait Subsection<'a>: Sized { /// Converts the section identifier provided with the section contents into /// a typed section fn from_reader(id: u8, reader: BinaryReader<'a>) -> Result; } /// Iterator/reader over the contents of a section which is composed of /// subsections. /// /// This reader is used for the core `name` section, for example. This type /// primarily implements [`Iterator`] for advancing through the sections. pub struct Subsections<'a, T> { reader: BinaryReader<'a>, _marker: marker::PhantomData, } impl<'a, T> Subsections<'a, T> { /// Creates a new reader for the specified section contents starting at /// `offset` within the original wasm file. pub fn new(reader: BinaryReader<'a>) -> Self { Subsections { reader, _marker: marker::PhantomData, } } /// Returns whether the original byte offset of this section. pub fn original_position(&self) -> usize { self.reader.original_position() } /// Returns the range, as byte offsets, of this section within the original /// wasm binary. pub fn range(&self) -> Range { self.reader.range() } fn read(&mut self) -> Result where T: Subsection<'a>, { let subsection_id = self.reader.read_u7()?; let reader = self.reader.read_reader()?; T::from_reader(subsection_id, reader) } } impl Clone for Subsections<'_, T> { fn clone(&self) -> Self { Subsections { reader: self.reader.clone(), _marker: self._marker, } } } impl fmt::Debug for Subsections<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Subsections") .field("range", &self.range()) .finish() } } impl<'a, T> Iterator for Subsections<'a, T> where T: Subsection<'a>, { type Item = Result; fn next(&mut self) -> Option> { if self.reader.eof() { None } else { Some(self.read()) } } } wasmparser-0.217.0/src/resources.rs000064400000000000000000000174201046102023000153320ustar 00000000000000/* Copyright 2019 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::{ types::CoreTypeId, BinaryReaderError, FuncType, GlobalType, HeapType, MemoryType, RefType, SubType, TableType, ValType, WasmFeatures, }; /// Types that qualify as Wasm validation database. /// /// # Note /// /// The `wasmparser` crate provides a builtin validation framework but allows /// users of this crate to also feed the parsed Wasm into their own data /// structure while parsing and also validate at the same time without /// the need of an additional parsing or validation step or copying data around. pub trait WasmModuleResources { /// Returns the table at given index if any. /// /// The table element type must be canonicalized. fn table_at(&self, at: u32) -> Option; /// Returns the linear memory at given index. fn memory_at(&self, at: u32) -> Option; /// Returns the tag at given index. /// /// The tag's function type must be canonicalized. fn tag_at(&self, at: u32) -> Option<&FuncType>; /// Returns the global variable at given index. /// /// The global's value type must be canonicalized. fn global_at(&self, at: u32) -> Option; /// Returns the `SubType` associated with the given type index. /// /// The sub type must be canonicalized. fn sub_type_at(&self, type_index: u32) -> Option<&SubType>; /// Returns the type ID associated with the given function index. fn type_id_of_function(&self, func_idx: u32) -> Option; /// Returns the type index associated with the given function index. fn type_index_of_function(&self, func_index: u32) -> Option; /// Returns the element type at the given index. /// /// The `RefType` must be canonicalized. fn element_type_at(&self, at: u32) -> Option; /// Is `a` a subtype of `b`? fn is_subtype(&self, a: ValType, b: ValType) -> bool; /// Is the given reference type `shared`? /// /// While abstract heap types do carry along a `shared` flag, concrete heap /// types do not. This function resolves those concrete heap types to /// determine `shared`-ness. fn is_shared(&self, ty: RefType) -> bool; /// Check and canonicalize a value type. /// /// This will validate that `t` is valid under the `features` provided and /// then additionally validate the structure of `t`. For example any type /// references that `t` makes are validated and canonicalized. fn check_value_type( &self, t: &mut ValType, features: &WasmFeatures, offset: usize, ) -> Result<(), BinaryReaderError> { features .check_value_type(*t) .map_err(|s| BinaryReaderError::new(s, offset))?; match t { ValType::Ref(r) => self.check_ref_type(r, offset), ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => Ok(()), } } /// Check and canonicalize a reference type. fn check_ref_type( &self, ref_type: &mut RefType, offset: usize, ) -> Result<(), BinaryReaderError> { let is_nullable = ref_type.is_nullable(); let mut heap_ty = ref_type.heap_type(); self.check_heap_type(&mut heap_ty, offset)?; *ref_type = RefType::new(is_nullable, heap_ty).unwrap(); Ok(()) } /// Checks that a `HeapType` is valid and then additionally place it in its /// canonical form. /// /// Similar to `check_value_type` but for heap types. fn check_heap_type( &self, heap_type: &mut HeapType, offset: usize, ) -> Result<(), BinaryReaderError>; /// Get the top type for the given heap type. fn top_type(&self, heap_type: &HeapType) -> HeapType; /// Returns the number of elements. fn element_count(&self) -> u32; /// Returns the number of bytes in the Wasm data section. fn data_count(&self) -> Option; /// Returns whether the function index is referenced in the module anywhere /// outside of the start/function sections. fn is_function_referenced(&self, idx: u32) -> bool; } impl WasmModuleResources for &'_ T where T: ?Sized + WasmModuleResources, { fn table_at(&self, at: u32) -> Option { T::table_at(self, at) } fn memory_at(&self, at: u32) -> Option { T::memory_at(self, at) } fn tag_at(&self, at: u32) -> Option<&FuncType> { T::tag_at(self, at) } fn global_at(&self, at: u32) -> Option { T::global_at(self, at) } fn sub_type_at(&self, at: u32) -> Option<&SubType> { T::sub_type_at(self, at) } fn type_id_of_function(&self, func_idx: u32) -> Option { T::type_id_of_function(self, func_idx) } fn type_index_of_function(&self, func_idx: u32) -> Option { T::type_index_of_function(self, func_idx) } fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<(), BinaryReaderError> { T::check_heap_type(self, t, offset) } fn top_type(&self, heap_type: &HeapType) -> HeapType { T::top_type(self, heap_type) } fn element_type_at(&self, at: u32) -> Option { T::element_type_at(self, at) } fn is_subtype(&self, a: ValType, b: ValType) -> bool { T::is_subtype(self, a, b) } fn is_shared(&self, ty: RefType) -> bool { T::is_shared(self, ty) } fn element_count(&self) -> u32 { T::element_count(self) } fn data_count(&self) -> Option { T::data_count(self) } fn is_function_referenced(&self, idx: u32) -> bool { T::is_function_referenced(self, idx) } } impl WasmModuleResources for alloc::sync::Arc where T: WasmModuleResources, { fn table_at(&self, at: u32) -> Option { T::table_at(self, at) } fn memory_at(&self, at: u32) -> Option { T::memory_at(self, at) } fn tag_at(&self, at: u32) -> Option<&FuncType> { T::tag_at(self, at) } fn global_at(&self, at: u32) -> Option { T::global_at(self, at) } fn sub_type_at(&self, type_idx: u32) -> Option<&SubType> { T::sub_type_at(self, type_idx) } fn type_id_of_function(&self, func_idx: u32) -> Option { T::type_id_of_function(self, func_idx) } fn type_index_of_function(&self, func_idx: u32) -> Option { T::type_index_of_function(self, func_idx) } fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<(), BinaryReaderError> { T::check_heap_type(self, t, offset) } fn top_type(&self, heap_type: &HeapType) -> HeapType { T::top_type(self, heap_type) } fn element_type_at(&self, at: u32) -> Option { T::element_type_at(self, at) } fn is_subtype(&self, a: ValType, b: ValType) -> bool { T::is_subtype(self, a, b) } fn is_shared(&self, ty: RefType) -> bool { T::is_shared(self, ty) } fn element_count(&self) -> u32 { T::element_count(self) } fn data_count(&self) -> Option { T::data_count(self) } fn is_function_referenced(&self, idx: u32) -> bool { T::is_function_referenced(self, idx) } } wasmparser-0.217.0/src/validator/component.rs000064400000000000000000003743471046102023000173250ustar 00000000000000//! State relating to validating a WebAssembly component. use super::{ check_max, core::{InternRecGroup, Module}, types::{ AliasableResourceId, ComponentCoreInstanceTypeId, ComponentDefinedTypeId, ComponentFuncType, ComponentFuncTypeId, ComponentInstanceType, ComponentInstanceTypeId, ComponentType, ComponentTypeId, ComponentValType, CoreTypeId, EntityType, InstanceType, ModuleType, RecordType, Remapping, ResourceId, TypeAlloc, TypeList, VariantCase, }, }; use crate::prelude::*; use crate::validator::names::{ComponentName, ComponentNameKind, KebabStr, KebabString}; use crate::{collections::index_map::Entry, CompositeInnerType}; use crate::{ limits::*, types::{ ComponentAnyTypeId, ComponentCoreModuleTypeId, ComponentCoreTypeId, ComponentDefinedType, ComponentEntityType, Context, CoreInstanceTypeKind, LoweringInfo, Remap, SubtypeCx, TupleType, TypeInfo, VariantType, }, BinaryReaderError, CanonicalOption, ComponentExportName, ComponentExternalKind, ComponentOuterAliasKind, ComponentTypeRef, CompositeType, ExternalKind, FuncType, GlobalType, InstantiationArgKind, MemoryType, RecGroup, Result, SubType, TableType, TypeBounds, ValType, WasmFeatures, }; use core::mem; fn to_kebab_str<'a>(s: &'a str, desc: &str, offset: usize) -> Result<&'a KebabStr> { match KebabStr::new(s) { Some(s) => Ok(s), None => { if s.is_empty() { bail!(offset, "{desc} name cannot be empty"); } bail!(offset, "{desc} name `{s}` is not in kebab case"); } } } pub(crate) struct ComponentState { /// Whether this state is a concrete component, an instance type, or a /// component type. kind: ComponentKind, // Core index spaces pub core_types: Vec, pub core_funcs: Vec, pub core_tags: Vec, pub core_modules: Vec, pub core_instances: Vec, pub core_memories: Vec, pub core_tables: Vec, pub core_globals: Vec, // Component index spaces pub types: Vec, pub funcs: Vec, pub values: Vec<(ComponentValType, bool)>, pub instances: Vec, pub components: Vec, pub imports: IndexMap, pub import_names: IndexSet, pub exports: IndexMap, pub export_names: IndexSet, has_start: bool, type_info: TypeInfo, /// A mapping of imported resources in this component. /// /// This mapping represents all "type variables" imported into the /// component, or resources. This could be resources imported directly as /// a top-level type import or additionally transitively through other /// imported instances. /// /// The mapping element here is a "path" which is a list of indexes into /// the import map that will be generated for this component. Each index /// is an index into an `IndexMap`, and each list is guaranteed to have at /// least one element. /// /// An example of this map is: /// /// ```wasm /// (component /// ;; [0] - the first import /// (import "r" (type (sub resource))) /// /// ;; [1] - the second import /// (import "r2" (type (sub resource))) /// /// (import "i" (instance /// ;; [2, 0] - the third import, and the first export the instance /// (export "r3" (type (sub resource))) /// ;; [2, 1] - the third import, and the second export the instance /// (export "r4" (type (sub resource))) /// )) /// /// ;; ... /// ) /// ``` /// /// The `Vec` here can be thought of as `Vec` but a /// (hopefully) more efficient representation. /// /// Finally note that this map is listed as an "append only" map because all /// insertions into it should always succeed. Any insertion which overlaps /// with a previous entry indicates a bug in the validator which needs to be /// corrected via other means. // // TODO: make these `SkolemResourceId` and then go fix all the compile // errors, don't add skolem things into the type area imported_resources: IndexMapAppendOnly>, /// A mapping of "defined" resources in this component, or those which /// are defined within the instantiation of this component. /// /// Defined resources, as the name implies, can sort of be thought of as /// "these are defined within the component". Note though that the means by /// which a local definition can occur are not simply those defined in the /// component but also in its transitively instantiated components /// internally. This means that this set closes over many transitive /// internal items in addition to those defined immediately in the component /// itself. /// /// The `Option` in this mapping is whether or not the underlying /// reprsentation of the resource is known to this component. Immediately /// defined resources, for example, will have `Some(I32)` here. Resources /// that come from transitively defined components, for example, will have /// `None`. In the type context all entries here are `None`. /// /// Note that like `imported_resources` all insertions into this map are /// expected to succeed to it's declared as append-only. defined_resources: IndexMapAppendOnly>, /// A mapping of explicitly exported resources from this component in /// addition to the path that they're exported at. /// /// For more information on the path here see the documentation for /// `imported_resources`. Note that the indexes here index into the /// list of exports of this component. explicit_resources: IndexMap>, /// The set of types which are considered "exported" from this component. /// /// This is added to whenever a type export is found, or an instance export /// which itself contains a type export. This additionally includes all /// imported types since those are suitable for export as well. /// /// This set is consulted whenever an exported item is added since all /// referenced types must be members of this set. exported_types: Set, /// Same as `exported_types`, but for imports. imported_types: Set, /// The set of top-level resource exports and their names. /// /// This context is used to validate method names such as `[method]foo.bar` /// to ensure that `foo` is an exported resource and that the type mentioned /// in a function type is actually named `foo`. /// /// Note that imports/exports have disjoint contexts to ensure that they're /// validated correctly. Namely you can't retroactively attach methods to an /// import, for example. toplevel_exported_resources: ComponentNameContext, /// Same as `toplevel_exported_resources`, but for imports. toplevel_imported_resources: ComponentNameContext, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ComponentKind { Component, InstanceType, ComponentType, } /// Helper context used to track information about resource names for method /// name validation. #[derive(Default)] struct ComponentNameContext { /// A map from a resource type id to an index in the `all_resource_names` /// set for the name of that resource. resource_name_map: Map, /// All known resource names in this context, used to validate static method /// names to by ensuring that static methods' resource names are somewhere /// in this set. all_resource_names: IndexSet, } #[derive(Debug, Copy, Clone)] pub enum ExternKind { Import, Export, } impl ExternKind { pub fn desc(&self) -> &'static str { match self { ExternKind::Import => "import", ExternKind::Export => "export", } } } impl ComponentState { pub fn new(kind: ComponentKind) -> Self { Self { kind, core_types: Default::default(), core_modules: Default::default(), core_instances: Default::default(), core_funcs: Default::default(), core_memories: Default::default(), core_tables: Default::default(), core_globals: Default::default(), core_tags: Default::default(), types: Default::default(), funcs: Default::default(), values: Default::default(), instances: Default::default(), components: Default::default(), imports: Default::default(), exports: Default::default(), import_names: Default::default(), export_names: Default::default(), has_start: Default::default(), type_info: TypeInfo::new(), imported_resources: Default::default(), defined_resources: Default::default(), explicit_resources: Default::default(), exported_types: Default::default(), imported_types: Default::default(), toplevel_exported_resources: Default::default(), toplevel_imported_resources: Default::default(), } } pub fn type_count(&self) -> usize { self.core_types.len() + self.types.len() } pub fn instance_count(&self) -> usize { self.core_instances.len() + self.instances.len() } pub fn function_count(&self) -> usize { self.core_funcs.len() + self.funcs.len() } pub fn add_core_type( components: &mut [Self], ty: crate::CoreType, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, check_limit: bool, ) -> Result<()> { let current = components.last_mut().unwrap(); if check_limit { check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; } match ty { crate::CoreType::Sub(sub) => { current.canonicalize_and_intern_rec_group( features, types, RecGroup::implicit(offset, sub), offset, )?; } crate::CoreType::Module(decls) => { let mod_ty = Self::create_module_type( components, decls.into_vec(), features, types, offset, )?; let id = ComponentCoreTypeId::Module(types.push_ty(mod_ty)); components.last_mut().unwrap().core_types.push(id); } } Ok(()) } pub fn add_core_module( &mut self, module: &Module, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let imports = module.imports_for_module_type(offset)?; // We have to clone the module's imports and exports here // because we cannot take the data out of the `MaybeOwned` // as it might be shared with a function validator. let mod_ty = ModuleType { info: TypeInfo::core(module.type_size), imports, exports: module.exports.clone(), }; let mod_id = types.push_ty(mod_ty); self.core_modules.push(mod_id); Ok(()) } pub fn add_core_instance( &mut self, instance: crate::Instance, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let instance = match instance { crate::Instance::Instantiate { module_index, args } => { self.instantiate_core_module(module_index, args.into_vec(), types, offset)? } crate::Instance::FromExports(exports) => { self.instantiate_core_exports(exports.into_vec(), types, offset)? } }; self.core_instances.push(instance); Ok(()) } pub fn add_type( components: &mut Vec, ty: crate::ComponentType, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, check_limit: bool, ) -> Result<()> { assert!(!components.is_empty()); fn current(components: &mut Vec) -> &mut ComponentState { components.last_mut().unwrap() } let id = match ty { crate::ComponentType::Defined(ty) => { let ty = current(components).create_defined_type(ty, types, features, offset)?; types.push(ty).into() } crate::ComponentType::Func(ty) => { let ty = current(components).create_function_type(ty, types, features, offset)?; types.push(ty).into() } crate::ComponentType::Component(decls) => { let ty = Self::create_component_type( components, decls.into_vec(), features, types, offset, )?; types.push(ty).into() } crate::ComponentType::Instance(decls) => { let ty = Self::create_instance_type( components, decls.into_vec(), features, types, offset, )?; types.push(ty).into() } crate::ComponentType::Resource { rep, dtor } => { let component = current(components); // Resource types cannot be declared in a type context, only // within a component context. if component.kind != ComponentKind::Component { bail!( offset, "resources can only be defined within a concrete component" ); } // Current MVP restriction of the component model. if rep != ValType::I32 { bail!(offset, "resources can only be represented by `i32`"); } // If specified validate that the destructor is both a valid // function and has the correct signature. if let Some(dtor) = dtor { let ty = component.core_function_at(dtor, offset)?; let ty = types[ty].composite_type.unwrap_func(); if ty.params() != [rep] || ty.results() != [] { bail!( offset, "core function {dtor} has wrong signature for a destructor" ); } } // As this is the introduction of a resource create a fresh new // identifier for the resource. This is then added into the // list of defined resources for this component, notably with a // rep listed to enable getting access to various intrinsics // such as `resource.rep`. let id = types.alloc_resource_id(); component.defined_resources.insert(id.resource(), Some(rep)); id.into() } }; let current = current(components); if check_limit { check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; } current.types.push(id); Ok(()) } pub fn add_import( &mut self, import: crate::ComponentImport, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let mut entity = self.check_type_ref(&import.ty, features, types, offset)?; self.add_entity( &mut entity, Some((import.name.0, ExternKind::Import)), features, types, offset, )?; self.toplevel_imported_resources.validate_extern( import.name.0, ExternKind::Import, &entity, types, offset, &mut self.import_names, &mut self.imports, &mut self.type_info, features, )?; Ok(()) } fn add_entity( &mut self, ty: &mut ComponentEntityType, name_and_kind: Option<(&str, ExternKind)>, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let kind = name_and_kind.map(|(_, k)| k); let (len, max, desc) = match ty { ComponentEntityType::Module(id) => { self.core_modules.push(*id); (self.core_modules.len(), MAX_WASM_MODULES, "modules") } ComponentEntityType::Component(id) => { self.components.push(*id); (self.components.len(), MAX_WASM_COMPONENTS, "components") } ComponentEntityType::Instance(id) => { match kind { Some(ExternKind::Import) => self.prepare_instance_import(id, types), Some(ExternKind::Export) => self.prepare_instance_export(id, types), None => {} } self.instances.push(*id); (self.instance_count(), MAX_WASM_INSTANCES, "instances") } ComponentEntityType::Func(id) => { self.funcs.push(*id); (self.function_count(), MAX_WASM_FUNCTIONS, "functions") } ComponentEntityType::Value(ty) => { self.check_value_support(features, offset)?; let value_used = match kind { Some(ExternKind::Import) | None => false, Some(ExternKind::Export) => true, }; self.values.push((*ty, value_used)); (self.values.len(), MAX_WASM_VALUES, "values") } ComponentEntityType::Type { created, referenced, } => { self.types.push(*created); // Extra logic here for resources being imported and exported. // Note that if `created` is the same as `referenced` then this // is the original introduction of the resource which is where // `self.{imported,defined}_resources` are updated. if let ComponentAnyTypeId::Resource(id) = *created { match kind { Some(ExternKind::Import) => { // A fresh new resource is being imported into a // component. This arises from the import section of // a component or from the import declaration in a // component type. In both cases a new imported // resource is injected with a fresh new identifier // into our state. if created == referenced { self.imported_resources .insert(id.resource(), vec![self.imports.len()]); } } Some(ExternKind::Export) => { // A fresh resource is being exported from this // component. This arises as part of the // declaration of a component type, for example. In // this situation brand new resource identifier is // allocated and a definition is added, unlike the // import case where an imported resource is added. // Notably the representation of this new resource // is unknown so it's listed as `None`. if created == referenced { self.defined_resources.insert(id.resource(), None); } // If this is a type export of a resource type then // update the `explicit_resources` list. A new // export path is about to be created for this // resource and this keeps track of that. self.explicit_resources .insert(id.resource(), vec![self.exports.len()]); } None => {} } } (self.types.len(), MAX_WASM_TYPES, "types") } }; check_max(len, 0, max, desc, offset)?; // Before returning perform the final validation of the type of the item // being imported/exported. This will ensure that everything is // appropriately named with respect to type definitions, resources, etc. if let Some((name, kind)) = name_and_kind { if !self.validate_and_register_named_types(Some(name), kind, ty, types) { bail!( offset, "{} not valid to be used as {}", ty.desc(), kind.desc() ); } } Ok(()) } /// Validates that the `ty` referenced only refers to named types internally /// and then inserts anything necessary, if applicable, to the defined sets /// within this component. /// /// This function will validate that `ty` only refers to named types. For /// example if it's a record then all of its fields must refer to named /// types. This consults either `self.imported_types` or /// `self.exported_types` as specified by `kind`. Note that this is not /// inherently recursive itself but it ends up being recursive since if /// recursive members were named then all their components must also be /// named. Consequently this check stops at the "one layer deep" position, /// or more accurately the position where types must be named (e.g. tuples /// aren't required to be named). fn validate_and_register_named_types( &mut self, toplevel_name: Option<&str>, kind: ExternKind, ty: &ComponentEntityType, types: &TypeAlloc, ) -> bool { if let ComponentEntityType::Type { created, .. } = ty { // If this is a top-level resource then register it in the // appropriate context so later validation of method-like-names // works out. if let Some(name) = toplevel_name { if let ComponentAnyTypeId::Resource(id) = *created { let cx = match kind { ExternKind::Import => &mut self.toplevel_imported_resources, ExternKind::Export => &mut self.toplevel_exported_resources, }; cx.register(name, id); } } } match self.kind { ComponentKind::Component | ComponentKind::ComponentType => {} ComponentKind::InstanceType => return true, } let set = match kind { ExternKind::Import => &self.imported_types, ExternKind::Export => &self.exported_types, }; match ty { // When a type is imported or exported than any recursive type // referred to by that import/export must additionally be exported // or imported. Here this walks the "first layer" of the type which // delegates to `TypeAlloc::type_named_type_id` to determine whether // the components of the type being named here are indeed all they // themselves named. ComponentEntityType::Type { created, referenced, } => { if !self.all_valtypes_named(types, *referenced, set) { return false; } match kind { // Imported types are both valid for import and valid for // export. ExternKind::Import => { self.imported_types.insert(*created); self.exported_types.insert(*created); } ExternKind::Export => { self.exported_types.insert(*created); } } true } // Instances are slightly nuanced here. The general idea is that if // an instance is imported, then any type exported by the instance // is then also exported. Additionally for exports. To get this to // work out this arm will recursively call // `validate_and_register_named_types` which means that types are // inserted into `self.{imported,exported}_types` as-we-go rather // than all at once. // // This then recursively validates that all items in the instance // itself are valid to import/export, recursive instances are // captured, and everything is appropriately added to the right // imported/exported set. ComponentEntityType::Instance(i) => types[*i] .exports .iter() .all(|(_name, ty)| self.validate_and_register_named_types(None, kind, ty, types)), // All types referred to by a function must be named. ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set), ComponentEntityType::Value(ty) => types.type_named_valtype(ty, set), // Components/modules are always "closed" or "standalone" and don't // need validation with respect to their named types. ComponentEntityType::Component(_) | ComponentEntityType::Module(_) => true, } } fn all_valtypes_named( &self, types: &TypeAlloc, id: ComponentAnyTypeId, set: &Set, ) -> bool { match id { // Resource types, in isolation, are always valid to import or // export since they're either attached to an import or being // exported. // // Note that further validation of this happens in `finish`, too. ComponentAnyTypeId::Resource(_) => true, // Component types are validated as they are constructed, // so all component types are valid to export if they've // already been constructed. ComponentAnyTypeId::Component(_) => true, ComponentAnyTypeId::Defined(id) => self.all_valtypes_named_in_defined(types, id, set), ComponentAnyTypeId::Func(id) => self.all_valtypes_named_in_func(types, id, set), ComponentAnyTypeId::Instance(id) => self.all_valtypes_named_in_instance(types, id, set), } } fn all_valtypes_named_in_instance( &self, types: &TypeAlloc, id: ComponentInstanceTypeId, set: &Set, ) -> bool { // Instances must recursively have all referenced types named. let ty = &types[id]; ty.exports.values().all(|ty| match ty { ComponentEntityType::Module(_) => true, ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set), ComponentEntityType::Type { created: id, .. } => { self.all_valtypes_named(types, *id, set) } ComponentEntityType::Value(ComponentValType::Type(id)) => { self.all_valtypes_named_in_defined(types, *id, set) } ComponentEntityType::Instance(id) => { self.all_valtypes_named_in_instance(types, *id, set) } ComponentEntityType::Component(_) | ComponentEntityType::Value(ComponentValType::Primitive(_)) => return true, }) } fn all_valtypes_named_in_defined( &self, types: &TypeAlloc, id: ComponentDefinedTypeId, set: &Set, ) -> bool { let ty = &types[id]; match ty { // These types do not contain anything which must be // named. ComponentDefinedType::Primitive(_) | ComponentDefinedType::Flags(_) | ComponentDefinedType::Enum(_) => true, // Referenced types of all these aggregates must all be // named. ComponentDefinedType::Record(r) => { r.fields.values().all(|t| types.type_named_valtype(t, set)) } ComponentDefinedType::Tuple(r) => { r.types.iter().all(|t| types.type_named_valtype(t, set)) } ComponentDefinedType::Variant(r) => r .cases .values() .filter_map(|t| t.ty.as_ref()) .all(|t| types.type_named_valtype(t, set)), ComponentDefinedType::Result { ok, err } => { ok.as_ref() .map(|t| types.type_named_valtype(t, set)) .unwrap_or(true) && err .as_ref() .map(|t| types.type_named_valtype(t, set)) .unwrap_or(true) } ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => { types.type_named_valtype(ty, set) } // The resource referred to by own/borrow must be named. ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => { set.contains(&ComponentAnyTypeId::from(*id)) } } } fn all_valtypes_named_in_func( &self, types: &TypeAlloc, id: ComponentFuncTypeId, set: &Set, ) -> bool { let ty = &types[id]; // Function types must have all their parameters/results named. ty.params .iter() .map(|(_, ty)| ty) .chain(ty.results.iter().map(|(_, ty)| ty)) .all(|ty| types.type_named_valtype(ty, set)) } /// Updates the type `id` specified, an identifier for a component instance /// type, to be imported into this component. /// /// Importing an instance type into a component specially handles the /// defined resources registered in the instance type. Notably all /// defined resources are "freshened" into brand new type variables and /// these new variables are substituted within the type. This is what /// creates a new `TypeId` and may update the `id` specified. /// /// One side effect of this operation, for example, is that if an instance /// type is used twice to import two different instances then the instances /// do not share resource types despite sharing the same original instance /// type. fn prepare_instance_import(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) { let ty = &types[*id]; // No special treatment for imports of instances which themselves have // no defined resources if ty.defined_resources.is_empty() { return; } let mut new_ty = ComponentInstanceType { // Copied from the input verbatim info: ty.info, // Copied over as temporary storage for now, and both of these are // filled out and expanded below. exports: ty.exports.clone(), explicit_resources: ty.explicit_resources.clone(), // Explicitly discard this field since the // defined resources are lifted into `self` defined_resources: Default::default(), }; // Create brand new resources for all defined ones in the instance. let resources = (0..ty.defined_resources.len()) .map(|_| types.alloc_resource_id()) .collect::>(); // Build a map from the defined resources in `ty` to those in `new_ty`. // // As part of this same loop the new resources, which were previously // defined in `ty`, now become imported variables in `self`. Their // path for where they're imported is updated as well with // `self.next_import_index` as the import-to-be soon. let mut mapping = Remapping::default(); let ty = &types[*id]; for (old, new) in ty.defined_resources.iter().zip(&resources) { let prev = mapping.resources.insert(*old, new.resource()); assert!(prev.is_none()); let mut base = vec![self.imports.len()]; base.extend(ty.explicit_resources[old].iter().copied()); self.imported_resources.insert(new.resource(), base); } // Using the old-to-new resource mapping perform a substitution on // the `exports` and `explicit_resources` fields of `new_ty` for ty in new_ty.exports.values_mut() { types.remap_component_entity(ty, &mut mapping); } for (id, path) in mem::take(&mut new_ty.explicit_resources) { let id = *mapping.resources.get(&id).unwrap_or(&id); new_ty.explicit_resources.insert(id, path); } // Now that `new_ty` is complete finish its registration and then // update `id` on the way out. *id = types.push_ty(new_ty); } /// Prepares an instance type, pointed to `id`, for being exported as a /// concrete instance from `self`. /// /// This will internally perform any resource "freshening" as required and /// then additionally update metadata within `self` about resources being /// exported or defined. fn prepare_instance_export(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) { // Exports of an instance mean that the enclosing context // is inheriting the resources that the instance // encapsulates. This means that the instance type // recorded for this export will itself have no // defined resources. let ty = &types[*id]; // Check to see if `defined_resources` is non-empty, and if so then // "freshen" all the resources and inherit them to our own defined // resources, updating `id` in the process. // // Note though that this specifically is not rewriting the resources of // exported instances. The `defined_resources` set on instance types is // a little subtle (see its documentation for more info), but the // general idea is that for a concrete instance it's always empty. Only // for instance type definitions does it ever have elements in it. // // That means that if this set is non-empty then what's happening is // that we're in a type context an exporting an instance of a previously // specified type. In this case all resources are required to be // "freshened" to ensure that multiple exports of the same type all // export different types of resources. // // And finally note that this operation empties out the // `defined_resources` set of the type that is registered for the // instance, as this export is modeled as producing a concrete instance. if !ty.defined_resources.is_empty() { let mut new_ty = ty.clone(); let mut mapping = Remapping::default(); for old in mem::take(&mut new_ty.defined_resources) { let new = types.alloc_resource_id(); mapping.resources.insert(old, new.resource()); self.defined_resources.insert(new.resource(), None); } for ty in new_ty.exports.values_mut() { types.remap_component_entity(ty, &mut mapping); } for (id, path) in mem::take(&mut new_ty.explicit_resources) { let id = mapping.resources.get(&id).copied().unwrap_or(id); new_ty.explicit_resources.insert(id, path); } *id = types.push_ty(new_ty); } // Any explicit resources in the instance are now additionally explicit // in this component since it's exported. // // The path to each explicit resources gets one element prepended which // is `self.next_export_index`, the index of the export about to be // generated. let ty = &types[*id]; for (id, path) in ty.explicit_resources.iter() { let mut new_path = vec![self.exports.len()]; new_path.extend(path); self.explicit_resources.insert(*id, new_path); } } pub fn add_export( &mut self, name: ComponentExportName<'_>, mut ty: ComponentEntityType, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, check_limit: bool, ) -> Result<()> { if check_limit { check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?; } self.add_entity( &mut ty, Some((name.0, ExternKind::Export)), features, types, offset, )?; self.toplevel_exported_resources.validate_extern( name.0, ExternKind::Export, &ty, types, offset, &mut self.export_names, &mut self.exports, &mut self.type_info, features, )?; Ok(()) } pub fn lift_function( &mut self, core_func_index: u32, type_index: u32, options: Vec, types: &TypeList, offset: usize, ) -> Result<()> { let ty = self.function_type_at(type_index, types, offset)?; let core_ty = types[self.core_function_at(core_func_index, offset)?].unwrap_func(); // Lifting a function is for an export, so match the expected canonical ABI // export signature let info = ty.lower(types, false); self.check_options(Some(core_ty), &info, &options, types, offset)?; if core_ty.params() != info.params.as_slice() { bail!( offset, "lowered parameter types `{:?}` do not match parameter types \ `{:?}` of core function {core_func_index}", info.params.as_slice(), core_ty.params(), ); } if core_ty.results() != info.results.as_slice() { bail!( offset, "lowered result types `{:?}` do not match result types \ `{:?}` of core function {core_func_index}", info.results.as_slice(), core_ty.results() ); } self.funcs .push(self.types[type_index as usize].unwrap_func()); Ok(()) } pub fn lower_function( &mut self, func_index: u32, options: Vec, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let ty = &types[self.function_at(func_index, offset)?]; // Lowering a function is for an import, so use a function type that matches // the expected canonical ABI import signature. let info = ty.lower(types, true); self.check_options(None, &info, &options, types, offset)?; let composite_type = CompositeType { inner: CompositeInnerType::Func(info.into_func_type()), shared: false, }; let lowered_ty = SubType { is_final: true, supertype_idx: None, composite_type, }; let (_is_new, group_id) = types.intern_canonical_rec_group(RecGroup::implicit(offset, lowered_ty)); let id = types[group_id].start; self.core_funcs.push(id); Ok(()) } pub fn resource_new( &mut self, resource: u32, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let rep = self.check_local_resource(resource, types, offset)?; let composite_type = CompositeType { inner: CompositeInnerType::Func(FuncType::new([rep], [ValType::I32])), shared: false, }; let core_ty = SubType { is_final: true, supertype_idx: None, composite_type, }; let (_is_new, group_id) = types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty)); let id = types[group_id].start; self.core_funcs.push(id); Ok(()) } pub fn resource_drop( &mut self, resource: u32, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { self.resource_at(resource, types, offset)?; let composite_type = CompositeType { inner: CompositeInnerType::Func(FuncType::new([ValType::I32], [])), shared: false, }; let core_ty = SubType { is_final: true, supertype_idx: None, composite_type, }; let (_is_new, group_id) = types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty)); let id = types[group_id].start; self.core_funcs.push(id); Ok(()) } pub fn resource_rep( &mut self, resource: u32, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let rep = self.check_local_resource(resource, types, offset)?; let composite_type = CompositeType { inner: CompositeInnerType::Func(FuncType::new([ValType::I32], [rep])), shared: false, }; let core_ty = SubType { is_final: true, supertype_idx: None, composite_type, }; let (_is_new, group_id) = types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty)); let id = types[group_id].start; self.core_funcs.push(id); Ok(()) } fn check_local_resource(&self, idx: u32, types: &TypeList, offset: usize) -> Result { let resource = self.resource_at(idx, types, offset)?; match self .defined_resources .get(&resource.resource()) .and_then(|rep| *rep) { Some(ty) => Ok(ty), None => bail!(offset, "type {idx} is not a local resource"), } } fn resource_at<'a>( &self, idx: u32, _types: &'a TypeList, offset: usize, ) -> Result { if let ComponentAnyTypeId::Resource(id) = self.component_type_at(idx, offset)? { return Ok(id); } bail!(offset, "type index {} is not a resource type", idx) } pub fn add_component(&mut self, component: ComponentType, types: &mut TypeAlloc) -> Result<()> { let id = types.push_ty(component); self.components.push(id); Ok(()) } pub fn add_instance( &mut self, instance: crate::ComponentInstance, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let instance = match instance { crate::ComponentInstance::Instantiate { component_index, args, } => self.instantiate_component( component_index, args.into_vec(), features, types, offset, )?, crate::ComponentInstance::FromExports(exports) => { self.instantiate_component_exports(exports.into_vec(), features, types, offset)? } }; self.instances.push(instance); Ok(()) } pub fn add_alias( components: &mut [Self], alias: crate::ComponentAlias, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { match alias { crate::ComponentAlias::InstanceExport { instance_index, kind, name, } => components.last_mut().unwrap().alias_instance_export( instance_index, kind, name, features, types, offset, ), crate::ComponentAlias::CoreInstanceExport { instance_index, kind, name, } => components.last_mut().unwrap().alias_core_instance_export( instance_index, kind, name, types, offset, ), crate::ComponentAlias::Outer { kind, count, index } => match kind { ComponentOuterAliasKind::CoreModule => { Self::alias_module(components, count, index, offset) } ComponentOuterAliasKind::CoreType => { Self::alias_core_type(components, count, index, offset) } ComponentOuterAliasKind::Type => { Self::alias_type(components, count, index, types, offset) } ComponentOuterAliasKind::Component => { Self::alias_component(components, count, index, offset) } }, } } pub fn add_start( &mut self, func_index: u32, args: &[u32], results: u32, features: &WasmFeatures, types: &mut TypeList, offset: usize, ) -> Result<()> { if !features.component_model_values() { bail!( offset, "support for component model `value`s is not enabled" ); } if self.has_start { return Err(BinaryReaderError::new( "component cannot have more than one start function", offset, )); } let ft = &types[self.function_at(func_index, offset)?]; if ft.params.len() != args.len() { bail!( offset, "component start function requires {} arguments but was given {}", ft.params.len(), args.len() ); } if ft.results.len() as u32 != results { bail!( offset, "component start function has a result count of {results} \ but the function type has a result count of {type_results}", type_results = ft.results.len(), ); } let cx = SubtypeCx::new(types, types); for (i, ((_, ty), arg)) in ft.params.iter().zip(args).enumerate() { // Ensure the value's type is a subtype of the parameter type cx.component_val_type(self.value_at(*arg, offset)?, ty, offset) .with_context(|| { format!("value type mismatch for component start function argument {i}") })?; } for (_, ty) in ft.results.iter() { self.values.push((*ty, false)); } self.has_start = true; Ok(()) } fn check_options( &self, core_ty: Option<&FuncType>, info: &LoweringInfo, options: &[CanonicalOption], types: &TypeList, offset: usize, ) -> Result<()> { fn display(option: CanonicalOption) -> &'static str { match option { CanonicalOption::UTF8 => "utf8", CanonicalOption::UTF16 => "utf16", CanonicalOption::CompactUTF16 => "latin1-utf16", CanonicalOption::Memory(_) => "memory", CanonicalOption::Realloc(_) => "realloc", CanonicalOption::PostReturn(_) => "post-return", } } let mut encoding = None; let mut memory = None; let mut realloc = None; let mut post_return = None; for option in options { match option { CanonicalOption::UTF8 | CanonicalOption::UTF16 | CanonicalOption::CompactUTF16 => { match encoding { Some(existing) => { bail!( offset, "canonical encoding option `{}` conflicts with option `{}`", display(existing), display(*option), ) } None => encoding = Some(*option), } } CanonicalOption::Memory(idx) => { memory = match memory { None => { self.memory_at(*idx, offset)?; Some(*idx) } Some(_) => { return Err(BinaryReaderError::new( "canonical option `memory` is specified more than once", offset, )) } } } CanonicalOption::Realloc(idx) => { realloc = match realloc { None => { let ty = types[self.core_function_at(*idx, offset)?].unwrap_func(); if ty.params() != [ValType::I32, ValType::I32, ValType::I32, ValType::I32] || ty.results() != [ValType::I32] { return Err(BinaryReaderError::new( "canonical option `realloc` uses a core function with an incorrect signature", offset, )); } Some(*idx) } Some(_) => { return Err(BinaryReaderError::new( "canonical option `realloc` is specified more than once", offset, )) } } } CanonicalOption::PostReturn(idx) => { post_return = match post_return { None => { let core_ty = core_ty.ok_or_else(|| { BinaryReaderError::new( "canonical option `post-return` cannot be specified for lowerings", offset, ) })?; let ty = types[self.core_function_at(*idx, offset)?].unwrap_func(); if ty.params() != core_ty.results() || !ty.results().is_empty() { return Err(BinaryReaderError::new( "canonical option `post-return` uses a core function with an incorrect signature", offset, )); } Some(*idx) } Some(_) => { return Err(BinaryReaderError::new( "canonical option `post-return` is specified more than once", offset, )) } } } } } if info.requires_memory && memory.is_none() { return Err(BinaryReaderError::new( "canonical option `memory` is required", offset, )); } if info.requires_realloc && realloc.is_none() { return Err(BinaryReaderError::new( "canonical option `realloc` is required", offset, )); } Ok(()) } fn check_type_ref( &mut self, ty: &ComponentTypeRef, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result { Ok(match ty { ComponentTypeRef::Module(index) => { let id = self.core_type_at(*index, offset)?; match id { ComponentCoreTypeId::Sub(_) => { bail!(offset, "core type index {index} is not a module type") } ComponentCoreTypeId::Module(id) => ComponentEntityType::Module(id), } } ComponentTypeRef::Func(index) => { let id = self.component_type_at(*index, offset)?; match id { ComponentAnyTypeId::Func(id) => ComponentEntityType::Func(id), _ => bail!(offset, "type index {index} is not a function type"), } } ComponentTypeRef::Value(ty) => { self.check_value_support(features, offset)?; let ty = match ty { crate::ComponentValType::Primitive(ty) => ComponentValType::Primitive(*ty), crate::ComponentValType::Type(index) => { ComponentValType::Type(self.defined_type_at(*index, offset)?) } }; ComponentEntityType::Value(ty) } ComponentTypeRef::Type(TypeBounds::Eq(index)) => { let referenced = self.component_type_at(*index, offset)?; let created = types.with_unique(referenced); ComponentEntityType::Type { referenced, created, } } ComponentTypeRef::Type(TypeBounds::SubResource) => { let id = types.alloc_resource_id(); ComponentEntityType::Type { referenced: id.into(), created: id.into(), } } ComponentTypeRef::Instance(index) => { let id = self.component_type_at(*index, offset)?; match id { ComponentAnyTypeId::Instance(id) => ComponentEntityType::Instance(id), _ => bail!(offset, "type index {index} is not an instance type"), } } ComponentTypeRef::Component(index) => { let id = self.component_type_at(*index, offset)?; match id { ComponentAnyTypeId::Component(id) => ComponentEntityType::Component(id), _ => bail!(offset, "type index {index} is not a component type"), } } }) } pub fn export_to_entity_type( &mut self, export: &crate::ComponentExport, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result { let actual = match export.kind { ComponentExternalKind::Module => { ComponentEntityType::Module(self.module_at(export.index, offset)?) } ComponentExternalKind::Func => { ComponentEntityType::Func(self.function_at(export.index, offset)?) } ComponentExternalKind::Value => { self.check_value_support(features, offset)?; ComponentEntityType::Value(*self.value_at(export.index, offset)?) } ComponentExternalKind::Type => { let referenced = self.component_type_at(export.index, offset)?; let created = types.with_unique(referenced); ComponentEntityType::Type { referenced, created, } } ComponentExternalKind::Instance => { ComponentEntityType::Instance(self.instance_at(export.index, offset)?) } ComponentExternalKind::Component => { ComponentEntityType::Component(self.component_at(export.index, offset)?) } }; let ascribed = match &export.ty { Some(ty) => self.check_type_ref(ty, features, types, offset)?, None => return Ok(actual), }; SubtypeCx::new(types, types) .component_entity_type(&actual, &ascribed, offset) .with_context(|| "ascribed type of export is not compatible with item's type")?; Ok(ascribed) } fn create_module_type( components: &[Self], decls: Vec, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result { let mut state = Module::default(); for decl in decls { match decl { crate::ModuleTypeDeclaration::Type(ty) => { state.add_types( RecGroup::implicit(offset, ty), features, types, offset, true, )?; } crate::ModuleTypeDeclaration::Export { name, mut ty } => { let ty = state.check_type_ref(&mut ty, features, types, offset)?; state.add_export(name, ty, features, offset, true, types)?; } crate::ModuleTypeDeclaration::OuterAlias { kind, count, index } => { match kind { crate::OuterAliasKind::Type => { let ty = if count == 0 { // Local alias, check the local module state ComponentCoreTypeId::Sub(state.type_id_at(index, offset)?) } else { // Otherwise, check the enclosing component state let component = Self::check_alias_count(components, count - 1, offset)?; component.core_type_at(index, offset)? }; check_max(state.types.len(), 1, MAX_WASM_TYPES, "types", offset)?; match ty { ComponentCoreTypeId::Sub(ty) => state.types.push(ty), // TODO https://github.com/WebAssembly/component-model/issues/265 ComponentCoreTypeId::Module(_) => bail!( offset, "not implemented: aliasing core module types into a core \ module's types index space" ), } } } } crate::ModuleTypeDeclaration::Import(import) => { state.add_import(import, features, types, offset)?; } } } let imports = state.imports_for_module_type(offset)?; Ok(ModuleType { info: TypeInfo::core(state.type_size), imports, exports: state.exports, }) } fn create_component_type( components: &mut Vec, decls: Vec, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result { components.push(ComponentState::new(ComponentKind::ComponentType)); for decl in decls { match decl { crate::ComponentTypeDeclaration::CoreType(ty) => { Self::add_core_type(components, ty, features, types, offset, true)?; } crate::ComponentTypeDeclaration::Type(ty) => { Self::add_type(components, ty, features, types, offset, true)?; } crate::ComponentTypeDeclaration::Export { name, ty } => { let current = components.last_mut().unwrap(); let ty = current.check_type_ref(&ty, features, types, offset)?; current.add_export(name, ty, features, types, offset, true)?; } crate::ComponentTypeDeclaration::Import(import) => { components .last_mut() .unwrap() .add_import(import, features, types, offset)?; } crate::ComponentTypeDeclaration::Alias(alias) => { Self::add_alias(components, alias, features, types, offset)?; } }; } components.pop().unwrap().finish(types, offset) } fn create_instance_type( components: &mut Vec, decls: Vec, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result { components.push(ComponentState::new(ComponentKind::InstanceType)); for decl in decls { match decl { crate::InstanceTypeDeclaration::CoreType(ty) => { Self::add_core_type(components, ty, features, types, offset, true)?; } crate::InstanceTypeDeclaration::Type(ty) => { Self::add_type(components, ty, features, types, offset, true)?; } crate::InstanceTypeDeclaration::Export { name, ty } => { let current = components.last_mut().unwrap(); let ty = current.check_type_ref(&ty, features, types, offset)?; current.add_export(name, ty, features, types, offset, true)?; } crate::InstanceTypeDeclaration::Alias(alias) => { Self::add_alias(components, alias, features, types, offset)?; } }; } let mut state = components.pop().unwrap(); assert!(state.imported_resources.is_empty()); Ok(ComponentInstanceType { info: state.type_info, // The defined resources for this instance type are those listed on // the component state. The path to each defined resource is // guaranteed to live within the `explicit_resources` map since, // when in the type context, the introduction of any defined // resource must have been done with `(export "x" (type (sub // resource)))` which, in a sense, "fuses" the introduction of the // variable with the export. This means that all defined resources, // if any, should be guaranteed to have an `explicit_resources` path // listed. defined_resources: mem::take(&mut state.defined_resources) .into_iter() .map(|(id, rep)| { assert!(rep.is_none()); id }) .collect(), // The map of what resources are explicitly exported and where // they're exported is plumbed through as-is. explicit_resources: mem::take(&mut state.explicit_resources), exports: mem::take(&mut state.exports), }) } fn create_function_type( &self, ty: crate::ComponentFuncType, types: &TypeList, features: &WasmFeatures, offset: usize, ) -> Result { let mut info = TypeInfo::new(); if ty.results.type_count() > 1 && !features.component_model_multiple_returns() { bail!( offset, "multiple returns on a function is now a gated feature \ -- https://github.com/WebAssembly/component-model/pull/368" ); } let mut set = Set::default(); #[cfg(not(feature = "no-hash-maps"))] // TODO: remove when unified map type is available set.reserve(core::cmp::max(ty.params.len(), ty.results.type_count())); let params = ty .params .iter() .map(|(name, ty)| { let name: &KebabStr = to_kebab_str(name, "function parameter", offset)?; if !set.insert(name) { bail!( offset, "function parameter name `{name}` conflicts with previous parameter name `{prev}`", prev = set.get(&name).unwrap(), ); } let ty = self.create_component_val_type(*ty, offset)?; info.combine(ty.info(types), offset)?; Ok((name.to_owned(), ty)) }) .collect::>()?; set.clear(); let results = ty .results .iter() .map(|(name, ty)| { let name = name .map(|name| { let name = to_kebab_str(name, "function result", offset)?; if !set.insert(name) { bail!( offset, "function result name `{name}` conflicts with previous result name `{prev}`", prev = set.get(name).unwrap(), ); } Ok(name.to_owned()) }) .transpose()?; let ty = self.create_component_val_type(*ty, offset)?; let ty_info = ty.info(types); if ty_info.contains_borrow() { bail!(offset, "function result cannot contain a `borrow` type"); } info.combine(ty.info(types), offset)?; Ok((name, ty)) }) .collect::>()?; Ok(ComponentFuncType { info, params, results, }) } fn instantiate_core_module( &self, module_index: u32, module_args: Vec, types: &mut TypeAlloc, offset: usize, ) -> Result { fn insert_arg<'a>( name: &'a str, arg: &'a InstanceType, args: &mut IndexMap<&'a str, &'a InstanceType>, offset: usize, ) -> Result<()> { if args.insert(name, arg).is_some() { bail!( offset, "duplicate module instantiation argument named `{name}`" ); } Ok(()) } let module_type_id = self.module_at(module_index, offset)?; let mut args = IndexMap::default(); // Populate the arguments for module_arg in module_args { match module_arg.kind { InstantiationArgKind::Instance => { let instance_type = &types[self.core_instance_at(module_arg.index, offset)?]; insert_arg(module_arg.name, instance_type, &mut args, offset)?; } } } // Validate the arguments let module_type = &types[module_type_id]; let cx = SubtypeCx::new(types, types); for ((module, name), expected) in module_type.imports.iter() { let instance = args.get(module.as_str()).ok_or_else(|| { format_err!( offset, "missing module instantiation argument named `{module}`" ) })?; let arg = instance .internal_exports(types) .get(name.as_str()) .ok_or_else(|| { format_err!( offset, "module instantiation argument `{module}` does not \ export an item named `{name}`", ) })?; cx.entity_type(arg, expected, offset).with_context(|| { format!( "type mismatch for export `{name}` of module \ instantiation argument `{module}`" ) })?; } let mut info = TypeInfo::new(); for (_, ty) in module_type.exports.iter() { info.combine(ty.info(types), offset)?; } Ok(types.push_ty(InstanceType { info, kind: CoreInstanceTypeKind::Instantiated(module_type_id), })) } fn instantiate_component( &mut self, component_index: u32, component_args: Vec, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result { let component_type_id = self.component_at(component_index, offset)?; let mut args = IndexMap::default(); // Populate the arguments for component_arg in component_args { let ty = match component_arg.kind { ComponentExternalKind::Module => { ComponentEntityType::Module(self.module_at(component_arg.index, offset)?) } ComponentExternalKind::Component => { ComponentEntityType::Component(self.component_at(component_arg.index, offset)?) } ComponentExternalKind::Instance => { ComponentEntityType::Instance(self.instance_at(component_arg.index, offset)?) } ComponentExternalKind::Func => { ComponentEntityType::Func(self.function_at(component_arg.index, offset)?) } ComponentExternalKind::Value => { self.check_value_support(features, offset)?; ComponentEntityType::Value(*self.value_at(component_arg.index, offset)?) } ComponentExternalKind::Type => { let ty = self.component_type_at(component_arg.index, offset)?; ComponentEntityType::Type { referenced: ty, created: ty, } } }; match args.entry(component_arg.name.to_string()) { Entry::Occupied(e) => { bail!( offset, "instantiation argument `{name}` conflicts with previous argument `{prev}`", prev = e.key(), name = component_arg.name ); } Entry::Vacant(e) => { e.insert(ty); } } } // Here comes the fun part of the component model, we're instantiating // the component with type `component_type_id` with the `args` // specified. Easy enough! // // This operation, however, is one of the lynchpins of safety in the // component model. Additionally what this ends up implementing ranges // from "well just check the types are equal" to "let's have a // full-blown ML-style module type system in the component model". There // are primarily two major tricky pieces to the component model which // make this operation, instantiating components, hard: // // 1. Components can import and exports other components. This means // that arguments to instantiation are along the lines of functions // being passed to functions or similar. Effectively this means that // the term "variance" comes into play with either contravariance // or covariance depending on where you are in typechecking. This is // one of the main rationales, however, that this check below is a // check for subtyping as opposed to exact type equivalence. For // example an instance that exports something is a subtype of an // instance that exports nothing. Components get a bit trick since // they both have imports and exports. My way of thinking about it // is "who's asking for what". If you're asking for imports then // I need to at least supply those imports, but I can possibly // supply more. If you're asking for a thing which you'll give a set // of imports, then I can give you something which takes less imports // because what you give still suffices. (things like that). The // real complication with components, however, comes with... // // 2. Resources. Resources in the component model are akin to "abstract // types". They're not abstract in the sense that they have no // representation, they're always backed by a 32-bit integer right // now. Instead they're abstract in the sense that some components // aren't allowed to understand the representation of a resource. // For example if you import a resource you can't get the underlying // internals of it. Furthermore the resource is strictly tracked // within the component with `own` and `borrow` runtime semantics. // The hardest part about resources, though, is handling them as // part of instantiation and subtyping. // // For example one major aspect of resources is that if a component // exports a resource then each instantiation of the component // produces a fresh resource type. This means that the type recorded // for the instantiation here can't simply be "I instantiated // component X" since in such a situation the type of all // instantiations would be the same, which they aren't. // // This sort of subtelty comes up quite frequently for resources. // This file contains references to `imported_resources` and // `defined_resources` for example which refer to the formal // nature of components and their abstract variables. Specifically // for instantiation though we're eventually faced with the problem // of subtype checks where resource subtyping is defined as "does // your id equal mine". Naively implemented that means anything with // resources isn't subtypes of anything else since resource ids are // unique between components. Instead what actually needs to happen // is types need to be substituted. // // Much of the complexity here is not actually apparent here in this // literal one function. Instead it's spread out across validation // in this file and type-checking in the `types.rs` module. Note that // the "spread out" nature isn't because we're bad maintainers // (hopefully), but rather it's quite infectious how many parts need // to handle resources and account for defined/imported variables. // // For example only one subtyping method is called here where `args` is // passed in. This method is quite recursive in its nature though and // will internally touch all the fields that this file maintains to // end up putting into various bits and pieces of type information. // // Unfortunately there's probably not really a succinct way to read // this method and understand everything. If you've written ML module // type systems this will probably look quite familiar, but otherwise // the whole system is not really easily approachable at this time. It's // hoped in the future that there's a formalism to refer to which will // make things more clear as the code would be able to reference this // hypothetical formalism. Until that's the case, though, these // comments are hopefully enough when augmented with communication with // the authors. let component_type = &types[component_type_id]; let mut exports = component_type.exports.clone(); let mut info = TypeInfo::new(); for (_, ty) in component_type.exports.iter() { info.combine(ty.info(types), offset)?; } // Perform the subtype check that `args` matches the imports of // `component_type_id`. The result of this subtype check is the // production of a mapping of resource types from the imports to the // arguments provided. This is a substitution map which is then used // below to perform a substitution into the exports of the instance // since the types of the exports are now in terms of whatever was // supplied as imports. let mut mapping = SubtypeCx::new(types, types).open_instance_type( &args, component_type_id, ExternKind::Import, offset, )?; // Part of the instantiation of a component is that all of its // defined resources become "fresh" on each instantiation. This // means that each instantiation of a component gets brand new type // variables representing its defined resources, modeling that each // instantiation produces distinct types. The freshening is performed // here by allocating new ids and inserting them into `mapping`. // // Note that technically the `mapping` from subtyping should be applied // first and then the mapping for freshening should be applied // afterwards. The keys of the map from subtyping are the imported // resources from this component which are disjoint from its defined // resources. That means it should be possible to place everything // into one large map which maps from: // // * the component's imported resources go to whatever was explicitly // supplied in the import map // * the component's defined resources go to fresh new resources // // These two remapping operations can then get folded into one by // placing everything in the same `mapping` and using that for a remap // only once. let fresh_defined_resources = (0..component_type.defined_resources.len()) .map(|_| types.alloc_resource_id().resource()) .collect::>(); let component_type = &types[component_type_id]; for ((old, _path), new) in component_type .defined_resources .iter() .zip(&fresh_defined_resources) { let prev = mapping.resources.insert(*old, *new); assert!(prev.is_none()); } // Perform the remapping operation over all the exports that will be // listed for the final instance type. Note that this is performed // both for all the export types in addition to the explicitly exported // resources list. // // Note that this is a crucial step of the instantiation process which // is intentionally transforming the type of a component based on the // variables provided by imports and additionally ensuring that all // references to the component's defined resources are rebound to the // fresh ones introduced just above. for entity in exports.values_mut() { types.remap_component_entity(entity, &mut mapping); } let component_type = &types[component_type_id]; let explicit_resources = component_type .explicit_resources .iter() .map(|(id, path)| { ( mapping.resources.get(id).copied().unwrap_or(*id), path.clone(), ) }) .collect::>(); // Technically in the last formalism that was consulted in writing this // implementation there are two further steps that are part of the // instantiation process: // // 1. The set of defined resources from the instance created, which are // added to the outer component, is the subset of the instance's // original defined resources and the free variables of the exports. // // 2. Each element of this subset is required to be "explicit in" the // instance, or otherwise explicitly exported somewhere within the // instance. // // With the syntactic structure of the component model, however, neither // of these conditions should be necessary. The main reason for this is // that this function is specifically dealing with instantiation of // components which should already have these properties validated // about them. Subsequently we shouldn't have to re-check them. // // In debug mode, however, do a sanity check. if cfg!(debug_assertions) { let mut free = IndexSet::default(); for ty in exports.values() { types.free_variables_component_entity(ty, &mut free); } assert!(fresh_defined_resources.is_subset(&free)); for resource in fresh_defined_resources.iter() { assert!(explicit_resources.contains_key(resource)); } } // And as the final step of the instantiation process all of the // new defined resources from this component instantiation are moved // onto `self`. Note that concrete instances never have defined // resources (see more comments in `instantiate_exports`) so the // `defined_resources` listing in the final type is always empty. This // represents how by having a concrete instance the definitions // referred to in that instance are now problems for the outer // component rather than the inner instance since the instance is bound // to the component. // // All defined resources here have no known representation, so they're // all listed with `None`. Also note that none of the resources were // exported yet so `self.explicit_resources` is not updated yet. If // this instance is exported, however, it'll consult the type's // `explicit_resources` array and use that appropriately. for resource in fresh_defined_resources { self.defined_resources.insert(resource, None); } Ok(types.push_ty(ComponentInstanceType { info, defined_resources: Default::default(), explicit_resources, exports, })) } fn instantiate_component_exports( &mut self, exports: Vec, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result { let mut info = TypeInfo::new(); let mut inst_exports = IndexMap::default(); let mut explicit_resources = IndexMap::default(); let mut export_names = IndexSet::default(); // NB: It's intentional that this context is empty since no indices are // introduced in the bag-of-exports construct which means there's no // way syntactically to register something inside of this. let names = ComponentNameContext::default(); for export in exports { assert!(export.ty.is_none()); let ty = match export.kind { ComponentExternalKind::Module => { ComponentEntityType::Module(self.module_at(export.index, offset)?) } ComponentExternalKind::Component => { ComponentEntityType::Component(self.component_at(export.index, offset)?) } ComponentExternalKind::Instance => { let ty = self.instance_at(export.index, offset)?; // When an instance is exported from an instance then // all explicitly exported resources on the sub-instance are // now also listed as exported resources on the outer // instance, just with one more element in their path. explicit_resources.extend(types[ty].explicit_resources.iter().map( |(id, path)| { let mut new_path = vec![inst_exports.len()]; new_path.extend(path); (*id, new_path) }, )); ComponentEntityType::Instance(ty) } ComponentExternalKind::Func => { ComponentEntityType::Func(self.function_at(export.index, offset)?) } ComponentExternalKind::Value => { self.check_value_support(features, offset)?; ComponentEntityType::Value(*self.value_at(export.index, offset)?) } ComponentExternalKind::Type => { let ty = self.component_type_at(export.index, offset)?; // If this is an export of a resource type be sure to // record that in the explicit list with the appropriate // path because if this instance ends up getting used // it'll count towards the "explicit in" check. if let ComponentAnyTypeId::Resource(id) = ty { explicit_resources.insert(id.resource(), vec![inst_exports.len()]); } ComponentEntityType::Type { referenced: ty, // The created type index here isn't used anywhere // in index spaces because a "bag of exports" // doesn't build up its own index spaces. Just fill // in the same index here in this case as what's // referenced. created: ty, } } }; names.validate_extern( export.name.0, ExternKind::Export, &ty, types, offset, &mut export_names, &mut inst_exports, &mut info, features, )?; } Ok(types.push_ty(ComponentInstanceType { info, explicit_resources, exports: inst_exports, // NB: the list of defined resources for this instance itself // is always empty. Even if this instance exports resources, // it's empty. // // The reason for this is a bit subtle. The general idea, though, is // that the defined resources list here is only used for instance // types that are sort of "floating around" and haven't actually // been attached to something yet. For example when an instance type // is simply declared it can have defined resources introduced // through `(export "name" (type (sub resource)))`. These // definitions, however, are local to the instance itself and aren't // defined elsewhere. // // Here, though, no new definitions were introduced. The instance // created here is a "bag of exports" which could only refer to // preexisting items. This means that inherently no new resources // were created so there's nothing to put in this list. Any // resources referenced by the instance must be bound by the outer // component context or further above. // // Furthermore, however, actual instances of instances, which this // is, aren't allowed to have defined resources. Instead the // resources would have to be injected into the outer component // enclosing the instance. That means that even if bag-of-exports // could declare a new resource then the resource would be moved // from here to `self.defined_resources`. This doesn't exist at this // time, though, so this still remains empty and // `self.defined_resources` remains unperturbed. defined_resources: Default::default(), })) } fn instantiate_core_exports( &mut self, exports: Vec, types: &mut TypeAlloc, offset: usize, ) -> Result { fn insert_export( types: &TypeList, name: &str, export: EntityType, exports: &mut IndexMap, info: &mut TypeInfo, offset: usize, ) -> Result<()> { info.combine(export.info(types), offset)?; if exports.insert(name.to_string(), export).is_some() { bail!( offset, "duplicate instantiation export name `{name}` already defined", ) } Ok(()) } let mut info = TypeInfo::new(); let mut inst_exports = IndexMap::default(); for export in exports { match export.kind { ExternalKind::Func => { insert_export( types, export.name, EntityType::Func(self.core_function_at(export.index, offset)?), &mut inst_exports, &mut info, offset, )?; } ExternalKind::Table => insert_export( types, export.name, EntityType::Table(*self.table_at(export.index, offset)?), &mut inst_exports, &mut info, offset, )?, ExternalKind::Memory => insert_export( types, export.name, EntityType::Memory(*self.memory_at(export.index, offset)?), &mut inst_exports, &mut info, offset, )?, ExternalKind::Global => { insert_export( types, export.name, EntityType::Global(*self.global_at(export.index, offset)?), &mut inst_exports, &mut info, offset, )?; } ExternalKind::Tag => insert_export( types, export.name, EntityType::Tag(self.core_function_at(export.index, offset)?), &mut inst_exports, &mut info, offset, )?, } } Ok(types.push_ty(InstanceType { info, kind: CoreInstanceTypeKind::Exports(inst_exports), })) } fn alias_core_instance_export( &mut self, instance_index: u32, kind: ExternalKind, name: &str, types: &TypeList, offset: usize, ) -> Result<()> { macro_rules! push_module_export { ($expected:path, $collection:ident, $ty:literal) => {{ match self.core_instance_export(instance_index, name, types, offset)? { $expected(ty) => { self.$collection.push(*ty); Ok(()) } _ => { bail!( offset, "export `{name}` for core instance {instance_index} is not a {}", $ty ) } } }}; } match kind { ExternalKind::Func => { check_max( self.function_count(), 1, MAX_WASM_FUNCTIONS, "functions", offset, )?; push_module_export!(EntityType::Func, core_funcs, "function") } ExternalKind::Table => { check_max( self.core_tables.len(), 1, MAX_CORE_INDEX_SPACE_ITEMS, "tables", offset, )?; push_module_export!(EntityType::Table, core_tables, "table") } ExternalKind::Memory => { check_max( self.core_memories.len(), 1, MAX_CORE_INDEX_SPACE_ITEMS, "memories", offset, )?; push_module_export!(EntityType::Memory, core_memories, "memory") } ExternalKind::Global => { check_max( self.core_globals.len(), 1, MAX_CORE_INDEX_SPACE_ITEMS, "globals", offset, )?; push_module_export!(EntityType::Global, core_globals, "global") } ExternalKind::Tag => { check_max( self.core_tags.len(), 1, MAX_CORE_INDEX_SPACE_ITEMS, "tags", offset, )?; push_module_export!(EntityType::Tag, core_tags, "tag") } } } fn alias_instance_export( &mut self, instance_index: u32, kind: ComponentExternalKind, name: &str, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { if let ComponentExternalKind::Value = kind { self.check_value_support(features, offset)?; } let mut ty = match types[self.instance_at(instance_index, offset)?] .exports .get(name) { Some(ty) => *ty, None => bail!( offset, "instance {instance_index} has no export named `{name}`" ), }; let ok = match (&ty, kind) { (ComponentEntityType::Module(_), ComponentExternalKind::Module) => true, (ComponentEntityType::Module(_), _) => false, (ComponentEntityType::Component(_), ComponentExternalKind::Component) => true, (ComponentEntityType::Component(_), _) => false, (ComponentEntityType::Func(_), ComponentExternalKind::Func) => true, (ComponentEntityType::Func(_), _) => false, (ComponentEntityType::Instance(_), ComponentExternalKind::Instance) => true, (ComponentEntityType::Instance(_), _) => false, (ComponentEntityType::Value(_), ComponentExternalKind::Value) => true, (ComponentEntityType::Value(_), _) => false, (ComponentEntityType::Type { .. }, ComponentExternalKind::Type) => true, (ComponentEntityType::Type { .. }, _) => false, }; if !ok { bail!( offset, "export `{name}` for instance {instance_index} is not a {}", kind.desc(), ); } self.add_entity(&mut ty, None, features, types, offset)?; Ok(()) } fn alias_module(components: &mut [Self], count: u32, index: u32, offset: usize) -> Result<()> { let component = Self::check_alias_count(components, count, offset)?; let ty = component.module_at(index, offset)?; let current = components.last_mut().unwrap(); check_max( current.core_modules.len(), 1, MAX_WASM_MODULES, "modules", offset, )?; current.core_modules.push(ty); Ok(()) } fn alias_component( components: &mut [Self], count: u32, index: u32, offset: usize, ) -> Result<()> { let component = Self::check_alias_count(components, count, offset)?; let ty = component.component_at(index, offset)?; let current = components.last_mut().unwrap(); check_max( current.components.len(), 1, MAX_WASM_COMPONENTS, "components", offset, )?; current.components.push(ty); Ok(()) } fn alias_core_type( components: &mut [Self], count: u32, index: u32, offset: usize, ) -> Result<()> { let component = Self::check_alias_count(components, count, offset)?; let ty = component.core_type_at(index, offset)?; let current = components.last_mut().unwrap(); check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; current.core_types.push(ty); Ok(()) } fn alias_type( components: &mut [Self], count: u32, index: u32, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let component = Self::check_alias_count(components, count, offset)?; let ty = component.component_type_at(index, offset)?; // If `count` "crossed a component boundary", meaning that it went from // one component to another, then this must additionally verify that // `ty` has no free variables with respect to resources. This is // intended to preserve the property for components where each component // is an isolated unit that can theoretically be extracted from other // components. If resources from other components were allowed to leak // in then it would prevent that. // // This check is done by calculating the `pos` within `components` that // our target `component` above was selected at. Once this is acquired // the component to the "right" is checked, and if that's a component // then it's considered as crossing a component boundary meaning the // free variables check runs. // // The reason this works is that in the list of `ComponentState` types // it's guaranteed that any `is_type` components are contiguous at the // end of the array. This means that if state one level deeper than the // target of this alias is a `!is_type` component, then the target must // be a component as well. If the one-level deeper state `is_type` then // the target is either a type or a component, both of which are valid // (as aliases can reach the enclosing component and have as many free // variables as they want). let pos_after_component = components.len() - (count as usize); if let Some(component) = components.get(pos_after_component) { if component.kind == ComponentKind::Component { let mut free = IndexSet::default(); types.free_variables_any_type_id(ty, &mut free); if !free.is_empty() { bail!( offset, "cannot alias outer type which transitively refers \ to resources not defined in the current component" ); } } } let current = components.last_mut().unwrap(); check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; current.types.push(ty); Ok(()) } fn check_alias_count(components: &[Self], count: u32, offset: usize) -> Result<&Self> { let count = count as usize; if count >= components.len() { bail!(offset, "invalid outer alias count of {count}"); } Ok(&components[components.len() - count - 1]) } fn create_defined_type( &self, ty: crate::ComponentDefinedType, types: &TypeList, features: &WasmFeatures, offset: usize, ) -> Result { match ty { crate::ComponentDefinedType::Primitive(ty) => Ok(ComponentDefinedType::Primitive(ty)), crate::ComponentDefinedType::Record(fields) => { self.create_record_type(fields.as_ref(), types, offset) } crate::ComponentDefinedType::Variant(cases) => { self.create_variant_type(cases.as_ref(), types, offset) } crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List( self.create_component_val_type(ty, offset)?, )), crate::ComponentDefinedType::Tuple(tys) => { self.create_tuple_type(tys.as_ref(), types, offset) } crate::ComponentDefinedType::Flags(names) => { self.create_flags_type(names.as_ref(), features, offset) } crate::ComponentDefinedType::Enum(cases) => { self.create_enum_type(cases.as_ref(), offset) } crate::ComponentDefinedType::Option(ty) => Ok(ComponentDefinedType::Option( self.create_component_val_type(ty, offset)?, )), crate::ComponentDefinedType::Result { ok, err } => Ok(ComponentDefinedType::Result { ok: ok .map(|ty| self.create_component_val_type(ty, offset)) .transpose()?, err: err .map(|ty| self.create_component_val_type(ty, offset)) .transpose()?, }), crate::ComponentDefinedType::Own(idx) => Ok(ComponentDefinedType::Own( self.resource_at(idx, types, offset)?, )), crate::ComponentDefinedType::Borrow(idx) => Ok(ComponentDefinedType::Borrow( self.resource_at(idx, types, offset)?, )), } } fn create_record_type( &self, fields: &[(&str, crate::ComponentValType)], types: &TypeList, offset: usize, ) -> Result { let mut info = TypeInfo::new(); let mut field_map = IndexMap::default(); field_map.reserve(fields.len()); if fields.is_empty() { bail!(offset, "record type must have at least one field"); } for (name, ty) in fields { let name = to_kebab_str(name, "record field", offset)?; let ty = self.create_component_val_type(*ty, offset)?; match field_map.entry(name.to_owned()) { Entry::Occupied(e) => bail!( offset, "record field name `{name}` conflicts with previous field name `{prev}`", prev = e.key() ), Entry::Vacant(e) => { info.combine(ty.info(types), offset)?; e.insert(ty); } } } Ok(ComponentDefinedType::Record(RecordType { info, fields: field_map, })) } fn create_variant_type( &self, cases: &[crate::VariantCase], types: &TypeList, offset: usize, ) -> Result { let mut info = TypeInfo::new(); let mut case_map: IndexMap = IndexMap::default(); case_map.reserve(cases.len()); if cases.is_empty() { bail!(offset, "variant type must have at least one case"); } if cases.len() > u32::MAX as usize { return Err(BinaryReaderError::new( "variant type cannot be represented with a 32-bit discriminant value", offset, )); } for (i, case) in cases.iter().enumerate() { if let Some(refines) = case.refines { if refines >= i as u32 { return Err(BinaryReaderError::new( "variant case can only refine a previously defined case", offset, )); } } let name = to_kebab_str(case.name, "variant case", offset)?; let ty = case .ty .map(|ty| self.create_component_val_type(ty, offset)) .transpose()?; match case_map.entry(name.to_owned()) { Entry::Occupied(e) => bail!( offset, "variant case name `{name}` conflicts with previous case name `{prev}`", name = case.name, prev = e.key() ), Entry::Vacant(e) => { if let Some(ty) = ty { info.combine(ty.info(types), offset)?; } // Safety: the use of `KebabStr::new_unchecked` here is safe because the string // was already verified to be kebab case. e.insert(VariantCase { ty, refines: case .refines .map(|i| KebabStr::new_unchecked(cases[i as usize].name).to_owned()), }); } } } Ok(ComponentDefinedType::Variant(VariantType { info, cases: case_map, })) } fn create_tuple_type( &self, tys: &[crate::ComponentValType], types: &TypeList, offset: usize, ) -> Result { let mut info = TypeInfo::new(); if tys.is_empty() { bail!(offset, "tuple type must have at least one type"); } let types = tys .iter() .map(|ty| { let ty = self.create_component_val_type(*ty, offset)?; info.combine(ty.info(types), offset)?; Ok(ty) }) .collect::>()?; Ok(ComponentDefinedType::Tuple(TupleType { info, types })) } fn create_flags_type( &self, names: &[&str], features: &WasmFeatures, offset: usize, ) -> Result { let mut names_set = IndexSet::default(); names_set.reserve(names.len()); if names.is_empty() { bail!(offset, "flags must have at least one entry"); } if names.len() > 32 && !features.component_model_more_flags() { bail!( offset, "cannot have more than 32 flags; this was previously \ accepted and if this is required for your project please \ leave a comment on \ https://github.com/WebAssembly/component-model/issues/370" ); } for name in names { let name = to_kebab_str(name, "flag", offset)?; if !names_set.insert(name.to_owned()) { bail!( offset, "flag name `{name}` conflicts with previous flag name `{prev}`", prev = names_set.get(name).unwrap() ); } } Ok(ComponentDefinedType::Flags(names_set)) } fn create_enum_type(&self, cases: &[&str], offset: usize) -> Result { if cases.len() > u32::MAX as usize { return Err(BinaryReaderError::new( "enumeration type cannot be represented with a 32-bit discriminant value", offset, )); } if cases.is_empty() { bail!(offset, "enum type must have at least one variant"); } let mut tags = IndexSet::default(); tags.reserve(cases.len()); for tag in cases { let tag = to_kebab_str(tag, "enum tag", offset)?; if !tags.insert(tag.to_owned()) { bail!( offset, "enum tag name `{tag}` conflicts with previous tag name `{prev}`", prev = tags.get(tag).unwrap() ); } } Ok(ComponentDefinedType::Enum(tags)) } fn create_component_val_type( &self, ty: crate::ComponentValType, offset: usize, ) -> Result { Ok(match ty { crate::ComponentValType::Primitive(pt) => ComponentValType::Primitive(pt), crate::ComponentValType::Type(idx) => { ComponentValType::Type(self.defined_type_at(idx, offset)?) } }) } pub fn core_type_at(&self, idx: u32, offset: usize) -> Result { self.core_types .get(idx as usize) .copied() .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) } pub fn component_type_at(&self, idx: u32, offset: usize) -> Result { self.types .get(idx as usize) .copied() .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) } fn function_type_at<'a>( &self, idx: u32, types: &'a TypeList, offset: usize, ) -> Result<&'a ComponentFuncType> { let id = self.component_type_at(idx, offset)?; match id { ComponentAnyTypeId::Func(id) => Ok(&types[id]), _ => bail!(offset, "type index {idx} is not a function type"), } } fn function_at(&self, idx: u32, offset: usize) -> Result { self.funcs.get(idx as usize).copied().ok_or_else(|| { format_err!( offset, "unknown function {idx}: function index out of bounds" ) }) } fn component_at(&self, idx: u32, offset: usize) -> Result { self.components.get(idx as usize).copied().ok_or_else(|| { format_err!( offset, "unknown component {idx}: component index out of bounds" ) }) } fn instance_at(&self, idx: u32, offset: usize) -> Result { self.instances.get(idx as usize).copied().ok_or_else(|| { format_err!( offset, "unknown instance {idx}: instance index out of bounds" ) }) } fn value_at(&mut self, idx: u32, offset: usize) -> Result<&ComponentValType> { match self.values.get_mut(idx as usize) { Some((ty, used)) if !*used => { *used = true; Ok(ty) } Some(_) => bail!(offset, "value {idx} cannot be used more than once"), None => bail!(offset, "unknown value {idx}: value index out of bounds"), } } fn defined_type_at(&self, idx: u32, offset: usize) -> Result { match self.component_type_at(idx, offset)? { ComponentAnyTypeId::Defined(id) => Ok(id), _ => bail!(offset, "type index {idx} is not a defined type"), } } fn core_function_at(&self, idx: u32, offset: usize) -> Result { match self.core_funcs.get(idx as usize) { Some(id) => Ok(*id), None => bail!( offset, "unknown core function {idx}: function index out of bounds" ), } } fn module_at(&self, idx: u32, offset: usize) -> Result { match self.core_modules.get(idx as usize) { Some(id) => Ok(*id), None => bail!(offset, "unknown module {idx}: module index out of bounds"), } } fn core_instance_at(&self, idx: u32, offset: usize) -> Result { match self.core_instances.get(idx as usize) { Some(id) => Ok(*id), None => bail!( offset, "unknown core instance {idx}: instance index out of bounds" ), } } fn core_instance_export<'a>( &self, instance_index: u32, name: &str, types: &'a TypeList, offset: usize, ) -> Result<&'a EntityType> { match types[self.core_instance_at(instance_index, offset)?] .internal_exports(types) .get(name) { Some(export) => Ok(export), None => bail!( offset, "core instance {instance_index} has no export named `{name}`" ), } } fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> { match self.core_globals.get(idx as usize) { Some(t) => Ok(t), None => bail!(offset, "unknown global {idx}: global index out of bounds"), } } fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> { match self.core_tables.get(idx as usize) { Some(t) => Ok(t), None => bail!(offset, "unknown table {idx}: table index out of bounds"), } } fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> { match self.core_memories.get(idx as usize) { Some(t) => Ok(t), None => bail!(offset, "unknown memory {idx}: memory index out of bounds"), } } /// Completes the translation of this component, performing final /// validation of its structure. /// /// This method is required to be called for translating all components. /// Internally this will convert local data structures into a /// `ComponentType` which is suitable to use to describe the type of this /// component. pub fn finish(&mut self, types: &TypeAlloc, offset: usize) -> Result { let mut ty = ComponentType { // Inherit some fields based on translation of the component. info: self.type_info, imports: self.imports.clone(), exports: self.exports.clone(), // This is filled in as a subset of `self.defined_resources` // depending on what's actually used by the exports. See the // bottom of this function. defined_resources: Default::default(), // These are inherited directly from what was calculated for this // component. imported_resources: mem::take(&mut self.imported_resources) .into_iter() .collect(), explicit_resources: mem::take(&mut self.explicit_resources), }; // Collect all "free variables", or resources, from the imports of this // component. None of the resources defined within this component can // be used as part of the exports. This set is then used to reject any // of `self.defined_resources` which show up. let mut free = IndexSet::default(); for ty in ty.imports.values() { types.free_variables_component_entity(ty, &mut free); } for (resource, _path) in self.defined_resources.iter() { // FIXME: this error message is quite opaque and doesn't indicate // more contextual information such as: // // * what was the exported resource found in the imports // * which import was the resource found within // // These are possible to calculate here if necessary, however. if free.contains(resource) { bail!(offset, "local resource type found in imports"); } } // The next step in validation a component, with respect to resources, // is to minimize the set of defined resources to only those that // are actually used by the exports. This weeds out resources that are // defined, used within a component, and never exported, for example. // // The free variables of all exports are inserted into the `free` set // (which is reused from the imports after clearing it). The defined // resources calculated for this component are then inserted into this // type's list of defined resources if it's contained somewhere in // the free variables. // // Note that at the same time all defined resources must be exported, // somehow, transitively from this component. The `explicit_resources` // map is consulted for this purpose which lists all explicitly // exported resources in the component, regardless from whence they // came. If not present in this map then it's not exported and an error // is returned. // // NB: the "types are exported" check is probably sufficient nowadays // that the check of the `explicit_resources` map is probably not // necessary, but it's left here for completeness and out of an // abundance of caution. free.clear(); for ty in ty.exports.values() { types.free_variables_component_entity(ty, &mut free); } for (id, _rep) in mem::take(&mut self.defined_resources) { if !free.contains(&id) { continue; } let path = match ty.explicit_resources.get(&id).cloned() { Some(path) => path, // FIXME: this error message is quite opaque and doesn't // indicate more contextual information such as: // // * which resource wasn't found in an export // * which export has a reference to the resource // // These are possible to calculate here if necessary, however. None => bail!( offset, "local resource type found in export but not exported itself" ), }; ty.defined_resources.push((id, path)); } Ok(ty) } fn check_value_support(&self, features: &WasmFeatures, offset: usize) -> Result<()> { if !features.component_model_values() { bail!( offset, "support for component model `value`s is not enabled" ); } Ok(()) } } impl InternRecGroup for ComponentState { fn add_type_id(&mut self, id: CoreTypeId) { self.core_types.push(ComponentCoreTypeId::Sub(id)); } fn type_id_at(&self, idx: u32, offset: usize) -> Result { match self.core_type_at(idx, offset)? { ComponentCoreTypeId::Sub(id) => Ok(id), ComponentCoreTypeId::Module(_) => { bail!(offset, "type index {idx} is a module type, not a sub type"); } } } fn types_len(&self) -> u32 { u32::try_from(self.core_types.len()).unwrap() } } impl ComponentNameContext { /// Registers that the resource `id` is named `name` within this context. fn register(&mut self, name: &str, id: AliasableResourceId) { let idx = self.all_resource_names.len(); let prev = self.resource_name_map.insert(id, idx); assert!( prev.is_none(), "for {id:?}, inserted {idx:?} but already had {prev:?}" ); self.all_resource_names.insert(name.to_string()); } fn validate_extern( &self, name: &str, kind: ExternKind, ty: &ComponentEntityType, types: &TypeAlloc, offset: usize, kind_names: &mut IndexSet, items: &mut IndexMap, info: &mut TypeInfo, features: &WasmFeatures, ) -> Result<()> { // First validate that `name` is even a valid kebab name, meaning it's // in kebab-case, is an ID, etc. let kebab = ComponentName::new_with_features(name, offset, *features) .with_context(|| format!("{} name `{name}` is not a valid extern name", kind.desc()))?; if let ExternKind::Export = kind { match kebab.kind() { ComponentNameKind::Label(_) | ComponentNameKind::Method(_) | ComponentNameKind::Static(_) | ComponentNameKind::Constructor(_) | ComponentNameKind::Interface(_) => {} ComponentNameKind::Hash(_) | ComponentNameKind::Url(_) | ComponentNameKind::Dependency(_) => { bail!(offset, "name `{name}` is not a valid export name") } } } // Validate that the kebab name, if it has structure such as // `[method]a.b`, is indeed valid with respect to known resources. self.validate(&kebab, ty, types, offset) .with_context(|| format!("{} name `{kebab}` is not valid", kind.desc()))?; // Top-level kebab-names must all be unique, even between both imports // and exports ot a component. For those names consult the `kebab_names` // set. if let Some(prev) = kind_names.replace(kebab.clone()) { bail!( offset, "{} name `{kebab}` conflicts with previous name `{prev}`", kind.desc() ); } // Otherwise all strings must be unique, regardless of their name, so // consult the `items` set to ensure that we're not for example // importing the same interface ID twice. match items.entry(name.to_string()) { Entry::Occupied(e) => { bail!( offset, "{kind} name `{name}` conflicts with previous name `{prev}`", kind = kind.desc(), prev = e.key(), ); } Entry::Vacant(e) => { e.insert(*ty); info.combine(ty.info(types), offset)?; } } Ok(()) } /// Validates that the `name` provided is allowed to have the type `ty`. fn validate( &self, name: &ComponentName, ty: &ComponentEntityType, types: &TypeAlloc, offset: usize, ) -> Result<()> { let func = || { let id = match ty { ComponentEntityType::Func(id) => *id, _ => bail!(offset, "item is not a func"), }; Ok(&types[id]) }; match name.kind() { // No validation necessary for these styles of names ComponentNameKind::Label(_) | ComponentNameKind::Interface(_) | ComponentNameKind::Url(_) | ComponentNameKind::Dependency(_) | ComponentNameKind::Hash(_) => {} // Constructors must return `(own $resource)` and the `$resource` // must be named within this context to match `rname` ComponentNameKind::Constructor(rname) => { let ty = func()?; if ty.results.len() != 1 { bail!(offset, "function should return one value"); } let ty = ty.results[0].1; let resource = match ty { ComponentValType::Primitive(_) => None, ComponentValType::Type(ty) => match &types[ty] { ComponentDefinedType::Own(id) => Some(id), _ => None, }, }; let resource = match resource { Some(id) => id, None => bail!(offset, "function should return `(own $T)`"), }; self.validate_resource_name(*resource, rname, offset)?; } // Methods must take `(param "self" (borrow $resource))` as the // first argument where `$resources` matches the name `resource` as // named in this context. ComponentNameKind::Method(name) => { let ty = func()?; if ty.params.len() == 0 { bail!(offset, "function should have at least one argument"); } let (pname, pty) = &ty.params[0]; if pname.as_str() != "self" { bail!( offset, "function should have a first argument called `self`", ); } let id = match pty { ComponentValType::Primitive(_) => None, ComponentValType::Type(ty) => match &types[*ty] { ComponentDefinedType::Borrow(id) => Some(id), _ => None, }, }; let id = match id { Some(id) => id, None => bail!( offset, "function should take a first argument of `(borrow $T)`" ), }; self.validate_resource_name(*id, name.resource(), offset)?; } // Static methods don't have much validation beyond that they must // be a function and the resource name referred to must already be // in this context. ComponentNameKind::Static(name) => { func()?; if !self.all_resource_names.contains(name.resource().as_str()) { bail!(offset, "static resource name is not known in this context"); } } } Ok(()) } fn validate_resource_name( &self, id: AliasableResourceId, name: &KebabStr, offset: usize, ) -> Result<()> { let expected_name_idx = match self.resource_name_map.get(&id) { Some(idx) => *idx, None => { bail!( offset, "resource used in function does not have a name in this context" ) } }; let expected_name = &self.all_resource_names[expected_name_idx]; if name.as_str() != expected_name { bail!( offset, "function does not match expected \ resource name `{expected_name}`" ); } Ok(()) } } use self::append_only::*; mod append_only { use crate::prelude::IndexMap; use core::hash::Hash; use core::ops::Deref; pub struct IndexMapAppendOnly(IndexMap); impl IndexMapAppendOnly where K: Hash + Eq + Ord + PartialEq + Clone, { pub fn insert(&mut self, key: K, value: V) { let prev = self.0.insert(key, value); assert!(prev.is_none()); } } impl Deref for IndexMapAppendOnly { type Target = IndexMap; fn deref(&self) -> &IndexMap { &self.0 } } impl Default for IndexMapAppendOnly { fn default() -> Self { Self(Default::default()) } } impl IntoIterator for IndexMapAppendOnly { type IntoIter = as IntoIterator>::IntoIter; type Item = as IntoIterator>::Item; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } } wasmparser-0.217.0/src/validator/core/canonical.rs000064400000000000000000000401651046102023000201660ustar 00000000000000//! Canonicalization of types. //! //! The unit of canonicalization is a recursion group. Having "unnecessary" //! types in a recursion group can "break" canonicalization of other types //! within that same recursion group, as can reordering types within a recursion //! group. //! //! It is an invariant that all types defined before the recursion group we are //! currently canonicalizing have already been canonicalized themselves. //! //! Canonicalizing a recursion group then proceeds as follows: //! //! * First we walk each of its `SubType` elements and put their type references //! (i.e. their `PackedIndex`es) into canonical form. Canonicalizing a //! `PackedIndex` means switching it from indexing into the Wasm module's //! types space into either //! //! 1. Referencing an already-canonicalized type, for types outside of this //! recursion group. Because inter-group type references can only go //! towards types defined before this recursion group, we know the type is //! already canonicalized and we have a `CoreTypeId` for each of those //! types. This updates the `PackedIndex` into a `CoreTypeId`. //! //! 2. Indexing into the current recursion group, for intra-group type //! references. //! //! Note that (2) has the effect of making the "same" structure of mutual type //! recursion look identical across recursion groups: //! //! ```wat //! ;; Before //! (rec (struct (field (module-type 1))) (struct (field (module-type 0)))) //! (rec (struct (field (module-type 3))) (struct (field (module-type 2)))) //! //! ;; After //! (rec (struct (field (rec-group-type 1))) (struct (field (rec-group-type 0)))) //! (rec (struct (field (rec-group-type 1))) (struct (field (rec-group-type 0)))) //! ``` //! //! * Now that the recursion group's elements are in canonical form, we can //! "simply" hash cons whole rec groups at a time. The `TypesList` morally //! maintains a hash map from `Vec` to `RecGroupId` and we can do //! get-or-create operations on it. I say "morally" because we don't actually //! duplicate the `Vec` key in that hash map since those elements are //! already stored in the `TypeList`'s internal `SnapshotList`. This //! means we need to do some low-level hash table fiddling with the //! `hashbrown` crate. //! //! And that's it! That is the whole canonicalization algorithm. //! //! Some more random things to note: //! //! * Because we essentially already have to do the check to canonicalize, and //! to avoid additional passes over the types, the canonicalization pass also //! checks that type references are in bounds. These are the only errors that //! can be returned from canonicalization. //! //! * Canonicalizing requires the `Module` to translate type indices to //! actual `CoreTypeId`s. //! //! * It is important that *after* we have canonicalized all types, we don't //! need the `Module` anymore. This makes sure that we can, for example, //! intern all types from the same store into the same `TypeList`. Which in //! turn lets us type check function imports of a same-store instance's //! exported functions and we don't need to translate from one module's //! canonical representation to another module's canonical representation or //! perform additional expensive checks to see if the types match or not //! (since the whole point of canonicalization is to avoid that!). use super::{RecGroupId, TypeAlloc, TypeList}; use crate::{ types::{CoreTypeId, TypeIdentifier}, BinaryReaderError, CompositeInnerType, CompositeType, PackedIndex, RecGroup, Result, StorageType, UnpackedIndex, ValType, WasmFeatures, }; pub(crate) trait InternRecGroup { fn add_type_id(&mut self, id: CoreTypeId); fn type_id_at(&self, idx: u32, offset: usize) -> Result; fn types_len(&self) -> u32; /// Canonicalize the rec group and return its id and whether it is a new group /// (we added its types to the `TypeAlloc`) or not (we deduplicated it with an /// existing canonical rec group). fn canonicalize_and_intern_rec_group( &mut self, features: &WasmFeatures, types: &mut TypeAlloc, mut rec_group: RecGroup, offset: usize, ) -> Result<()> where Self: Sized, { debug_assert!(rec_group.is_explicit_rec_group() || rec_group.types().len() == 1); if rec_group.is_explicit_rec_group() && !features.gc() { bail!( offset, "rec group usage requires `gc` proposal to be enabled" ); } TypeCanonicalizer::new(self, offset) .with_features(features) .canonicalize_rec_group(&mut rec_group)?; let (is_new, rec_group_id) = types.intern_canonical_rec_group(rec_group); let range = &types[rec_group_id]; let start = range.start.index(); let end = range.end.index(); for i in start..end { let i = u32::try_from(i).unwrap(); let id = CoreTypeId::from_index(i); debug_assert!(types.get(id).is_some()); self.add_type_id(id); if is_new { self.check_subtype(rec_group_id, id, features, types, offset)?; } } Ok(()) } fn check_subtype( &mut self, rec_group: RecGroupId, id: CoreTypeId, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result<()> { let ty = &types[id]; if !features.gc() && (!ty.is_final || ty.supertype_idx.is_some()) { bail!(offset, "gc proposal must be enabled to use subtypes"); } self.check_composite_type(&ty.composite_type, features, &types, offset)?; let depth = if let Some(supertype_index) = ty.supertype_idx { debug_assert!(supertype_index.is_canonical()); let sup_id = self.at_packed_index(types, rec_group, supertype_index, offset)?; if types[sup_id].is_final { bail!(offset, "sub type cannot have a final super type"); } if !types.matches(id, sup_id) { bail!(offset, "sub type must match super type"); } let depth = types.get_subtyping_depth(sup_id) + 1; if usize::from(depth) > crate::limits::MAX_WASM_SUBTYPING_DEPTH { bail!( offset, "sub type hierarchy too deep: found depth {}, cannot exceed depth {}", depth, crate::limits::MAX_WASM_SUBTYPING_DEPTH, ); } depth } else { 0 }; types.set_subtyping_depth(id, depth); Ok(()) } fn check_composite_type( &mut self, ty: &CompositeType, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { let check = |ty: &ValType, shared: bool| { features .check_value_type(*ty) .map_err(|e| BinaryReaderError::new(e, offset))?; if shared && !types.valtype_is_shared(*ty) { return Err(BinaryReaderError::new( "shared composite type must contain shared types", offset, )); // The other cases are fine: // - both shared or unshared: good to go // - the func type is unshared, `ty` is shared: though // odd, we _can_ in fact use shared values in // unshared composite types (e.g., functions). } Ok(()) }; if !features.shared_everything_threads() && ty.shared { return Err(BinaryReaderError::new( "shared composite types require the shared-everything-threads proposal", offset, )); } match &ty.inner { CompositeInnerType::Func(t) => { for vt in t.params().iter().chain(t.results()) { check(vt, ty.shared)?; } if t.results().len() > 1 && !features.multi_value() { return Err(BinaryReaderError::new( "func type returns multiple values but the multi-value feature is not enabled", offset, )); } } CompositeInnerType::Array(t) => { if !features.gc() { bail!( offset, "array indexed types not supported without the gc feature", ); } if !features.gc_types() { bail!( offset, "cannot define array types when gc types are disabled", ); } match &t.0.element_type { StorageType::I8 | StorageType::I16 => { // Note: scalar types are always `shared`. } StorageType::Val(value_type) => check(value_type, ty.shared)?, }; } CompositeInnerType::Struct(t) => { if !features.gc() { bail!( offset, "struct indexed types not supported without the gc feature", ); } if !features.gc_types() { bail!( offset, "cannot define struct types when gc types are disabled", ); } for ft in t.fields.iter() { match &ft.element_type { StorageType::I8 | StorageType::I16 => { // Note: scalar types are always `shared`. } StorageType::Val(value_type) => check(value_type, ty.shared)?, } } } } Ok(()) } fn at_packed_index( &self, types: &TypeList, rec_group: RecGroupId, index: PackedIndex, offset: usize, ) -> Result { match index.unpack() { UnpackedIndex::Id(id) => Ok(id), UnpackedIndex::Module(idx) => self.type_id_at(idx, offset), UnpackedIndex::RecGroup(idx) => types.rec_group_local_id(rec_group, idx, offset), } } } /// The kind of canonicalization we are doing. #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum CanonicalizationMode { /// Standard canonicalization: turns module indices into either (1) /// `CoreTypeId`s for inter-group references or (2) rec-group-local indices /// for intra-group references. HashConsing, /// Turns all type reference indices into `CoreTypeId`s, even from within /// the same rec group. Not useful for hash consing, but useful when /// exposing types to end users so they don't have to deal with /// rec-group-local indices. OnlyIds, } pub(crate) struct TypeCanonicalizer<'a> { module: &'a dyn InternRecGroup, features: Option<&'a WasmFeatures>, rec_group_start: u32, rec_group_len: u32, offset: usize, mode: CanonicalizationMode, within_rec_group: Option>, } impl<'a> TypeCanonicalizer<'a> { pub fn new(module: &'a dyn InternRecGroup, offset: usize) -> Self { // These defaults will work for when we are canonicalizing types from // outside of a rec group definition, forcing all `PackedIndex`es to be // canonicalized to `CoreTypeId`s. let rec_group_start = u32::MAX; let rec_group_len = 0; Self { module, features: None, rec_group_start, rec_group_len, offset, mode: CanonicalizationMode::HashConsing, within_rec_group: None, } } pub fn with_features(&mut self, features: &'a WasmFeatures) -> &mut Self { debug_assert!(self.features.is_none()); self.features = Some(features); self } fn allow_gc(&self) -> bool { self.features.map_or(true, |f| f.gc()) } fn canonicalize_rec_group(&mut self, rec_group: &mut RecGroup) -> Result<()> { // Re-initialize these fields so that we properly canonicalize // intra-rec-group type references into indices into the rec group // rather than as `CoreTypeId`s. self.rec_group_start = self.module.types_len(); self.rec_group_len = u32::try_from(rec_group.types().len()).unwrap(); for (rec_group_local_index, ty) in rec_group.types_mut().enumerate() { let rec_group_local_index = u32::try_from(rec_group_local_index).unwrap(); let type_index = self.rec_group_start + rec_group_local_index; if let Some(sup) = ty.supertype_idx.as_mut() { if sup.as_module_index().map_or(false, |i| i >= type_index) { bail!(self.offset, "supertypes must be defined before subtypes"); } } ty.remap_indices(&mut |idx| self.canonicalize_type_index(idx))?; } Ok(()) } fn canonicalize_type_index(&self, ty: &mut PackedIndex) -> Result<()> { match ty.unpack() { UnpackedIndex::Id(_) => Ok(()), UnpackedIndex::Module(index) => { if index < self.rec_group_start || self.mode == CanonicalizationMode::OnlyIds { let id = self.module.type_id_at(index, self.offset)?; if let Some(id) = PackedIndex::from_id(id) { *ty = id; return Ok(()); } else { bail!( self.offset, "implementation limit: too many types in `TypeList`" ) } } // When GC is not enabled the `rec_group_len == 1` so any rec group // local type references will be direct self references. But any kind of // type recursion, including self references, is not allowed in the // typed function references proposal, only the GC proposal. debug_assert!(self.allow_gc() || self.rec_group_len == 1); let local = index - self.rec_group_start; if self.allow_gc() && local < self.rec_group_len { if let Some(id) = PackedIndex::from_rec_group_index(local) { *ty = id; return Ok(()); } else { bail!( self.offset, "implementation limit: too many types in a recursion group" ) } } bail!( self.offset, "unknown type {index}: type index out of bounds" ) } UnpackedIndex::RecGroup(local_index) => match self.mode { CanonicalizationMode::HashConsing => Ok(()), CanonicalizationMode::OnlyIds => { let rec_group_elems = self.within_rec_group.as_ref().expect( "configured to canonicalize all type reference indices to `CoreTypeId`s \ and found rec-group-local index, but missing `within_rec_group` context", ); let rec_group_len = rec_group_elems.end.index() - rec_group_elems.start.index(); let rec_group_len = u32::try_from(rec_group_len).unwrap(); assert!(local_index < rec_group_len); let rec_group_start = u32::try_from(rec_group_elems.start.index()).unwrap(); let id = CoreTypeId::from_index(rec_group_start + local_index); *ty = PackedIndex::from_id(id).expect( "should fit in impl limits since we already have the end of the rec group \ constructed successfully", ); Ok(()) } }, } } } wasmparser-0.217.0/src/validator/core.rs000064400000000000000000001312101046102023000162270ustar 00000000000000//! State relating to validating a WebAssembly module. //! mod canonical; pub(crate) use canonical::InternRecGroup; use self::arc::MaybeOwned; use super::{ check_max, combine_type_sizes, operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations}, types::{CoreTypeId, EntityType, RecGroupId, TypeAlloc, TypeList}, }; use crate::{ limits::*, BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FuncType, Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result, SubType, Table, TableInit, TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, WasmFeatures, WasmModuleResources, }; use crate::{prelude::*, CompositeInnerType}; use alloc::sync::Arc; use core::mem; // Section order for WebAssembly modules. // // Component sections are unordered and allow for duplicates, // so this isn't used for components. #[derive(Copy, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Debug)] pub enum Order { #[default] Initial, Type, Import, Function, Table, Memory, Tag, Global, Export, Start, Element, DataCount, Code, Data, } #[derive(Default)] pub(crate) struct ModuleState { /// Internal state that is incrementally built-up for the module being /// validated. This houses type information for all wasm items, like /// functions. Note that this starts out as a solely owned `Arc` so we can /// get mutable access, but after we get to the code section this is never /// mutated to we can clone it cheaply and hand it to sub-validators. pub module: arc::MaybeOwned, /// Where we are, order-wise, in the wasm binary. order: Order, /// The number of data segments in the data section (if present). pub data_segment_count: u32, /// The number of functions we expect to be defined in the code section, or /// basically the length of the function section if it was found. The next /// index is where we are, in the code section index space, for the next /// entry in the code section (used to figure out what type is next for the /// function being validated). pub expected_code_bodies: Option, const_expr_allocs: OperatorValidatorAllocations, /// When parsing the code section, represents the current index in the section. code_section_index: Option, } impl ModuleState { pub fn update_order(&mut self, order: Order, offset: usize) -> Result<()> { if self.order >= order { return Err(BinaryReaderError::new("section out of order", offset)); } self.order = order; Ok(()) } pub fn validate_end(&self, offset: usize) -> Result<()> { // Ensure that the data count section, if any, was correct. if let Some(data_count) = self.module.data_count { if data_count != self.data_segment_count { return Err(BinaryReaderError::new( "data count and data section have inconsistent lengths", offset, )); } } // Ensure that the function section, if nonzero, was paired with a code // section with the appropriate length. if let Some(n) = self.expected_code_bodies { if n > 0 { return Err(BinaryReaderError::new( "function and code section have inconsistent lengths", offset, )); } } Ok(()) } pub fn next_code_index_and_type(&mut self, offset: usize) -> Result<(u32, u32)> { let index = self .code_section_index .get_or_insert(self.module.num_imported_functions as usize); if *index >= self.module.functions.len() { return Err(BinaryReaderError::new( "code section entry exceeds number of functions", offset, )); } let ty = self.module.functions[*index]; *index += 1; Ok(((*index - 1) as u32, ty)) } pub fn add_global( &mut self, mut global: Global, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { self.module .check_global_type(&mut global.ty, features, types, offset)?; self.check_const_expr(&global.init_expr, global.ty.content_type, features, types)?; self.module.assert_mut().globals.push(global.ty); Ok(()) } pub fn add_table( &mut self, mut table: Table<'_>, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { self.module .check_table_type(&mut table.ty, features, types, offset)?; match &table.init { TableInit::RefNull => { if !table.ty.element_type.is_nullable() { bail!(offset, "type mismatch: non-defaultable element type"); } } TableInit::Expr(expr) => { if !features.function_references() { bail!( offset, "tables with expression initializers require \ the function-references proposal" ); } self.check_const_expr(expr, table.ty.element_type.into(), features, types)?; } } self.module.assert_mut().tables.push(table.ty); Ok(()) } pub fn add_data_segment( &mut self, data: Data, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { match data.kind { DataKind::Passive => Ok(()), DataKind::Active { memory_index, offset_expr, } => { let ty = self.module.memory_at(memory_index, offset)?.index_type(); self.check_const_expr(&offset_expr, ty, features, types) } } } pub fn add_element_segment( &mut self, mut e: Element, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { // the `funcref` value type is allowed all the way back to the MVP, so // don't check it here let element_ty = match &mut e.items { crate::ElementItems::Functions(_) => RefType::FUNC, crate::ElementItems::Expressions(ty, _) => { self.module.check_ref_type(ty, features, offset)?; *ty } }; match e.kind { ElementKind::Active { table_index, offset_expr, } => { let table = self.module.table_at(table_index.unwrap_or(0), offset)?; if !types.reftype_is_subtype(element_ty, table.element_type) { return Err(BinaryReaderError::new( format!( "type mismatch: invalid element type `{}` for table type `{}`", ty_to_str(element_ty.into()), ty_to_str(table.element_type.into()), ), offset, )); } self.check_const_expr(&offset_expr, table.index_type(), features, types)?; } ElementKind::Passive | ElementKind::Declared => { if !features.bulk_memory() { return Err(BinaryReaderError::new( "bulk memory must be enabled", offset, )); } } } let validate_count = |count: u32| -> Result<(), BinaryReaderError> { if count > MAX_WASM_TABLE_ENTRIES as u32 { Err(BinaryReaderError::new( "number of elements is out of bounds", offset, )) } else { Ok(()) } }; match e.items { crate::ElementItems::Functions(reader) => { let count = reader.count(); validate_count(count)?; for f in reader.into_iter_with_offsets() { let (offset, f) = f?; self.module.get_func_type(f, types, offset)?; self.module.assert_mut().function_references.insert(f); } } crate::ElementItems::Expressions(ty, reader) => { validate_count(reader.count())?; for expr in reader { self.check_const_expr(&expr?, ValType::Ref(ty), features, types)?; } } } self.module.assert_mut().element_types.push(element_ty); Ok(()) } fn check_const_expr( &mut self, expr: &ConstExpr<'_>, expected_ty: ValType, features: &WasmFeatures, types: &TypeList, ) -> Result<()> { let mut validator = VisitConstOperator { offset: 0, order: self.order, uninserted_funcref: false, ops: OperatorValidator::new_const_expr( features, expected_ty, mem::take(&mut self.const_expr_allocs), ), resources: OperatorValidatorResources { types, module: &mut self.module, }, features, }; let mut ops = expr.get_operators_reader(); while !ops.eof() { validator.offset = ops.original_position(); ops.visit_operator(&mut validator)??; } validator.ops.finish(ops.original_position())?; // See comment in `RefFunc` below for why this is an assert. assert!(!validator.uninserted_funcref); self.const_expr_allocs = validator.ops.into_allocations(); return Ok(()); struct VisitConstOperator<'a> { offset: usize, uninserted_funcref: bool, ops: OperatorValidator, resources: OperatorValidatorResources<'a>, order: Order, features: &'a WasmFeatures, } impl VisitConstOperator<'_> { fn validator(&mut self) -> impl VisitOperator<'_, Output = Result<()>> { self.ops.with_resources(&self.resources, self.offset) } fn validate_extended_const(&mut self, op: &str) -> Result<()> { if self.ops.features.extended_const() { Ok(()) } else { Err(BinaryReaderError::new( format!( "constant expression required: non-constant operator: {}", op ), self.offset, )) } } fn validate_gc(&mut self, op: &str) -> Result<()> { if self.features.gc() { Ok(()) } else { Err(BinaryReaderError::new( format!( "constant expression required: non-constant operator: {}", op ), self.offset, )) } } fn validate_shared_everything_threads(&mut self, op: &str) -> Result<()> { if self.features.shared_everything_threads() { Ok(()) } else { Err(BinaryReaderError::new( format!( "constant expression required: non-constant operator: {}", op ), self.offset, )) } } fn validate_global(&mut self, index: u32) -> Result<()> { let module = &self.resources.module; let global = module.global_at(index, self.offset)?; if index >= module.num_imported_globals && !self.features.gc() { return Err(BinaryReaderError::new( "constant expression required: global.get of locally defined global", self.offset, )); } if global.mutable { return Err(BinaryReaderError::new( "constant expression required: global.get of mutable global", self.offset, )); } Ok(()) } // Functions in initialization expressions are only valid in // element segment initialization expressions and globals. In // these contexts we want to record all function references. // // Initialization expressions can also be found in the data // section, however. A `RefFunc` instruction in those situations // is always invalid and needs to produce a validation error. In // this situation, though, we can no longer modify // the state since it's been "snapshot" already for // parallel validation of functions. // // If we cannot modify the function references then this function // *should* result in a validation error, but we defer that // validation error to happen later. The `uninserted_funcref` // boolean here is used to track this and will cause a panic // (aka a fuzz bug) if we somehow forget to emit an error somewhere // else. fn insert_ref_func(&mut self, index: u32) { if self.order == Order::Data { self.uninserted_funcref = true; } else { self.resources .module .assert_mut() .function_references .insert(index); } } } macro_rules! define_visit_operator { ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( #[allow(unused_variables)] fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { define_visit_operator!(@visit self $visit $($($arg)*)?) } )* }; // These are always valid in const expressions (@visit $self:ident visit_i32_const $val:ident) => {{ $self.validator().visit_i32_const($val) }}; (@visit $self:ident visit_i64_const $val:ident) => {{ $self.validator().visit_i64_const($val) }}; (@visit $self:ident visit_f32_const $val:ident) => {{ $self.validator().visit_f32_const($val) }}; (@visit $self:ident visit_f64_const $val:ident) => {{ $self.validator().visit_f64_const($val) }}; (@visit $self:ident visit_v128_const $val:ident) => {{ $self.validator().visit_v128_const($val) }}; (@visit $self:ident visit_ref_null $val:ident) => {{ $self.validator().visit_ref_null($val) }}; (@visit $self:ident visit_end) => {{ $self.validator().visit_end() }}; // These are valid const expressions when the extended-const proposal is enabled. (@visit $self:ident visit_i32_add) => {{ $self.validate_extended_const("i32.add")?; $self.validator().visit_i32_add() }}; (@visit $self:ident visit_i32_sub) => {{ $self.validate_extended_const("i32.sub")?; $self.validator().visit_i32_sub() }}; (@visit $self:ident visit_i32_mul) => {{ $self.validate_extended_const("i32.mul")?; $self.validator().visit_i32_mul() }}; (@visit $self:ident visit_i64_add) => {{ $self.validate_extended_const("i64.add")?; $self.validator().visit_i64_add() }}; (@visit $self:ident visit_i64_sub) => {{ $self.validate_extended_const("i64.sub")?; $self.validator().visit_i64_sub() }}; (@visit $self:ident visit_i64_mul) => {{ $self.validate_extended_const("i64.mul")?; $self.validator().visit_i64_mul() }}; // These are valid const expressions with the gc proposal is // enabled. (@visit $self:ident visit_struct_new $type_index:ident) => {{ $self.validate_gc("struct.new")?; $self.validator().visit_struct_new($type_index) }}; (@visit $self:ident visit_struct_new_default $type_index:ident) => {{ $self.validate_gc("struct.new_default")?; $self.validator().visit_struct_new_default($type_index) }}; (@visit $self:ident visit_array_new $type_index:ident) => {{ $self.validate_gc("array.new")?; $self.validator().visit_array_new($type_index) }}; (@visit $self:ident visit_array_new_default $type_index:ident) => {{ $self.validate_gc("array.new_default")?; $self.validator().visit_array_new_default($type_index) }}; (@visit $self:ident visit_array_new_fixed $type_index:ident $n:ident) => {{ $self.validate_gc("array.new_fixed")?; $self.validator().visit_array_new_fixed($type_index, $n) }}; (@visit $self:ident visit_ref_i31) => {{ $self.validate_gc("ref.i31")?; $self.validator().visit_ref_i31() }}; (@visit $self:ident visit_ref_i31_shared) => {{ $self.validate_shared_everything_threads("ref.i31_shared")?; $self.validator().visit_ref_i31_shared() }}; // `global.get` is a valid const expression for imported, immutable // globals. (@visit $self:ident visit_global_get $idx:ident) => {{ $self.validate_global($idx)?; $self.validator().visit_global_get($idx) }}; // `ref.func`, if it's in a `global` initializer, will insert into // the set of referenced functions so it's processed here. (@visit $self:ident visit_ref_func $idx:ident) => {{ $self.insert_ref_func($idx); $self.validator().visit_ref_func($idx) }}; (@visit $self:ident $op:ident $($args:tt)*) => {{ Err(BinaryReaderError::new( format!("constant expression required: non-constant operator: {}", stringify!($op)), $self.offset, )) }} } impl<'a> VisitOperator<'a> for VisitConstOperator<'a> { type Output = Result<()>; for_each_operator!(define_visit_operator); } } } #[derive(Debug)] pub(crate) struct Module { // This is set once the code section starts. // `WasmModuleResources` implementations use the snapshot to // enable parallel validation of functions. pub snapshot: Option>, // Stores indexes into the validator's types list. pub types: Vec, pub tables: Vec, pub memories: Vec, pub globals: Vec, pub element_types: Vec, pub data_count: Option, // Stores indexes into `types`. pub functions: Vec, pub tags: Vec, pub function_references: Set, pub imports: IndexMap<(String, String), Vec>, pub exports: IndexMap, pub type_size: u32, num_imported_globals: u32, num_imported_functions: u32, } impl Module { pub fn add_types( &mut self, rec_group: RecGroup, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, check_limit: bool, ) -> Result<()> { if check_limit { check_max( self.types.len(), rec_group.types().len() as u32, MAX_WASM_TYPES, "types", offset, )?; } self.canonicalize_and_intern_rec_group(features, types, rec_group, offset) } pub fn add_import( &mut self, mut import: crate::Import, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { let entity = self.check_type_ref(&mut import.ty, features, types, offset)?; let (len, max, desc) = match import.ty { TypeRef::Func(type_index) => { self.functions.push(type_index); self.num_imported_functions += 1; (self.functions.len(), MAX_WASM_FUNCTIONS, "functions") } TypeRef::Table(ty) => { self.tables.push(ty); (self.tables.len(), self.max_tables(features), "tables") } TypeRef::Memory(ty) => { self.memories.push(ty); (self.memories.len(), self.max_memories(features), "memories") } TypeRef::Tag(ty) => { self.tags.push(self.types[ty.func_type_idx as usize]); (self.tags.len(), MAX_WASM_TAGS, "tags") } TypeRef::Global(ty) => { if !features.mutable_global() && ty.mutable { return Err(BinaryReaderError::new( "mutable global support is not enabled", offset, )); } self.globals.push(ty); self.num_imported_globals += 1; (self.globals.len(), MAX_WASM_GLOBALS, "globals") } }; check_max(len, 0, max, desc, offset)?; self.type_size = combine_type_sizes(self.type_size, entity.info(types).size(), offset)?; self.imports .entry((import.module.to_string(), import.name.to_string())) .or_default() .push(entity); Ok(()) } pub fn add_export( &mut self, name: &str, ty: EntityType, features: &WasmFeatures, offset: usize, check_limit: bool, types: &TypeList, ) -> Result<()> { if !features.mutable_global() { if let EntityType::Global(global_type) = ty { if global_type.mutable { return Err(BinaryReaderError::new( "mutable global support is not enabled", offset, )); } } } if check_limit { check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?; } self.type_size = combine_type_sizes(self.type_size, ty.info(types).size(), offset)?; match self.exports.insert(name.to_string(), ty) { Some(_) => Err(format_err!( offset, "duplicate export name `{name}` already defined" )), None => Ok(()), } } pub fn add_function(&mut self, type_index: u32, types: &TypeList, offset: usize) -> Result<()> { self.func_type_at(type_index, types, offset)?; self.functions.push(type_index); Ok(()) } pub fn add_memory( &mut self, ty: MemoryType, features: &WasmFeatures, offset: usize, ) -> Result<()> { self.check_memory_type(&ty, features, offset)?; self.memories.push(ty); Ok(()) } pub fn add_tag( &mut self, ty: TagType, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { self.check_tag_type(&ty, features, types, offset)?; self.tags.push(self.types[ty.func_type_idx as usize]); Ok(()) } fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> { let id = self.type_id_at(idx, offset)?; Ok(&types[id]) } fn func_type_at<'a>( &self, type_index: u32, types: &'a TypeList, offset: usize, ) -> Result<&'a FuncType> { match &self .sub_type_at(types, type_index, offset)? .composite_type .inner { CompositeInnerType::Func(f) => Ok(f), _ => bail!(offset, "type index {type_index} is not a function type"), } } pub fn check_type_ref( &self, type_ref: &mut TypeRef, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result { Ok(match type_ref { TypeRef::Func(type_index) => { self.func_type_at(*type_index, types, offset)?; EntityType::Func(self.types[*type_index as usize]) } TypeRef::Table(t) => { self.check_table_type(t, features, types, offset)?; EntityType::Table(*t) } TypeRef::Memory(t) => { self.check_memory_type(t, features, offset)?; EntityType::Memory(*t) } TypeRef::Tag(t) => { self.check_tag_type(t, features, types, offset)?; EntityType::Tag(self.types[t.func_type_idx as usize]) } TypeRef::Global(t) => { self.check_global_type(t, features, types, offset)?; EntityType::Global(*t) } }) } fn check_table_type( &self, ty: &mut TableType, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { // The `funcref` value type is allowed all the way back to the MVP, so // don't check it here. if ty.element_type != RefType::FUNCREF { self.check_ref_type(&mut ty.element_type, features, offset)? } if ty.table64 && !features.memory64() { return Err(BinaryReaderError::new( "memory64 must be enabled for 64-bit tables", offset, )); } self.check_limits(ty.initial, ty.maximum, offset)?; if ty.initial > MAX_WASM_TABLE_ENTRIES as u64 { return Err(BinaryReaderError::new( "minimum table size is out of bounds", offset, )); } if ty.shared { if !features.shared_everything_threads() { return Err(BinaryReaderError::new( "shared tables require the shared-everything-threads proposal", offset, )); } if !types.reftype_is_shared(ty.element_type) { return Err(BinaryReaderError::new( "shared tables must have a shared element type", offset, )); } } Ok(()) } fn check_memory_type( &self, ty: &MemoryType, features: &WasmFeatures, offset: usize, ) -> Result<()> { self.check_limits(ty.initial, ty.maximum, offset)?; let (page_size, page_size_log2) = if let Some(page_size_log2) = ty.page_size_log2 { if !features.custom_page_sizes() { return Err(BinaryReaderError::new( "the custom page sizes proposal must be enabled to \ customize a memory's page size", offset, )); } // Currently 2**0 and 2**16 are the only valid page sizes, but this // may be relaxed to allow any power of two in the future. if page_size_log2 != 0 && page_size_log2 != 16 { return Err(BinaryReaderError::new("invalid custom page size", offset)); } let page_size = 1_u64 << page_size_log2; debug_assert!(page_size.is_power_of_two()); debug_assert!(page_size == DEFAULT_WASM_PAGE_SIZE || page_size == 1); (page_size, page_size_log2) } else { let page_size_log2 = 16; debug_assert_eq!(DEFAULT_WASM_PAGE_SIZE, 1 << page_size_log2); (DEFAULT_WASM_PAGE_SIZE, page_size_log2) }; let (true_maximum, err) = if ty.memory64 { if !features.memory64() { return Err(BinaryReaderError::new( "memory64 must be enabled for 64-bit memories", offset, )); } ( max_wasm_memory64_pages(page_size), format!( "memory size must be at most 2**{} pages", 64 - page_size_log2 ), ) } else { let max = max_wasm_memory32_pages(page_size); ( max, format!("memory size must be at most {max} pages (4GiB)"), ) }; if ty.initial > true_maximum { return Err(BinaryReaderError::new(err, offset)); } if let Some(maximum) = ty.maximum { if maximum > true_maximum { return Err(BinaryReaderError::new(err, offset)); } } if ty.shared { if !features.threads() { return Err(BinaryReaderError::new( "threads must be enabled for shared memories", offset, )); } if ty.maximum.is_none() { return Err(BinaryReaderError::new( "shared memory must have maximum size", offset, )); } } Ok(()) } pub(crate) fn imports_for_module_type( &self, offset: usize, ) -> Result> { // Ensure imports are unique, which is a requirement of the component model self.imports .iter() .map(|((module, name), types)| { if types.len() != 1 { bail!( offset, "module has a duplicate import name `{module}:{name}` \ that is not allowed in components", ); } Ok(((module.clone(), name.clone()), types[0])) }) .collect::>() } fn check_value_type( &self, ty: &mut ValType, features: &WasmFeatures, offset: usize, ) -> Result<()> { // The above only checks the value type for features. // We must check it if it's a reference. match ty { ValType::Ref(rt) => self.check_ref_type(rt, features, offset), _ => features .check_value_type(*ty) .map_err(|e| BinaryReaderError::new(e, offset)), } } fn check_ref_type( &self, ty: &mut RefType, features: &WasmFeatures, offset: usize, ) -> Result<()> { features .check_ref_type(*ty) .map_err(|e| BinaryReaderError::new(e, offset))?; let mut hty = ty.heap_type(); self.check_heap_type(&mut hty, offset)?; *ty = RefType::new(ty.is_nullable(), hty).unwrap(); Ok(()) } fn check_heap_type(&self, ty: &mut HeapType, offset: usize) -> Result<()> { // Check that the heap type is valid. let type_index = match ty { HeapType::Abstract { .. } => return Ok(()), HeapType::Concrete(type_index) => type_index, }; match type_index { UnpackedIndex::Module(idx) => { let id = self.type_id_at(*idx, offset)?; *type_index = UnpackedIndex::Id(id); Ok(()) } // Types at this stage should not be canonicalized. All // canonicalized types should already be validated meaning they // shouldn't be double-checked here again. UnpackedIndex::RecGroup(_) | UnpackedIndex::Id(_) => unreachable!(), } } fn check_tag_type( &self, ty: &TagType, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { if !features.exceptions() { return Err(BinaryReaderError::new( "exceptions proposal not enabled", offset, )); } let ty = self.func_type_at(ty.func_type_idx, types, offset)?; if !ty.results().is_empty() { return Err(BinaryReaderError::new( "invalid exception type: non-empty tag result type", offset, )); } Ok(()) } fn check_global_type( &self, ty: &mut GlobalType, features: &WasmFeatures, types: &TypeList, offset: usize, ) -> Result<()> { self.check_value_type(&mut ty.content_type, features, offset)?; if ty.shared { if !features.shared_everything_threads() { return Err(BinaryReaderError::new( "shared globals require the shared-everything-threads proposal", offset, )); } if !types.valtype_is_shared(ty.content_type) { return Err(BinaryReaderError::new( "shared globals must have a shared value type", offset, )); } } Ok(()) } fn check_limits(&self, initial: T, maximum: Option, offset: usize) -> Result<()> where T: Into, { if let Some(max) = maximum { if initial.into() > max.into() { return Err(BinaryReaderError::new( "size minimum must not be greater than maximum", offset, )); } } Ok(()) } pub fn max_tables(&self, features: &WasmFeatures) -> usize { if features.reference_types() { MAX_WASM_TABLES } else { 1 } } pub fn max_memories(&self, features: &WasmFeatures) -> usize { if features.multi_memory() { MAX_WASM_MEMORIES } else { 1 } } pub fn export_to_entity_type( &mut self, export: &crate::Export, offset: usize, ) -> Result { let check = |ty: &str, index: u32, total: usize| { if index as usize >= total { Err(format_err!( offset, "unknown {ty} {index}: exported {ty} index out of bounds", )) } else { Ok(()) } }; Ok(match export.kind { ExternalKind::Func => { check("function", export.index, self.functions.len())?; self.function_references.insert(export.index); EntityType::Func(self.types[self.functions[export.index as usize] as usize]) } ExternalKind::Table => { check("table", export.index, self.tables.len())?; EntityType::Table(self.tables[export.index as usize]) } ExternalKind::Memory => { check("memory", export.index, self.memories.len())?; EntityType::Memory(self.memories[export.index as usize]) } ExternalKind::Global => { check("global", export.index, self.globals.len())?; EntityType::Global(self.globals[export.index as usize]) } ExternalKind::Tag => { check("tag", export.index, self.tags.len())?; EntityType::Tag(self.tags[export.index as usize]) } }) } pub fn get_func_type<'a>( &self, func_idx: u32, types: &'a TypeList, offset: usize, ) -> Result<&'a FuncType> { match self.functions.get(func_idx as usize) { Some(idx) => self.func_type_at(*idx, types, offset), None => Err(format_err!( offset, "unknown function {func_idx}: func index out of bounds", )), } } fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> { match self.globals.get(idx as usize) { Some(t) => Ok(t), None => Err(format_err!( offset, "unknown global {idx}: global index out of bounds" )), } } fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> { match self.tables.get(idx as usize) { Some(t) => Ok(t), None => Err(format_err!( offset, "unknown table {idx}: table index out of bounds" )), } } fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> { match self.memories.get(idx as usize) { Some(t) => Ok(t), None => Err(format_err!( offset, "unknown memory {idx}: memory index out of bounds" )), } } } impl InternRecGroup for Module { fn add_type_id(&mut self, id: CoreTypeId) { self.types.push(id); } fn type_id_at(&self, idx: u32, offset: usize) -> Result { self.types .get(idx as usize) .copied() .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) } fn types_len(&self) -> u32 { u32::try_from(self.types.len()).unwrap() } } impl Default for Module { fn default() -> Self { Self { snapshot: Default::default(), types: Default::default(), tables: Default::default(), memories: Default::default(), globals: Default::default(), element_types: Default::default(), data_count: Default::default(), functions: Default::default(), tags: Default::default(), function_references: Default::default(), imports: Default::default(), exports: Default::default(), type_size: 1, num_imported_globals: Default::default(), num_imported_functions: Default::default(), } } } struct OperatorValidatorResources<'a> { module: &'a mut MaybeOwned, types: &'a TypeList, } impl WasmModuleResources for OperatorValidatorResources<'_> { fn table_at(&self, at: u32) -> Option { self.module.tables.get(at as usize).cloned() } fn memory_at(&self, at: u32) -> Option { self.module.memories.get(at as usize).cloned() } fn tag_at(&self, at: u32) -> Option<&FuncType> { let type_id = *self.module.tags.get(at as usize)?; Some(self.types[type_id].unwrap_func()) } fn global_at(&self, at: u32) -> Option { self.module.globals.get(at as usize).cloned() } fn sub_type_at(&self, at: u32) -> Option<&SubType> { let id = *self.module.types.get(at as usize)?; Some(&self.types[id]) } fn type_id_of_function(&self, at: u32) -> Option { let type_index = self.module.functions.get(at as usize)?; self.module.types.get(*type_index as usize).copied() } fn type_index_of_function(&self, at: u32) -> Option { self.module.functions.get(at as usize).copied() } fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> { self.module.check_heap_type(t, offset) } fn top_type(&self, heap_type: &HeapType) -> HeapType { self.types.top_type(heap_type) } fn element_type_at(&self, at: u32) -> Option { self.module.element_types.get(at as usize).cloned() } fn is_subtype(&self, a: ValType, b: ValType) -> bool { self.types.valtype_is_subtype(a, b) } fn is_shared(&self, ty: RefType) -> bool { self.types.reftype_is_shared(ty) } fn element_count(&self) -> u32 { self.module.element_types.len() as u32 } fn data_count(&self) -> Option { self.module.data_count } fn is_function_referenced(&self, idx: u32) -> bool { self.module.function_references.contains(&idx) } } /// The implementation of [`WasmModuleResources`] used by /// [`Validator`](crate::Validator). #[derive(Debug)] pub struct ValidatorResources(pub(crate) Arc); impl WasmModuleResources for ValidatorResources { fn table_at(&self, at: u32) -> Option { self.0.tables.get(at as usize).cloned() } fn memory_at(&self, at: u32) -> Option { self.0.memories.get(at as usize).cloned() } fn tag_at(&self, at: u32) -> Option<&FuncType> { let id = *self.0.tags.get(at as usize)?; let types = self.0.snapshot.as_ref().unwrap(); match &types[id].composite_type.inner { CompositeInnerType::Func(f) => Some(f), _ => None, } } fn global_at(&self, at: u32) -> Option { self.0.globals.get(at as usize).cloned() } fn sub_type_at(&self, at: u32) -> Option<&SubType> { let id = *self.0.types.get(at as usize)?; let types = self.0.snapshot.as_ref().unwrap(); Some(&types[id]) } fn type_id_of_function(&self, at: u32) -> Option { let type_index = *self.0.functions.get(at as usize)?; self.0.types.get(type_index as usize).copied() } fn type_index_of_function(&self, at: u32) -> Option { self.0.functions.get(at as usize).copied() } fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> { self.0.check_heap_type(t, offset) } fn top_type(&self, heap_type: &HeapType) -> HeapType { self.0.snapshot.as_ref().unwrap().top_type(heap_type) } fn element_type_at(&self, at: u32) -> Option { self.0.element_types.get(at as usize).cloned() } fn is_subtype(&self, a: ValType, b: ValType) -> bool { self.0.snapshot.as_ref().unwrap().valtype_is_subtype(a, b) } fn is_shared(&self, ty: RefType) -> bool { self.0.snapshot.as_ref().unwrap().reftype_is_shared(ty) } fn element_count(&self) -> u32 { self.0.element_types.len() as u32 } fn data_count(&self) -> Option { self.0.data_count } fn is_function_referenced(&self, idx: u32) -> bool { self.0.function_references.contains(&idx) } } const _: () = { fn assert_send() {} // Assert that `ValidatorResources` is Send so function validation // can be parallelizable fn assert() { assert_send::(); } }; mod arc { use alloc::sync::Arc; use core::ops::Deref; enum Inner { Owned(T), Shared(Arc), Empty, // Only used for swapping from owned to shared. } pub struct MaybeOwned { inner: Inner, } impl MaybeOwned { #[inline] fn as_mut(&mut self) -> Option<&mut T> { match &mut self.inner { Inner::Owned(x) => Some(x), Inner::Shared(_) => None, Inner::Empty => Self::unreachable(), } } #[inline] pub fn assert_mut(&mut self) -> &mut T { self.as_mut().unwrap() } pub fn arc(&mut self) -> &Arc { self.make_shared(); match &self.inner { Inner::Shared(x) => x, _ => Self::unreachable(), } } #[inline] fn make_shared(&mut self) { if let Inner::Shared(_) = self.inner { return; } let inner = core::mem::replace(&mut self.inner, Inner::Empty); let x = match inner { Inner::Owned(x) => x, _ => Self::unreachable(), }; let x = Arc::new(x); self.inner = Inner::Shared(x); } #[cold] #[inline(never)] fn unreachable() -> ! { unreachable!() } } impl Default for MaybeOwned { fn default() -> MaybeOwned { MaybeOwned { inner: Inner::Owned(T::default()), } } } impl Deref for MaybeOwned { type Target = T; fn deref(&self) -> &T { match &self.inner { Inner::Owned(x) => x, Inner::Shared(x) => x, Inner::Empty => Self::unreachable(), } } } } wasmparser-0.217.0/src/validator/func.rs000064400000000000000000000300011046102023000162260ustar 00000000000000use super::operators::{Frame, OperatorValidator, OperatorValidatorAllocations}; use crate::{BinaryReader, Result, ValType, VisitOperator}; use crate::{FunctionBody, Operator, WasmFeatures, WasmModuleResources}; /// Resources necessary to perform validation of a function. /// /// This structure is created by /// [`Validator::code_section_entry`](crate::Validator::code_section_entry) and /// is created per-function in a WebAssembly module. This structure is suitable /// for sending to other threads while the original /// [`Validator`](crate::Validator) continues processing other functions. #[derive(Debug)] pub struct FuncToValidate { /// Reusable, heap allocated resources to drive the Wasm validation. pub resources: T, /// The core Wasm function index being validated. pub index: u32, /// The core Wasm type index of the function being validated, /// defining the results and parameters to the function. pub ty: u32, /// The Wasm features enabled to validate the function. pub features: WasmFeatures, } impl FuncToValidate { /// Converts this [`FuncToValidate`] into a [`FuncValidator`] using the /// `allocs` provided. /// /// This method, in conjunction with [`FuncValidator::into_allocations`], /// provides a means to reuse allocations across validation of each /// individual function. Note that it is also sufficient to call this /// method with `Default::default()` if no prior allocations are /// available. /// /// # Panics /// /// If a `FuncToValidate` was created with an invalid `ty` index then this /// function will panic. pub fn into_validator(self, allocs: FuncValidatorAllocations) -> FuncValidator { let FuncToValidate { resources, index, ty, features, } = self; let validator = OperatorValidator::new_func(ty, 0, &features, &resources, allocs.0).unwrap(); FuncValidator { validator, resources, index, } } } /// Validation context for a WebAssembly function. /// /// This is a finalized validator which is ready to process a [`FunctionBody`]. /// This is created from the [`FuncToValidate::into_validator`] method. pub struct FuncValidator { validator: OperatorValidator, resources: T, index: u32, } /// External handle to the internal allocations used during function validation. /// /// This is created with either the `Default` implementation or with /// [`FuncValidator::into_allocations`]. It is then passed as an argument to /// [`FuncToValidate::into_validator`] to provide a means of reusing allocations /// between each function. #[derive(Default)] pub struct FuncValidatorAllocations(OperatorValidatorAllocations); impl FuncValidator { /// Convenience function to validate an entire function's body. /// /// You may not end up using this in final implementations because you'll /// often want to interleave validation with parsing. pub fn validate(&mut self, body: &FunctionBody<'_>) -> Result<()> { let mut reader = body.get_binary_reader(); self.read_locals(&mut reader)?; #[cfg(feature = "features")] { reader.set_features(self.validator.features); } while !reader.eof() { reader.visit_operator(&mut self.visitor(reader.original_position()))??; } self.finish(reader.original_position()) } /// Reads the local definitions from the given `BinaryReader`, often sourced /// from a `FunctionBody`. /// /// This function will automatically advance the `BinaryReader` forward, /// leaving reading operators up to the caller afterwards. pub fn read_locals(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> { for _ in 0..reader.read_var_u32()? { let offset = reader.original_position(); let cnt = reader.read()?; let ty = reader.read()?; self.define_locals(offset, cnt, ty)?; } Ok(()) } /// Defines locals into this validator. /// /// This should be used if the application is already reading local /// definitions and there's no need to re-parse the function again. pub fn define_locals(&mut self, offset: usize, count: u32, ty: ValType) -> Result<()> { self.validator .define_locals(offset, count, ty, &self.resources) } /// Validates the next operator in a function. /// /// This functions is expected to be called once-per-operator in a /// WebAssembly function. Each operator's offset in the original binary and /// the operator itself are passed to this function to provide more useful /// error messages. pub fn op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> { self.visitor(offset).visit_operator(operator) } /// Get the operator visitor for the next operator in the function. /// /// The returned visitor is intended to visit just one instruction at the `offset`. /// /// # Example /// /// ``` /// # use wasmparser::{WasmModuleResources, FuncValidator, FunctionBody, Result}; /// pub fn validate(validator: &mut FuncValidator, body: &FunctionBody<'_>) -> Result<()> /// where R: WasmModuleResources /// { /// let mut operator_reader = body.get_binary_reader(); /// while !operator_reader.eof() { /// let mut visitor = validator.visitor(operator_reader.original_position()); /// operator_reader.visit_operator(&mut visitor)??; /// } /// validator.finish(operator_reader.original_position()) /// } /// ``` pub fn visitor<'this, 'a: 'this>( &'this mut self, offset: usize, ) -> impl VisitOperator<'a, Output = Result<()>> + 'this { self.validator.with_resources(&self.resources, offset) } /// Function that must be called after the last opcode has been processed. /// /// This will validate that the function was properly terminated with the /// `end` opcode. If this function is not called then the function will not /// be properly validated. /// /// The `offset` provided to this function will be used as a position for an /// error if validation fails. pub fn finish(&mut self, offset: usize) -> Result<()> { self.validator.finish(offset) } /// Returns the underlying module resources that this validator is using. pub fn resources(&self) -> &T { &self.resources } /// The index of the function within the module's function index space that /// is being validated. pub fn index(&self) -> u32 { self.index } /// Returns the number of defined local variables in the function. pub fn len_locals(&self) -> u32 { self.validator.locals.len_locals() } /// Returns the type of the local variable at the given `index` if any. pub fn get_local_type(&self, index: u32) -> Option { self.validator.locals.get(index) } /// Get the current height of the operand stack. /// /// This returns the height of the whole operand stack for this function, /// not just for the current control frame. pub fn operand_stack_height(&self) -> u32 { self.validator.operand_stack_height() as u32 } /// Returns the optional value type of the value operand at the given /// `depth` from the top of the operand stack. /// /// - Returns `None` if the `depth` is out of bounds. /// - Returns `Some(None)` if there is a value with unknown type /// at the given `depth`. /// /// # Note /// /// A `depth` of 0 will refer to the last operand on the stack. pub fn get_operand_type(&self, depth: usize) -> Option> { self.validator.peek_operand_at(depth) } /// Returns the number of frames on the control flow stack. /// /// This returns the height of the whole control stack for this function, /// not just for the current control frame. pub fn control_stack_height(&self) -> u32 { self.validator.control_stack_height() as u32 } /// Returns a shared reference to the control flow [`Frame`] of the /// control flow stack at the given `depth` if any. /// /// Returns `None` if the `depth` is out of bounds. /// /// # Note /// /// A `depth` of 0 will refer to the last frame on the stack. pub fn get_control_frame(&self, depth: usize) -> Option<&Frame> { self.validator.get_frame(depth) } /// Consumes this validator and returns the underlying allocations that /// were used during the validation process. /// /// The returned value here can be paired with /// [`FuncToValidate::into_validator`] to reuse the allocations already /// created by this validator. pub fn into_allocations(self) -> FuncValidatorAllocations { FuncValidatorAllocations(self.validator.into_allocations()) } } #[cfg(test)] mod tests { use super::*; use crate::types::CoreTypeId; use crate::{HeapType, RefType}; struct EmptyResources(crate::SubType); impl Default for EmptyResources { fn default() -> Self { EmptyResources(crate::SubType { supertype_idx: None, is_final: true, composite_type: crate::CompositeType { inner: crate::CompositeInnerType::Func(crate::FuncType::new([], [])), shared: false, }, }) } } impl WasmModuleResources for EmptyResources { fn table_at(&self, _at: u32) -> Option { todo!() } fn memory_at(&self, _at: u32) -> Option { todo!() } fn tag_at(&self, _at: u32) -> Option<&crate::FuncType> { todo!() } fn global_at(&self, _at: u32) -> Option { todo!() } fn sub_type_at(&self, _type_idx: u32) -> Option<&crate::SubType> { Some(&self.0) } fn type_id_of_function(&self, _at: u32) -> Option { todo!() } fn type_index_of_function(&self, _at: u32) -> Option { todo!() } fn check_heap_type(&self, _t: &mut HeapType, _offset: usize) -> Result<()> { Ok(()) } fn top_type(&self, _heap_type: &HeapType) -> HeapType { todo!() } fn element_type_at(&self, _at: u32) -> Option { todo!() } fn is_subtype(&self, _t1: ValType, _t2: ValType) -> bool { todo!() } fn is_shared(&self, _ty: RefType) -> bool { todo!() } fn element_count(&self) -> u32 { todo!() } fn data_count(&self) -> Option { todo!() } fn is_function_referenced(&self, _idx: u32) -> bool { todo!() } } #[test] fn operand_stack_height() { let mut v = FuncToValidate { index: 0, ty: 0, resources: EmptyResources::default(), features: Default::default(), } .into_validator(Default::default()); // Initially zero values on the stack. assert_eq!(v.operand_stack_height(), 0); // Pushing a constant value makes use have one value on the stack. assert!(v.op(0, &Operator::I32Const { value: 0 }).is_ok()); assert_eq!(v.operand_stack_height(), 1); // Entering a new control block does not affect the stack height. assert!(v .op( 1, &Operator::Block { blockty: crate::BlockType::Empty } ) .is_ok()); assert_eq!(v.operand_stack_height(), 1); // Pushing another constant value makes use have two values on the stack. assert!(v.op(2, &Operator::I32Const { value: 99 }).is_ok()); assert_eq!(v.operand_stack_height(), 2); } } wasmparser-0.217.0/src/validator/names.rs000064400000000000000000000731541046102023000164160ustar 00000000000000//! Definitions of name-related helpers and newtypes, primarily for the //! component model. use crate::prelude::*; use crate::{Result, WasmFeatures}; use core::borrow::Borrow; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; use core::ops::Deref; use semver::Version; /// Represents a kebab string slice used in validation. /// /// This is a wrapper around `str` that ensures the slice is /// a valid kebab case string according to the component model /// specification. /// /// It also provides an equality and hashing implementation /// that ignores ASCII case. #[derive(Debug, Eq)] #[repr(transparent)] pub struct KebabStr(str); impl KebabStr { /// Creates a new kebab string slice. /// /// Returns `None` if the given string is not a valid kebab string. pub fn new<'a>(s: impl AsRef + 'a) -> Option<&'a Self> { let s = Self::new_unchecked(s); if s.is_kebab_case() { Some(s) } else { None } } pub(crate) fn new_unchecked<'a>(s: impl AsRef + 'a) -> &'a Self { // Safety: `KebabStr` is a transparent wrapper around `str` // Therefore transmuting `&str` to `&KebabStr` is safe. #[allow(unsafe_code)] unsafe { core::mem::transmute::<_, &Self>(s.as_ref()) } } /// Gets the underlying string slice. pub fn as_str(&self) -> &str { &self.0 } /// Converts the slice to an owned string. pub fn to_kebab_string(&self) -> KebabString { KebabString(self.to_string()) } fn is_kebab_case(&self) -> bool { let mut lower = false; let mut upper = false; for c in self.chars() { match c { 'a'..='z' if !lower && !upper => lower = true, 'A'..='Z' if !lower && !upper => upper = true, 'a'..='z' if lower => {} 'A'..='Z' if upper => {} '0'..='9' if lower || upper => {} '-' if lower || upper => { lower = false; upper = false; } _ => return false, } } !self.is_empty() && !self.ends_with('-') } } impl Deref for KebabStr { type Target = str; fn deref(&self) -> &str { self.as_str() } } impl PartialEq for KebabStr { fn eq(&self, other: &Self) -> bool { if self.len() != other.len() { return false; } self.chars() .zip(other.chars()) .all(|(a, b)| a.to_ascii_lowercase() == b.to_ascii_lowercase()) } } impl PartialEq for KebabStr { fn eq(&self, other: &KebabString) -> bool { self.eq(other.as_kebab_str()) } } impl Ord for KebabStr { fn cmp(&self, other: &Self) -> Ordering { let self_chars = self.chars().map(|c| c.to_ascii_lowercase()); let other_chars = other.chars().map(|c| c.to_ascii_lowercase()); self_chars.cmp(other_chars) } } impl PartialOrd for KebabStr { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Hash for KebabStr { fn hash(&self, state: &mut H) { self.len().hash(state); for b in self.chars() { b.to_ascii_lowercase().hash(state); } } } impl fmt::Display for KebabStr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (self as &str).fmt(f) } } impl ToOwned for KebabStr { type Owned = KebabString; fn to_owned(&self) -> Self::Owned { self.to_kebab_string() } } /// Represents an owned kebab string for validation. /// /// This is a wrapper around `String` that ensures the string is /// a valid kebab case string according to the component model /// specification. /// /// It also provides an equality and hashing implementation /// that ignores ASCII case. #[derive(Debug, Clone, Eq)] pub struct KebabString(String); impl KebabString { /// Creates a new kebab string. /// /// Returns `None` if the given string is not a valid kebab string. pub fn new(s: impl Into) -> Option { let s = s.into(); if KebabStr::new(&s).is_some() { Some(Self(s)) } else { None } } /// Gets the underlying string. pub fn as_str(&self) -> &str { self.0.as_str() } /// Converts the kebab string to a kebab string slice. pub fn as_kebab_str(&self) -> &KebabStr { // Safety: internal string is always valid kebab-case KebabStr::new_unchecked(self.as_str()) } } impl Deref for KebabString { type Target = KebabStr; fn deref(&self) -> &Self::Target { self.as_kebab_str() } } impl Borrow for KebabString { fn borrow(&self) -> &KebabStr { self.as_kebab_str() } } impl Ord for KebabString { fn cmp(&self, other: &Self) -> Ordering { self.as_kebab_str().cmp(other.as_kebab_str()) } } impl PartialOrd for KebabString { fn partial_cmp(&self, other: &Self) -> Option { self.as_kebab_str().partial_cmp(other.as_kebab_str()) } } impl PartialEq for KebabString { fn eq(&self, other: &Self) -> bool { self.as_kebab_str().eq(other.as_kebab_str()) } } impl PartialEq for KebabString { fn eq(&self, other: &KebabStr) -> bool { self.as_kebab_str().eq(other) } } impl Hash for KebabString { fn hash(&self, state: &mut H) { self.as_kebab_str().hash(state) } } impl fmt::Display for KebabString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.as_kebab_str().fmt(f) } } impl From for String { fn from(s: KebabString) -> String { s.0 } } /// An import or export name in the component model which is backed by `T`, /// which defaults to `String`. /// /// This name can be either: /// /// * a plain label or "kebab string": `a-b-c` /// * a plain method name : `[method]a-b.c-d` /// * a plain static method name : `[static]a-b.c-d` /// * a plain constructor: `[constructor]a-b` /// * an interface name: `wasi:cli/reactor@0.1.0` /// * a dependency name: `locked-dep=foo:bar/baz` /// * a URL name: `url=https://..` /// * a hash name: `integrity=sha256:...` /// /// # Equality and hashing /// /// Note that this type the `[method]...` and `[static]...` variants are /// considered equal and hash to the same value. This enables disallowing /// clashes between the two where method name overlap cannot happen. #[derive(Clone)] pub struct ComponentName { raw: String, kind: ParsedComponentNameKind, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] enum ParsedComponentNameKind { Label, Constructor, Method, Static, Interface, Dependency, Url, Hash, } /// Created via [`ComponentName::kind`] and classifies a name. #[derive(Debug, Clone)] pub enum ComponentNameKind<'a> { /// `a-b-c` Label(&'a KebabStr), /// `[constructor]a-b` Constructor(&'a KebabStr), /// `[method]a-b.c-d` #[allow(missing_docs)] Method(ResourceFunc<'a>), /// `[static]a-b.c-d` #[allow(missing_docs)] Static(ResourceFunc<'a>), /// `wasi:http/types@2.0` #[allow(missing_docs)] Interface(InterfaceName<'a>), /// `locked-dep=foo:bar/baz` #[allow(missing_docs)] Dependency(DependencyName<'a>), /// `url=https://...` #[allow(missing_docs)] Url(UrlName<'a>), /// `integrity=sha256:...` #[allow(missing_docs)] Hash(HashName<'a>), } const CONSTRUCTOR: &str = "[constructor]"; const METHOD: &str = "[method]"; const STATIC: &str = "[static]"; impl ComponentName { /// Attempts to parse `name` as a valid component name, returning `Err` if /// it's not valid. pub fn new(name: &str, offset: usize) -> Result { Self::new_with_features(name, offset, WasmFeatures::default()) } /// Attempts to parse `name` as a valid component name, returning `Err` if /// it's not valid. /// /// `features` can be used to enable or disable validation of certain forms /// of supported import names. pub fn new_with_features(name: &str, offset: usize, features: WasmFeatures) -> Result { let mut parser = ComponentNameParser { next: name, offset, features, }; let kind = parser.parse()?; if !parser.next.is_empty() { bail!(offset, "trailing characters found: `{}`", parser.next); } Ok(ComponentName { raw: name.to_string(), kind, }) } /// Returns the [`ComponentNameKind`] corresponding to this name. pub fn kind(&self) -> ComponentNameKind<'_> { use ComponentNameKind::*; use ParsedComponentNameKind as PK; match self.kind { PK::Label => Label(KebabStr::new_unchecked(&self.raw)), PK::Constructor => Constructor(KebabStr::new_unchecked(&self.raw[CONSTRUCTOR.len()..])), PK::Method => Method(ResourceFunc(&self.raw[METHOD.len()..])), PK::Static => Static(ResourceFunc(&self.raw[STATIC.len()..])), PK::Interface => Interface(InterfaceName(&self.raw)), PK::Dependency => Dependency(DependencyName(&self.raw)), PK::Url => Url(UrlName(&self.raw)), PK::Hash => Hash(HashName(&self.raw)), } } /// Returns the raw underlying name as a string. pub fn as_str(&self) -> &str { &self.raw } } impl From for String { fn from(name: ComponentName) -> String { name.raw } } impl Hash for ComponentName { fn hash(&self, hasher: &mut H) { self.kind().hash(hasher) } } impl PartialEq for ComponentName { fn eq(&self, other: &ComponentName) -> bool { self.kind().eq(&other.kind()) } } impl Eq for ComponentName {} impl Ord for ComponentName { fn cmp(&self, other: &ComponentName) -> Ordering { self.kind().cmp(&other.kind()) } } impl PartialOrd for ComponentName { fn partial_cmp(&self, other: &Self) -> Option { self.kind.partial_cmp(&other.kind) } } impl fmt::Display for ComponentName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.raw.fmt(f) } } impl fmt::Debug for ComponentName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.raw.fmt(f) } } impl ComponentNameKind<'_> { /// Returns the [`ParsedComponentNameKind`] of the [`ComponentNameKind`]. fn kind(&self) -> ParsedComponentNameKind { match self { Self::Label(_) => ParsedComponentNameKind::Label, Self::Constructor(_) => ParsedComponentNameKind::Constructor, Self::Method(_) => ParsedComponentNameKind::Method, Self::Static(_) => ParsedComponentNameKind::Static, Self::Interface(_) => ParsedComponentNameKind::Interface, Self::Dependency(_) => ParsedComponentNameKind::Dependency, Self::Url(_) => ParsedComponentNameKind::Url, Self::Hash(_) => ParsedComponentNameKind::Hash, } } } impl Ord for ComponentNameKind<'_> { fn cmp(&self, other: &Self) -> Ordering { match self.kind().cmp(&other.kind()) { Ordering::Equal => (), unequal => return unequal, } match (self, other) { (ComponentNameKind::Label(lhs), ComponentNameKind::Label(rhs)) => lhs.cmp(rhs), (ComponentNameKind::Constructor(lhs), ComponentNameKind::Constructor(rhs)) => { lhs.cmp(rhs) } (ComponentNameKind::Method(lhs), ComponentNameKind::Method(rhs)) => lhs.cmp(rhs), (ComponentNameKind::Method(lhs), ComponentNameKind::Static(rhs)) => lhs.cmp(rhs), (ComponentNameKind::Static(lhs), ComponentNameKind::Method(rhs)) => lhs.cmp(rhs), (ComponentNameKind::Static(lhs), ComponentNameKind::Static(rhs)) => lhs.cmp(rhs), (ComponentNameKind::Interface(lhs), ComponentNameKind::Interface(rhs)) => lhs.cmp(rhs), (ComponentNameKind::Dependency(lhs), ComponentNameKind::Dependency(rhs)) => { lhs.cmp(rhs) } (ComponentNameKind::Url(lhs), ComponentNameKind::Url(rhs)) => lhs.cmp(rhs), (ComponentNameKind::Hash(lhs), ComponentNameKind::Hash(rhs)) => lhs.cmp(rhs), _ => unreachable!("already compared for different kinds above"), } } } impl PartialOrd for ComponentNameKind<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Hash for ComponentNameKind<'_> { fn hash(&self, hasher: &mut H) { use ComponentNameKind::*; match self { Label(name) => (0u8, name).hash(hasher), Constructor(name) => (1u8, name).hash(hasher), // for hashing method == static Method(name) | Static(name) => (2u8, name).hash(hasher), Interface(name) => (3u8, name).hash(hasher), Dependency(name) => (4u8, name).hash(hasher), Url(name) => (5u8, name).hash(hasher), Hash(name) => (6u8, name).hash(hasher), } } } impl PartialEq for ComponentNameKind<'_> { fn eq(&self, other: &ComponentNameKind<'_>) -> bool { use ComponentNameKind::*; match (self, other) { (Label(a), Label(b)) => a == b, (Label(_), _) => false, (Constructor(a), Constructor(b)) => a == b, (Constructor(_), _) => false, // method == static for the purposes of hashing so equate them here // as well. (Method(a), Method(b)) | (Static(a), Static(b)) | (Method(a), Static(b)) | (Static(a), Method(b)) => a == b, (Method(_), _) => false, (Static(_), _) => false, (Interface(a), Interface(b)) => a == b, (Interface(_), _) => false, (Dependency(a), Dependency(b)) => a == b, (Dependency(_), _) => false, (Url(a), Url(b)) => a == b, (Url(_), _) => false, (Hash(a), Hash(b)) => a == b, (Hash(_), _) => false, } } } impl Eq for ComponentNameKind<'_> {} /// A resource name and its function, stored as `a.b`. #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct ResourceFunc<'a>(&'a str); impl<'a> ResourceFunc<'a> { /// Returns the the underlying string as `a.b` pub fn as_str(&self) -> &'a str { self.0 } /// Returns the resource name or the `a` in `a.b` pub fn resource(&self) -> &'a KebabStr { let dot = self.0.find('.').unwrap(); KebabStr::new_unchecked(&self.0[..dot]) } } /// An interface name, stored as `a:b/c@1.2.3` #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct InterfaceName<'a>(&'a str); impl<'a> InterfaceName<'a> { /// Returns the entire underlying string. pub fn as_str(&self) -> &'a str { self.0 } /// Returns the `a:b` in `a:b:c/d/e` pub fn namespace(&self) -> &'a KebabStr { let colon = self.0.rfind(':').unwrap(); KebabStr::new_unchecked(&self.0[..colon]) } /// Returns the `c` in `a:b:c/d/e` pub fn package(&self) -> &'a KebabStr { let colon = self.0.rfind(':').unwrap(); let slash = self.0.find('/').unwrap(); KebabStr::new_unchecked(&self.0[colon + 1..slash]) } /// Returns the `d` in `a:b:c/d/e`. pub fn interface(&self) -> &'a KebabStr { let projection = self.projection(); let slash = projection.find('/').unwrap_or(projection.len()); KebabStr::new_unchecked(&projection[..slash]) } /// Returns the `d/e` in `a:b:c/d/e` pub fn projection(&self) -> &'a KebabStr { let slash = self.0.find('/').unwrap(); let at = self.0.find('@').unwrap_or(self.0.len()); KebabStr::new_unchecked(&self.0[slash + 1..at]) } /// Returns the `1.2.3` in `a:b:c/d/e@1.2.3` pub fn version(&self) -> Option { let at = self.0.find('@')?; Some(Version::parse(&self.0[at + 1..]).unwrap()) } } /// A dependency on an implementation either as `locked-dep=...` or /// `unlocked-dep=...` #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct DependencyName<'a>(&'a str); impl<'a> DependencyName<'a> { /// Returns entire underlying import string pub fn as_str(&self) -> &'a str { self.0 } } /// A dependency on an implementation either as `url=...` #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct UrlName<'a>(&'a str); impl<'a> UrlName<'a> { /// Returns entire underlying import string pub fn as_str(&self) -> &'a str { self.0 } } /// A dependency on an implementation either as `integrity=...`. #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct HashName<'a>(&'a str); impl<'a> HashName<'a> { /// Returns entire underlying import string. pub fn as_str(&self) -> &'a str { self.0 } } // A small helper structure to parse `self.next` which is an import or export // name. // // Methods will update `self.next` as they go along and `self.offset` is used // for error messages. struct ComponentNameParser<'a> { next: &'a str, offset: usize, features: WasmFeatures, } impl<'a> ComponentNameParser<'a> { fn parse(&mut self) -> Result { if self.eat_str(CONSTRUCTOR) { self.expect_kebab()?; return Ok(ParsedComponentNameKind::Constructor); } if self.eat_str(METHOD) { let resource = self.take_until('.')?; self.kebab(resource)?; self.expect_kebab()?; return Ok(ParsedComponentNameKind::Method); } if self.eat_str(STATIC) { let resource = self.take_until('.')?; self.kebab(resource)?; self.expect_kebab()?; return Ok(ParsedComponentNameKind::Static); } // 'unlocked-dep=<' '>' if self.eat_str("unlocked-dep=") { self.expect_str("<")?; self.pkg_name_query()?; self.expect_str(">")?; return Ok(ParsedComponentNameKind::Dependency); } // 'locked-dep=<' '>' ( ',' )? if self.eat_str("locked-dep=") { self.expect_str("<")?; self.pkg_name(false)?; self.expect_str(">")?; self.eat_optional_hash()?; return Ok(ParsedComponentNameKind::Dependency); } // 'url=<' '>' (',' )? if self.eat_str("url=") { self.expect_str("<")?; let url = self.take_up_to('>')?; if url.contains('<') { bail!(self.offset, "url cannot contain `<`"); } self.expect_str(">")?; self.eat_optional_hash()?; return Ok(ParsedComponentNameKind::Url); } // 'integrity=<' '>' if self.eat_str("integrity=") { self.expect_str("<")?; let _hash = self.parse_hash()?; self.expect_str(">")?; return Ok(ParsedComponentNameKind::Hash); } if self.next.contains(':') { self.pkg_name(true)?; Ok(ParsedComponentNameKind::Interface) } else { self.expect_kebab()?; Ok(ParsedComponentNameKind::Label) } } // pkgnamequery ::= ? fn pkg_name_query(&mut self) -> Result<()> { self.pkg_path(false)?; if self.eat_str("@") { if self.eat_str("*") { return Ok(()); } self.expect_str("{")?; let range = self.take_up_to('}')?; self.expect_str("}")?; self.semver_range(range)?; } Ok(()) } // pkgname ::= ? fn pkg_name(&mut self, require_projection: bool) -> Result<()> { self.pkg_path(require_projection)?; if self.eat_str("@") { let version = match self.eat_up_to('>') { Some(version) => version, None => self.take_rest(), }; self.semver(version)?; } Ok(()) } // pkgpath ::= +