Bug 1825088 - wasm: Bump crate versions for wast and wasm-smith. r=rhunt,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D179127
This commit is contained in:
Julien Pages
2023-07-04 14:51:04 +00:00
parent 4d348c3ce3
commit a2734a31ec
80 changed files with 6691 additions and 1815 deletions

18
Cargo.lock generated
View File

@@ -5858,18 +5858,18 @@ version = "0.2.100"
[[package]]
name = "wasm-encoder"
version = "0.25.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7"
checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-smith"
version = "0.12.5"
version = "0.12.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "549cb78be46f43ad6746402871336cb6a989127fb847e93eb6ba0817647485a6"
checksum = "027ec1c470cd5d56c43b8e02040250b136ddb5975dd76a1c16915137f5f17e76"
dependencies = [
"arbitrary",
"flagset",
@@ -5881,19 +5881,19 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.102.0"
version = "0.107.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b"
checksum = "29e3ac9b780c7dda0cac7a52a5d6d2d6707cc6e3451c9db209b6c758f40d7acb"
dependencies = [
"indexmap",
"url",
"semver",
]
[[package]]
name = "wast"
version = "56.0.0"
version = "60.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b54185c051d7bbe23757d50fe575880a2426a2f06d2e9f6a10fd9a4a42920c0"
checksum = "bd06cc744b536e30387e72a48fdd492105b9c938bb4f415c39c616a7a0a697ad"
dependencies = [
"leb128",
"memchr",

View File

@@ -5,6 +5,6 @@ authors = ["Christian Holler"]
license = "MPL-2.0"
[dependencies]
wasm-smith = "0.12.5"
wasm-smith = "0.12.10"
arbitrary = { version = "1.0.0", features = ["derive"] }
libc = "0.2"

View File

@@ -20,4 +20,4 @@ mozilla-central-workspace-hack = { version = "0.1", features = ["jsrust"], optio
jsrust_shared = { path = "./shared" }
# Workaround for https://github.com/rust-lang/rust/issues/58393
mozglue-static = { path = "../../../mozglue/static/rust" }
wast = "56.0.0"
wast = "60.0.0"

View File

@@ -539,6 +539,34 @@ user-id = 1
user-login = "alexcrichton"
user-name = "Alex Crichton"
[[publisher.wasm-encoder]]
version = "0.29.0"
when = "2023-05-26"
user-id = 1
user-login = "alexcrichton"
user-name = "Alex Crichton"
[[publisher.wasm-smith]]
version = "0.12.10"
when = "2023-05-26"
user-id = 1
user-login = "alexcrichton"
user-name = "Alex Crichton"
[[publisher.wasmparser]]
version = "0.107.0"
when = "2023-05-26"
user-id = 1
user-login = "alexcrichton"
user-name = "Alex Crichton"
[[publisher.wast]]
version = "60.0.0"
when = "2023-05-26"
user-id = 1
user-login = "alexcrichton"
user-name = "Alex Crichton"
[[publisher.winapi-util]]
version = "0.1.5"
when = "2020-04-20"
@@ -569,6 +597,58 @@ start = "2020-01-14"
end = "2024-04-27"
notes = "I am an author of this crate"
[[audits.bytecode-alliance.wildcard-audits.wasm-encoder]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
user-id = 1 # Alex Crichton (alexcrichton)
start = "2020-12-11"
end = "2024-04-14"
notes = """
This is a Bytecode Alliance authored crate maintained in the `wasm-tools`
repository of which I'm one of the primary maintainers and publishers for.
I am employed by a member of the Bytecode Alliance and plan to continue doing
so and will actively maintain this crate over time.
"""
[[audits.bytecode-alliance.wildcard-audits.wasm-smith]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
user-id = 1 # Alex Crichton (alexcrichton)
start = "2020-09-03"
end = "2024-04-14"
notes = """
This is a Bytecode Alliance authored crate maintained in the `wasm-tools`
repository of which I'm one of the primary maintainers and publishers for.
I am employed by a member of the Bytecode Alliance and plan to continue doing
so and will actively maintain this crate over time.
"""
[[audits.bytecode-alliance.wildcard-audits.wasmparser]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
user-id = 1 # Alex Crichton (alexcrichton)
start = "2020-07-13"
end = "2024-04-14"
notes = """
This is a Bytecode Alliance authored crate maintained in the `wasm-tools`
repository of which I'm one of the primary maintainers and publishers for.
I am employed by a member of the Bytecode Alliance and plan to continue doing
so and will actively maintain this crate over time.
"""
[[audits.bytecode-alliance.wildcard-audits.wast]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
user-id = 1 # Alex Crichton (alexcrichton)
start = "2019-10-16"
end = "2024-04-14"
notes = """
This is a Bytecode Alliance authored crate maintained in the `wasm-tools`
repository of which I'm one of the primary maintainers and publishers for.
I am employed by a member of the Bytecode Alliance and plan to continue doing
so and will actively maintain this crate over time.
"""
[[audits.bytecode-alliance.audits.arrayref]]
who = "Nick Fitzgerald <fitzgen@gmail.com>"
criteria = "safe-to-deploy"

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"b96ee82a5f62db5b6f57000e3f1fdab6872b1bed6d36350e9af08d90984b47af","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"ac016c4843a7e1a5737255b39418732783592222dc518020730edf9dd46a1c13","src/component.rs":"b07b239571c54100a258da3fb00d9155c85d3750f93cc24d408ebc17301f3e66","src/component/aliases.rs":"be5215154b872ed5664f3bfe2aa1391f36a2575aa9d53971ab61868a1c446e9d","src/component/canonicals.rs":"8b270caef4e0f1e2b62af5b27560ac370399d11ce411aae6b811dddeaedb7750","src/component/components.rs":"07b8cae3a400e1955cc39d142569910b4fef2136021053d0ddbd6404d810aa52","src/component/exports.rs":"9a685fb44af0cfefbb2e92ddb0259955766cf6d1c0022830a865f92426ce0f7b","src/component/imports.rs":"41414a4cb71d79404e8721a985dd1d3a7df287f6417b4cab017d12e9aef31c43","src/component/instances.rs":"75e0ec12a578aa22d78053add818a960373171ff10c3619ca1e8058ded277a41","src/component/modules.rs":"9e80907e72360fae4d8057b6b0e7a6b58edd7ba6aba6e63ba17346518e169617","src/component/names.rs":"7b651284ffdf147aabf1e01226f34ddc8bbe3d38a2dcd26ca0fc15ad24002b6f","src/component/start.rs":"4055553d5c99c99abbc01bb8abb928ecb8b909d148b647a9977fbd444bb464a3","src/component/types.rs":"02982d0b7962462f0b083ade9a639216cc040775a0426ad431afbf9fbedf664b","src/core.rs":"7b02ef53a430693cfadcfd83f3aa9f556062eb9b9408e661ead5364e2150cd5e","src/core/code.rs":"b340cb948d1e28e8bd369353c729674146bcc28508bbb427a8d249af4fd491fe","src/core/custom.rs":"df2d6a8c5a64603822301522e9df4344022226301df846941d95930f1f4d99c4","src/core/data.rs":"c9d59eab2ab811bd950da52e6766c7df2367986c7a3a0d94d7aeb47d86f203ac","src/core/elements.rs":"76ab78bed1956fad030821b267a115511a99c61f33f2c6e588b5a8d4ee9b4204","src/core/exports.rs":"f37351587cd0cfa7608f94e0fcbfa09d41c7df1d123c47825992c364d5d9ff11","src/core/functions.rs":"c18b9872ac0c21048a3ce32e5e44e8e702f97a57fa1b3a07bdd98c7f6c820f09","src/core/globals.rs":"560d8e8c818d968cd8a101ab3c639f723b1c754716aa1178d1c4570efd84d010","src/core/imports.rs":"782bbc2e70b379831f85716c0f50c0221d1487c8cba26e8845028b2ae1962c0c","src/core/linking.rs":"5c7d5bce822fad92dc096ceafde878c6d25869004ca26dde1378e78ae37071c9","src/core/memories.rs":"840d15fcd9bd4a668298491c6a91455b145c5c7ff1adf8c769aaf49d6c664d3a","src/core/names.rs":"1337a5ab769e1a1b83113041d6fc59652db34a01ef44f14a2641b3748c216dc1","src/core/producers.rs":"e96156e2b87a4e9162b918b8f9cae870c5abc9410835651d74ac54f0ff2665e7","src/core/start.rs":"a01d4a91bcd93048977ccafc6af160357297450395bf598351f5a3e6d322e0de","src/core/tables.rs":"d5ae8c92c8707332eda6be6e5649f2f8042a1c6242494b0174cbee5e1971cace","src/core/tags.rs":"26d58904871d92f395eed67d4c25f0914b337d213bab2288011abe3ad31fa12b","src/core/types.rs":"988175ad3a47f4aca29c7fad9d594b54c5a5717fedf7ecd6296fbb45cb058999","src/lib.rs":"aaec7fa68ae0764721d7b4b6d0d935ea60380c6268cbd512480fee2f7a61b755","src/raw.rs":"a6a72cfe8f88ea6476eccee4acf362030ba2d2e5710215bc4f13cde7de6d71ae"},"package":"4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7"}
{"files":{"Cargo.toml":"017a8c6533b5524a7cc9c9c4f67c64fac705139e6690f792db35aa53a4060b3e","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"ac016c4843a7e1a5737255b39418732783592222dc518020730edf9dd46a1c13","src/component.rs":"469b853ac8bd9b034bc701f9fdf3da9c4c944b8a08438de1608d5d5d63e4765b","src/component/aliases.rs":"be5215154b872ed5664f3bfe2aa1391f36a2575aa9d53971ab61868a1c446e9d","src/component/canonicals.rs":"9ab83dc38f01be083e4f4b391214232d5527eb81f6adb459c0719da9c8c7e168","src/component/components.rs":"07b8cae3a400e1955cc39d142569910b4fef2136021053d0ddbd6404d810aa52","src/component/exports.rs":"25582534c0dec431c767c0e7f6cfb389cb597c24c8a80b72e81f08e25f13dadb","src/component/imports.rs":"c824ffeba530fe142655d3143b1c86e172485d46f05ea1c45fdb9f31ad22f00e","src/component/instances.rs":"4238b7cd93e987cbc3a7b57a397f82faea4cbc4b884cabb915f8f16ec9b02e20","src/component/modules.rs":"9e80907e72360fae4d8057b6b0e7a6b58edd7ba6aba6e63ba17346518e169617","src/component/names.rs":"f3b6691f822d53eb743b397a4e735786501cf000451753ae9a65031ae3249835","src/component/start.rs":"4055553d5c99c99abbc01bb8abb928ecb8b909d148b647a9977fbd444bb464a3","src/component/types.rs":"3d714f89e94293d285ad9347cc649788cdf45e4816f9ea9b91c5fb87084dbf44","src/core.rs":"a00656f82a623656c59a2d7230b40a5849a5083e117bc57061746f6e3022c7bb","src/core/code.rs":"ee841eab282781257f6d22f70d2061ffc106b93a0615aedd00f2b8d8feea0b50","src/core/custom.rs":"6883b79152f38ab902fa52a647098bcc64821ef3bc7b0f1cc9911e702b4f2856","src/core/data.rs":"c9d59eab2ab811bd950da52e6766c7df2367986c7a3a0d94d7aeb47d86f203ac","src/core/dump.rs":"8feaa532e3851186277ec1f4906e7fdc82c6399b211b8c928b16e293db1205b0","src/core/elements.rs":"35e64746d1924f37a101969e289fe0049d967c9b5452516d78ced4e1a7858b4d","src/core/exports.rs":"f37351587cd0cfa7608f94e0fcbfa09d41c7df1d123c47825992c364d5d9ff11","src/core/functions.rs":"c18b9872ac0c21048a3ce32e5e44e8e702f97a57fa1b3a07bdd98c7f6c820f09","src/core/globals.rs":"560d8e8c818d968cd8a101ab3c639f723b1c754716aa1178d1c4570efd84d010","src/core/imports.rs":"782bbc2e70b379831f85716c0f50c0221d1487c8cba26e8845028b2ae1962c0c","src/core/linking.rs":"2f1053d9c2671e91a2b6e253dd38921bfc5f1b8a1a047b10c843033fe0f492de","src/core/memories.rs":"840d15fcd9bd4a668298491c6a91455b145c5c7ff1adf8c769aaf49d6c664d3a","src/core/names.rs":"80e93b756cdd1234cd088f98be8dd720fadede755ed5b884e6adacd7ef050072","src/core/producers.rs":"f4916c1cf61e26170cd10fe350de7dad18005461c362909b9557c16c507517bc","src/core/start.rs":"a01d4a91bcd93048977ccafc6af160357297450395bf598351f5a3e6d322e0de","src/core/tables.rs":"d5ae8c92c8707332eda6be6e5649f2f8042a1c6242494b0174cbee5e1971cace","src/core/tags.rs":"26d58904871d92f395eed67d4c25f0914b337d213bab2288011abe3ad31fa12b","src/core/types.rs":"3d7cbd703608aa8a49116685ab41d4f505507bb53dfaadaf41620be12dea582b","src/lib.rs":"e61325ab8de1b4c404f4ee7665eef404fca2d23322a9c8f94efec8426edc166b","src/raw.rs":"a6a72cfe8f88ea6476eccee4acf362030ba2d2e5710215bc4f13cde7de6d71ae"},"package":"18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881"}

View File

@@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "wasm-encoder"
version = "0.25.0"
version = "0.29.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
description = """
A low-level WebAssembly encoder.

View File

@@ -113,7 +113,7 @@ impl Component {
// Magic
0x00, 0x61, 0x73, 0x6D,
// Version
0x0c, 0x00, 0x01, 0x00,
0x0d, 0x00, 0x01, 0x00,
];
/// Begin writing a new `Component`.

View File

@@ -1,4 +1,4 @@
use crate::{encode_section, ComponentSection, ComponentSectionId, Encode};
use crate::{encode_section, ComponentSection, ComponentSectionId, ComponentValType, Encode};
/// Represents options for canonical function definitions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -118,6 +118,32 @@ impl CanonicalFunctionSection {
self.num_added += 1;
self
}
/// Defines a function which will create an owned handle to the resource
/// specified by `ty_index`.
pub fn resource_new(&mut self, ty_index: u32) -> &mut Self {
self.bytes.push(0x02);
ty_index.encode(&mut self.bytes);
self.num_added += 1;
self
}
/// Defines a function which will drop the specified type of handle.
pub fn resource_drop(&mut self, ty: ComponentValType) -> &mut Self {
self.bytes.push(0x03);
ty.encode(&mut self.bytes);
self.num_added += 1;
self
}
/// Defines a function which will return the representation of the specified
/// resource type.
pub fn resource_rep(&mut self, ty_index: u32) -> &mut Self {
self.bytes.push(0x04);
ty_index.encode(&mut self.bytes);
self.num_added += 1;
self
}
}
impl Encode for CanonicalFunctionSection {

View File

@@ -2,7 +2,10 @@ use super::{
COMPONENT_SORT, CORE_MODULE_SORT, CORE_SORT, FUNCTION_SORT, INSTANCE_SORT, TYPE_SORT,
VALUE_SORT,
};
use crate::{encode_section, ComponentSection, ComponentSectionId, ComponentTypeRef, Encode};
use crate::{
encode_section, AsComponentExternName, ComponentSection, ComponentSectionId, ComponentTypeRef,
Encode,
};
/// Represents the kind of an export from a WebAssembly component.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -52,11 +55,12 @@ impl Encode for ComponentExportKind {
/// # Example
///
/// ```rust
/// use wasm_encoder::{Component, ComponentExportSection, ComponentExportKind};
/// use wasm_encoder::{Component, ComponentExportSection, ComponentExportKind, ComponentExternName};
///
/// // This exports a function named "foo"
/// let mut exports = ComponentExportSection::new();
/// exports.export("foo", "", ComponentExportKind::Func, 0, None);
/// let name = ComponentExternName::Kebab("foo");
/// exports.export(name, ComponentExportKind::Func, 0, None);
///
/// let mut component = Component::new();
/// component.section(&exports);
@@ -88,14 +92,12 @@ impl ComponentExportSection {
/// Define an export in the export section.
pub fn export(
&mut self,
name: &str,
url: &str,
name: impl AsComponentExternName,
kind: ComponentExportKind,
index: u32,
ty: Option<ComponentTypeRef>,
) -> &mut Self {
name.encode(&mut self.bytes);
url.encode(&mut self.bytes);
name.as_component_extern_name().encode(&mut self.bytes);
kind.encode(&mut self.bytes);
index.encode(&mut self.bytes);
match ty {

View File

@@ -6,14 +6,20 @@ use crate::{
/// Represents the possible type bounds for type references.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum TypeBounds {
/// The type is bounded by equality.
Eq,
/// The type is bounded by equality to the type index specified.
Eq(u32),
/// This type is a fresh resource type,
SubResource,
}
impl Encode for TypeBounds {
fn encode(&self, sink: &mut Vec<u8>) {
match self {
Self::Eq => sink.push(0x00),
Self::Eq(i) => {
sink.push(0x00);
i.encode(sink);
}
Self::SubResource => sink.push(0x01),
}
}
}
@@ -32,9 +38,7 @@ pub enum ComponentTypeRef {
/// 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, u32),
Type(TypeBounds),
/// The reference is to an instance type.
///
/// The index is expected to be a type index to an instance type.
@@ -68,10 +72,7 @@ impl Encode for ComponentTypeRef {
idx.encode(sink);
}
Self::Value(ty) => ty.encode(sink),
Self::Type(bounds, idx) => {
bounds.encode(sink);
idx.encode(sink);
}
Self::Type(bounds) => bounds.encode(sink),
}
}
}
@@ -81,7 +82,7 @@ impl Encode for ComponentTypeRef {
/// # Example
///
/// ```rust
/// use wasm_encoder::{Component, ComponentTypeSection, PrimitiveValType, ComponentImportSection, ComponentTypeRef};
/// use wasm_encoder::{Component, ComponentTypeSection, PrimitiveValType, ComponentImportSection, ComponentTypeRef, ComponentExternName};
///
/// let mut types = ComponentTypeSection::new();
///
@@ -98,7 +99,8 @@ impl Encode for ComponentTypeRef {
///
/// // This imports a function named `f` with the type defined above
/// let mut imports = ComponentImportSection::new();
/// imports.import("f", "", ComponentTypeRef::Func(0));
/// let name = ComponentExternName::Kebab("f");
/// imports.import(name, ComponentTypeRef::Func(0));
///
/// let mut component = Component::new();
/// component.section(&types);
@@ -129,9 +131,8 @@ impl ComponentImportSection {
}
/// Define an import in the component import section.
pub fn import(&mut self, name: &str, url: &str, ty: ComponentTypeRef) -> &mut Self {
name.encode(&mut self.bytes);
url.encode(&mut self.bytes);
pub fn import(&mut self, name: impl AsComponentExternName, ty: ComponentTypeRef) -> &mut Self {
name.as_component_extern_name().encode(&mut self.bytes);
ty.encode(&mut self.bytes);
self.num_added += 1;
self
@@ -149,3 +150,51 @@ impl ComponentSection for ComponentImportSection {
ComponentSectionId::Import.into()
}
}
/// The different names that can be assigned to component imports
#[derive(Debug, Copy, Clone)]
pub enum ComponentExternName<'a> {
/// This is a "kebab name" along the lines of "a-foo-bar"
Kebab(&'a str),
/// This is an ID along the lines of "wasi:http/types@2.0"
Interface(&'a str),
}
impl Encode for ComponentExternName<'_> {
fn encode(&self, sink: &mut Vec<u8>) {
match self {
ComponentExternName::Kebab(name) => {
sink.push(0x00);
name.encode(sink);
}
ComponentExternName::Interface(name) => {
sink.push(0x01);
name.encode(sink);
}
}
}
}
/// Helper trait to convert into a `ComponentExternName` either from that type
/// or from a string.
pub trait AsComponentExternName {
/// Converts this receiver into a `ComponentExternName`.
fn as_component_extern_name(&self) -> ComponentExternName<'_>;
}
impl AsComponentExternName for ComponentExternName<'_> {
fn as_component_extern_name(&self) -> ComponentExternName<'_> {
*self
}
}
impl<S: AsRef<str>> AsComponentExternName for S {
fn as_component_extern_name(&self) -> ComponentExternName<'_> {
let s = self.as_ref();
if s.contains("/") {
ComponentExternName::Interface(s)
} else {
ComponentExternName::Kebab(s)
}
}
}

View File

@@ -1,6 +1,7 @@
use super::CORE_INSTANCE_SORT;
use crate::{
encode_section, ComponentExportKind, ComponentSection, ComponentSectionId, Encode, ExportKind,
encode_section, ComponentExportKind, ComponentExternName, ComponentSection, ComponentSectionId,
Encode, ExportKind,
};
/// Represents an argument to a module instantiation.
@@ -114,10 +115,10 @@ impl ComponentSection for InstanceSection {
/// # Example
///
/// ```rust
/// use wasm_encoder::{Component, ComponentInstanceSection, ComponentExportKind};
/// use wasm_encoder::{Component, ComponentInstanceSection, ComponentExportKind, ComponentExternName};
///
/// let mut instances = ComponentInstanceSection::new();
/// instances.export_items([("foo", ComponentExportKind::Func, 0)]);
/// instances.export_items([(ComponentExternName::Kebab("foo"), ComponentExportKind::Func, 0)]);
/// instances.instantiate(1, [("foo", ComponentExportKind::Instance, 0)]);
///
/// let mut component = Component::new();
@@ -170,7 +171,7 @@ impl ComponentInstanceSection {
/// Define an instance by exporting items.
pub fn export_items<'a, E>(&mut self, exports: E) -> &mut Self
where
E: IntoIterator<Item = (&'a str, ComponentExportKind, u32)>,
E: IntoIterator<Item = (ComponentExternName<'a>, ComponentExportKind, u32)>,
E::IntoIter: ExactSizeIterator,
{
let exports = exports.into_iter();

View File

@@ -1,3 +1,5 @@
use std::borrow::Cow;
use super::*;
use crate::{encoding_size, CustomSection, Encode, ExportKind, NameMap, SectionId};
@@ -128,8 +130,8 @@ impl ComponentNameSection {
/// View the encoded section as a CustomSection.
pub fn as_custom<'a>(&'a self) -> CustomSection<'a> {
CustomSection {
name: "component-name",
data: &self.bytes,
name: "component-name".into(),
data: Cow::Borrowed(&self.bytes),
}
}
}

View File

@@ -1,7 +1,7 @@
use super::CORE_TYPE_SORT;
use crate::{
encode_section, Alias, ComponentExportKind, ComponentOuterAliasKind, ComponentSection,
ComponentSectionId, ComponentTypeRef, Encode, EntityType, ValType,
encode_section, Alias, AsComponentExternName, ComponentExportKind, ComponentOuterAliasKind,
ComponentSection, ComponentSectionId, ComponentTypeRef, Encode, EntityType, ValType,
};
/// Represents the type of a core module.
@@ -245,10 +245,9 @@ impl ComponentType {
}
/// Defines an import in this component type.
pub fn import(&mut self, name: &str, url: &str, ty: ComponentTypeRef) -> &mut Self {
pub fn import(&mut self, name: impl AsComponentExternName, ty: ComponentTypeRef) -> &mut Self {
self.bytes.push(0x03);
name.encode(&mut self.bytes);
url.encode(&mut self.bytes);
name.as_component_extern_name().encode(&mut self.bytes);
ty.encode(&mut self.bytes);
self.num_added += 1;
match ty {
@@ -259,10 +258,9 @@ impl ComponentType {
}
/// Defines an export in this component type.
pub fn export(&mut self, name: &str, url: &str, ty: ComponentTypeRef) -> &mut Self {
pub fn export(&mut self, name: impl AsComponentExternName, ty: ComponentTypeRef) -> &mut Self {
self.bytes.push(0x04);
name.encode(&mut self.bytes);
url.encode(&mut self.bytes);
name.as_component_extern_name().encode(&mut self.bytes);
ty.encode(&mut self.bytes);
self.num_added += 1;
match ty {
@@ -324,8 +322,8 @@ impl InstanceType {
}
/// Defines an export in this instance type.
pub fn export(&mut self, name: &str, url: &str, ty: ComponentTypeRef) -> &mut Self {
self.0.export(name, url, ty);
pub fn export(&mut self, name: impl AsComponentExternName, ty: ComponentTypeRef) -> &mut Self {
self.0.export(name, ty);
self
}
@@ -442,6 +440,19 @@ impl<'a> ComponentTypeEncoder<'a> {
pub fn defined_type(self) -> ComponentDefinedTypeEncoder<'a> {
ComponentDefinedTypeEncoder(self.0)
}
/// Define a resource type.
pub fn resource(self, rep: ValType, dtor: Option<u32>) {
self.0.push(0x3f);
rep.encode(self.0);
match dtor {
Some(i) => {
self.0.push(0x01);
i.encode(self.0);
}
None => self.0.push(0x00),
}
}
}
/// Represents a primitive component value type.
@@ -639,6 +650,18 @@ impl ComponentDefinedTypeEncoder<'_> {
ok.encode(self.0);
err.encode(self.0);
}
/// Define a `own` handle type
pub fn own(self, idx: u32) {
self.0.push(0x69);
idx.encode(self.0);
}
/// Define a `borrow` handle type
pub fn borrow(self, idx: u32) {
self.0.push(0x68);
idx.encode(self.0);
}
}
/// An encoder for the type section of WebAssembly components.

View File

@@ -1,6 +1,7 @@
mod code;
mod custom;
mod data;
mod dump;
mod elements;
mod exports;
mod functions;
@@ -18,6 +19,7 @@ mod types;
pub use code::*;
pub use custom::*;
pub use data::*;
pub use dump::*;
pub use elements::*;
pub use exports::*;
pub use functions::*;

View File

@@ -322,9 +322,9 @@ pub enum Instruction<'a> {
BrOnNonNull(u32),
Return,
Call(u32),
CallRef(HeapType),
CallRef(u32),
CallIndirect { ty: u32, table: u32 },
ReturnCallRef(HeapType),
ReturnCallRef(u32),
ReturnCall(u32),
ReturnCallIndirect { ty: u32, table: u32 },
Throw(u32),
@@ -522,6 +522,11 @@ pub enum Instruction<'a> {
RefFunc(u32),
RefAsNonNull,
// GC types instructions.
I31New,
I31GetS,
I31GetU,
// Bulk memory instructions.
TableInit { elem_index: u32, table: u32 },
ElemDrop(u32),
@@ -1315,6 +1320,20 @@ impl Encode for Instruction<'_> {
}
Instruction::RefAsNonNull => sink.push(0xD3),
// GC instructions.
Instruction::I31New => {
sink.push(0xfb);
sink.push(0x20)
}
Instruction::I31GetS => {
sink.push(0xfb);
sink.push(0x21)
}
Instruction::I31GetU => {
sink.push(0xfb);
sink.push(0x22)
}
// Bulk memory instructions.
Instruction::TableInit { elem_index, table } => {
sink.push(0xfc);

View File

@@ -1,12 +1,14 @@
use std::borrow::Cow;
use crate::{encoding_size, Encode, Section, SectionId};
/// A custom section holding arbitrary data.
#[derive(Clone, Debug)]
pub struct CustomSection<'a> {
/// The name of this custom section.
pub name: &'a str,
pub name: Cow<'a, str>,
/// This custom section's data.
pub data: &'a [u8],
pub data: Cow<'a, [u8]>,
}
impl Encode for CustomSection<'_> {
@@ -14,7 +16,7 @@ impl Encode for CustomSection<'_> {
let encoded_name_len = encoding_size(u32::try_from(self.name.len()).unwrap());
(encoded_name_len + self.name.len() + self.data.len()).encode(sink);
self.name.encode(sink);
sink.extend(self.data);
sink.extend(&*self.data);
}
}
@@ -31,8 +33,8 @@ mod tests {
#[test]
fn test_custom_section() {
let custom = CustomSection {
name: "test",
data: &[11, 22, 33, 44],
name: "test".into(),
data: Cow::Borrowed(&[11, 22, 33, 44]),
};
let mut encoded = vec![];

View File

@@ -0,0 +1,627 @@
use std::borrow::Cow;
use crate::{CustomSection, Encode, Section};
/// The "core" custom section for coredumps, as described in the
/// [tool-conventions
/// repository](https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md).
///
/// There are four sections that comprise a core dump:
/// - "core", which contains the name of the core dump
/// - "coremodules", a listing of modules
/// - "coreinstances", a listing of module instances
/// - "corestack", a listing of frames for a specific thread
///
/// # Example of how these could be constructed and encoded into a module:
///
/// ```
/// use wasm_encoder::{
/// CoreDumpInstancesSection, CoreDumpModulesSection, CoreDumpSection, CoreDumpStackSection,
/// CoreDumpValue, Module,
/// };
/// let core = CoreDumpSection::new("MyModule.wasm");
///
/// let mut modules = CoreDumpModulesSection::new();
/// modules.module("my_module");
///
/// let mut instances = CoreDumpInstancesSection::new();
/// let module_idx = 0;
/// let memories = vec![1];
/// let globals = vec![2];
/// instances.instance(module_idx, memories, globals);
///
/// let mut thread = CoreDumpStackSection::new("main");
/// let instance_index = 0;
/// let func_index = 42;
/// let code_offset = 0x1234;
/// let locals = vec![CoreDumpValue::I32(1)];
/// let stack = vec![CoreDumpValue::I32(2)];
/// thread.frame(instance_index, func_index, code_offset, locals, stack);
///
/// let mut module = Module::new();
/// module.section(&core);
/// module.section(&modules);
/// module.section(&instances);
/// module.section(&thread);
/// ```
#[derive(Clone, Debug, Default)]
pub struct CoreDumpSection {
name: String,
}
impl CoreDumpSection {
/// Create a new core dump section encoder
pub fn new(name: impl Into<String>) -> Self {
let name = name.into();
CoreDumpSection { name }
}
/// View the encoded section as a CustomSection.
fn as_custom<'a>(&'a self) -> CustomSection<'a> {
let mut data = vec![0];
self.name.encode(&mut data);
CustomSection {
name: "core".into(),
data: Cow::Owned(data),
}
}
}
impl Encode for CoreDumpSection {
fn encode(&self, sink: &mut Vec<u8>) {
self.as_custom().encode(sink);
}
}
impl Section for CoreDumpSection {
fn id(&self) -> u8 {
crate::core::SectionId::Custom as u8
}
}
/// The "coremodules" custom section for coredumps which lists the names of the
/// modules
///
/// # Example
///
/// ```
/// use wasm_encoder::{CoreDumpModulesSection, Module};
/// let mut modules_section = CoreDumpModulesSection::new();
/// modules_section.module("my_module");
/// let mut module = Module::new();
/// module.section(&modules_section);
/// ```
#[derive(Debug)]
pub struct CoreDumpModulesSection {
num_added: u32,
bytes: Vec<u8>,
}
impl CoreDumpModulesSection {
/// Create a new core dump modules section encoder.
pub fn new() -> Self {
CoreDumpModulesSection {
bytes: vec![],
num_added: 0,
}
}
/// View the encoded section as a CustomSection.
pub fn as_custom(&self) -> CustomSection<'_> {
let mut data = vec![];
self.num_added.encode(&mut data);
data.extend(self.bytes.iter().copied());
CustomSection {
name: "coremodules".into(),
data: Cow::Owned(data),
}
}
/// Encode a module name into the section's bytes.
pub fn module(&mut self, module_name: impl AsRef<str>) -> &mut Self {
self.bytes.push(0x0);
module_name.as_ref().encode(&mut self.bytes);
self.num_added += 1;
self
}
/// The number of modules that are encoded in the section.
pub fn len(&self) -> u32 {
self.num_added
}
}
impl Encode for CoreDumpModulesSection {
fn encode(&self, sink: &mut Vec<u8>) {
self.as_custom().encode(sink);
}
}
impl Section for CoreDumpModulesSection {
fn id(&self) -> u8 {
crate::core::SectionId::Custom as u8
}
}
/// The "coreinstances" section for the core dump
#[derive(Debug)]
pub struct CoreDumpInstancesSection {
num_added: u32,
bytes: Vec<u8>,
}
impl CoreDumpInstancesSection {
/// Create a new core dump instances section encoder.
pub fn new() -> Self {
CoreDumpInstancesSection {
bytes: vec![],
num_added: 0,
}
}
/// View the encoded section as a CustomSection.
pub fn as_custom(&self) -> CustomSection<'_> {
let mut data = vec![];
self.num_added.encode(&mut data);
data.extend(self.bytes.iter().copied());
CustomSection {
name: "coreinstances".into(),
data: Cow::Owned(data),
}
}
/// Encode an instance into the section's bytes.
pub fn instance<M, G>(&mut self, module_index: u32, memories: M, globals: G) -> &mut Self
where
M: IntoIterator<Item = u32>,
<M as IntoIterator>::IntoIter: ExactSizeIterator,
G: IntoIterator<Item = u32>,
<G as IntoIterator>::IntoIter: ExactSizeIterator,
{
self.bytes.push(0x0);
module_index.encode(&mut self.bytes);
crate::encode_vec(memories, &mut self.bytes);
crate::encode_vec(globals, &mut self.bytes);
self.num_added += 1;
self
}
/// The number of modules that are encoded in the section.
pub fn len(&self) -> u32 {
self.num_added
}
}
impl Encode for CoreDumpInstancesSection {
fn encode(&self, sink: &mut Vec<u8>) {
self.as_custom().encode(sink);
}
}
impl Section for CoreDumpInstancesSection {
fn id(&self) -> u8 {
crate::core::SectionId::Custom as u8
}
}
/// A "corestack" custom section as described in the [tool-conventions
/// repository](https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md)
///
/// # Example
///
/// ```
/// use wasm_encoder::{CoreDumpStackSection, Module, CoreDumpValue};
/// let mut thread = CoreDumpStackSection::new("main");
///
/// let instance_index = 0;
/// let func_index = 42;
/// let code_offset = 0x1234;
/// let locals = vec![CoreDumpValue::I32(1)];
/// let stack = vec![CoreDumpValue::I32(2)];
/// thread.frame(instance_index, func_index, code_offset, locals, stack);
///
/// let mut module = Module::new();
/// module.section(&thread);
/// ```
#[derive(Clone, Debug, Default)]
pub struct CoreDumpStackSection {
frame_bytes: Vec<u8>,
count: u32,
name: String,
}
impl CoreDumpStackSection {
/// Create a new core dump stack section encoder.
pub fn new(name: impl Into<String>) -> Self {
let name = name.into();
CoreDumpStackSection {
frame_bytes: Vec::new(),
count: 0,
name,
}
}
/// Add a stack frame to this coredump stack section.
pub fn frame<L, S>(
&mut self,
instanceidx: u32,
funcidx: u32,
codeoffset: u32,
locals: L,
stack: S,
) -> &mut Self
where
L: IntoIterator<Item = CoreDumpValue>,
<L as IntoIterator>::IntoIter: ExactSizeIterator,
S: IntoIterator<Item = CoreDumpValue>,
<S as IntoIterator>::IntoIter: ExactSizeIterator,
{
self.count += 1;
self.frame_bytes.push(0);
instanceidx.encode(&mut self.frame_bytes);
funcidx.encode(&mut self.frame_bytes);
codeoffset.encode(&mut self.frame_bytes);
crate::encode_vec(locals, &mut self.frame_bytes);
crate::encode_vec(stack, &mut self.frame_bytes);
self
}
/// View the encoded section as a CustomSection.
pub fn as_custom<'a>(&'a self) -> CustomSection<'a> {
let mut data = vec![0];
self.name.encode(&mut data);
self.count.encode(&mut data);
data.extend(&self.frame_bytes);
CustomSection {
name: "corestack".into(),
data: Cow::Owned(data),
}
}
}
impl Encode for CoreDumpStackSection {
fn encode(&self, sink: &mut Vec<u8>) {
self.as_custom().encode(sink);
}
}
impl Section for CoreDumpStackSection {
fn id(&self) -> u8 {
crate::core::SectionId::Custom as u8
}
}
/// 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 Encode for CoreDumpValue {
fn encode(&self, sink: &mut Vec<u8>) {
match self {
CoreDumpValue::Missing => sink.push(0x01),
CoreDumpValue::I32(x) => {
sink.push(0x7F);
x.encode(sink);
}
CoreDumpValue::I64(x) => {
sink.push(0x7E);
x.encode(sink);
}
CoreDumpValue::F32(x) => {
sink.push(0x7D);
x.encode(sink);
}
CoreDumpValue::F64(x) => {
sink.push(0x7C);
x.encode(sink);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Module;
use wasmparser::{BinaryReader, FromReader, Parser, Payload};
// Create new core dump section and test whether it is properly encoded and
// parsed back out by wasmparser
#[test]
fn test_roundtrip_core() {
let core = CoreDumpSection::new("test.wasm");
let mut module = Module::new();
module.section(&core);
let wasm_bytes = module.finish();
let mut parser = Parser::new(0).parse_all(&wasm_bytes);
match parser.next() {
Some(Ok(Payload::Version { .. })) => {}
_ => panic!(""),
}
let payload = parser
.next()
.expect("parser is not empty")
.expect("element is a payload");
match payload {
Payload::CustomSection(section) => {
assert_eq!(section.name(), "core");
let core = wasmparser::CoreDumpSection::from_reader(&mut BinaryReader::new(
section.data(),
))
.expect("data is readable into a core dump section");
assert_eq!(core.name, "test.wasm");
}
_ => panic!("unexpected payload"),
}
}
#[test]
fn test_roundtrip_coremodules() {
let mut coremodules = CoreDumpModulesSection::new();
coremodules.module("test_module");
let mut module = crate::Module::new();
module.section(&coremodules);
let wasm_bytes = module.finish();
let mut parser = Parser::new(0).parse_all(&wasm_bytes);
match parser.next() {
Some(Ok(Payload::Version { .. })) => {}
_ => panic!(""),
}
let payload = parser
.next()
.expect("parser is not empty")
.expect("element is a payload");
match payload {
Payload::CustomSection(section) => {
assert_eq!(section.name(), "coremodules");
let modules = wasmparser::CoreDumpModulesSection::from_reader(
&mut BinaryReader::new(section.data()),
)
.expect("data is readable into a core dump modules section");
assert_eq!(modules.modules[0], "test_module");
}
_ => panic!("unexpected payload"),
}
}
#[test]
fn test_roundtrip_coreinstances() {
let mut coreinstances = CoreDumpInstancesSection::new();
let module_index = 0;
let memories = vec![42];
let globals = vec![17];
coreinstances.instance(module_index, memories, globals);
let mut module = Module::new();
module.section(&coreinstances);
let wasm_bytes = module.finish();
let mut parser = Parser::new(0).parse_all(&wasm_bytes);
match parser.next() {
Some(Ok(Payload::Version { .. })) => {}
_ => panic!(""),
}
let payload = parser
.next()
.expect("parser is not empty")
.expect("element is a payload");
match payload {
Payload::CustomSection(section) => {
assert_eq!(section.name(), "coreinstances");
let coreinstances = wasmparser::CoreDumpInstancesSection::from_reader(
&mut BinaryReader::new(section.data()),
)
.expect("data is readable into a core dump instances section");
assert_eq!(coreinstances.instances.len(), 1);
let instance = coreinstances
.instances
.first()
.expect("instance is encoded");
assert_eq!(instance.module_index, 0);
assert_eq!(instance.memories.len(), 1);
assert_eq!(instance.globals.len(), 1);
}
_ => panic!("unexpected payload"),
}
}
// Create new corestack section and test whether it is properly encoded and
// parsed back out by wasmparser
#[test]
fn test_roundtrip_corestack() {
let mut corestack = CoreDumpStackSection::new("main");
corestack.frame(
0,
12,
0,
vec![CoreDumpValue::I32(10)],
vec![CoreDumpValue::I32(42)],
);
let mut module = Module::new();
module.section(&corestack);
let wasm_bytes = module.finish();
let mut parser = Parser::new(0).parse_all(&wasm_bytes);
match parser.next() {
Some(Ok(Payload::Version { .. })) => {}
_ => panic!(""),
}
let payload = parser
.next()
.expect("parser is not empty")
.expect("element is a payload");
match payload {
Payload::CustomSection(section) => {
assert_eq!(section.name(), "corestack");
let corestack = wasmparser::CoreDumpStackSection::from_reader(
&mut BinaryReader::new(section.data()),
)
.expect("data is readable into a core dump stack section");
assert_eq!(corestack.name, "main");
assert_eq!(corestack.frames.len(), 1);
let frame = corestack
.frames
.first()
.expect("frame is encoded in corestack");
assert_eq!(frame.instanceidx, 0);
assert_eq!(frame.funcidx, 12);
assert_eq!(frame.codeoffset, 0);
assert_eq!(frame.locals.len(), 1);
match frame.locals.first().expect("frame contains a local") {
&wasmparser::CoreDumpValue::I32(val) => assert_eq!(val, 10),
_ => panic!("unexpected local value"),
}
assert_eq!(frame.stack.len(), 1);
match frame.stack.first().expect("stack contains a value") {
&wasmparser::CoreDumpValue::I32(val) => assert_eq!(val, 42),
_ => panic!("unexpected stack value"),
}
}
_ => panic!("unexpected payload"),
}
}
#[test]
fn test_encode_coredump_section() {
let core = CoreDumpSection::new("test");
let mut encoded = vec![];
core.encode(&mut encoded);
#[rustfmt::skip]
assert_eq!(encoded, vec![
// section length
11,
// name length
4,
// section name (core)
b'c',b'o',b'r',b'e',
// process-info (0, data length, data)
0, 4, b't', b'e', b's', b't',
]);
}
#[test]
fn test_encode_coremodules_section() {
let mut modules = CoreDumpModulesSection::new();
modules.module("mod1");
modules.module("mod2");
let mut encoded = vec![];
modules.encode(&mut encoded);
#[rustfmt::skip]
assert_eq!(encoded, vec![
// section length
25,
// name length
11,
// section name (coremodules)
b'c',b'o',b'r',b'e',b'm',b'o',b'd',b'u',b'l',b'e',b's',
// module count
2,
// 0x0, name-length, module name (mod1)
0x0, 4, b'm',b'o',b'd',b'1',
// 0x0, name-length, module name (mod2)
0x0, 4, b'm',b'o',b'd',b'2'
]);
}
#[test]
fn test_encode_coreinstances_section() {
let mut instances = CoreDumpInstancesSection::new();
instances.instance(0, vec![42], vec![17]);
let mut encoded = vec![];
instances.encode(&mut encoded);
#[rustfmt::skip]
assert_eq!(encoded, vec![
// section length
21,
// name length
13,
// section name (coreinstances)
b'c',b'o',b'r',b'e',b'i',b'n',b's',b't',b'a',b'n',b'c',b'e',b's',
// instance count
1,
// 0x0, module_idx
0x0, 0,
// memories count, memories
1, 42,
// globals count, globals
1, 17
]);
}
#[test]
fn test_encode_corestack_section() {
let mut thread = CoreDumpStackSection::new("main");
thread.frame(
0,
42,
51,
vec![CoreDumpValue::I32(1)],
vec![CoreDumpValue::I32(2)],
);
let mut encoded = vec![];
thread.encode(&mut encoded);
#[rustfmt::skip]
assert_eq!(
encoded,
vec![
// section length
27,
// length of name.
9,
// section name (corestack)
b'c',b'o',b'r',b'e',b's',b't',b'a',b'c',b'k',
// 0x0, thread name length
0, 4,
// thread name (main)
b'm',b'a',b'i',b'n',
// frame count
1,
// 0x0, instanceidx, funcidx, codeoffset
0, 0, 42, 51,
// local count
1,
// local value type
0x7F,
// local value
1,
// stack count
1,
// stack value type
0x7F,
// stack value
2
]
);
}
}

View File

@@ -111,7 +111,7 @@ impl ElementSection {
ElementMode::Active {
table: None,
offset,
} => {
} if segment.element_type == RefType::FUNCREF => {
(/* 0x00 | */expr_bit).encode(&mut self.bytes);
offset.encode(&mut self.bytes);
}
@@ -123,12 +123,9 @@ impl ElementSection {
segment.element_type.encode(&mut self.bytes);
}
}
ElementMode::Active {
table: Some(i),
offset,
} => {
ElementMode::Active { table, offset } => {
(0x02 | expr_bit).encode(&mut self.bytes);
i.encode(&mut self.bytes);
table.unwrap_or(0).encode(&mut self.bytes);
offset.encode(&mut self.bytes);
if expr_bit == 0 {
self.bytes.push(0x00); // elemkind == funcref

View File

@@ -1,3 +1,5 @@
use std::borrow::Cow;
use crate::{encode_section, CustomSection, Encode, Section, SectionId};
const VERSION: u32 = 2;
@@ -72,8 +74,8 @@ impl Default for LinkingSection {
impl Encode for LinkingSection {
fn encode(&self, sink: &mut Vec<u8>) {
CustomSection {
name: "linking",
data: &self.bytes,
name: "linking".into(),
data: Cow::Borrowed(&self.bytes),
}
.encode(sink);
}

View File

@@ -1,3 +1,5 @@
use std::borrow::Cow;
use crate::{encoding_size, CustomSection, Encode, Section, SectionId};
/// An encoder for the custom `name` section.
@@ -153,8 +155,8 @@ impl NameSection {
/// View the encoded section as a CustomSection.
pub fn as_custom<'a>(&'a self) -> CustomSection<'a> {
CustomSection {
name: "name",
data: &self.bytes,
name: "name".into(),
data: Cow::Borrowed(&self.bytes),
}
}
}

View File

@@ -1,3 +1,5 @@
use std::borrow::Cow;
use crate::{CustomSection, Encode, Section, SectionId};
/// An encoder for the [producers custom
@@ -61,8 +63,8 @@ impl Encode for ProducersSection {
data.extend(&self.bytes);
CustomSection {
name: "producers",
data: &data,
name: "producers".into(),
data: Cow::Borrowed(&data),
}
.encode(sink);
}

View File

@@ -1,5 +1,16 @@
use crate::{encode_section, Encode, Section, SectionId};
/// Array or struct field type.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum StorageType {
/// The `i8` type.
I8,
/// The `i16` type.
I16,
/// A value type.
Val(ValType),
}
/// The type of a core WebAssembly value.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ValType {
@@ -30,6 +41,16 @@ impl ValType {
pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF);
}
impl Encode for StorageType {
fn encode(&self, sink: &mut Vec<u8>) {
match self {
StorageType::I8 => sink.push(0x7A),
StorageType::I16 => sink.push(0x79),
StorageType::Val(vt) => vt.encode(sink),
}
}
}
impl Encode for ValType {
fn encode(&self, sink: &mut Vec<u8>) {
match self {
@@ -99,12 +120,29 @@ impl From<RefType> for ValType {
/// Part of the function references proposal.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum HeapType {
/// A function reference. When nullable, equivalent to `funcref`
/// Untyped (any) function.
Func,
/// An extern reference. When nullable, equivalent to `externref`
/// External heap type.
Extern,
/// A reference to a particular index in a table.
TypedFunc(u32),
/// The `any` heap type. The common supertype (a.k.a. top) of all internal types.
Any,
/// The `none` heap type. The common subtype (a.k.a. bottom) of all internal types.
None,
/// The `noextern` heap type. The common subtype (a.k.a. bottom) of all external types.
NoExtern,
/// The `nofunc` heap type. The common subtype (a.k.a. bottom) of all function types.
NoFunc,
/// The `eq` heap type. The common supertype of all referenceable types on which comparison
/// (ref.eq) is allowed.
Eq,
/// The `struct` heap type. The common supertype of all struct types.
Struct,
/// The `array` heap type. The common supertype of all array types.
Array,
/// The i31 heap type.
I31,
/// User defined type at the given index.
Indexed(u32),
}
impl Encode for HeapType {
@@ -112,9 +150,17 @@ impl Encode for HeapType {
match self {
HeapType::Func => sink.push(0x70),
HeapType::Extern => sink.push(0x6F),
HeapType::Any => sink.push(0x6E),
HeapType::None => sink.push(0x65),
HeapType::NoExtern => sink.push(0x69),
HeapType::NoFunc => sink.push(0x68),
HeapType::Eq => sink.push(0x6D),
HeapType::Struct => sink.push(0x67),
HeapType::Array => sink.push(0x66),
HeapType::I31 => sink.push(0x6A),
// Note that this is encoded as a signed type rather than unsigned
// as it's decoded as an s33
HeapType::TypedFunc(i) => i64::from(*i).encode(sink),
HeapType::Indexed(i) => i64::from(*i).encode(sink),
}
}
}
@@ -176,6 +222,15 @@ impl TypeSection {
self.num_added += 1;
self
}
/// Define an array type in this type section.
pub fn array(&mut self, ty: StorageType, mutable: bool) -> &mut Self {
self.bytes.push(0x5e);
ty.encode(&mut self.bytes);
self.bytes.push(mutable as u8);
self.num_added += 1;
self
}
}
impl Encode for TypeSection {

View File

@@ -144,6 +144,33 @@ impl Encode for i64 {
}
}
impl Encode for f32 {
fn encode(&self, sink: &mut Vec<u8>) {
let bits = self.to_bits();
sink.extend(bits.to_le_bytes())
}
}
impl Encode for f64 {
fn encode(&self, sink: &mut Vec<u8>) {
let bits = self.to_bits();
sink.extend(bits.to_le_bytes())
}
}
fn encode_vec<T, V>(elements: V, sink: &mut Vec<u8>)
where
T: Encode,
V: IntoIterator<Item = T>,
V::IntoIter: ExactSizeIterator,
{
let elements = elements.into_iter();
u32::try_from(elements.len()).unwrap().encode(sink);
for x in elements {
x.encode(sink);
}
}
impl<T> Encode for Option<T>
where
T: Encode,
@@ -183,6 +210,6 @@ mod test {
#[test]
fn it_encodes_an_empty_component() {
let bytes = Component::new().finish();
assert_eq!(bytes, [0x00, b'a', b's', b'm', 0x0c, 0x00, 0x01, 0x00]);
assert_eq!(bytes, [0x00, b'a', b's', b'm', 0x0d, 0x00, 0x01, 0x00]);
}
}

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"3fab85439f91b5b0dfc20abd90cabf4a9f29ab1c422ab1308851c344302d32b3","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"9202d01e78acf04e38e23e162a91c20ece8968f6172c87bfa6f18bf0b3f27d74","benches/corpus.rs":"2df29556be0799f0cb1f32c8d0ae5ba0c4b9815cf4d59a8b71744d926c0693a0","src/component.rs":"32f93aac210f70fbc3f870c400993f4e7ff780ca6e111453a4dc4545906887e8","src/component/encode.rs":"09eddb96b5b607a87673714208c6d43b247ec4870168661d27d2c3ce92c43afd","src/config.rs":"3d33dea22d53081504e13fea4b5c99898624f26ed03013a831bae0c9759823fc","src/core.rs":"40949add77c2b90e9f3fc0ce6862a7c83ac03d5dd9babde5abee536d6fe074e6","src/core/code_builder.rs":"60e407a758ff58aafdcf4eb4e9141aca2ab7c7fe09d6644bc6e31043f88d0d69","src/core/code_builder/no_traps.rs":"e595dbde06551f5f8b23e03cfac0634beacab08e6243c66d6ffda95079212b24","src/core/encode.rs":"b4cc82895e3c3afe26e2e62bbdd63c44e7c1f091133e49f0eaf7e7ec22400e40","src/core/terminate.rs":"d24af5206a13aee7d6a6ea900ccdf088c09d053c36026cf1607cc38c972b3ba9","src/lib.rs":"77ed926d64d325a73613f1c307db39c65c428a1eafae114033c73a60da586fd3","tests/component.rs":"54c69ebdda583207f9f0467a600ee0ca87fbee6b365e97ec3deff7b46bd6af06","tests/core.rs":"da9e27e7057a7cd9435666d4bd4d57cec03b0d5d74bad289373db7beb8a2e1a6"},"package":"549cb78be46f43ad6746402871336cb6a989127fb847e93eb6ba0817647485a6"}
{"files":{"Cargo.toml":"f896f021b268aca35dc7d09e988612fc7651c586e38a9b074f3532674eb4e341","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"9202d01e78acf04e38e23e162a91c20ece8968f6172c87bfa6f18bf0b3f27d74","benches/corpus.rs":"2df29556be0799f0cb1f32c8d0ae5ba0c4b9815cf4d59a8b71744d926c0693a0","src/component.rs":"f11ed5adb46a746d875c15a7152e44e5ea1ba6bd270e160ce3b854e50d6ffc18","src/component/encode.rs":"b8625f9daa3eab0f405e6c4361b076e5ff7f2ebeb8beb6b7c22af35b8aadbf47","src/config.rs":"009364da9fb55ebe2625e2fa9a9fdc9033952f40304e353582e7bb22b10da4bf","src/core.rs":"cb38f2771d495f3af7d83e52750270bf781b49238821fb4a9e663d65fc8e35a5","src/core/code_builder.rs":"60e407a758ff58aafdcf4eb4e9141aca2ab7c7fe09d6644bc6e31043f88d0d69","src/core/code_builder/no_traps.rs":"e595dbde06551f5f8b23e03cfac0634beacab08e6243c66d6ffda95079212b24","src/core/encode.rs":"b4cc82895e3c3afe26e2e62bbdd63c44e7c1f091133e49f0eaf7e7ec22400e40","src/core/terminate.rs":"d24af5206a13aee7d6a6ea900ccdf088c09d053c36026cf1607cc38c972b3ba9","src/lib.rs":"07641b625f69b57e6e3275a6bdeb015fe68e5e1582cfd21c33395294caf0a84b","tests/component.rs":"44684e990e832590a0b348ce33fadf39ee55dec52fdfa9353570edcbf65325ee","tests/core.rs":"eafed78cccad968e64283505bb6d8ce905af5e1cabed3bd36abeba6eec2a0a9b"},"package":"027ec1c470cd5d56c43b8e02040250b136ddb5975dd76a1c16915137f5f17e76"}

View File

@@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "wasm-smith"
version = "0.12.5"
version = "0.12.10"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
exclude = ["/benches/corpus"]
description = "A WebAssembly test case generator"
@@ -50,10 +50,10 @@ features = ["derive"]
optional = true
[dependencies.wasm-encoder]
version = "0.25.0"
version = "0.29.0"
[dependencies.wasmparser]
version = "0.102.0"
version = "0.107.0"
[dev-dependencies.criterion]
version = "0.3.3"

View File

@@ -13,7 +13,7 @@ use std::{
rc::Rc,
};
use wasm_encoder::{ComponentTypeRef, ComponentValType, PrimitiveValType, TypeBounds, ValType};
use wasmparser::types::KebabString;
use wasmparser::names::KebabString;
mod encode;
@@ -637,12 +637,9 @@ impl ComponentBuilder {
&& (for_type_def || scope.types.len() < self.config.max_types())
{
choices.push(|me, u| {
Ok(ComponentTypeRef::Type(
TypeBounds::Eq,
u.int_in_range(
0..=u32::try_from(me.current_type_scope().types.len() - 1).unwrap(),
)?,
))
Ok(ComponentTypeRef::Type(TypeBounds::Eq(u.int_in_range(
0..=u32::try_from(me.current_type_scope().types.len() - 1).unwrap(),
)?)))
});
}
@@ -1115,7 +1112,7 @@ impl ComponentBuilder {
if self.current_type_scope().can_ref_type() {
choices.push(|me, exports, export_urls, u, _type_fuel| {
let ty = me.arbitrary_type_ref(u, false, true)?.unwrap();
if let ComponentTypeRef::Type(_, idx) = ty {
if let ComponentTypeRef::Type(TypeBounds::Eq(idx)) = ty {
let ty = me.current_type_scope().get(idx).clone();
me.current_type_scope_mut().push(ty);
}
@@ -1555,10 +1552,13 @@ impl ComponentBuilder {
self.total_values += 1;
self.component_mut().values.push(ty);
}
ComponentTypeRef::Type(TypeBounds::Eq, ty_index) => {
ComponentTypeRef::Type(TypeBounds::Eq(ty_index)) => {
let ty = self.current_type_scope().get(ty_index).clone();
self.current_type_scope_mut().push(ty);
}
ComponentTypeRef::Type(TypeBounds::SubResource) => {
unimplemented!()
}
ComponentTypeRef::Instance(ty_index) => {
let instance_ty = match self.current_type_scope().get(ty_index).as_ref() {
Type::Instance(ty) => ty.clone(),

View File

@@ -1,6 +1,8 @@
use std::borrow::Cow;
use super::*;
use wasm_encoder::{ComponentExportKind, ComponentOuterAliasKind, ExportKind};
use wasmparser::types::KebabStr;
use wasmparser::names::KebabStr;
impl Component {
/// Encode this Wasm component into bytes.
@@ -51,8 +53,8 @@ impl Section {
impl CustomSection {
fn encode(&self, component: &mut wasm_encoder::Component) {
component.section(&wasm_encoder::CustomSection {
name: &self.name,
data: &self.data,
name: (&self.name).into(),
data: Cow::Borrowed(&self.data),
});
}
}
@@ -71,7 +73,7 @@ impl ImportSection {
fn encode(&self, component: &mut wasm_encoder::Component) {
let mut sec = wasm_encoder::ComponentImportSection::new();
for imp in &self.imports {
sec.import(&imp.name, imp.url.as_deref().unwrap_or(""), imp.ty);
sec.import(wasm_encoder::ComponentExternName::Kebab(&imp.name), imp.ty);
}
component.section(&sec);
}
@@ -178,7 +180,10 @@ impl Type {
for def in &comp_ty.defs {
match def {
ComponentTypeDef::Import(imp) => {
enc_comp_ty.import(&imp.name, imp.url.as_deref().unwrap_or(""), imp.ty);
enc_comp_ty.import(
wasm_encoder::ComponentExternName::Kebab(&imp.name),
imp.ty,
);
}
ComponentTypeDef::CoreType(ty) => {
ty.encode(enc_comp_ty.core_type());
@@ -186,8 +191,8 @@ impl Type {
ComponentTypeDef::Type(ty) => {
ty.encode(enc_comp_ty.ty());
}
ComponentTypeDef::Export { name, url, ty } => {
enc_comp_ty.export(name, url.as_deref().unwrap_or(""), *ty);
ComponentTypeDef::Export { name, url: _, ty } => {
enc_comp_ty.export(wasm_encoder::ComponentExternName::Kebab(name), *ty);
}
ComponentTypeDef::Alias(a) => {
enc_comp_ty.alias(translate_alias(a));
@@ -206,8 +211,8 @@ impl Type {
InstanceTypeDecl::Type(ty) => {
ty.encode(enc_inst_ty.ty());
}
InstanceTypeDecl::Export { name, url, ty } => {
enc_inst_ty.export(name, url.as_deref().unwrap_or(""), *ty);
InstanceTypeDecl::Export { name, url: _, ty } => {
enc_inst_ty.export(wasm_encoder::ComponentExternName::Kebab(name), *ty);
}
InstanceTypeDecl::Alias(a) => {
enc_inst_ty.alias(translate_alias(a));

View File

@@ -578,7 +578,6 @@ impl<'a> Arbitrary<'a> for SwarmConfig {
min_uleb_size: u.int_in_range(0..=5)?,
bulk_memory_enabled: reference_types_enabled || u.arbitrary()?,
reference_types_enabled,
tail_call_enabled: u.arbitrary()?,
simd_enabled: u.arbitrary()?,
multi_value_enabled: u.arbitrary()?,
max_aliases: u.int_in_range(0..=MAX_MAXIMUM)?,
@@ -628,6 +627,7 @@ impl<'a> Arbitrary<'a> for SwarmConfig {
threads_enabled: false,
export_everything: false,
disallow_traps: false,
tail_call_enabled: false,
})
}
}

View File

@@ -609,6 +609,9 @@ impl Module {
new_types.push(Type::Func(Rc::clone(&func_type)));
new_index
}
Some((wasmparser::Type::Array(_array_type), _index_store)) => {
unimplemented!("Array and struct types are not supported yet.");
}
};
match &new_types[serialized_sig_idx - first_type_index] {
Type::Func(f) => Some((serialized_sig_idx as u32, Rc::clone(f))),
@@ -1639,11 +1642,19 @@ fn convert_type(parsed_type: wasmparser::ValType) -> ValType {
fn convert_reftype(ty: wasmparser::RefType) -> RefType {
wasm_encoder::RefType {
nullable: ty.nullable,
heap_type: match ty.heap_type {
wasmparser::HeapType::Func => wasm_encoder::HeapType::Func,
wasmparser::HeapType::Extern => wasm_encoder::HeapType::Extern,
wasmparser::HeapType::TypedFunc(i) => wasm_encoder::HeapType::TypedFunc(i.into()),
nullable: ty.is_nullable(),
heap_type: match ty.heap_type() {
wasmparser::HeapType::Func => HeapType::Func,
wasmparser::HeapType::Extern => HeapType::Extern,
wasmparser::HeapType::Any => HeapType::Any,
wasmparser::HeapType::None => HeapType::None,
wasmparser::HeapType::NoExtern => HeapType::NoExtern,
wasmparser::HeapType::NoFunc => HeapType::NoFunc,
wasmparser::HeapType::Eq => HeapType::Eq,
wasmparser::HeapType::Struct => HeapType::Struct,
wasmparser::HeapType::Array => HeapType::Array,
wasmparser::HeapType::I31 => HeapType::I31,
wasmparser::HeapType::Indexed(i) => HeapType::Indexed(i.into()),
},
}
}

View File

@@ -65,7 +65,7 @@ use arbitrary::{Result, Unstructured};
pub use component::{Component, ConfiguredComponent};
pub use config::{Config, DefaultConfig, SwarmConfig};
use std::{collections::HashSet, fmt::Write, str};
use wasmparser::types::{KebabStr, KebabString};
use wasmparser::names::{KebabStr, KebabString};
/// Do something an arbitrary number of times.
///

View File

@@ -3,6 +3,7 @@ use rand::{rngs::SmallRng, RngCore, SeedableRng};
use wasm_smith::Component;
#[test]
#[ignore] // FIXME(#1000): need to update wasm-smith's support for components
fn smoke_test_component() {
const NUM_RUNS: usize = 4096;

View File

@@ -132,6 +132,9 @@ fn smoke_test_imports_config() {
for ty in rdr {
match ty.unwrap() {
wasmparser::Type::Func(ft) => sig_types.push(ft),
wasmparser::Type::Array(_) => {
unimplemented!("Array and struct types are not supported yet.")
}
}
}
} else if let wasmparser::Payload::ImportSection(rdr) = payload {
@@ -302,6 +305,7 @@ fn parser_features_from_config(config: &impl Config) -> WasmFeatures {
component_model: false,
function_references: false,
memory_control: false,
gc: false,
}
}

View File

@@ -1 +1 @@
{"files":{"Cargo.lock":"ef76d24da3e4c9b0e0034844c4d3fa21f84ddb56af9f3134222046aaf26a48d3","Cargo.toml":"f1f05638e3da3d7304ccb0a41ef0a9889224bc5dc92352c634a553c6fa27d4bd","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"1c3b4f8db61a673ee2f7dc66118b1008ce8d1a7d0f5197fbe87f1271b23de3bd","benches/benchmark.rs":"b8417fbd86058b76f3ff15426075f9afee25b9f90155bbd9a2d774f4bad0af04","examples/simple.rs":"e9eb076367cc0932e2a32651372defa4a27ef800f47fad28c0ef840ba8ea7e08","src/binary_reader.rs":"1b1f0e1d831a716875ed578380f96b4b60245f0c6a4f43e2407c50fc94e50e6d","src/lib.rs":"d64774abb8d193f6a177130ffca94ce9fd199535fb6177dcb9910c4af7602082","src/limits.rs":"0c4dc6b96b35a320a8cff8269c0b55acd56f1f7b8a093c4885e04b9835441458","src/parser.rs":"b65b995296ed03a5b4d31c6459f2f3430ff685e50fec397c44f91b1364de5aaf","src/readers.rs":"406bf0cf694ed364c9c53cd25eb27a8e3fa97d099994f3e2a640747c49abf74b","src/readers/component.rs":"c259628ca3f09ff904f7e920aeec261b657f7a197c99b520a6a7d3e918b9fb3b","src/readers/component/aliases.rs":"72f3339cb452c1b055bd080281fe4923d132c280da112fcd1182594811b87494","src/readers/component/canonicals.rs":"c36e9bffc2e1ed752586189903eec77ea9897ae265e020096a9b92c467be26d9","src/readers/component/exports.rs":"f2c76e4be5045f1d099fd89dedf20e2f4817f046ea1e64bde7ca8f3ea9b2cdc6","src/readers/component/imports.rs":"725a688f2ab4306dbb421fe92ffff9373e3c88632557e352529f297bb2b96e0e","src/readers/component/instances.rs":"bca614178da3337e8c71a6ec431f12157a8cccc15411a0222bbc0106926a3ad5","src/readers/component/names.rs":"3f5dac9db8668b18cd6721cd780e6aba9b223b404c9d1173d09efe4e6b4d3a8a","src/readers/component/start.rs":"8e1e5d8aa5ece26d42251449cadcce0546c4d995f1641941b8a31ed4bc6ac761","src/readers/component/types.rs":"878fdf8f3d6a1c1387fd92b34838d8e79cee039710366bd5ea6739448d468a7a","src/readers/core.rs":"b1dbe0ffe61af1e6da4104de687009bcaa71fd3137300896159abbcfd903b800","src/readers/core/code.rs":"53f49986febb27f4cb67f4495e7b369fc80e2f70908e1e831750698dd15fe37f","src/readers/core/custom.rs":"f80d3a994e8778a912319834228cbb772c65a4b6b1f25b41fe00d220d388831f","src/readers/core/data.rs":"c1fcda7b548b15be40b3dd1f667d97c30c543867f2dc4734b590846beefe3ae3","src/readers/core/elements.rs":"197d3427fcf0fa8a3eb654e62dfa53159e6bc5cb160e75a06cf394f821bddef5","src/readers/core/exports.rs":"50dc1ee51b73f03f24077f7c290bca756966563cedbad9e49d735d60f60c91db","src/readers/core/functions.rs":"b5bbc7f7b7a429187376f63c695a9f1cbf92e515dba840ac876fa60f7290da34","src/readers/core/globals.rs":"d23f99a3adc9593652a5efd4dc81e8f014f57e776b49717dabbdcd0a811a96b1","src/readers/core/imports.rs":"4d247e8cac0b3cef7425d93f7a7f2412b2ae8979da944080d565415499e27e87","src/readers/core/init.rs":"ec6717063b0b7f2e9aa17ae52083019cee52594bf5a8b6858f2d77b10d7a0639","src/readers/core/memories.rs":"351f816d663b7d17546dc3b19ce0e43f406ce743289219a3758f7c837903fa6d","src/readers/core/names.rs":"408ebf052170bf0dc874b3158bb31089a891c3735cb35df2976e0b1e9791febb","src/readers/core/operators.rs":"46e927f6db9db9097217c5485da3a7b89e638730def809d177897f39356f5f52","src/readers/core/producers.rs":"4b42b7e1c9e22e7e2913d9da2291b491dc2d4fea417503d7ce99ad33c7beb439","src/readers/core/tables.rs":"cbe5b35893bd3929354b4487b344062ce6b540e6a70bde6eddf05a07a68574e9","src/readers/core/tags.rs":"c1dcfeb973195da9708549bcc2df4dd8ff282fe802c15a603bebc8a856f70949","src/readers/core/types.rs":"266a6c37ecdea656c235001b56b15564ad7777c23849a3f6e08ce63272347c7e","src/resources.rs":"b38b564ee425a392a30fb7440b50afa9e91be5ee933e4933b843e18c212a5871","src/validator.rs":"0dc406f1a61cf646719fa2d88675eae4d8c52c23cc3a4a222b6671cd25f84465","src/validator/component.rs":"4ca4d2d3c800e212a68e30577eead6bb69b2e7604f6d774d4f51f2aaa2f87084","src/validator/core.rs":"143f1a531485d411eb459fe608a64ca21f772740cbebe51e9f336e30ce3d1424","src/validator/func.rs":"63b66b7bb274be4115d8a26bce9b73128de0c079bd3318423288fadc1361fdb4","src/validator/operators.rs":"39b3bd04898b02b9dbd1d42c5727c79439593f5ef8fbf3c33fcc789d0b5be91a","src/validator/types.rs":"9ee9fb8a8afa0580c3e71bf9b39bd28c14b2a21f2d9acbb92e7d8ec14615ade2","tests/big-module.rs":"1f9241a4504d0421f6a5c759e3c688c87994fe04668b1d4e8912cedd98f2bd17"},"package":"48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b"}
{"files":{"Cargo.lock":"0094a4caa360684ecf8dba191e832d04ea783f722bb3d7203a861ab5f1b6652b","Cargo.toml":"f9969ee73f9611dd62ba995630b919e1d81d2ba2af3cd2e9813d73778d734614","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"1c3b4f8db61a673ee2f7dc66118b1008ce8d1a7d0f5197fbe87f1271b23de3bd","benches/benchmark.rs":"12b94c2a8ba04c5362b86ff36cdedb3596507a5606ce22a275cac2632f5d35ad","examples/simple.rs":"e9eb076367cc0932e2a32651372defa4a27ef800f47fad28c0ef840ba8ea7e08","src/binary_reader.rs":"0c346c4248b39ab3d7c9908834442ef6f3aa5dc921804c3e89e7bf46d49dbe15","src/lib.rs":"b7fbf8cdd67033c61c1b3a3c25823ef4518458eda6cbfd44d1c80b20cf0c5076","src/limits.rs":"93b8ae6ce2f95c14b62e41fd6dc3ab3869a6879af46bd0c560b43193cea0b496","src/parser.rs":"c69c24f2308b05e0a5072bbd07084d2002e0ae291afd669361b5a4bb8c7fcad4","src/readers.rs":"406bf0cf694ed364c9c53cd25eb27a8e3fa97d099994f3e2a640747c49abf74b","src/readers/component.rs":"c259628ca3f09ff904f7e920aeec261b657f7a197c99b520a6a7d3e918b9fb3b","src/readers/component/aliases.rs":"72f3339cb452c1b055bd080281fe4923d132c280da112fcd1182594811b87494","src/readers/component/canonicals.rs":"45b8c54e842bc698d180b7396579b93e16fddb9a055c753482fc03bf5cd443b4","src/readers/component/exports.rs":"96cc2f65666da77b6230335378550d9b45a193795ee939d75808cc49cc1c06f4","src/readers/component/imports.rs":"5c6a0066adb143455b92b5ef1b773184ccf62923c8a35b26262e6a80058971d2","src/readers/component/instances.rs":"5f3ef2b8424a3beff2c34d118f1c9f168d1beee589c0e87dabab94ba726a8f1d","src/readers/component/names.rs":"3f5dac9db8668b18cd6721cd780e6aba9b223b404c9d1173d09efe4e6b4d3a8a","src/readers/component/start.rs":"8e1e5d8aa5ece26d42251449cadcce0546c4d995f1641941b8a31ed4bc6ac761","src/readers/component/types.rs":"6579bb3131570f35415fc47ab35efbfc2688b970052425741ad3a1aa89588b86","src/readers/core.rs":"c2536abe2a3305b4aa3d91f7fcea9c89e65a5c94af74e4f6a1b19bbe09f8fb2c","src/readers/core/code.rs":"53f49986febb27f4cb67f4495e7b369fc80e2f70908e1e831750698dd15fe37f","src/readers/core/coredumps.rs":"7cde14b700bdf2459fa57e2cbb5a520a4d1d8547f075c1440db74b19ecf74764","src/readers/core/custom.rs":"f80d3a994e8778a912319834228cbb772c65a4b6b1f25b41fe00d220d388831f","src/readers/core/data.rs":"c1fcda7b548b15be40b3dd1f667d97c30c543867f2dc4734b590846beefe3ae3","src/readers/core/elements.rs":"c7fdde45032eec293d69b8bce5519391db84117aa33a98dad35b12c68a532c9f","src/readers/core/exports.rs":"50dc1ee51b73f03f24077f7c290bca756966563cedbad9e49d735d60f60c91db","src/readers/core/functions.rs":"b5bbc7f7b7a429187376f63c695a9f1cbf92e515dba840ac876fa60f7290da34","src/readers/core/globals.rs":"d23f99a3adc9593652a5efd4dc81e8f014f57e776b49717dabbdcd0a811a96b1","src/readers/core/imports.rs":"4d247e8cac0b3cef7425d93f7a7f2412b2ae8979da944080d565415499e27e87","src/readers/core/init.rs":"ec6717063b0b7f2e9aa17ae52083019cee52594bf5a8b6858f2d77b10d7a0639","src/readers/core/memories.rs":"351f816d663b7d17546dc3b19ce0e43f406ce743289219a3758f7c837903fa6d","src/readers/core/names.rs":"408ebf052170bf0dc874b3158bb31089a891c3735cb35df2976e0b1e9791febb","src/readers/core/operators.rs":"46e927f6db9db9097217c5485da3a7b89e638730def809d177897f39356f5f52","src/readers/core/producers.rs":"5b53d0979a68008f9aec0bb9bca64c071e49a6a1879d8459f1114fa666e661c5","src/readers/core/tables.rs":"cbe5b35893bd3929354b4487b344062ce6b540e6a70bde6eddf05a07a68574e9","src/readers/core/tags.rs":"c1dcfeb973195da9708549bcc2df4dd8ff282fe802c15a603bebc8a856f70949","src/readers/core/types.rs":"e644d7fce8a8a4cab0b617d2d4000f2979375e5b0389b0511ad1aeff7eab6cd3","src/resources.rs":"e3bf138ef8fc94d2773fc6159bbd7f47611b5d9121c12e724cbbb824d8278411","src/validator.rs":"fda1d806231f3e714c538d0f23aab0c6e1f6790df591c11903a716514373eec3","src/validator/component.rs":"1920424f9dacf904ad6112ce5bc0d272e3aa60fd88895ae06b44294341ec904f","src/validator/core.rs":"268e88fed44d03c09cc2a500d292d676cc234748f64a00c95757a2349d24e91e","src/validator/func.rs":"63b66b7bb274be4115d8a26bce9b73128de0c079bd3318423288fadc1361fdb4","src/validator/names.rs":"0beae05cae3bf6d8fe2d0ebea1c5dd993fc8ce1116b454aca2a79a41a8e1c9d8","src/validator/operators.rs":"d488964a60fcb44c03b41b7f9fa3c7cafdfc30be2e2d9fef87aac73273056f8f","src/validator/types.rs":"e50497731c493205965e2f1cb8aee40287f00abff8f671034733960e81e42c03","tests/big-module.rs":"1f9241a4504d0421f6a5c759e3c688c87994fe04668b1d4e8912cedd98f2bd17"},"package":"29e3ac9b780c7dda0cac7a52a5d6d2d6707cc6e3451c9db209b6c758f40d7acb"}

View File

@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "anyhow"
version = "1.0.69"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "atty"
@@ -31,23 +31,11 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
version = "3.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
[[package]]
name = "cast"
@@ -110,9 +98,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
@@ -120,9 +108,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
@@ -131,9 +119,9 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.13"
version = "0.9.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
dependencies = [
"autocfg",
"cfg-if",
@@ -144,22 +132,21 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [
"cfg-if",
]
[[package]]
name = "csv"
version = "1.1.6"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad"
dependencies = [
"bstr",
"csv-core",
"itoa 0.4.8",
"itoa",
"ryu",
"serde",
]
@@ -179,15 +166,6 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]]
name = "half"
version = "1.8.2"
@@ -218,21 +196,11 @@ dependencies = [
"libc",
]
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.9.2"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
@@ -249,21 +217,15 @@ dependencies = [
[[package]]
name = "itoa"
version = "0.4.8"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "js-sys"
version = "0.3.61"
version = "0.3.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5"
dependencies = [
"wasm-bindgen",
]
@@ -282,9 +244,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.139"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "log"
@@ -303,9 +265,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.7.1"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
dependencies = [
"autocfg",
]
@@ -331,9 +293,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.17.0"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "oorandom"
@@ -341,12 +303,6 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "plotters"
version = "0.3.4"
@@ -377,27 +333,27 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.51"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
checksum = "c4ec6d5fe0b140acb27c9a0444118cf55bfbb4e0b259739429abb4521dd67c16"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.6.1"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
@@ -405,9 +361,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.10.2"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@@ -417,30 +373,24 @@ dependencies = [
[[package]]
name = "regex"
version = "1.7.1"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.28"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
[[package]]
name = "ryu"
version = "1.0.12"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "same-file"
@@ -458,10 +408,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.152"
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
version = "1.0.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
[[package]]
name = "serde_cbor"
@@ -475,9 +431,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.152"
version = "1.0.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
dependencies = [
"proc-macro2",
"quote",
@@ -486,20 +442,20 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.93"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"itoa 1.0.5",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.107"
version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
dependencies = [
"proc-macro2",
"quote",
@@ -525,41 +481,11 @@ dependencies = [
"serde_json",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
[[package]]
name = "unicode-ident"
version = "1.0.6"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-width"
@@ -567,33 +493,21 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "walkdir"
version = "2.3.2"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
version = "0.2.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -601,9 +515,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
version = "0.2.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822"
dependencies = [
"bumpalo",
"log",
@@ -616,9 +530,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
version = "0.2.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -626,9 +540,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
version = "0.2.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869"
dependencies = [
"proc-macro2",
"quote",
@@ -639,37 +553,37 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
version = "0.2.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb"
[[package]]
name = "wasm-encoder"
version = "0.25.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7"
checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881"
dependencies = [
"leb128",
]
[[package]]
name = "wasmparser"
version = "0.102.0"
version = "0.107.0"
dependencies = [
"anyhow",
"criterion",
"indexmap",
"once_cell",
"rayon",
"url",
"semver",
"wasm-encoder",
]
[[package]]
name = "web-sys"
version = "0.3.61"
version = "0.3.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721"
dependencies = [
"js-sys",
"wasm-bindgen",

View File

@@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "wasmparser"
version = "0.102.0"
version = "0.107.0"
authors = ["Yury Delendik <ydelendik@mozilla.com>"]
exclude = ["benches/*.wasm"]
description = """
@@ -35,8 +35,8 @@ harness = false
[dependencies.indexmap]
version = "1.9.1"
[dependencies.url]
version = "2.0.0"
[dependencies.semver]
version = "1.0.0"
[dev-dependencies.anyhow]
version = "1.0.58"
@@ -51,4 +51,4 @@ version = "1.13.0"
version = "1.3"
[dev-dependencies.wasm-encoder]
version = "0.25.0"
version = "0.29.0"

View File

@@ -256,6 +256,7 @@ fn define_benchmarks(c: &mut Criterion) {
sign_extension: true,
function_references: true,
memory_control: true,
gc: true,
})
}

View File

@@ -21,7 +21,7 @@ use std::marker;
use std::ops::Range;
use std::str;
const WASM_MAGIC_NUMBER: &[u8; 4] = b"\0asm";
pub(crate) const WASM_MAGIC_NUMBER: &[u8; 4] = b"\0asm";
/// A binary reader for WebAssembly modules.
#[derive(Debug, Clone)]
@@ -92,6 +92,11 @@ impl BinaryReaderError {
pub fn offset(&self) -> usize {
self.inner.offset
}
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.
@@ -977,6 +982,7 @@ impl<'a> BinaryReader<'a> {
0xd4 => 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)?,
@@ -985,6 +991,24 @@ impl<'a> BinaryReader<'a> {
})
}
fn visit_0xfb_operator<T>(
&mut self,
pos: usize,
visitor: &mut T,
) -> Result<<T as VisitOperator<'a>>::Output>
where
T: VisitOperator<'a>,
{
let code = self.read_var_u32()?;
Ok(match code {
0x20 => visitor.visit_i31_new(),
0x21 => visitor.visit_i31_get_s(),
0x22 => visitor.visit_i31_get_u(),
_ => bail!(pos, "unknown 0xfb subopcode: 0x{code:x}"),
})
}
fn visit_0xfc_operator<T>(
&mut self,
pos: usize,

View File

@@ -49,6 +49,7 @@
/// - `@threads`: [Wasm `threads` proposal]
/// - `@simd`: [Wasm `simd` proposal]
/// - `@relaxed_simd`: [Wasm `relaxed-simd` proposal]
/// - `@gc`: [Wasm `gc` proposal]
///
/// [Wasm `expection-handling` proposal]:
/// https://github.com/WebAssembly/exception-handling
@@ -77,6 +78,9 @@
/// [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
@@ -306,6 +310,13 @@ macro_rules! for_each_operator {
@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 I31New => visit_i31_new
@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
@@ -679,8 +690,8 @@ macro_rules! for_each_operator {
@relaxed_simd I32x4RelaxedDotI8x16I7x16AddS => visit_i32x4_relaxed_dot_i8x16_i7x16_add_s
// Typed Function references
@function_references CallRef { hty: $crate::HeapType } => visit_call_ref
@function_references ReturnCallRef { hty: $crate::HeapType } => visit_return_call_ref
@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

View File

@@ -46,9 +46,9 @@ pub const MAX_WASM_TUPLE_TYPES: usize = 1000;
pub const MAX_WASM_FLAG_NAMES: usize = 1000;
pub const MAX_WASM_ENUM_CASES: usize = 1000;
pub const MAX_WASM_UNION_TYPES: usize = 1000;
pub const MAX_WASM_INSTANTIATION_EXPORTS: usize = 1000;
pub const MAX_WASM_INSTANTIATION_EXPORTS: usize = 100_000;
pub const MAX_WASM_CANONICAL_OPTIONS: usize = 10;
pub const MAX_WASM_INSTANTIATION_ARGS: usize = 1000;
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;

View File

@@ -1,3 +1,4 @@
use crate::binary_reader::WASM_MAGIC_NUMBER;
use crate::CoreTypeSectionReader;
use crate::{
limits::MAX_WASM_MODULE_SIZE, BinaryReader, BinaryReaderError, ComponentCanonicalSectionReader,
@@ -18,9 +19,14 @@ pub(crate) const WASM_MODULE_VERSION: u16 = 0x1;
// the component model is stabilized this will become 0x1. The changes here are:
//
// * [????-??-??] 0xa - original version
// * [2022-01-05] 0xb - `export` introduces an alias
// * [2022-02-06] 0xc - `export` has an optional type ascribed to it
pub(crate) const WASM_COMPONENT_VERSION: u16 = 0xc;
// * [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)]
@@ -332,6 +338,42 @@ impl Parser {
}
}
/// 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)
}
/// Attempts to parse a chunk of data.
///
/// This method will attempt to parse the next incremental portion of a
@@ -520,9 +562,6 @@ impl Parser {
match self.state {
State::Header => {
const KIND_MODULE: u16 = 0x00;
const KIND_COMPONENT: u16 = 0x01;
let start = reader.original_position();
let header_version = reader.read_header_version()?;
self.encoding = match (header_version >> 16) as u16 {
@@ -1167,7 +1206,7 @@ mod tests {
fn parser_after_component_header() -> Parser {
let mut p = Parser::default();
assert_matches!(
p.parse(b"\0asm\x0c\0\x01\0", false),
p.parse(b"\0asm\x0d\0\x01\0", false),
Ok(Chunk::Parsed {
consumed: 8,
payload: Payload::Version {

View File

@@ -1,5 +1,5 @@
use crate::limits::MAX_WASM_CANONICAL_OPTIONS;
use crate::{BinaryReader, FromReader, Result, SectionLimited};
use crate::{BinaryReader, ComponentValType, FromReader, Result, SectionLimited};
/// Represents options for component functions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -43,6 +43,23 @@ pub enum CanonicalFunction {
/// 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 of the resource that's being dropped, either an (own T) or
/// a (borrow T)
ty: ComponentValType,
},
/// 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.
@@ -75,6 +92,13 @@ impl<'a> FromReader<'a> for CanonicalFunction {
},
x => return reader.invalid_leading_byte(x, "canonical function lower"),
},
0x02 => CanonicalFunction::ResourceNew {
resource: reader.read()?,
},
0x03 => CanonicalFunction::ResourceDrop { ty: reader.read()? },
0x04 => CanonicalFunction::ResourceRep {
resource: reader.read()?,
},
x => return reader.invalid_leading_byte(x, "canonical function"),
})
}

View File

@@ -1,4 +1,6 @@
use crate::{BinaryReader, ComponentTypeRef, FromReader, Result, SectionLimited};
use crate::{
BinaryReader, ComponentExternName, ComponentTypeRef, FromReader, Result, SectionLimited,
};
/// Represents the kind of an external items of a WebAssembly component.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -48,15 +50,26 @@ impl ComponentExternalKind {
}
})
}
/// 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)]
pub struct ComponentExport<'a> {
/// The name of the exported item.
pub name: &'a str,
/// The optional URL of the exported item.
pub url: &'a str,
pub name: ComponentExternName<'a>,
/// The kind of the export.
pub kind: ComponentExternalKind,
/// The index of the exported item.
@@ -72,7 +85,6 @@ impl<'a> FromReader<'a> for ComponentExport<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(ComponentExport {
name: reader.read()?,
url: reader.read()?,
kind: reader.read()?,
index: reader.read()?,
ty: match reader.read_u8()? {

View File

@@ -6,13 +6,16 @@ use crate::{
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TypeBounds {
/// The type is bounded by equality.
Eq,
Eq(u32),
/// A fresh resource type,
SubResource,
}
impl<'a> FromReader<'a> for TypeBounds {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(match reader.read_u8()? {
0x00 => TypeBounds::Eq,
0x00 => TypeBounds::Eq(reader.read()?),
0x01 => TypeBounds::SubResource,
x => return reader.invalid_leading_byte(x, "type bound"),
})
}
@@ -34,7 +37,7 @@ pub enum ComponentTypeRef {
/// The reference is to a bounded type.
///
/// The index is expected to be a type index.
Type(TypeBounds, u32),
Type(TypeBounds),
/// The reference is to an instance type.
///
/// The index is a type index to an instance type.
@@ -65,7 +68,7 @@ impl<'a> FromReader<'a> for ComponentTypeRef {
ComponentExternalKind::Module => ComponentTypeRef::Module(reader.read()?),
ComponentExternalKind::Func => ComponentTypeRef::Func(reader.read()?),
ComponentExternalKind::Value => ComponentTypeRef::Value(reader.read()?),
ComponentExternalKind::Type => ComponentTypeRef::Type(reader.read()?, reader.read()?),
ComponentExternalKind::Type => ComponentTypeRef::Type(reader.read()?),
ComponentExternalKind::Instance => ComponentTypeRef::Instance(reader.read()?),
ComponentExternalKind::Component => ComponentTypeRef::Component(reader.read()?),
})
@@ -76,9 +79,7 @@ impl<'a> FromReader<'a> for ComponentTypeRef {
#[derive(Debug, Copy, Clone)]
pub struct ComponentImport<'a> {
/// The name of the imported item.
pub name: &'a str,
/// The optional URL of the imported item.
pub url: &'a str,
pub name: ComponentExternName<'a>,
/// The type reference for the import.
pub ty: ComponentTypeRef,
}
@@ -87,7 +88,6 @@ impl<'a> FromReader<'a> for ComponentImport<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(ComponentImport {
name: reader.read()?,
url: reader.read()?,
ty: reader.read()?,
})
}
@@ -99,7 +99,7 @@ impl<'a> FromReader<'a> for ComponentImport<'a> {
///
/// ```
/// use wasmparser::ComponentImportSectionReader;
/// let data: &[u8] = &[0x01, 0x01, 0x41, 0x00, 0x01, 0x66];
/// let data: &[u8] = &[0x01, 0x00, 0x01, 0x41, 0x01, 0x66];
/// let reader = ComponentImportSectionReader::new(data, 0).unwrap();
/// for import in reader {
/// let import = import.expect("import");
@@ -107,3 +107,31 @@ impl<'a> FromReader<'a> for ComponentImport<'a> {
/// }
/// ```
pub type ComponentImportSectionReader<'a> = SectionLimited<'a, ComponentImport<'a>>;
/// Represents the name of a component import.
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum ComponentExternName<'a> {
Kebab(&'a str),
Interface(&'a str),
}
impl<'a> ComponentExternName<'a> {
/// Returns the underlying string representing this name.
pub fn as_str(&self) -> &'a str {
match self {
ComponentExternName::Kebab(name) => name,
ComponentExternName::Interface(name) => name,
}
}
}
impl<'a> FromReader<'a> for ComponentExternName<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(match reader.read_u8()? {
0x00 => ComponentExternName::Kebab(reader.read()?),
0x01 => ComponentExternName::Interface(reader.read()?),
x => return reader.invalid_leading_byte(x, "import name"),
})
}
}

View File

@@ -141,7 +141,6 @@ impl<'a> FromReader<'a> for ComponentInstance<'a> {
.map(|_| {
Ok(ComponentExport {
name: reader.read()?,
url: "",
kind: reader.read()?,
index: reader.read()?,
ty: None,

View File

@@ -1,8 +1,9 @@
use crate::limits::*;
use crate::{
BinaryReader, ComponentAlias, ComponentImport, ComponentTypeRef, FromReader, FuncType, Import,
Result, SectionLimited, Type, TypeRef,
BinaryReader, ComponentAlias, ComponentExternName, ComponentImport, ComponentTypeRef,
FromReader, FuncType, Import, Result, SectionLimited, Type, TypeRef, ValType,
};
use std::fmt;
/// Represents the kind of an outer core alias in a WebAssembly component.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -201,6 +202,28 @@ impl PrimitiveValType {
}
}
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",
Float32 => "float32",
Float64 => "float64",
Char => "char",
String => "string",
};
s.fmt(f)
}
}
/// Represents a type in a WebAssembly component.
#[derive(Debug, Clone)]
pub enum ComponentType<'a> {
@@ -212,11 +235,27 @@ pub enum ComponentType<'a> {
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<u32>,
},
}
impl<'a> FromReader<'a> for ComponentType<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
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")?
@@ -257,9 +296,7 @@ pub enum ComponentTypeDeclaration<'a> {
/// The component type declaration is for an export.
Export {
/// The name of the export.
name: &'a str,
/// The optional URL of the export.
url: &'a str,
name: ComponentExternName<'a>,
/// The type reference for the export.
ty: ComponentTypeRef,
},
@@ -281,8 +318,8 @@ impl<'a> FromReader<'a> for ComponentTypeDeclaration<'a> {
InstanceTypeDeclaration::CoreType(t) => ComponentTypeDeclaration::CoreType(t),
InstanceTypeDeclaration::Type(t) => ComponentTypeDeclaration::Type(t),
InstanceTypeDeclaration::Alias(a) => ComponentTypeDeclaration::Alias(a),
InstanceTypeDeclaration::Export { name, url, ty } => {
ComponentTypeDeclaration::Export { name, url, ty }
InstanceTypeDeclaration::Export { name, ty } => {
ComponentTypeDeclaration::Export { name, ty }
}
})
}
@@ -300,9 +337,7 @@ pub enum InstanceTypeDeclaration<'a> {
/// The instance type declaration is for an export.
Export {
/// The name of the export.
name: &'a str,
/// The URL for the export.
url: &'a str,
name: ComponentExternName<'a>,
/// The type reference for the export.
ty: ComponentTypeRef,
},
@@ -316,7 +351,6 @@ impl<'a> FromReader<'a> for InstanceTypeDeclaration<'a> {
0x02 => InstanceTypeDeclaration::Alias(reader.read()?),
0x04 => InstanceTypeDeclaration::Export {
name: reader.read()?,
url: reader.read()?,
ty: reader.read()?,
},
x => return reader.invalid_leading_byte(x, "component or instance type declaration"),
@@ -447,6 +481,10 @@ pub enum ComponentDefinedType<'a> {
/// The type returned for failure.
err: Option<ComponentValType>,
},
/// An owned handle to a resource.
Own(u32),
/// A borrowed handle to a resource.
Borrow(u32),
}
impl<'a> ComponentDefinedType<'a> {
@@ -488,6 +526,8 @@ impl<'a> ComponentDefinedType<'a> {
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"),
})
}

View File

@@ -1,4 +1,5 @@
mod code;
mod coredumps;
mod custom;
mod data;
mod elements;
@@ -16,6 +17,7 @@ mod tags;
mod types;
pub use self::code::*;
pub use self::coredumps::*;
pub use self::custom::*;
pub use self::data::*;
pub use self::elements::*;

View File

@@ -0,0 +1,243 @@
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);
/// let core = CoreDumpSection::from_reader(&mut 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> FromReader<'a> for CoreDumpSection<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let pos = reader.original_position();
if reader.read_u8()? != 0 {
bail!(pos, "invalid start byte for core dump name");
}
let name = reader.read_string()?;
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 mut reader = BinaryReader::new(data);
/// let modules_section = CoreDumpModulesSection::from_reader(&mut 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> FromReader<'a> for CoreDumpModulesSection<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
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()?);
}
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<CoreDumpInstance>,
}
impl<'a> FromReader<'a> for CoreDumpInstancesSection {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let mut instances = vec![];
for _ in 0..reader.read_var_u32()? {
instances.push(CoreDumpInstance::from_reader(reader)?);
}
Ok(CoreDumpInstancesSection { instances })
}
}
/// A single instance from a coredump instances section
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<u32>,
/// Which of the coredump's globals are this instance's globals, via
/// indexing into the global index space.
pub globals: Vec<u32>,
}
impl<'a> FromReader<'a> for CoreDumpInstance {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
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
///
/// ```
/// let data: &[u8] = &[0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x01, 0x00, 0x04,
/// 0x2a, 0x33, 0x01, 0x7f, 0x01, 0x01, 0x7f, 0x02];
/// use wasmparser::{ BinaryReader, CoreDumpStackSection, FromReader };
/// let mut reader = BinaryReader::new(data);
/// let corestack = CoreDumpStackSection::from_reader(&mut 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<CoreDumpStackFrame>,
}
impl<'a> FromReader<'a> for CoreDumpStackSection<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
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(reader)?);
}
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<CoreDumpValue>,
/// The values on the stack
pub stack: Vec<CoreDumpValue>,
}
impl<'a> FromReader<'a> for CoreDumpStackFrame {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
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<Self> {
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"),
}
}
}

View File

@@ -40,7 +40,7 @@ pub enum ElementKind<'a> {
/// The element segment is active.
Active {
/// The index of the table being initialized.
table_index: u32,
table_index: Option<u32>,
/// The initial expression of the element segment.
offset_expr: ConstExpr<'a>,
},
@@ -91,9 +91,9 @@ impl<'a> FromReader<'a> for Element<'a> {
}
} else {
let table_index = if flags & 0b010 == 0 {
0
None
} else {
reader.read_var_u32()?
Some(reader.read_var_u32()?)
};
let offset_expr = reader.read()?;
ElementKind::Active {

View File

@@ -44,7 +44,12 @@ pub struct ProducersField<'a> {
impl<'a> FromReader<'a> for ProducersField<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
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()? {

View File

@@ -15,7 +15,7 @@
use crate::limits::{MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS};
use crate::{BinaryReader, FromReader, Result, SectionLimited};
use std::fmt::Debug;
use std::fmt::{self, Debug, Write};
/// Represents the types of values in a WebAssembly module.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -30,37 +30,25 @@ pub enum ValType {
F64,
/// The value type is v128.
V128,
/// The value type is a reference. Which type of reference is decided by
/// RefType. This is a change in syntax from the function references proposal,
/// which now provides FuncRef and ExternRef as sugar for the generic ref
/// construct.
/// The value type is a reference.
Ref(RefType),
}
/// A reference type. When the function references feature is disabled, this
/// only represents funcref and externref, using the following format:
/// RefType { nullable: true, heap_type: Func | Extern })
/// Represents storage types introduced in the GC spec for array and struct fields.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(packed)]
pub struct RefType {
/// Whether it's nullable
pub nullable: bool,
/// The relevant heap type
pub heap_type: HeapType,
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 RefType {
/// Alias for the wasm `funcref` type.
pub const FUNCREF: RefType = RefType {
nullable: true,
heap_type: HeapType::Func,
};
/// Alias for the wasm `externref` type.
pub const EXTERNREF: RefType = RefType {
nullable: true,
heap_type: HeapType::Extern,
};
}
// The size of `ValType` is performance sensitive.
const _: () = {
assert!(std::mem::size_of::<ValType>() == 4);
};
impl From<RefType> for ValType {
fn from(ty: RefType) -> ValType {
@@ -68,44 +56,10 @@ impl From<RefType> for ValType {
}
}
/// Used as a performance optimization in HeapType. Call `.into()` to get the u32
// A u16 forces 2-byte alignment, which forces HeapType to be 4 bytes,
// which forces ValType to 5 bytes. This newtype is annotated as unaligned to
// store the necessary bits compactly
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(packed)]
pub struct PackedIndex(u16);
impl TryFrom<u32> for PackedIndex {
type Error = ();
fn try_from(idx: u32) -> Result<PackedIndex, ()> {
idx.try_into().map(PackedIndex).map_err(|_| ())
}
}
impl From<PackedIndex> for u32 {
fn from(x: PackedIndex) -> u32 {
x.0 as u32
}
}
/// A heap type from function references. When the proposal is disabled, Index
/// is an invalid type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum HeapType {
/// Function type index
/// Note: [PackedIndex] may need to be unpacked
TypedFunc(PackedIndex),
/// From reference types
Func,
/// From reference types
Extern,
}
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);
@@ -116,26 +70,41 @@ impl ValType {
pub fn is_reference_type(&self) -> bool {
matches!(self, ValType::Ref(_))
}
/// Whether the type is defaultable according to function references
/// spec. This amounts to whether it's a non-nullable ref
/// Whether the type is defaultable, i.e. it is not a non-nullable reference
/// type.
pub fn is_defaultable(&self) -> bool {
!matches!(
self,
ValType::Ref(RefType {
nullable: false,
..
})
)
match *self {
Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128 => true,
Self::Ref(rt) => rt.is_nullable(),
}
}
pub(crate) fn is_valtype_byte(byte: u8) -> bool {
match byte {
0x7F | 0x7E | 0x7D | 0x7C | 0x7B | 0x70 | 0x6F | 0x6B | 0x6C => true,
0x7F | 0x7E | 0x7D | 0x7C | 0x7B | 0x70 | 0x6F | 0x6B | 0x6C | 0x6E | 0x65 | 0x69
| 0x68 | 0x6D | 0x67 | 0x66 | 0x6A => true,
_ => false,
}
}
}
impl<'a> FromReader<'a> for StorageType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
match reader.peek()? {
0x7A => {
reader.position += 1;
Ok(StorageType::I8)
}
0x79 => {
reader.position += 1;
Ok(StorageType::I16)
}
_ => Ok(StorageType::Val(reader.read()?)),
}
}
}
impl<'a> FromReader<'a> for ValType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
match reader.peek()? {
@@ -159,26 +128,473 @@ impl<'a> FromReader<'a> for ValType {
reader.position += 1;
Ok(ValType::V128)
}
0x70 | 0x6F | 0x6B | 0x6C => Ok(ValType::Ref(reader.read()?)),
0x70 | 0x6F | 0x6B | 0x6C | 0x6E | 0x65 | 0x69 | 0x68 | 0x6D | 0x67 | 0x66 | 0x6A => {
Ok(ValType::Ref(reader.read()?))
}
_ => bail!(reader.original_position(), "invalid value type"),
}
}
}
impl fmt::Display for ValType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
ValType::I32 => "i32",
ValType::I64 => "i64",
ValType::F32 => "f32",
ValType::F64 => "f64",
ValType::V128 => "v128",
ValType::Ref(r) => return fmt::Display::fmt(r, f),
};
f.write_str(s)
}
}
/// 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] [indexed==1:u1] [kind:u2] [index:u20]
// [nullable:u1] [indexed==0:u1] [type:u4] [(unused):u18]
// ```
// , where
// - `nullable` determines nullability of the ref
// - `indexed` 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
// - `kind` determines what kind of indexed type the index is pointing to:
// ```
// 10 = struct
// 11 = array
// 01 = function
// ```
// - `index` is the type index
// - `type` is an enumeration of known types:
// ```
// 1111 = any
//
// 1101 = eq
// 1000 = i31
// 1001 = struct
// 1100 = array
//
// 0101 = func
// 0100 = nofunc
//
// 0011 = extern
// 0010 = noextern
//
// 0000 = none
// ```
// - `(unused)` is unused sequence of bits
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct RefType([u8; 3]);
impl Debug for RefType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match (self.is_nullable(), self.heap_type()) {
(true, HeapType::Any) => write!(f, "anyref"),
(false, HeapType::Any) => write!(f, "(ref any)"),
(true, HeapType::None) => write!(f, "nullref"),
(false, HeapType::None) => write!(f, "(ref none)"),
(true, HeapType::NoExtern) => write!(f, "nullexternref"),
(false, HeapType::NoExtern) => write!(f, "(ref noextern)"),
(true, HeapType::NoFunc) => write!(f, "nullfuncref"),
(false, HeapType::NoFunc) => write!(f, "(ref nofunc)"),
(true, HeapType::Eq) => write!(f, "eqref"),
(false, HeapType::Eq) => write!(f, "(ref eq)"),
(true, HeapType::Struct) => write!(f, "structref"),
(false, HeapType::Struct) => write!(f, "(ref struct)"),
(true, HeapType::Array) => write!(f, "arrayref"),
(false, HeapType::Array) => write!(f, "(ref array)"),
(true, HeapType::I31) => write!(f, "i31ref"),
(false, HeapType::I31) => write!(f, "(ref i31)"),
(true, HeapType::Extern) => write!(f, "externref"),
(false, HeapType::Extern) => write!(f, "(ref extern)"),
(true, HeapType::Func) => write!(f, "funcref"),
(false, HeapType::Func) => write!(f, "(ref func)"),
(true, HeapType::Indexed(idx)) => write!(f, "(ref null {idx})"),
(false, HeapType::Indexed(idx)) => write!(f, "(ref {idx})"),
}
}
}
// Static assert that we can fit indices up to `MAX_WASM_TYPES` inside `RefType`.
const _: () = {
const fn can_roundtrip_index(index: u32) -> bool {
assert!(RefType::can_represent_type_index(index));
let rt = match RefType::indexed_func(true, index) {
Some(rt) => rt,
None => panic!(),
};
assert!(rt.is_nullable());
let actual_index = match rt.type_index() {
Some(i) => i,
None => panic!(),
};
actual_index == 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 {
const NULLABLE_BIT: u32 = 1 << 23; // bit #23
const INDEXED_BIT: u32 = 1 << 22; // bit #22
const TYPE_MASK: u32 = 0b1111 << 18; // 4 bits #21-#18 (if `indexed == 0`)
const ANY_TYPE: u32 = 0b1111 << 18;
const EQ_TYPE: u32 = 0b1101 << 18;
const I31_TYPE: u32 = 0b1000 << 18;
const STRUCT_TYPE: u32 = 0b1001 << 18;
const ARRAY_TYPE: u32 = 0b1100 << 18;
const FUNC_TYPE: u32 = 0b0101 << 18;
const NOFUNC_TYPE: u32 = 0b0100 << 18;
const EXTERN_TYPE: u32 = 0b0011 << 18;
const NOEXTERN_TYPE: u32 = 0b0010 << 18;
const NONE_TYPE: u32 = 0b0000 << 18;
const KIND_MASK: u32 = 0b11 << 20; // 2 bits #21-#20 (if `indexed == 1`)
const STRUCT_KIND: u32 = 0b10 << 20;
const ARRAY_KIND: u32 = 0b11 << 20;
const FUNC_KIND: u32 = 0b01 << 20;
const INDEX_MASK: u32 = (1 << 20) - 1; // 20 bits #19-#0 (if `indexed == 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 non-nullable untyped function reference aka `(ref func)`.
pub const FUNC: Self = RefType::from_u32(Self::FUNC_TYPE);
/// A non-nullable reference to an extern object aka `(ref extern)`.
pub const EXTERN: Self = RefType::from_u32(Self::EXTERN_TYPE);
/// A non-nullable reference to any object aka `(ref any)`.
pub const ANY: Self = RefType::from_u32(Self::ANY_TYPE);
/// A non-nullable reference to no object aka `(ref none)`.
pub const NONE: Self = RefType::from_u32(Self::NONE_TYPE);
/// A non-nullable reference to a noextern object aka `(ref noextern)`.
pub const NOEXTERN: Self = RefType::from_u32(Self::NOEXTERN_TYPE);
/// A non-nullable reference to a nofunc object aka `(ref nofunc)`.
pub const NOFUNC: Self = RefType::from_u32(Self::NOFUNC_TYPE);
/// A non-nullable reference to an eq object aka `(ref eq)`.
pub const EQ: Self = RefType::from_u32(Self::EQ_TYPE);
/// A non-nullable reference to a struct aka `(ref struct)`.
pub const STRUCT: Self = RefType::from_u32(Self::STRUCT_TYPE);
/// A non-nullable reference to an array aka `(ref array)`.
pub const ARRAY: Self = RefType::from_u32(Self::ARRAY_TYPE);
/// A non-nullable reference to an i31 object aka `(ref i31)`.
pub const I31: Self = RefType::from_u32(Self::I31_TYPE);
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);
// if not indexed, type must be any/eq/i31/struct/array/func/extern/nofunc/noextern/none
debug_assert!(
x & Self::INDEXED_BIT != 0
|| matches!(
x & Self::TYPE_MASK,
Self::ANY_TYPE
| Self::EQ_TYPE
| Self::I31_TYPE
| Self::STRUCT_TYPE
| Self::ARRAY_TYPE
| Self::FUNC_TYPE
| Self::NOFUNC_TYPE
| Self::EXTERN_TYPE
| Self::NOEXTERN_TYPE
| Self::NONE_TYPE
)
);
RefType(Self::u32_to_u24(x))
}
/// Create a reference to a typed function with the type at the given index.
///
/// Returns `None` when the type index is beyond this crate's implementation
/// limits and therefore is not representable.
pub const fn indexed_func(nullable: bool, index: u32) -> Option<Self> {
Self::indexed(nullable, Self::FUNC_KIND, index)
}
/// Create a reference to an array with the type at the given index.
///
/// Returns `None` when the type index is beyond this crate's implementation
/// limits and therefore is not representable.
pub const fn indexed_array(nullable: bool, index: u32) -> Option<Self> {
Self::indexed(nullable, Self::ARRAY_KIND, index)
}
/// Create a reference to a struct with the type at the given index.
///
/// Returns `None` when the type index is beyond this crate's implementation
/// limits and therefore is not representable.
pub const fn indexed_struct(nullable: bool, index: u32) -> Option<Self> {
Self::indexed(nullable, Self::STRUCT_KIND, index)
}
/// Create a reference to a user defined type at the given index.
///
/// Returns `None` when the type index is beyond this crate's implementation
/// limits and therefore is not representable, or when the heap type is not
/// a typed array, struct or function.
const fn indexed(nullable: bool, kind: u32, index: u32) -> Option<Self> {
if Self::can_represent_type_index(index) {
let nullable32 = Self::NULLABLE_BIT * nullable as u32;
Some(RefType::from_u32(
nullable32 | Self::INDEXED_BIT | kind | index,
))
} else {
None
}
}
/// Create a new `RefType`.
///
/// Returns `None` when the heap type's type index (if any) is beyond this
/// crate's implementation limits and therfore is not representable.
pub const fn new(nullable: bool, heap_type: HeapType) -> Option<Self> {
let nullable32 = Self::NULLABLE_BIT * nullable as u32;
match heap_type {
HeapType::Indexed(index) => RefType::indexed(nullable, 0, index), // 0 bc we don't know the kind
HeapType::Func => Some(Self::from_u32(nullable32 | Self::FUNC_TYPE)),
HeapType::Extern => Some(Self::from_u32(nullable32 | Self::EXTERN_TYPE)),
HeapType::Any => Some(Self::from_u32(nullable32 | Self::ANY_TYPE)),
HeapType::None => Some(Self::from_u32(nullable32 | Self::NONE_TYPE)),
HeapType::NoExtern => Some(Self::from_u32(nullable32 | Self::NOEXTERN_TYPE)),
HeapType::NoFunc => Some(Self::from_u32(nullable32 | Self::NOFUNC_TYPE)),
HeapType::Eq => Some(Self::from_u32(nullable32 | Self::EQ_TYPE)),
HeapType::Struct => Some(Self::from_u32(nullable32 | Self::STRUCT_TYPE)),
HeapType::Array => Some(Self::from_u32(nullable32 | Self::ARRAY_TYPE)),
HeapType::I31 => Some(Self::from_u32(nullable32 | Self::I31_TYPE)),
}
}
/// Is this a reference to a typed function?
pub const fn is_typed_func_ref(&self) -> bool {
self.is_indexed_type_ref() && self.as_u32() & Self::KIND_MASK == Self::FUNC_KIND
}
/// Is this a reference to an indexed type?
pub const fn is_indexed_type_ref(&self) -> bool {
self.as_u32() & Self::INDEXED_BIT != 0
}
/// If this is a reference to a typed function, get its type index.
pub const fn type_index(&self) -> Option<u32> {
if self.is_indexed_type_ref() {
Some(self.as_u32() & Self::INDEX_MASK)
} else {
None
}
}
/// Is this an untyped function reference aka `(ref null func)` aka `funcref` aka `anyfunc`?
pub const fn is_func_ref(&self) -> bool {
!self.is_indexed_type_ref() && self.as_u32() & Self::TYPE_MASK == Self::FUNC_TYPE
}
/// Is this a `(ref null extern)` aka `externref`?
pub const fn is_extern_ref(&self) -> bool {
!self.is_indexed_type_ref() && self.as_u32() & Self::TYPE_MASK == Self::EXTERN_TYPE
}
/// 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 non-nullable version of this ref type.
pub const fn nullable(&self) -> Self {
Self::from_u32(self.as_u32() | Self::NULLABLE_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_indexed_type_ref() {
HeapType::Indexed(self.type_index().unwrap())
} else {
match s & Self::TYPE_MASK {
Self::FUNC_TYPE => HeapType::Func,
Self::EXTERN_TYPE => HeapType::Extern,
Self::ANY_TYPE => HeapType::Any,
Self::NONE_TYPE => HeapType::None,
Self::NOEXTERN_TYPE => HeapType::NoExtern,
Self::NOFUNC_TYPE => HeapType::NoFunc,
Self::EQ_TYPE => HeapType::Eq,
Self::STRUCT_TYPE => HeapType::Struct,
Self::ARRAY_TYPE => HeapType::Array,
Self::I31_TYPE => HeapType::I31,
_ => unreachable!(),
}
}
}
// Note that this is similar to `Display for RefType` except that it has
// the indexes stubbed out.
pub(crate) fn wat(&self) -> &'static str {
match (self.is_nullable(), self.heap_type()) {
(true, HeapType::Func) => "funcref",
(true, HeapType::Extern) => "externref",
(true, HeapType::Indexed(_)) => "(ref null $type)",
(true, HeapType::Any) => "anyref",
(true, HeapType::None) => "nullref",
(true, HeapType::NoExtern) => "nullexternref",
(true, HeapType::NoFunc) => "nullfuncref",
(true, HeapType::Eq) => "eqref",
(true, HeapType::Struct) => "structref",
(true, HeapType::Array) => "arrayref",
(true, HeapType::I31) => "i31ref",
(false, HeapType::Func) => "(ref func)",
(false, HeapType::Extern) => "(ref extern)",
(false, HeapType::Indexed(_)) => "(ref $type)",
(false, HeapType::Any) => "(ref any)",
(false, HeapType::None) => "(ref none)",
(false, HeapType::NoExtern) => "(ref noextern)",
(false, HeapType::NoFunc) => "(ref nofunc)",
(false, HeapType::Eq) => "(ref eq)",
(false, HeapType::Struct) => "(ref struct)",
(false, HeapType::Array) => "(ref array)",
(false, HeapType::I31) => "(ref i31)",
}
}
}
impl<'a> FromReader<'a> for RefType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
match reader.read()? {
0x70 => Ok(RefType::FUNCREF),
0x6F => Ok(RefType::EXTERNREF),
byte @ (0x6B | 0x6C) => Ok(RefType {
nullable: byte == 0x6C,
heap_type: reader.read()?,
}),
0x70 => Ok(RefType::FUNC.nullable()),
0x6F => Ok(RefType::EXTERN.nullable()),
0x6E => Ok(RefType::ANY.nullable()),
0x65 => Ok(RefType::NONE.nullable()),
0x69 => Ok(RefType::NOEXTERN.nullable()),
0x68 => Ok(RefType::NOFUNC.nullable()),
0x6D => Ok(RefType::EQ.nullable()),
0x67 => Ok(RefType::STRUCT.nullable()),
0x66 => Ok(RefType::ARRAY.nullable()),
0x6A => Ok(RefType::I31.nullable()),
byte @ (0x6B | 0x6C) => {
let nullable = byte == 0x6C;
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 fmt::Display for RefType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Note that this is similar to `RefType::wat` except that it has the
// indexes filled out.
let s = match (self.is_nullable(), self.heap_type()) {
(true, HeapType::Func) => "funcref",
(true, HeapType::Extern) => "externref",
(true, HeapType::Indexed(i)) => return write!(f, "(ref null {i})"),
(true, HeapType::Any) => "anyref",
(true, HeapType::None) => "nullref",
(true, HeapType::NoExtern) => "nullexternref",
(true, HeapType::NoFunc) => "nullfuncref",
(true, HeapType::Eq) => "eqref",
(true, HeapType::Struct) => "structref",
(true, HeapType::Array) => "arrayref",
(true, HeapType::I31) => "i31ref",
(false, HeapType::Func) => "(ref func)",
(false, HeapType::Extern) => "(ref extern)",
(false, HeapType::Indexed(i)) => return write!(f, "(ref {i})"),
(false, HeapType::Any) => "(ref any)",
(false, HeapType::None) => "(ref none)",
(false, HeapType::NoExtern) => "(ref noextern)",
(false, HeapType::NoFunc) => "(ref nofunc)",
(false, HeapType::Eq) => "(ref eq)",
(false, HeapType::Struct) => "(ref struct)",
(false, HeapType::Array) => "(ref array)",
(false, HeapType::I31) => "(ref i31)",
};
f.write_str(s)
}
}
/// A heap type from function references. When the proposal is disabled, Index
/// is an invalid type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum HeapType {
/// User defined type at the given index.
Indexed(u32),
/// Untyped (any) function.
Func,
/// External heap type.
Extern,
/// The `any` heap type. The common supertype (a.k.a. top) of all internal types.
Any,
/// The `none` heap type. The common subtype (a.k.a. bottom) of all internal types.
None,
/// The `noextern` heap type. The common subtype (a.k.a. bottom) of all external types.
NoExtern,
/// The `nofunc` heap type. The common subtype (a.k.a. bottom) of all function types.
NoFunc,
/// The `eq` heap type. The common supertype of all referenceable types on which comparison
/// (ref.eq) is allowed.
Eq,
/// The `struct` heap type. The common supertype of all struct types.
Struct,
/// The `array` heap type. The common supertype of all array types.
Array,
/// The i31 heap type.
I31,
}
impl<'a> FromReader<'a> for HeapType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
match reader.peek()? {
@@ -190,19 +606,46 @@ impl<'a> FromReader<'a> for HeapType {
reader.position += 1;
Ok(HeapType::Extern)
}
0x6E => {
reader.position += 1;
Ok(HeapType::Any)
}
0x65 => {
reader.position += 1;
Ok(HeapType::None)
}
0x69 => {
reader.position += 1;
Ok(HeapType::NoExtern)
}
0x68 => {
reader.position += 1;
Ok(HeapType::NoFunc)
}
0x6D => {
reader.position += 1;
Ok(HeapType::Eq)
}
0x67 => {
reader.position += 1;
Ok(HeapType::Struct)
}
0x66 => {
reader.position += 1;
Ok(HeapType::Array)
}
0x6A => {
reader.position += 1;
Ok(HeapType::I31)
}
_ => {
let idx = match u32::try_from(reader.read_var_s33()?) {
Ok(idx) => idx,
Err(_) => {
bail!(reader.original_position(), "invalid function heap type",);
bail!(reader.original_position(), "invalid indexed ref heap type");
}
};
match idx.try_into() {
Ok(packed) => Ok(HeapType::TypedFunc(packed)),
Err(_) => {
bail!(reader.original_position(), "function index too large");
}
}
Ok(HeapType::Indexed(idx))
}
}
}
@@ -213,6 +656,9 @@ impl<'a> FromReader<'a> for HeapType {
pub enum Type {
/// The type is for a function.
Func(FuncType),
/// The type is for an array.
Array(ArrayType),
// Struct(StructType),
}
/// Represents a type of a function in a WebAssembly module.
@@ -224,6 +670,15 @@ pub struct FuncType {
len_params: usize,
}
/// Represents a type of an array in a WebAssembly module.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct ArrayType {
/// Array element type.
pub element_type: StorageType,
/// Are elements mutable.
pub mutable: bool,
}
impl Debug for FuncType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FuncType")
@@ -273,6 +728,26 @@ impl FuncType {
pub fn results(&self) -> &[ValType] {
&self.params_results[self.len_params..]
}
pub(crate) fn desc(&self) -> String {
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 table's type.
@@ -359,6 +834,7 @@ impl<'a> FromReader<'a> for Type {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(match reader.read_u8()? {
0x60 => Type::Func(reader.read()?),
0x5e => Type::Array(reader.read()?),
x => return reader.invalid_leading_byte(x, "type"),
})
}
@@ -378,3 +854,21 @@ impl<'a> FromReader<'a> for FuncType {
Ok(FuncType::from_raw_parts(params_results.into(), len_params))
}
}
impl<'a> FromReader<'a> for ArrayType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let element_type = reader.read()?;
let mutable = reader.read_u8()?;
Ok(ArrayType {
element_type,
mutable: match mutable {
0 => false,
1 => true,
_ => bail!(
reader.original_position(),
"invalid mutability byte for array type"
),
},
})
}
}

View File

@@ -240,11 +240,14 @@ pub trait WasmModuleResources {
// Delegate to the generic value type validation which will have the
// same validity checks.
self.check_value_type(
RefType {
nullable: true,
heap_type,
}
.into(),
RefType::new(true, heap_type)
.ok_or_else(|| {
BinaryReaderError::new(
"heap type index beyond this crate's implementation limits",
offset,
)
})?
.into(),
features,
offset,
)

View File

@@ -48,6 +48,7 @@ fn test_validate() {
mod component;
mod core;
mod func;
pub mod names;
mod operators;
pub mod types;
@@ -244,6 +245,8 @@ pub struct WasmFeatures {
pub function_references: bool,
/// The WebAssembly memory control proposal
pub memory_control: bool,
/// The WebAssembly gc proposal
pub gc: bool,
}
impl WasmFeatures {
@@ -262,22 +265,50 @@ impl WasmFeatures {
}
}
ValType::Ref(r) => {
if self.reference_types {
if !self.function_references {
match (r.heap_type, r.nullable) {
(_, false) => {
Err("function references required for non-nullable types")
}
(HeapType::TypedFunc(_), _) => {
Err("function references required for index reference types")
}
_ => Ok(()),
if !self.reference_types {
return Err("reference types support is not enabled");
}
match (r.heap_type(), r.is_nullable()) {
// funcref/externref only require `reference-types`
(HeapType::Func, true) | (HeapType::Extern, true) => Ok(()),
// non-nullable func/extern references requires the
// `function-references` proposal
(HeapType::Func | HeapType::Extern, false) => {
if self.function_references {
Ok(())
} else {
Err("function references required for non-nullable types")
}
}
// indexed types require at least the function-references
// proposal
(HeapType::Indexed(_), _) => {
if self.function_references {
Ok(())
} else {
Err("function references required for index reference types")
}
}
// types added in the gc proposal
(
HeapType::Any
| HeapType::None
| HeapType::Eq
| HeapType::Struct
| HeapType::Array
| HeapType::I31
| HeapType::NoExtern
| HeapType::NoFunc,
_,
) => {
if self.gc {
Ok(())
} else {
Err("heap types not supported without the gc feature")
}
} else {
Ok(())
}
} else {
Err("reference types support is not enabled")
}
}
ValType::V128 => {
@@ -304,6 +335,7 @@ impl Default for WasmFeatures {
component_model: false,
function_references: false,
memory_control: false,
gc: false,
// On-by-default features (phase 4 or greater).
mutable_global: true,
@@ -540,7 +572,8 @@ impl Validator {
);
}
if num == WASM_COMPONENT_VERSION {
self.components.push(ComponentState::default());
self.components
.push(ComponentState::new(ComponentKind::Component));
State::Component
} else if num < WASM_COMPONENT_VERSION {
bail!(range.start, "unsupported component version: {num:#x}");
@@ -1120,6 +1153,15 @@ impl Validator {
func_index,
options,
} => current.lower_function(func_index, options.into_vec(), types, offset),
crate::CanonicalFunction::ResourceNew { resource } => {
current.resource_new(resource, types, offset)
}
crate::CanonicalFunction::ResourceDrop { ty } => {
current.resource_drop(ty, types, offset)
}
crate::CanonicalFunction::ResourceRep { resource } => {
current.resource_rep(resource, types, offset)
}
}
},
)
@@ -1135,16 +1177,6 @@ impl Validator {
) -> Result<()> {
self.state.ensure_component("start", range.start)?;
// let mut section = section.clone();
// let f = section.read()?;
// if !section.eof() {
// return Err(BinaryReaderError::new(
// "trailing data at the end of the start section",
// section.original_position(),
// ));
// }
self.components.last_mut().unwrap().add_start(
f.func_index,
&f.arguments,
@@ -1187,13 +1219,13 @@ impl Validator {
|components, _, count, offset| {
let current = components.last_mut().unwrap();
check_max(
current.externs.len(),
current.exports.len(),
count,
MAX_WASM_EXPORTS,
"imports and exports",
"exports",
offset,
)?;
current.externs.reserve(count as usize);
current.exports.reserve(count as usize);
Ok(())
},
|components, types, _, export, offset| {
@@ -1201,8 +1233,8 @@ impl Validator {
let ty = current.export_to_entity_type(&export, types, offset)?;
current.add_export(
export.name,
export.url,
ty,
types,
offset,
false, /* checked above */
)
@@ -1251,16 +1283,18 @@ impl Validator {
// Validate that all values were used for the component
if let Some(index) = component.values.iter().position(|(_, used)| !*used) {
return Err(
format_err!(offset,"value index {index} was not used as part of an instantiation, start function, or export"
)
bail!(
offset,
"value index {index} was not used as part of an \
instantiation, start function, or export"
);
}
// If there's a parent component, pop the stack, add it to the parent,
// and continue to validate the component
let ty = component.finish(&mut self.types, offset)?;
if let Some(parent) = self.components.last_mut() {
parent.add_component(&mut component, &mut self.types);
parent.add_component(ty, &mut self.types)?;
self.state = State::Component;
}
@@ -1494,7 +1528,48 @@ mod tests {
let a1_id = types.id_from_type_index(1, false).unwrap();
let a2_id = types.id_from_type_index(2, false).unwrap();
// The ids should all be different
// The ids should all be the same
assert!(t_id == a1_id);
assert!(t_id == a2_id);
assert!(a1_id == a2_id);
// However, they should all point to the same type
assert!(std::ptr::eq(
types.type_from_id(t_id).unwrap(),
types.type_from_id(a1_id).unwrap()
));
assert!(std::ptr::eq(
types.type_from_id(t_id).unwrap(),
types.type_from_id(a2_id).unwrap()
));
Ok(())
}
#[test]
fn test_type_id_exports() -> Result<()> {
let bytes = wat::parse_str(
r#"
(component
(type $T (list string))
(export $A1 "A1" (type $T))
(export $A2 "A2" (type $T))
)
"#,
)?;
let mut validator = Validator::new_with_features(WasmFeatures {
component_model: true,
..Default::default()
});
let types = validator.validate_all(&bytes)?;
let t_id = types.id_from_type_index(0, false).unwrap();
let a1_id = types.id_from_type_index(1, false).unwrap();
let a2_id = types.id_from_type_index(2, false).unwrap();
// The ids should all be the same
assert!(t_id != a1_id);
assert!(t_id != a2_id);
assert!(a1_id != a2_id);

File diff suppressed because it is too large Load Diff

View File

@@ -9,8 +9,9 @@ use crate::limits::*;
use crate::validator::core::arc::MaybeOwned;
use crate::{
BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FuncType,
Global, GlobalType, HeapType, MemoryType, RefType, Result, Table, TableInit, TableType,
TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmFuncType, WasmModuleResources,
Global, GlobalType, HeapType, MemoryType, RefType, Result, StorageType, Table, TableInit,
TableType, TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmFuncType,
WasmModuleResources,
};
use indexmap::IndexMap;
use std::mem;
@@ -133,7 +134,7 @@ impl ModuleState {
offset: usize,
) -> Result<()> {
self.module
.check_global_type(&global.ty, features, types, offset)?;
.check_global_type(&global.ty, features, offset)?;
self.check_const_expr(&global.init_expr, global.ty.content_type, features, types)?;
self.module.assert_mut().globals.push(global.ty);
Ok(())
@@ -146,12 +147,11 @@ impl ModuleState {
types: &TypeList,
offset: usize,
) -> Result<()> {
self.module
.check_table_type(&table.ty, features, types, offset)?;
self.module.check_table_type(&table.ty, features, offset)?;
match &table.init {
TableInit::RefNull => {
if !table.ty.element_type.nullable {
if !table.ty.element_type.is_nullable() {
bail!(offset, "type mismatch: non-defaultable element type");
}
}
@@ -200,14 +200,14 @@ impl ModuleState {
// don't check it here
if e.ty != RefType::FUNCREF {
self.module
.check_value_type(ValType::Ref(e.ty), features, types, offset)?;
.check_value_type(ValType::Ref(e.ty), features, offset)?;
}
match e.kind {
ElementKind::Active {
table_index,
offset_expr,
} => {
let table = self.module.table_at(table_index, offset)?;
let table = self.module.table_at(table_index.unwrap_or(0), offset)?;
if !self
.module
.matches(ValType::Ref(e.ty), ValType::Ref(table.element_type), types)
@@ -247,7 +247,7 @@ impl ModuleState {
match e.items {
crate::ElementItems::Functions(reader) => {
let count = reader.count();
if !e.ty.nullable && count <= 0 {
if !e.ty.is_nullable() && count <= 0 {
return Err(BinaryReaderError::new(
"a non-nullable element must come with an initialization expression",
offset,
@@ -503,7 +503,7 @@ impl Module {
let ty = match ty {
crate::Type::Func(t) => {
for ty in t.params().iter().chain(t.results()) {
self.check_value_type(*ty, features, types, offset)?;
self.check_value_type(*ty, features, offset)?;
}
if t.results().len() > 1 && !features.multi_value {
return Err(BinaryReaderError::new(
@@ -513,13 +513,28 @@ impl Module {
}
Type::Func(t)
}
crate::Type::Array(t) => {
if !features.gc {
return Err(BinaryReaderError::new(
"array indexed types not supported without the gc feature",
offset,
));
}
match t.element_type {
crate::StorageType::I8 | crate::StorageType::I16 => {}
crate::StorageType::Val(value_type) => {
self.check_value_type(value_type, features, offset)?;
}
};
Type::Array(t)
}
};
if check_limit {
check_max(self.types.len(), 1, MAX_WASM_TYPES, "types", offset)?;
}
let id = types.push_defined(ty);
let id = types.push_ty(ty);
self.types.push(id);
Ok(())
}
@@ -639,20 +654,24 @@ impl Module {
Ok(())
}
pub fn type_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
pub fn type_id_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
self.types
.get(idx as usize)
.copied()
.ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
}
fn type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a Type> {
self.type_id_at(idx, offset).map(|type_id| &types[type_id])
}
fn func_type_at<'a>(
&self,
type_index: u32,
types: &'a TypeList,
offset: usize,
) -> Result<&'a FuncType> {
types[self.type_at(type_index, offset)?]
types[self.type_id_at(type_index, offset)?]
.as_func_type()
.ok_or_else(|| format_err!(offset, "type index {type_index} is not a function type"))
}
@@ -670,7 +689,7 @@ impl Module {
EntityType::Func(self.types[*type_index as usize])
}
TypeRef::Table(t) => {
self.check_table_type(t, features, types, offset)?;
self.check_table_type(t, features, offset)?;
EntityType::Table(*t)
}
TypeRef::Memory(t) => {
@@ -682,7 +701,7 @@ impl Module {
EntityType::Tag(self.types[t.func_type_idx as usize])
}
TypeRef::Global(t) => {
self.check_global_type(t, features, types, offset)?;
self.check_global_type(t, features, offset)?;
EntityType::Global(*t)
}
})
@@ -692,13 +711,12 @@ impl Module {
&self,
ty: &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_value_type(ValType::Ref(ty.element_type), features, types, offset)?
self.check_value_type(ValType::Ref(ty.element_type), features, offset)?
}
self.check_limits(ty.initial, ty.maximum, offset)?;
@@ -780,13 +798,7 @@ impl Module {
.collect::<Result<_>>()
}
fn check_value_type(
&self,
ty: ValType,
features: &WasmFeatures,
types: &TypeList,
offset: usize,
) -> Result<()> {
fn check_value_type(&self, ty: ValType, features: &WasmFeatures, offset: usize) -> Result<()> {
match features.check_value_type(ty) {
Ok(()) => Ok(()),
Err(e) => Err(BinaryReaderError::new(e, offset)),
@@ -795,20 +807,29 @@ impl Module {
// We must check it if it's a reference.
match ty {
ValType::Ref(rt) => {
self.check_ref_type(rt, types, offset)?;
self.check_ref_type(rt, offset)?;
}
_ => (),
}
Ok(())
}
fn check_ref_type(&self, ty: RefType, types: &TypeList, offset: usize) -> Result<()> {
fn check_ref_type(&self, ty: RefType, offset: usize) -> Result<()> {
// Check that the heap type is valid
match ty.heap_type {
HeapType::Func | HeapType::Extern => (),
HeapType::TypedFunc(type_index) => {
match ty.heap_type() {
HeapType::Func
| HeapType::Extern
| HeapType::Any
| HeapType::None
| HeapType::NoExtern
| HeapType::NoFunc
| HeapType::Eq
| HeapType::Struct
| HeapType::Array
| HeapType::I31 => (),
HeapType::Indexed(type_index) => {
// Just check that the index is valid
self.func_type_at(type_index.into(), types, offset)?;
self.type_id_at(type_index, offset)?;
}
}
Ok(())
@@ -817,21 +838,36 @@ impl Module {
fn eq_valtypes(&self, ty1: ValType, ty2: ValType, types: &TypeList) -> bool {
match (ty1, ty2) {
(ValType::Ref(rt1), ValType::Ref(rt2)) => {
rt1.nullable == rt2.nullable
&& match (rt1.heap_type, rt2.heap_type) {
(HeapType::Func, HeapType::Func) => true,
(HeapType::Extern, HeapType::Extern) => true,
(HeapType::TypedFunc(n1), HeapType::TypedFunc(n2)) => {
let n1 = self.func_type_at(n1.into(), types, 0).unwrap();
let n2 = self.func_type_at(n2.into(), types, 0).unwrap();
self.eq_fns(n1, n2, types)
rt1.is_nullable() == rt2.is_nullable()
&& match (rt1.heap_type(), rt2.heap_type()) {
(HeapType::Indexed(n1), HeapType::Indexed(n2)) => {
self.eq_indexed_types(n1, n2, types)
}
(_, _) => false,
(h1, h2) => h1 == h2,
}
}
_ => ty1 == ty2,
}
}
fn eq_indexed_types(&self, n1: u32, n2: u32, types: &TypeList) -> bool {
let n1 = self.type_at(types, n1.into(), 0).unwrap();
let n2 = self.type_at(types, n2.into(), 0).unwrap();
match (n1, n2) {
(Type::Func(f1), Type::Func(f2)) => self.eq_fns(f1, f2, types),
(Type::Array(a1), Type::Array(a2)) => {
a1.mutable == a2.mutable
&& match (a1.element_type, a2.element_type) {
(StorageType::Val(vt1), StorageType::Val(vt2)) => {
self.eq_valtypes(vt1, vt2, types)
}
(st1, st2) => st1 == st2,
}
}
_ => false,
}
}
fn eq_fns(&self, f1: &impl WasmFuncType, f2: &impl WasmFuncType, types: &TypeList) -> bool {
f1.len_inputs() == f2.len_inputs()
&& f2.len_outputs() == f2.len_outputs()
@@ -845,6 +881,9 @@ impl Module {
.all(|(t1, t2)| self.eq_valtypes(t1, t2, types))
}
/// Check that a value of type ty1 is assignable to a variable / table element of type ty2.
/// E.g. a non-nullable reference can be assigned to a nullable reference, but not vice versa.
/// Or an indexed func ref is assignable to a generic func ref, but not vice versa.
pub(crate) fn matches(&self, ty1: ValType, ty2: ValType, types: &TypeList) -> bool {
fn matches_null(null1: bool, null2: bool) -> bool {
(null1 == null2) || null2
@@ -852,20 +891,40 @@ impl Module {
let matches_heap = |ty1: HeapType, ty2: HeapType, types: &TypeList| -> bool {
match (ty1, ty2) {
(HeapType::TypedFunc(n1), HeapType::TypedFunc(n2)) => {
(HeapType::Indexed(n1), HeapType::Indexed(n2)) => {
// Check whether the defined types are (structurally) equivalent.
let n1 = self.func_type_at(n1.into(), types, 0).unwrap();
let n2 = self.func_type_at(n2.into(), types, 0).unwrap();
self.eq_fns(n1, n2, types)
let n1 = self.type_at(types, n1.into(), 0);
let n2 = self.type_at(types, n2.into(), 0);
match (n1, n2) {
(Ok(Type::Func(n1)), Ok(Type::Func(n2))) => self.eq_fns(n1, n2, types),
(Ok(Type::Array(n1)), Ok(Type::Array(n2))) => {
(n1.mutable == n2.mutable || n2.mutable)
&& match (n1.element_type, n2.element_type) {
(StorageType::Val(vt1), StorageType::Val(vt2)) => {
self.matches(vt1, vt2, types)
}
(st1, st2) => st1 == st2,
}
}
_ => false,
}
}
(HeapType::Indexed(n1), HeapType::Func) => {
self.func_type_at(n1.into(), types, 0).is_ok()
}
(HeapType::Indexed(n1), HeapType::Array) => {
match self.type_at(types, n1.into(), 0) {
Ok(Type::Array(_)) => true,
_ => false,
}
}
(HeapType::TypedFunc(_), HeapType::Func) => true,
(_, _) => ty1 == ty2,
}
};
let matches_ref = |ty1: RefType, ty2: RefType, types: &TypeList| -> bool {
matches_heap(ty1.heap_type, ty2.heap_type, types)
&& matches_null(ty1.nullable, ty2.nullable)
matches_heap(ty1.heap_type(), ty2.heap_type(), types)
&& matches_null(ty1.is_nullable(), ty2.is_nullable())
};
match (ty1, ty2) {
@@ -901,10 +960,9 @@ impl Module {
&self,
ty: &GlobalType,
features: &WasmFeatures,
types: &TypeList,
offset: usize,
) -> Result<()> {
self.check_value_type(ty.content_type, features, types, offset)
self.check_value_type(ty.content_type, features, offset)
}
fn check_limits<T>(&self, initial: T, maximum: Option<T>, offset: usize) -> Result<()>
@@ -1092,8 +1150,7 @@ impl WasmModuleResources for OperatorValidatorResources<'_> {
}
fn check_value_type(&self, t: ValType, features: &WasmFeatures, offset: usize) -> Result<()> {
self.module
.check_value_type(t, features, self.types, offset)
self.module.check_value_type(t, features, offset)
}
fn element_type_at(&self, at: u32) -> Option<RefType> {
@@ -1161,8 +1218,7 @@ impl WasmModuleResources for ValidatorResources {
}
fn check_value_type(&self, t: ValType, features: &WasmFeatures, offset: usize) -> Result<()> {
self.0
.check_value_type(t, features, self.0.snapshot.as_ref().unwrap(), offset)
self.0.check_value_type(t, features, offset)
}
fn element_type_at(&self, at: u32) -> Option<RefType> {

View File

@@ -0,0 +1,606 @@
//! Definitions of name-related helpers and newtypes, primarily for the
//! component model.
use crate::{ComponentExternName, Result};
use semver::Version;
use std::borrow::Borrow;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
/// 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<str> + '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<str> + 'a) -> &'a Self {
// Safety: `KebabStr` is a transparent wrapper around `str`
// Therefore transmuting `&str` to `&KebabStr` is safe.
unsafe { std::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<KebabString> for KebabStr {
fn eq(&self, other: &KebabString) -> bool {
self.eq(other.as_kebab_str())
}
}
impl Hash for KebabStr {
fn hash<H: Hasher>(&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<String>) -> Option<Self> {
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<KebabStr> for KebabString {
fn borrow(&self) -> &KebabStr {
self.as_kebab_str()
}
}
impl PartialEq for KebabString {
fn eq(&self, other: &Self) -> bool {
self.as_kebab_str().eq(other.as_kebab_str())
}
}
impl PartialEq<KebabStr> for KebabString {
fn eq(&self, other: &KebabStr) -> bool {
self.as_kebab_str().eq(other)
}
}
impl Hash for KebabString {
fn hash<H: Hasher>(&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<KebabString> for String {
fn from(s: KebabString) -> String {
s.0
}
}
/// A "kebab name" in the component model which is backed by `T`, which defaults
/// to `String`.
///
/// This name can be either:
///
/// * a `KebabStr`: `a-b-c`
/// * a method name : `[method]a-b.c-d`
/// * a static method name : `[static]a-b.c-d`
/// * a constructor: `[constructor]a-b`
///
/// # 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 KebabName {
raw: String,
parsed: ParsedKebabName,
}
#[derive(Copy, Clone)]
enum ParsedKebabName {
Normal,
Constructor,
Method {
dot: u32,
},
Static {
dot: u32,
},
Id {
colon: u32,
slash: u32,
at: Option<u32>,
},
}
/// Created via [`KebabName::kind`] and classifies a name.
#[derive(Debug, Clone)]
pub enum KebabNameKind<'a> {
/// `a-b-c`
Normal(&'a KebabStr),
/// `[constructor]a-b`
Constructor(&'a KebabStr),
/// `[method]a-b.c-d`
#[allow(missing_docs)]
Method {
resource: &'a KebabStr,
name: &'a KebabStr,
},
/// `[static]a-b.c-d`
#[allow(missing_docs)]
Static {
resource: &'a KebabStr,
name: &'a KebabStr,
},
/// `wasi:http/types@2.0`
#[allow(missing_docs)]
Id {
namespace: &'a KebabStr,
package: &'a KebabStr,
interface: &'a KebabStr,
version: Option<Version>,
},
}
const CONSTRUCTOR: &str = "[constructor]";
const METHOD: &str = "[method]";
const STATIC: &str = "[static]";
impl KebabName {
/// Attempts to parse `name` as a kebab name, returning `None` if it's not
/// valid.
pub fn new(name: ComponentExternName<'_>, offset: usize) -> Result<KebabName> {
let validate_kebab = |s: &str| {
if KebabStr::new(s).is_none() {
bail!(offset, "`{s}` is not in kebab case")
} else {
Ok(())
}
};
let find = |s: &str, c: char| match s.find(c) {
Some(i) => Ok(i),
None => bail!(offset, "failed to find `{c}` character"),
};
let parsed = match name {
ComponentExternName::Kebab(s) => {
if let Some(s) = s.strip_prefix(CONSTRUCTOR) {
validate_kebab(s)?;
ParsedKebabName::Constructor
} else if let Some(s) = s.strip_prefix(METHOD) {
let dot = find(s, '.')?;
validate_kebab(&s[..dot])?;
validate_kebab(&s[dot + 1..])?;
ParsedKebabName::Method { dot: dot as u32 }
} else if let Some(s) = s.strip_prefix(STATIC) {
let dot = find(s, '.')?;
validate_kebab(&s[..dot])?;
validate_kebab(&s[dot + 1..])?;
ParsedKebabName::Static { dot: dot as u32 }
} else {
validate_kebab(s)?;
ParsedKebabName::Normal
}
}
ComponentExternName::Interface(s) => {
let colon = find(s, ':')?;
validate_kebab(&s[..colon])?;
let slash = find(s, '/')?;
let at = s[slash..].find('@').map(|i| i + slash);
validate_kebab(&s[colon + 1..slash])?;
validate_kebab(&s[slash + 1..at.unwrap_or(s.len())])?;
if let Some(at) = at {
let version = &s[at + 1..];
if let Err(e) = version.parse::<Version>() {
bail!(offset, "failed to parse version: {e}")
}
}
ParsedKebabName::Id {
colon: colon as u32,
slash: slash as u32,
at: at.map(|i| i as u32),
}
}
};
Ok(KebabName {
raw: name.as_str().to_string(),
parsed,
})
}
/// Returns the [`KebabNameKind`] corresponding to this name.
pub fn kind(&self) -> KebabNameKind<'_> {
match self.parsed {
ParsedKebabName::Normal => KebabNameKind::Normal(KebabStr::new_unchecked(&self.raw)),
ParsedKebabName::Constructor => {
let kebab = &self.raw[CONSTRUCTOR.len()..];
KebabNameKind::Constructor(KebabStr::new_unchecked(kebab))
}
ParsedKebabName::Method { dot } => {
let dotted = &self.raw[METHOD.len()..];
let resource = KebabStr::new_unchecked(&dotted[..dot as usize]);
let name = KebabStr::new_unchecked(&dotted[dot as usize + 1..]);
KebabNameKind::Method { resource, name }
}
ParsedKebabName::Static { dot } => {
let dotted = &self.raw[METHOD.len()..];
let resource = KebabStr::new_unchecked(&dotted[..dot as usize]);
let name = KebabStr::new_unchecked(&dotted[dot as usize + 1..]);
KebabNameKind::Static { resource, name }
}
ParsedKebabName::Id { colon, slash, at } => {
let colon = colon as usize;
let slash = slash as usize;
let at = at.map(|i| i as usize);
let namespace = KebabStr::new_unchecked(&self.raw[..colon]);
let package = KebabStr::new_unchecked(&self.raw[colon + 1..slash]);
let interface =
KebabStr::new_unchecked(&self.raw[slash + 1..at.unwrap_or(self.raw.len())]);
let version = at.map(|i| Version::parse(&self.raw[i + 1..]).unwrap());
KebabNameKind::Id {
namespace,
package,
interface,
version,
}
}
}
}
/// Returns the raw underlying name as a string.
pub fn as_str(&self) -> &str {
&self.raw
}
}
impl From<KebabName> for String {
fn from(name: KebabName) -> String {
name.raw
}
}
impl Hash for KebabName {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.kind().hash(hasher)
}
}
impl PartialEq for KebabName {
fn eq(&self, other: &KebabName) -> bool {
self.kind().eq(&other.kind())
}
}
impl Eq for KebabName {}
impl fmt::Display for KebabName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.raw.fmt(f)
}
}
impl fmt::Debug for KebabName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.raw.fmt(f)
}
}
impl Hash for KebabNameKind<'_> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
match self {
KebabNameKind::Normal(name) => {
hasher.write_u8(0);
name.hash(hasher);
}
KebabNameKind::Constructor(name) => {
hasher.write_u8(1);
name.hash(hasher);
}
// for hashing method == static
KebabNameKind::Method { resource, name } | KebabNameKind::Static { resource, name } => {
hasher.write_u8(2);
resource.hash(hasher);
name.hash(hasher);
}
KebabNameKind::Id {
namespace,
package,
interface,
version,
} => {
hasher.write_u8(3);
namespace.hash(hasher);
package.hash(hasher);
interface.hash(hasher);
version.hash(hasher);
}
}
}
}
impl PartialEq for KebabNameKind<'_> {
fn eq(&self, other: &KebabNameKind<'_>) -> bool {
match (self, other) {
(KebabNameKind::Normal(a), KebabNameKind::Normal(b)) => a == b,
(KebabNameKind::Normal(_), _) => false,
(KebabNameKind::Constructor(a), KebabNameKind::Constructor(b)) => a == b,
(KebabNameKind::Constructor(_), _) => false,
// method == static for the purposes of hashing so equate them here
// as well.
(
KebabNameKind::Method {
resource: ar,
name: an,
},
KebabNameKind::Method {
resource: br,
name: bn,
},
)
| (
KebabNameKind::Static {
resource: ar,
name: an,
},
KebabNameKind::Static {
resource: br,
name: bn,
},
)
| (
KebabNameKind::Method {
resource: ar,
name: an,
},
KebabNameKind::Static {
resource: br,
name: bn,
},
)
| (
KebabNameKind::Static {
resource: ar,
name: an,
},
KebabNameKind::Method {
resource: br,
name: bn,
},
) => ar == br && an == bn,
(KebabNameKind::Method { .. }, _) => false,
(KebabNameKind::Static { .. }, _) => false,
(
KebabNameKind::Id {
namespace: an,
package: ap,
interface: ai,
version: av,
},
KebabNameKind::Id {
namespace: bn,
package: bp,
interface: bi,
version: bv,
},
) => an == bn && ap == bp && ai == bi && av == bv,
(KebabNameKind::Id { .. }, _) => false,
}
}
}
impl Eq for KebabNameKind<'_> {}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
fn parse_kebab_name(s: &str) -> Option<KebabName> {
KebabName::new(ComponentExternName::Kebab(s), 0).ok()
}
#[test]
fn kebab_smoke() {
assert!(KebabStr::new("").is_none());
assert!(KebabStr::new("a").is_some());
assert!(KebabStr::new("aB").is_none());
assert!(KebabStr::new("a-B").is_some());
assert!(KebabStr::new("a-").is_none());
assert!(KebabStr::new("-").is_none());
assert!(KebabStr::new("").is_none());
assert!(KebabStr::new("0").is_none());
assert!(KebabStr::new("a0").is_some());
assert!(KebabStr::new("a-0").is_none());
}
#[test]
fn name_smoke() {
assert!(parse_kebab_name("a").is_some());
assert!(parse_kebab_name("[foo]a").is_none());
assert!(parse_kebab_name("[constructor]a").is_some());
assert!(parse_kebab_name("[method]a").is_none());
assert!(parse_kebab_name("[method]a.b").is_some());
assert!(parse_kebab_name("[method]a.b.c").is_none());
assert!(parse_kebab_name("[static]a.b").is_some());
assert!(parse_kebab_name("[static]a").is_none());
}
#[test]
fn name_equality() {
assert_eq!(parse_kebab_name("a"), parse_kebab_name("a"));
assert_ne!(parse_kebab_name("a"), parse_kebab_name("b"));
assert_eq!(
parse_kebab_name("[constructor]a"),
parse_kebab_name("[constructor]a")
);
assert_ne!(
parse_kebab_name("[constructor]a"),
parse_kebab_name("[constructor]b")
);
assert_eq!(
parse_kebab_name("[method]a.b"),
parse_kebab_name("[method]a.b")
);
assert_ne!(
parse_kebab_name("[method]a.b"),
parse_kebab_name("[method]b.b")
);
assert_eq!(
parse_kebab_name("[static]a.b"),
parse_kebab_name("[static]a.b")
);
assert_ne!(
parse_kebab_name("[static]a.b"),
parse_kebab_name("[static]b.b")
);
assert_eq!(
parse_kebab_name("[static]a.b"),
parse_kebab_name("[method]a.b")
);
assert_eq!(
parse_kebab_name("[method]a.b"),
parse_kebab_name("[static]a.b")
);
assert_ne!(
parse_kebab_name("[method]b.b"),
parse_kebab_name("[static]a.b")
);
let mut s = HashSet::new();
assert!(s.insert(parse_kebab_name("a")));
assert!(s.insert(parse_kebab_name("[constructor]a")));
assert!(s.insert(parse_kebab_name("[method]a.b")));
assert!(!s.insert(parse_kebab_name("[static]a.b")));
assert!(s.insert(parse_kebab_name("[static]b.b")));
}
}

View File

@@ -173,6 +173,13 @@ impl From<ValType> for MaybeType {
}
}
impl From<RefType> for MaybeType {
fn from(ty: RefType) -> MaybeType {
let ty: ValType = ty.into();
ty.into()
}
}
impl OperatorValidator {
fn new(features: &WasmFeatures, allocs: OperatorValidatorAllocations) -> Self {
let OperatorValidatorAllocations {
@@ -506,7 +513,7 @@ impl<'resources, R: WasmModuleResources> OperatorValidatorTemp<'_, 'resources, R
) => {
bail!(
self.offset,
"type mismatche: expected {}, found heap type",
"type mismatch: expected {}, found heap type",
ty_to_str(expected)
)
}
@@ -983,24 +990,7 @@ pub fn ty_to_str(ty: ValType) -> &'static str {
ValType::F32 => "f32",
ValType::F64 => "f64",
ValType::V128 => "v128",
ValType::FUNCREF => "funcref",
ValType::EXTERNREF => "externref",
ValType::Ref(RefType {
nullable: false,
heap_type: HeapType::Func,
}) => "(ref func)",
ValType::Ref(RefType {
nullable: false,
heap_type: HeapType::Extern,
}) => "(ref extern)",
ValType::Ref(RefType {
nullable: false,
heap_type: HeapType::TypedFunc(_),
}) => "(ref $type)",
ValType::Ref(RefType {
nullable: true,
heap_type: HeapType::TypedFunc(_),
}) => "(ref null $type)",
ValType::Ref(r) => r.wat(),
}
}
@@ -1051,6 +1041,7 @@ macro_rules! validate_proposal {
(desc tail_call) => ("tail calls");
(desc function_references) => ("function references");
(desc memory_control) => ("memory control");
(desc gc) => ("gc");
}
impl<'a, T> VisitOperator<'a> for WasmProposalValidator<'_, '_, T>
@@ -1280,16 +1271,15 @@ where
self.check_return()?;
Ok(())
}
fn visit_call_ref(&mut self, hty: HeapType) -> Self::Output {
fn visit_call_ref(&mut self, type_index: u32) -> Self::Output {
let hty = HeapType::Indexed(type_index);
self.resources
.check_heap_type(hty, &self.features, self.offset)?;
// If `None` is popped then that means a "bottom" type was popped which
// is always considered equivalent to the `hty` tag.
if let Some(rt) = self.pop_ref()? {
let expected = RefType {
nullable: true,
heap_type: hty,
};
let expected = RefType::indexed_func(true, type_index)
.expect("existing heap types should be within our limits");
if !self
.resources
.matches(ValType::Ref(rt), ValType::Ref(expected))
@@ -1300,17 +1290,10 @@ where
);
}
}
match hty {
HeapType::TypedFunc(type_index) => self.check_call_ty(type_index.into())?,
_ => bail!(
self.offset,
"type mismatch: instruction requires function reference type",
),
}
Ok(())
self.check_call_ty(type_index)
}
fn visit_return_call_ref(&mut self, hty: HeapType) -> Self::Output {
self.visit_call_ref(hty)?;
fn visit_return_call_ref(&mut self, type_index: u32) -> Self::Output {
self.visit_call_ref(type_index)?;
self.check_return()
}
fn visit_call_indirect(
@@ -2221,19 +2204,15 @@ where
fn visit_ref_null(&mut self, heap_type: HeapType) -> Self::Output {
self.resources
.check_heap_type(heap_type, &self.features, self.offset)?;
self.push_operand(ValType::Ref(RefType {
nullable: true,
heap_type,
}))?;
self.push_operand(ValType::Ref(
RefType::new(true, heap_type).expect("existing heap types should be within our limits"),
))?;
Ok(())
}
fn visit_ref_as_non_null(&mut self) -> Self::Output {
let ty = match self.pop_ref()? {
Some(ty) => MaybeType::Type(ValType::Ref(RefType {
nullable: false,
heap_type: ty.heap_type,
})),
Some(ty) => MaybeType::Type(ValType::Ref(ty.as_non_null())),
None => MaybeType::HeapBot,
};
self.push_operand(ty)?;
@@ -2242,10 +2221,7 @@ where
fn visit_br_on_null(&mut self, relative_depth: u32) -> Self::Output {
let ty = match self.pop_ref()? {
None => MaybeType::HeapBot,
Some(ty) => MaybeType::Type(ValType::Ref(RefType {
nullable: false,
heap_type: ty.heap_type,
})),
Some(ty) => MaybeType::Type(ValType::Ref(ty.as_non_null())),
};
let (ft, kind) = self.jump(relative_depth)?;
for ty in self.label_types(ft, kind)?.rev() {
@@ -2271,10 +2247,7 @@ where
// Switch rt0, our popped type, to a non-nullable type and
// perform the match because if the branch is taken it's a
// non-null value.
let ty = RefType {
nullable: false,
heap_type: rt0.heap_type,
};
let ty = rt0.as_non_null();
if !self.resources.matches(ty.into(), rt1) {
bail!(
self.offset,
@@ -2318,16 +2291,10 @@ where
// FIXME(#924) this should not be conditional based on enabled
// proposals.
if self.features.function_references {
let heap_type = HeapType::TypedFunc(match type_index.try_into() {
Ok(packed) => packed,
Err(_) => {
bail!(self.offset, "type index of `ref.func` target too large")
}
});
self.push_operand(ValType::Ref(RefType {
nullable: false,
heap_type,
}))?;
self.push_operand(
RefType::indexed_func(false, type_index)
.expect("our limits on number of types should fit into ref type"),
)?;
} else {
self.push_operand(ValType::FUNCREF)?;
}
@@ -3368,6 +3335,28 @@ where
self.pop_operand(Some(ValType::I32))?;
Ok(())
}
fn visit_i31_new(&mut self) -> Self::Output {
self.pop_operand(Some(ValType::I32))?;
self.push_operand(ValType::Ref(RefType::I31))
}
fn visit_i31_get_s(&mut self) -> Self::Output {
match self.pop_ref()? {
Some(ref_type) => match ref_type.heap_type() {
HeapType::I31 => self.push_operand(ValType::I32),
_ => bail!(self.offset, "ref heap type mismatch: expected i31"),
},
_ => bail!(self.offset, "type mismatch: expected (ref null? i31)"),
}
}
fn visit_i31_get_u(&mut self) -> Self::Output {
match self.pop_ref()? {
Some(ref_type) => match ref_type.heap_type() {
HeapType::I31 => self.push_operand(ValType::I32),
_ => bail!(self.offset, "ref heap type mismatch: expected i31"),
},
_ => bail!(self.offset, "type mismatch: expected (ref null? i31)"),
}
}
}
#[derive(Clone)]

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "wast"
version = "56.0.0"
version = "60.0.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
description = """
Customizable Rust parsers for the WebAssembly Text formats WAT and WAST
@@ -37,7 +37,7 @@ version = "2.4.1"
version = "0.1.9"
[dependencies.wasm-encoder]
version = "0.25.0"
version = "0.29.0"
[dev-dependencies.anyhow]
version = "1.0.58"

View File

@@ -4,17 +4,22 @@ use crate::parser::{Parse, Parser, Result};
use crate::token::{Id, Index, NameAnnotation, Span};
/// A inline alias for component exported items.
///
/// Handles both `core export` and `export` aliases
#[derive(Debug)]
pub struct InlineExportAlias<'a> {
pub struct InlineExportAlias<'a, const CORE: bool> {
/// The instance to alias the export from.
pub instance: Index<'a>,
/// The name of the export to alias.
pub name: &'a str,
}
impl<'a> Parse<'a> for InlineExportAlias<'a> {
impl<'a, const CORE: bool> Parse<'a> for InlineExportAlias<'a, CORE> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<kw::alias>()?;
if CORE {
parser.parse::<kw::core>()?;
}
parser.parse::<kw::export>()?;
let instance = parser.parse()?;
let name = parser.parse()?;

View File

@@ -90,6 +90,10 @@ fn encode_type(encoder: ComponentTypeEncoder, ty: &TypeDef) {
TypeDef::Instance(i) => {
encoder.instance(&i.into());
}
TypeDef::Resource(i) => {
let dtor = i.dtor.as_ref().map(|i| i.idx.into());
encoder.resource(i.rep.into(), dtor);
}
}
}
@@ -130,6 +134,8 @@ fn encode_defined_type(encoder: ComponentDefinedTypeEncoder, ty: &ComponentDefin
e.err.as_deref().map(Into::into),
);
}
ComponentDefinedType::Own(i) => encoder.own((*i).into()),
ComponentDefinedType::Borrow(i) => encoder.borrow((*i).into()),
}
}
@@ -256,7 +262,7 @@ impl<'a> Encoder<'a> {
InstanceKind::BundleOfExports(exports) => {
self.instances.export_items(exports.iter().map(|e| {
let (kind, index) = (&e.kind).into();
(e.name, kind, index)
(e.name.into(), kind, index)
}));
}
}
@@ -315,6 +321,18 @@ impl<'a> Encoder<'a> {
self.funcs
.lower(info.func.idx.into(), info.opts.iter().map(Into::into));
}
CanonicalFuncKind::ResourceNew(info) => {
self.core_func_names.push(name);
self.funcs.resource_new(info.ty.into());
}
CanonicalFuncKind::ResourceDrop(info) => {
self.core_func_names.push(name);
self.funcs.resource_drop((&info.ty).into());
}
CanonicalFuncKind::ResourceRep(info) => {
self.core_func_names.push(name);
self.funcs.resource_rep(info.ty.into());
}
}
self.flush(Some(self.funcs.id()));
@@ -324,8 +342,7 @@ impl<'a> Encoder<'a> {
let name = get_name(&import.item.id, &import.item.name);
self.names_for_item_kind(&import.item.kind).push(name);
self.imports.import(
import.name,
import.url.unwrap_or(""),
wasm_encoder::ComponentExternName::from(import.name),
(&import.item.kind).into(),
);
self.flush(Some(self.imports.id()));
@@ -335,8 +352,7 @@ impl<'a> Encoder<'a> {
let name = get_name(&export.id, &export.debug_name);
let (kind, index) = (&export.kind).into();
self.exports.export(
export.name,
export.url.unwrap_or(""),
wasm_encoder::ComponentExternName::from(export.name),
kind,
index,
export.ty.as_ref().map(|ty| (&ty.0.kind).into()),
@@ -571,7 +587,7 @@ impl From<core::HeapType<'_>> for wasm_encoder::HeapType {
match r {
core::HeapType::Func => Self::Func,
core::HeapType::Extern => Self::Extern,
core::HeapType::Index(Index::Num(i, _)) => Self::TypedFunc(i),
core::HeapType::Index(Index::Num(i, _)) => Self::Indexed(i),
core::HeapType::Index(_) => panic!("unresolved index"),
core::HeapType::Any
| core::HeapType::Eq
@@ -777,7 +793,10 @@ impl From<&ItemSigKind<'_>> for wasm_encoder::ComponentTypeRef {
ItemSigKind::Value(v) => Self::Value((&v.0).into()),
ItemSigKind::Func(f) => Self::Func(f.into()),
ItemSigKind::Type(TypeBounds::Eq(t)) => {
Self::Type(wasm_encoder::TypeBounds::Eq, (*t).into())
Self::Type(wasm_encoder::TypeBounds::Eq((*t).into()))
}
ItemSigKind::Type(TypeBounds::SubResource) => {
Self::Type(wasm_encoder::TypeBounds::SubResource)
}
}
}
@@ -799,10 +818,16 @@ impl From<&ComponentType<'_>> for wasm_encoder::ComponentType {
encoded.alias((&a.target).into());
}
ComponentTypeDecl::Import(i) => {
encoded.import(i.name, i.url.unwrap_or(""), (&i.item.kind).into());
encoded.import(
wasm_encoder::ComponentExternName::from(i.name),
(&i.item.kind).into(),
);
}
ComponentTypeDecl::Export(e) => {
encoded.export(e.name, e.url.unwrap_or(""), (&e.item.kind).into());
encoded.export(
wasm_encoder::ComponentExternName::from(e.name),
(&e.item.kind).into(),
);
}
}
}
@@ -827,7 +852,10 @@ impl From<&InstanceType<'_>> for wasm_encoder::InstanceType {
encoded.alias((&a.target).into());
}
InstanceTypeDecl::Export(e) => {
encoded.export(e.name, e.url.unwrap_or(""), (&e.item.kind).into());
encoded.export(
wasm_encoder::ComponentExternName::from(e.name),
(&e.item.kind).into(),
);
}
}
}
@@ -970,3 +998,12 @@ impl<'a> From<&AliasTarget<'a>> for wasm_encoder::Alias<'a> {
}
}
}
impl<'a> From<ComponentExternName<'a>> for wasm_encoder::ComponentExternName<'a> {
fn from(name: ComponentExternName<'a>) -> Self {
match name {
ComponentExternName::Kebab(name) => Self::Kebab(name),
ComponentExternName::Interface(name) => Self::Interface(name),
}
}
}

View File

@@ -188,7 +188,7 @@ impl<'a> Parse<'a> for ComponentField<'a> {
return Ok(Self::Alias(parser.parse()?));
}
if parser.peek::<kw::r#type>() {
return Ok(Self::Type(parser.parse()?));
return Ok(Self::Type(Type::parse_maybe_with_inline_exports(parser)?));
}
if parser.peek::<kw::import>() {
return Ok(Self::Import(parser.parse()?));

View File

@@ -136,7 +136,7 @@ impl<'a> Expander<'a> {
}
fn expand_core_module(&mut self, module: &mut CoreModule<'a>) -> Option<ComponentField<'a>> {
for (name, url) in module.exports.names.drain(..) {
for name in module.exports.names.drain(..) {
let id = gensym::fill(module.span, &mut module.id);
self.component_fields_to_append
.push(ComponentField::Export(ComponentExport {
@@ -144,7 +144,6 @@ impl<'a> Expander<'a> {
id: None,
debug_name: None,
name,
url,
kind: ComponentExportKind::module(module.span, id),
ty: None,
}));
@@ -157,7 +156,6 @@ impl<'a> Expander<'a> {
Some(ComponentField::Import(ComponentImport {
span: module.span,
name: import.name,
url: import.url,
item: ItemSig {
span: module.span,
id: module.id,
@@ -184,7 +182,7 @@ impl<'a> Expander<'a> {
&mut self,
component: &mut NestedComponent<'a>,
) -> Option<ComponentField<'a>> {
for (name, url) in component.exports.names.drain(..) {
for name in component.exports.names.drain(..) {
let id = gensym::fill(component.span, &mut component.id);
self.component_fields_to_append
.push(ComponentField::Export(ComponentExport {
@@ -192,7 +190,6 @@ impl<'a> Expander<'a> {
id: None,
debug_name: None,
name,
url,
kind: ComponentExportKind::component(component.span, id),
ty: None,
}));
@@ -207,7 +204,6 @@ impl<'a> Expander<'a> {
Some(ComponentField::Import(ComponentImport {
span: component.span,
name: import.name,
url: import.url,
item: ItemSig {
span: component.span,
id: component.id,
@@ -220,7 +216,7 @@ impl<'a> Expander<'a> {
}
fn expand_instance(&mut self, instance: &mut Instance<'a>) -> Option<ComponentField<'a>> {
for (name, url) in instance.exports.names.drain(..) {
for name in instance.exports.names.drain(..) {
let id = gensym::fill(instance.span, &mut instance.id);
self.component_fields_to_append
.push(ComponentField::Export(ComponentExport {
@@ -228,7 +224,6 @@ impl<'a> Expander<'a> {
id: None,
debug_name: None,
name,
url,
kind: ComponentExportKind::instance(instance.span, id),
ty: None,
}));
@@ -239,7 +234,6 @@ impl<'a> Expander<'a> {
Some(ComponentField::Import(ComponentImport {
span: instance.span,
name: import.name,
url: import.url,
item: ItemSig {
span: instance.span,
id: instance.id,
@@ -263,7 +257,13 @@ impl<'a> Expander<'a> {
CanonicalFuncKind::Lift { ty, .. } => {
self.expand_component_type_use(ty);
}
CanonicalFuncKind::Lower(_) => {}
CanonicalFuncKind::Lower(_)
| CanonicalFuncKind::ResourceNew(_)
| CanonicalFuncKind::ResourceRep(_) => {}
CanonicalFuncKind::ResourceDrop(info) => {
self.expand_component_val_ty(&mut info.ty);
}
}
}
@@ -285,11 +285,32 @@ impl<'a> Expander<'a> {
name: func.name,
kind: CanonicalFuncKind::Lower(mem::take(info)),
})),
CoreFuncKind::ResourceNew(info) => Some(ComponentField::CanonicalFunc(CanonicalFunc {
span: func.span,
id: func.id,
name: func.name,
kind: CanonicalFuncKind::ResourceNew(mem::take(info)),
})),
CoreFuncKind::ResourceDrop(info) => {
self.expand_component_val_ty(&mut info.ty);
Some(ComponentField::CanonicalFunc(CanonicalFunc {
span: func.span,
id: func.id,
name: func.name,
kind: CanonicalFuncKind::ResourceDrop(mem::take(info)),
}))
}
CoreFuncKind::ResourceRep(info) => Some(ComponentField::CanonicalFunc(CanonicalFunc {
span: func.span,
id: func.id,
name: func.name,
kind: CanonicalFuncKind::ResourceRep(mem::take(info)),
})),
}
}
fn expand_func(&mut self, func: &mut Func<'a>) -> Option<ComponentField<'a>> {
for (name, url) in func.exports.names.drain(..) {
for name in func.exports.names.drain(..) {
let id = gensym::fill(func.span, &mut func.id);
self.component_fields_to_append
.push(ComponentField::Export(ComponentExport {
@@ -297,7 +318,6 @@ impl<'a> Expander<'a> {
id: None,
debug_name: None,
name,
url,
kind: ComponentExportKind::func(func.span, id),
ty: None,
}));
@@ -308,7 +328,6 @@ impl<'a> Expander<'a> {
Some(ComponentField::Import(ComponentImport {
span: func.span,
name: import.name,
url: import.url,
item: ItemSig {
span: func.span,
id: func.id,
@@ -362,6 +381,7 @@ impl<'a> Expander<'a> {
TypeDef::Func(f) => self.expand_func_ty(f),
TypeDef::Component(c) => self.expand_component_ty(c),
TypeDef::Instance(i) => self.expand_instance_ty(i),
TypeDef::Resource(_) => {}
}
let id = gensym::fill(field.span, &mut field.id);
@@ -371,15 +391,15 @@ impl<'a> Expander<'a> {
TypeDef::Func(t) => t.key().insert(self, index),
TypeDef::Component(t) => t.key().insert(self, index),
TypeDef::Instance(t) => t.key().insert(self, index),
TypeDef::Resource(_) => {}
}
for (name, url) in field.exports.names.drain(..) {
for name in field.exports.names.drain(..) {
self.component_fields_to_append
.push(ComponentField::Export(ComponentExport {
span: field.span,
id: None,
debug_name: None,
name,
url,
kind: ComponentExportKind::ty(field.span, id),
ty: None,
}));
@@ -459,6 +479,7 @@ impl<'a> Expander<'a> {
name: None,
def: key.to_def(item.span),
parent: None,
final_type: None,
}));
let idx = Index::Id(id);
t.index = Some(idx);
@@ -552,6 +573,7 @@ impl<'a> Expander<'a> {
self.expand_component_val_ty(ty);
}
}
ComponentDefinedType::Own(_) | ComponentDefinedType::Borrow(_) => {}
}
}

View File

@@ -1,4 +1,4 @@
use super::{ItemRef, ItemSigNoName};
use super::{ComponentExternName, ItemRef, ItemSigNoName};
use crate::kw;
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
use crate::token::{Id, Index, NameAnnotation, Span};
@@ -13,9 +13,7 @@ pub struct ComponentExport<'a> {
/// An optional name for this instance stored in the custom `name` section.
pub debug_name: Option<NameAnnotation<'a>>,
/// The name of this export from the component.
pub name: &'a str,
/// The URL of the export.
pub url: Option<&'a str>,
pub name: ComponentExternName<'a>,
/// The kind of export.
pub kind: ComponentExportKind<'a>,
/// The kind of export.
@@ -28,7 +26,6 @@ impl<'a> Parse<'a> for ComponentExport<'a> {
let id = parser.parse()?;
let debug_name = parser.parse()?;
let name = parser.parse()?;
let url = parser.parse()?;
let kind = parser.parse()?;
let ty = if !parser.is_empty() {
Some(parser.parens(|p| p.parse())?)
@@ -40,7 +37,6 @@ impl<'a> Parse<'a> for ComponentExport<'a> {
id,
debug_name,
name,
url,
kind,
ty,
})
@@ -177,7 +173,7 @@ impl Peek for ComponentExportKind<'_> {
#[derive(Debug, Default)]
pub struct InlineExport<'a> {
/// The extra names to export an item as, if any.
pub names: Vec<(&'a str, Option<&'a str>)>,
pub names: Vec<ComponentExternName<'a>>,
}
impl<'a> Parse<'a> for InlineExport<'a> {
@@ -186,7 +182,7 @@ impl<'a> Parse<'a> for InlineExport<'a> {
while parser.peek::<Self>() {
names.push(parser.parens(|p| {
p.parse::<kw::export>()?;
Ok((p.parse()?, p.parse()?))
p.parse()
})?);
}
Ok(InlineExport { names })
@@ -203,15 +199,29 @@ impl Peek for InlineExport<'_> {
Some(("export", cursor)) => cursor,
_ => return false,
};
// Name
let mut cursor = match cursor.string() {
Some((_, cursor)) => cursor,
// (export "foo")
if let Some((_, cursor)) = cursor.string() {
return cursor.rparen().is_some();
}
// (export (interface "foo"))
let cursor = match cursor.lparen() {
Some(cursor) => cursor,
None => return false,
};
// Optional URL
if let Some((_, c)) = cursor.string() {
cursor = c;
}
let cursor = match cursor.keyword() {
Some(("interface", cursor)) => cursor,
_ => return false,
};
let cursor = match cursor.string() {
Some((_, cursor)) => cursor,
_ => return false,
};
let cursor = match cursor.rparen() {
Some(cursor) => cursor,
_ => return false,
};
cursor.rparen().is_some()
}

View File

@@ -38,6 +38,7 @@ impl<'a> Parse<'a> for CoreFunc<'a> {
/// Represents the kind of core functions.
#[derive(Debug)]
#[allow(missing_docs)]
pub enum CoreFuncKind<'a> {
/// The core function is defined in terms of lowering a component function.
///
@@ -46,7 +47,10 @@ pub enum CoreFuncKind<'a> {
/// The core function is defined in terms of aliasing a module instance export.
///
/// The core function is actually a member of the core alias section.
Alias(InlineExportAlias<'a>),
Alias(InlineExportAlias<'a, true>),
ResourceNew(CanonResourceNew<'a>),
ResourceDrop(CanonResourceDrop<'a>),
ResourceRep(CanonResourceRep<'a>),
}
impl<'a> Parse<'a> for CoreFuncKind<'a> {
@@ -55,9 +59,20 @@ impl<'a> Parse<'a> for CoreFuncKind<'a> {
let mut l = parser.lookahead1();
if l.peek::<kw::canon>() {
parser.parse::<kw::canon>()?;
Ok(Self::Lower(parser.parse()?))
} else if l.peek::<kw::alias>() {
Ok(Self::Alias(parser.parse()?))
return Ok(Self::Alias(parser.parse()?));
} else {
return Err(l.error());
}
let mut l = parser.lookahead1();
if l.peek::<kw::lower>() {
Ok(CoreFuncKind::Lower(parser.parse()?))
} else if l.peek::<kw::resource_new>() {
Ok(CoreFuncKind::ResourceNew(parser.parse()?))
} else if l.peek::<kw::resource_drop>() {
Ok(CoreFuncKind::ResourceDrop(parser.parse()?))
} else if l.peek::<kw::resource_rep>() {
Ok(CoreFuncKind::ResourceRep(parser.parse()?))
} else {
Err(l.error())
}
@@ -128,7 +143,7 @@ pub enum FuncKind<'a> {
/// The function is defined in terms of aliasing a component instance export.
///
/// The function is actually a member of the alias section.
Alias(InlineExportAlias<'a>),
Alias(InlineExportAlias<'a, false>),
}
impl<'a> Parse<'a> for FuncKind<'a> {
@@ -189,29 +204,49 @@ impl<'a> Parse<'a> for CanonicalFunc<'a> {
kind: CanonicalFuncKind::Lift { info, ty },
})
} else if parser.peek::<kw::lower>() {
let info = parser.parse()?;
let (id, name) = parser.parens(|parser| {
parser.parse::<kw::core>()?;
parser.parse::<kw::func>()?;
let id = parser.parse()?;
let name = parser.parse()?;
Ok((id, name))
})?;
Ok(Self {
span,
id,
name,
kind: CanonicalFuncKind::Lower(info),
})
Self::parse_core_func(span, parser, CanonicalFuncKind::Lower)
} else if parser.peek::<kw::resource_new>() {
Self::parse_core_func(span, parser, CanonicalFuncKind::ResourceNew)
} else if parser.peek::<kw::resource_drop>() {
Self::parse_core_func(span, parser, CanonicalFuncKind::ResourceDrop)
} else if parser.peek::<kw::resource_rep>() {
Self::parse_core_func(span, parser, CanonicalFuncKind::ResourceRep)
} else {
Err(parser.error("expected `canon lift` or `canon lower`"))
}
}
}
impl<'a> CanonicalFunc<'a> {
fn parse_core_func<T>(
span: Span,
parser: Parser<'a>,
variant: fn(T) -> CanonicalFuncKind<'a>,
) -> Result<Self>
where
T: Parse<'a>,
{
let info = parser.parse()?;
let (id, name) = parser.parens(|parser| {
parser.parse::<kw::core>()?;
parser.parse::<kw::func>()?;
let id = parser.parse()?;
let name = parser.parse()?;
Ok((id, name))
})?;
Ok(Self {
span,
id,
name,
kind: variant(info),
})
}
}
/// Possible ways to define a canonical function in the text format.
#[derive(Debug)]
#[allow(missing_docs)]
pub enum CanonicalFuncKind<'a> {
/// A canonical function that is defined in terms of lifting a core function.
Lift {
@@ -222,6 +257,10 @@ pub enum CanonicalFuncKind<'a> {
},
/// A canonical function that is defined in terms of lowering a component function.
Lower(CanonLower<'a>),
ResourceNew(CanonResourceNew<'a>),
ResourceDrop(CanonResourceDrop<'a>),
ResourceRep(CanonResourceRep<'a>),
}
/// Information relating to lifting a core function.
@@ -295,6 +334,81 @@ impl Default for CanonLower<'_> {
}
}
/// Information relating to the `resource.new` intrinsic.
#[derive(Debug)]
pub struct CanonResourceNew<'a> {
/// The resource type that this intrinsic creates an owned reference to.
pub ty: Index<'a>,
}
impl<'a> Parse<'a> for CanonResourceNew<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<kw::resource_new>()?;
Ok(Self {
ty: parser.parse()?,
})
}
}
impl Default for CanonResourceNew<'_> {
fn default() -> Self {
CanonResourceNew {
ty: Index::Num(0, Span::from_offset(0)),
}
}
}
/// Information relating to the `resource.drop` intrinsic.
#[derive(Debug)]
pub struct CanonResourceDrop<'a> {
/// The type that this intrinsic is dropping, either (borrow T) or (own T)
pub ty: ComponentValType<'a>,
}
impl<'a> Parse<'a> for CanonResourceDrop<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<kw::resource_drop>()?;
Ok(Self {
ty: parser.parse()?,
})
}
}
impl Default for CanonResourceDrop<'_> {
fn default() -> Self {
CanonResourceDrop {
ty: ComponentValType::Ref(Index::Num(0, Span::from_offset(0))),
}
}
}
/// Information relating to the `resource.rep` intrinsic.
#[derive(Debug)]
pub struct CanonResourceRep<'a> {
/// The resource type that this intrinsic is accessing.
pub ty: Index<'a>,
}
impl<'a> Parse<'a> for CanonResourceRep<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<kw::resource_rep>()?;
Ok(Self {
ty: parser.parse()?,
})
}
}
impl Default for CanonResourceRep<'_> {
fn default() -> Self {
CanonResourceRep {
ty: Index::Num(0, Span::from_offset(0)),
}
}
}
#[derive(Debug)]
/// Canonical ABI options.
pub enum CanonOpt<'a> {

View File

@@ -1,18 +1,15 @@
use crate::component::*;
use crate::kw;
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
use crate::token::Index;
use crate::token::{Id, NameAnnotation, Span};
use crate::token::{Id, Index, LParen, NameAnnotation, Span};
/// An `import` statement and entry in a WebAssembly component.
#[derive(Debug)]
pub struct ComponentImport<'a> {
/// Where this `import` was defined
pub span: Span,
/// The name of the item to import.
pub name: &'a str,
/// The optional URL of the import.
pub url: Option<&'a str>,
/// The name of the item being imported.
pub name: ComponentExternName<'a>,
/// The item that's being imported.
pub item: ItemSig<'a>,
}
@@ -21,14 +18,30 @@ impl<'a> Parse<'a> for ComponentImport<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let span = parser.parse::<kw::import>()?.0;
let name = parser.parse()?;
let url = parser.parse()?;
let item = parser.parens(|p| p.parse())?;
Ok(ComponentImport {
span,
name,
url,
item,
})
Ok(ComponentImport { span, name, item })
}
}
/// The different ways an import can be named.
#[derive(Debug, Copy, Clone)]
pub enum ComponentExternName<'a> {
/// This is a kebab-named import where a top-level name is assigned.
Kebab(&'a str),
/// This is an interface import where the string is an ID.
Interface(&'a str),
}
impl<'a> Parse<'a> for ComponentExternName<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
if parser.peek::<LParen>() {
Ok(ComponentExternName::Interface(parser.parens(|p| {
p.parse::<kw::interface>()?;
p.parse()
})?))
} else {
Ok(ComponentExternName::Kebab(parser.parse()?))
}
}
}
@@ -119,13 +132,23 @@ pub enum ItemSigKind<'a> {
pub enum TypeBounds<'a> {
/// The equality type bounds.
Eq(Index<'a>),
/// A resource type is imported/exported,
SubResource,
}
impl<'a> Parse<'a> for TypeBounds<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
// Currently this is the only supported type bounds.
parser.parse::<kw::eq>()?;
Ok(Self::Eq(parser.parse()?))
let mut l = parser.lookahead1();
if l.peek::<kw::eq>() {
parser.parse::<kw::eq>()?;
Ok(Self::Eq(parser.parse()?))
} else if l.peek::<kw::sub>() {
parser.parse::<kw::sub>()?;
parser.parse::<kw::resource>()?;
Ok(Self::SubResource)
} else {
Err(l.error())
}
}
}
@@ -136,19 +159,14 @@ impl<'a> Parse<'a> for TypeBounds<'a> {
#[derive(Debug, Clone)]
pub struct InlineImport<'a> {
/// The name of the item being imported.
pub name: &'a str,
/// The optional URL of the item being imported.
pub url: Option<&'a str>,
pub name: ComponentExternName<'a>,
}
impl<'a> Parse<'a> for InlineImport<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parens(|p| {
p.parse::<kw::import>()?;
Ok(InlineImport {
name: p.parse()?,
url: p.parse()?,
})
Ok(InlineImport { name: p.parse()? })
})
}
}
@@ -163,9 +181,28 @@ impl Peek for InlineImport<'_> {
Some(("import", cursor)) => cursor,
_ => return false,
};
// (import "foo")
if let Some((_, cursor)) = cursor.string() {
return cursor.rparen().is_some();
}
// (import (interface "foo"))
let cursor = match cursor.lparen() {
Some(cursor) => cursor,
None => return false,
};
let cursor = match cursor.keyword() {
Some(("interface", cursor)) => cursor,
_ => return false,
};
let cursor = match cursor.string() {
Some((_, cursor)) => cursor,
None => return false,
_ => return false,
};
let cursor = match cursor.rparen() {
Some(cursor) => cursor,
_ => return false,
};
cursor.rparen().is_some()
}

View File

@@ -1,5 +1,5 @@
use crate::component::*;
use crate::core;
use crate::core::{self, ValType};
use crate::kw;
use crate::names::Namespace;
use crate::token::Span;
@@ -257,6 +257,7 @@ impl<'a> Resolver<'a> {
ItemSigKind::Value(t) => self.component_val_type(&mut t.0),
ItemSigKind::Type(b) => match b {
TypeBounds::Eq(i) => self.resolve_ns(i, Ns::Type),
TypeBounds::SubResource => Ok(()),
},
}
}
@@ -372,6 +373,9 @@ impl<'a> Resolver<'a> {
self.component_item_ref(&mut info.func)?;
&mut info.opts
}
CanonicalFuncKind::ResourceNew(info) => return self.resolve_ns(&mut info.ty, Ns::Type),
CanonicalFuncKind::ResourceRep(info) => return self.resolve_ns(&mut info.ty, Ns::Type),
CanonicalFuncKind::ResourceDrop(info) => return self.component_val_type(&mut info.ty),
};
for opt in opts {
@@ -465,6 +469,9 @@ impl<'a> Resolver<'a> {
self.component_val_type(ty)?;
}
}
ComponentDefinedType::Own(t) | ComponentDefinedType::Borrow(t) => {
self.resolve_ns(t, Ns::Type)?;
}
}
Ok(())
}
@@ -513,6 +520,29 @@ impl<'a> Resolver<'a> {
self.instance_type(i)?;
self.stack.pop();
}
TypeDef::Resource(r) => {
match &mut r.rep {
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {}
ValType::Ref(r) => match &mut r.heap {
core::HeapType::Func
| core::HeapType::Extern
| core::HeapType::Any
| core::HeapType::Eq
| core::HeapType::Array
| core::HeapType::I31
| core::HeapType::Struct
| core::HeapType::None
| core::HeapType::NoFunc
| core::HeapType::NoExtern => {}
core::HeapType::Index(id) => {
self.resolve_ns(id, Ns::Type)?;
}
},
}
if let Some(dtor) = &mut r.dtor {
self.core_item_ref(dtor)?;
}
}
}
Ok(())
}
@@ -788,7 +818,12 @@ impl<'a> ComponentState<'a> {
ComponentField::Type(t) => self.types.register(t.id, "type")?,
ComponentField::CanonicalFunc(f) => match &f.kind {
CanonicalFuncKind::Lift { .. } => self.funcs.register(f.id, "func")?,
CanonicalFuncKind::Lower(_) => self.core_funcs.register(f.id, "core func")?,
CanonicalFuncKind::Lower(_)
| CanonicalFuncKind::ResourceNew(_)
| CanonicalFuncKind::ResourceRep(_)
| CanonicalFuncKind::ResourceDrop(_) => {
self.core_funcs.register(f.id, "core func")?
}
},
ComponentField::CoreFunc(_) | ComponentField::Func(_) => {
unreachable!("should be expanded already")

View File

@@ -138,12 +138,26 @@ pub struct Type<'a> {
pub def: TypeDef<'a>,
}
impl<'a> Parse<'a> for Type<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
impl<'a> Type<'a> {
/// Parses a `Type` while allowing inline `(export "...")` names to be
/// defined.
pub fn parse_maybe_with_inline_exports(parser: Parser<'a>) -> Result<Self> {
Type::parse(parser, true)
}
fn parse_no_inline_exports(parser: Parser<'a>) -> Result<Self> {
Type::parse(parser, false)
}
fn parse(parser: Parser<'a>, allow_inline_exports: bool) -> Result<Self> {
let span = parser.parse::<kw::r#type>()?.0;
let id = parser.parse()?;
let name = parser.parse()?;
let exports = parser.parse()?;
let exports = if allow_inline_exports {
parser.parse()?
} else {
Default::default()
};
let def = parser.parse()?;
Ok(Self {
@@ -167,6 +181,8 @@ pub enum TypeDef<'a> {
Component(ComponentType<'a>),
/// An instance type.
Instance(InstanceType<'a>),
/// A resource type.
Resource(ResourceType<'a>),
}
impl<'a> Parse<'a> for TypeDef<'a> {
@@ -183,6 +199,9 @@ impl<'a> Parse<'a> for TypeDef<'a> {
} else if l.peek::<kw::instance>() {
parser.parse::<kw::instance>()?;
Ok(Self::Instance(parser.parse()?))
} else if l.peek::<kw::resource>() {
parser.parse::<kw::resource>()?;
Ok(Self::Resource(parser.parse()?))
} else {
Ok(Self::Defined(ComponentDefinedType::parse_non_primitive(
parser, l,
@@ -356,6 +375,8 @@ pub enum ComponentDefinedType<'a> {
Union(Union<'a>),
Option(OptionType<'a>),
Result(ResultType<'a>),
Own(Index<'a>),
Borrow(Index<'a>),
}
impl<'a> ComponentDefinedType<'a> {
@@ -379,6 +400,12 @@ impl<'a> ComponentDefinedType<'a> {
Ok(Self::Option(parser.parse()?))
} else if l.peek::<kw::result>() {
Ok(Self::Result(parser.parse()?))
} else if l.peek::<kw::own>() {
parser.parse::<kw::own>()?;
Ok(Self::Own(parser.parse()?))
} else if l.peek::<kw::borrow>() {
parser.parse::<kw::borrow>()?;
Ok(Self::Borrow(parser.parse()?))
} else {
Err(l.error())
}
@@ -409,6 +436,8 @@ impl Peek for ComponentDefinedType<'_> {
| Some(("union", _))
| Some(("option", _))
| Some(("result", _))
| Some(("own", _))
| Some(("borrow", _))
),
None => false,
}
@@ -739,9 +768,7 @@ pub struct ComponentExportType<'a> {
/// Where this export was defined.
pub span: Span,
/// The name of this export.
pub name: &'a str,
/// The optional URL of this export.
pub url: Option<&'a str>,
pub name: ComponentExternName<'a>,
/// The signature of the item.
pub item: ItemSig<'a>,
}
@@ -752,19 +779,13 @@ impl<'a> Parse<'a> for ComponentExportType<'a> {
let id = parser.parse()?;
let debug_name = parser.parse()?;
let name = parser.parse()?;
let url = parser.parse()?;
let item = parser.parens(|p| {
let mut item = p.parse::<ItemSigNoName<'_>>()?.0;
item.id = id;
item.name = debug_name;
Ok(item)
})?;
Ok(Self {
span,
name,
url,
item,
})
Ok(Self { span, name, item })
}
}
@@ -805,7 +826,7 @@ impl<'a> Parse<'a> for ComponentTypeDecl<'a> {
if l.peek::<kw::core>() {
Ok(Self::CoreType(parser.parse()?))
} else if l.peek::<kw::r#type>() {
Ok(Self::Type(parser.parse()?))
Ok(Self::Type(Type::parse_no_inline_exports(parser)?))
} else if l.peek::<kw::alias>() {
Ok(Self::Alias(parser.parse()?))
} else if l.peek::<kw::import>() {
@@ -863,7 +884,7 @@ impl<'a> Parse<'a> for InstanceTypeDecl<'a> {
if l.peek::<kw::core>() {
Ok(Self::CoreType(parser.parse()?))
} else if l.peek::<kw::r#type>() {
Ok(Self::Type(parser.parse()?))
Ok(Self::Type(Type::parse_no_inline_exports(parser)?))
} else if l.peek::<kw::alias>() {
Ok(Self::Alias(parser.parse()?))
} else if l.peek::<kw::export>() {
@@ -884,6 +905,33 @@ impl<'a> Parse<'a> for Vec<InstanceTypeDecl<'a>> {
}
}
/// A type definition for an instance type.
#[derive(Debug)]
pub struct ResourceType<'a> {
/// Representation, in core WebAssembly, of this resource.
pub rep: core::ValType<'a>,
/// The declarations of the instance type.
pub dtor: Option<CoreItemRef<'a, kw::func>>,
}
impl<'a> Parse<'a> for ResourceType<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let rep = parser.parens(|p| {
p.parse::<kw::rep>()?;
p.parse()
})?;
let dtor = if parser.is_empty() {
None
} else {
Some(parser.parens(|p| {
p.parse::<kw::dtor>()?;
p.parens(|p| p.parse())
})?)
};
Ok(Self { rep, dtor })
}
}
/// A value type declaration used for values in import signatures.
#[derive(Debug)]
pub struct ComponentValTypeUse<'a>(pub ComponentValType<'a>);

View File

@@ -108,8 +108,8 @@ impl Encoder<'_> {
fn custom_sections(&mut self, place: CustomPlace) {
for entry in self.customs.iter() {
if entry.place == place {
self.section(0, &(entry.name, entry));
if entry.place() == place {
self.section(0, &(entry.name(), entry));
}
}
}
@@ -173,10 +173,25 @@ impl Encode for RecOrType<'_> {
impl Encode for Type<'_> {
fn encode(&self, e: &mut Vec<u8>) {
if let Some(parent) = &self.parent {
e.push(0x50);
(1 as usize).encode(e);
parent.encode(e);
match (&self.parent, self.final_type) {
(Some(parent), Some(true)) => {
// Type is final with a supertype
e.push(0x4e);
e.push(0x01);
parent.encode(e);
}
(Some(parent), Some(false) | None) => {
// Type is not final and has a declared supertype
e.push(0x50);
e.push(0x01);
parent.encode(e);
}
(None, Some(false)) => {
// Sub was used without any declared supertype
e.push(0x50);
e.push(0x00);
}
(None, _) => {} // No supertype, sub wasn't used
}
match &self.def {
TypeDef::Func(func) => {
@@ -1043,6 +1058,15 @@ impl<'a> Encode for SelectTypes<'a> {
}
impl Encode for Custom<'_> {
fn encode(&self, e: &mut Vec<u8>) {
match self {
Custom::Raw(r) => r.encode(e),
Custom::Producers(p) => p.encode(e),
}
}
}
impl Encode for RawCustomSection<'_> {
fn encode(&self, e: &mut Vec<u8>) {
for list in self.data.iter() {
e.extend_from_slice(list);
@@ -1050,6 +1074,12 @@ impl Encode for Custom<'_> {
}
}
impl Encode for Producers<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.fields.encode(e);
}
}
impl Encode for Tag<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.ty.encode(e);
@@ -1161,7 +1191,11 @@ impl Encode for BrOnCast<'_> {
fn encode(&self, e: &mut Vec<u8>) {
e.push(0xfb);
e.push(0x4f);
e.push(br_on_cast_flags(false, self.from_type.nullable, self.to_type.nullable));
e.push(br_on_cast_flags(
false,
self.from_type.nullable,
self.to_type.nullable,
));
self.label.encode(e);
self.from_type.heap.encode(e);
self.to_type.heap.encode(e);
@@ -1172,7 +1206,11 @@ impl Encode for BrOnCastFail<'_> {
fn encode(&self, e: &mut Vec<u8>) {
e.push(0xfb);
e.push(0x4f);
e.push(br_on_cast_flags(true, self.from_type.nullable, self.to_type.nullable));
e.push(br_on_cast_flags(
true,
self.from_type.nullable,
self.to_type.nullable,
));
self.label.encode(e);
self.from_type.heap.encode(e);
self.to_type.heap.encode(e);

View File

@@ -2,9 +2,46 @@ use crate::parser::{Parse, Parser, Result};
use crate::token::{self, Span};
use crate::{annotation, kw};
/// A custom section within a wasm module.
#[derive(Debug)]
pub enum Custom<'a> {
/// A raw custom section with the manual placement and bytes specified.
Raw(RawCustomSection<'a>),
/// A producers custom section.
Producers(Producers<'a>),
}
impl Custom<'_> {
/// Where this custom section is placed.
pub fn place(&self) -> CustomPlace {
match self {
Custom::Raw(s) => s.place,
Custom::Producers(_) => CustomPlace::AfterLast,
}
}
/// The name of this custom section
pub fn name(&self) -> &str {
match self {
Custom::Raw(s) => s.name,
Custom::Producers(_) => "producers",
}
}
}
impl<'a> Parse<'a> for Custom<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
if parser.peek::<annotation::producers>() {
Ok(Custom::Producers(parser.parse()?))
} else {
Ok(Custom::Raw(parser.parse()?))
}
}
}
/// A wasm custom section within a module.
#[derive(Debug)]
pub struct Custom<'a> {
pub struct RawCustomSection<'a> {
/// Where this `@custom` was defined.
pub span: Span,
@@ -49,7 +86,7 @@ pub enum CustomPlaceAnchor {
Tag,
}
impl<'a> Parse<'a> for Custom<'a> {
impl<'a> Parse<'a> for RawCustomSection<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let span = parser.parse::<annotation::custom>()?.0;
let name = parser.parse()?;
@@ -62,7 +99,7 @@ impl<'a> Parse<'a> for Custom<'a> {
while !parser.is_empty() {
data.push(parser.parse()?);
}
Ok(Custom {
Ok(RawCustomSection {
span,
name,
place,
@@ -149,3 +186,51 @@ impl<'a> Parse<'a> for CustomPlaceAnchor {
Err(parser.error("expected a valid section name"))
}
}
/// A producers custom section
#[allow(missing_docs)]
#[derive(Debug)]
pub struct Producers<'a> {
pub fields: Vec<(&'a str, Vec<(&'a str, &'a str)>)>,
}
impl<'a> Parse<'a> for Producers<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<annotation::producers>()?.0;
let mut languages = Vec::new();
let mut sdks = Vec::new();
let mut processed_by = Vec::new();
while !parser.is_empty() {
parser.parens(|parser| {
let mut l = parser.lookahead1();
let dst = if l.peek::<kw::language>() {
parser.parse::<kw::language>()?;
&mut languages
} else if l.peek::<kw::sdk>() {
parser.parse::<kw::sdk>()?;
&mut sdks
} else if l.peek::<kw::processed_by>() {
parser.parse::<kw::processed_by>()?;
&mut processed_by
} else {
return Err(l.error());
};
dst.push((parser.parse()?, parser.parse()?));
Ok(())
})?;
}
let mut fields = Vec::new();
if !languages.is_empty() {
fields.push(("language", languages));
}
if !sdks.is_empty() {
fields.push(("sdk", sdks));
}
if !processed_by.is_empty() {
fields.push(("processed-by", processed_by));
}
Ok(Producers { fields })
}
}

View File

@@ -522,8 +522,8 @@ instructions! {
ReturnCallIndirect(CallIndirect<'a>) : [0x13] : "return_call_indirect",
// function-references proposal
CallRef(HeapType<'a>) : [0x14] : "call_ref",
ReturnCallRef(HeapType<'a>) : [0x15] : "return_call_ref",
CallRef(Index<'a>) : [0x14] : "call_ref",
ReturnCallRef(Index<'a>) : [0x15] : "return_call_ref",
FuncBind(FuncBindType<'a>) : [0x16] : "func.bind",
Let(LetType<'a>) : [0x17] : "let",

View File

@@ -110,7 +110,6 @@ impl<'a> Module<'a> {
impl<'a> Parse<'a> for Module<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let _r = parser.register_annotation("custom");
let span = parser.parse::<kw::module>()?.0;
let id = parser.parse()?;
let name = parser.parse()?;
@@ -155,6 +154,8 @@ pub enum ModuleField<'a> {
impl<'a> ModuleField<'a> {
pub(crate) fn parse_remaining(parser: Parser<'a>) -> Result<Vec<ModuleField>> {
let _r = parser.register_annotation("custom");
let _r = parser.register_annotation("producers");
let mut fields = Vec::new();
while !parser.is_empty() {
fields.push(parser.parens(ModuleField::parse)?);
@@ -202,7 +203,7 @@ impl<'a> Parse<'a> for ModuleField<'a> {
if parser.peek::<kw::tag>() {
return Ok(ModuleField::Tag(parser.parse()?));
}
if parser.peek::<annotation::custom>() {
if parser.peek::<annotation::custom>() || parser.peek::<annotation::producers>() {
return Ok(ModuleField::Custom(parser.parse()?));
}
Err(parser.error("expected valid module field"))

View File

@@ -482,6 +482,10 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
self.resolver.resolve_type_use(&mut c.ty)?;
}
CallRef(i) | ReturnCallRef(i) => {
self.resolver.resolve(i, Ns::Type)?;
}
FuncBind(b) => {
self.resolver.resolve_type_use(&mut b.ty)?;
}
@@ -637,7 +641,7 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
self.resolver.elems.resolve(&mut a.segment, "elem")?;
}
RefNull(ty) | CallRef(ty) | ReturnCallRef(ty) => self.resolver.resolve_heaptype(ty)?,
RefNull(ty) => self.resolver.resolve_heaptype(ty)?,
_ => {}
}

View File

@@ -218,6 +218,7 @@ impl<'a> Expander<'a> {
name: None,
def: key.to_def(span),
parent: None,
final_type: None,
}));
let idx = Index::Id(id);
key.insert(self, idx);

View File

@@ -724,6 +724,8 @@ pub struct Type<'a> {
pub def: TypeDef<'a>,
/// The declared parent type of this definition.
pub parent: Option<Index<'a>>,
/// Whether this type is final or not. By default types are final.
pub final_type: Option<bool>,
}
impl<'a> Peek for Type<'a> {
@@ -741,19 +743,28 @@ impl<'a> Parse<'a> for Type<'a> {
let id = parser.parse()?;
let name = parser.parse()?;
let (parent, def) = if parser.peek2::<kw::sub>() {
let (parent, def, final_type) = if parser.peek2::<kw::sub>() {
parser.parens(|parser| {
parser.parse::<kw::sub>()?;
let final_type: Option<bool> =
if parser.peek::<kw::r#final>() {
parser.parse::<kw::r#final>()?;
Some(true)
} else {
Some(false)
};
let parent = if parser.peek::<Index<'a>>() {
parser.parse()?
} else {
None
};
let def = parser.parens(|parser| parser.parse())?;
Ok((parent, def))
Ok((parent, def, final_type))
})?
} else {
(None, parser.parens(|parser| parser.parse())?)
(None, parser.parens(|parser| parser.parse())?, None)
};
Ok(Type {
@@ -762,6 +773,7 @@ impl<'a> Parse<'a> for Type<'a> {
name,
def,
parent,
final_type,
})
}
}

View File

@@ -391,6 +391,7 @@ pub mod kw {
custom_keyword!(before);
custom_keyword!(binary);
custom_keyword!(block);
custom_keyword!(borrow);
custom_keyword!(catch);
custom_keyword!(catch_all);
custom_keyword!(code);
@@ -399,6 +400,7 @@ pub mod kw {
custom_keyword!(declare);
custom_keyword!(delegate);
custom_keyword!(r#do = "do");
custom_keyword!(dtor);
custom_keyword!(elem);
custom_keyword!(end);
custom_keyword!(tag);
@@ -430,6 +432,7 @@ pub mod kw {
custom_keyword!(import);
custom_keyword!(instance);
custom_keyword!(instantiate);
custom_keyword!(interface);
custom_keyword!(invoke);
custom_keyword!(item);
custom_keyword!(last);
@@ -448,6 +451,7 @@ pub mod kw {
custom_keyword!(nullref);
custom_keyword!(offset);
custom_keyword!(outer);
custom_keyword!(own);
custom_keyword!(param);
custom_keyword!(parent);
custom_keyword!(passive);
@@ -462,10 +466,16 @@ pub mod kw {
custom_keyword!(ref_null = "ref.null");
custom_keyword!(register);
custom_keyword!(rec);
custom_keyword!(rep);
custom_keyword!(resource);
custom_keyword!(resource_new = "resource.new");
custom_keyword!(resource_drop = "resource.drop");
custom_keyword!(resource_rep = "resource.rep");
custom_keyword!(result);
custom_keyword!(shared);
custom_keyword!(start);
custom_keyword!(sub);
custom_keyword!(r#final = "final");
custom_keyword!(table);
custom_keyword!(then);
custom_keyword!(r#try = "try");
@@ -509,10 +519,14 @@ pub mod kw {
custom_keyword!(core);
custom_keyword!(true_ = "true");
custom_keyword!(false_ = "false");
custom_keyword!(language);
custom_keyword!(sdk);
custom_keyword!(processed_by = "processed-by");
}
/// Common annotations used to parse WebAssembly text files.
pub mod annotation {
annotation!(custom);
annotation!(name);
annotation!(producers);
}

View File

@@ -0,0 +1,7 @@
(component
(core module $mod (func (export "fun")))
(core instance $inst (instantiate $mod))
(alias core export $inst "fun" (core func $fun1))
(core func $fun2 (alias core export $inst "fun"))
(core func $fun3 (alias export $inst "fun"))
)

View File

@@ -0,0 +1,5 @@
expected keyword `core`
--> tests/parse-fail/bad-core-func-alias.wat:6:27
|
6 | (core func $fun3 (alias export $inst "fun"))
| ^

View File

@@ -0,0 +1,7 @@
(component
(component $comp)
(instance $inst (instantiate $comp))
(func $alias1 (alias export $inst "fun"))
(alias export $inst "fun" (func $alias2))
(alias export $inst "fun" (core func $alias3))
)

View File

@@ -0,0 +1,5 @@
unexpected token, expected `module`
--> tests/parse-fail/bad-func-alias.wat:6:37
|
6 | (alias export $inst "fun" (core func $alias3))
| ^