macro_rules! define_wasm_features { ( $(#[$outer:meta])* pub struct WasmFeatures: $repr:ty { $( $(#[$inner:ident $($args:tt)*])* pub $field:ident: $const:ident($flag:expr) = $default:expr; )* } ) => { #[cfg(feature = "features")] bitflags::bitflags! { $(#[$outer])* pub struct WasmFeatures: $repr { $( $(#[$inner $($args)*])* #[doc = "\nDefaults to `"] #[doc = stringify!($default)] #[doc = "`.\n"] const $const = $flag; )* } } /// Enabled WebAssembly proposals and features. /// /// This is the disabled zero-size version of this structure because the /// `features` feature was disabled at compile time of this crate. #[cfg(not(feature = "features"))] #[derive(Clone, Debug, Default, Hash, Copy)] pub struct WasmFeatures { _priv: (), } #[cfg(feature = "features")] impl Default for WasmFeatures { #[inline] fn default() -> Self { let mut features = WasmFeatures::empty(); $( features.set(WasmFeatures::$const, $default); )* features } } impl WasmFeatures { /// Construct a bit-packed `WasmFeatures` from the inflated struct version. #[inline] #[cfg(feature = "features")] pub fn from_inflated(inflated: WasmFeaturesInflated) -> Self { let mut features = WasmFeatures::empty(); $( features.set(WasmFeatures::$const, inflated.$field); )* features } /// Inflate these bit-packed features into a struct with a field per /// feature. /// /// Although the inflated struct takes up much more memory than the /// bit-packed version, its fields can be exhaustively matched /// upon. This makes it useful for temporarily checking against, /// while keeping the bit-packed version as the method of storing /// the features for longer periods of time. #[inline] #[cfg(feature = "features")] pub fn inflate(&self) -> WasmFeaturesInflated { WasmFeaturesInflated { $( $field: self.$field(), )* } } $( /// Returns whether this feature is enabled in this feature set. #[inline] pub fn $field(&self) -> bool { #[cfg(feature = "features")] { self.contains(WasmFeatures::$const) } #[cfg(not(feature = "features"))] { $default } } )* } /// Inflated version of [`WasmFeatures`][crate::WasmFeatures] that /// allows for exhaustive matching on fields. #[cfg(feature = "features")] pub struct WasmFeaturesInflated { $( $(#[$inner $($args)*])* #[doc = "\nDefaults to `"] #[doc = stringify!($default)] #[doc = "`.\n"] pub $field: bool, )* } macro_rules! foreach_wasm_feature { ($f:ident) => { $($f!($field = $default);)* } } pub(crate) use foreach_wasm_feature; }; } define_wasm_features! { /// Flags for features that are enabled for validation. /// /// This type controls the set of WebAssembly proposals and features that /// are active during validation and parsing of WebAssembly binaries. This /// is used in conjunction with /// [`Validator::new_with_features`](crate::Validator::new_with_features) /// for example. /// /// The [`Default`] implementation for this structure returns the set of /// supported WebAssembly proposals this crate implements. All features are /// required to be in Phase 4 or above in the WebAssembly standardization /// process. /// /// Enabled/disabled features can affect both parsing and validation at this /// time. When decoding a WebAssembly binary it's generally recommended if /// possible to enable all features, as is the default with /// [`BinaryReader::new`](crate::BinaryReader::new). If strict conformance /// with historical versions of the specification are required then /// [`BinaryReader::new_features`](crate::BinaryReader::new_features) or /// [`BinaryReader::set_features`](crate::BinaryReader::set_features) can be /// used. /// /// This crate additionally has a compile-time Cargo feature called /// `features` which can be used to enable/disable most of this type at /// compile time. This crate feature is turned on by default and enables /// this bitflags-representation of this structure. When the `features` /// feature is disabled then this is a zero-sized structure and no longer /// has any associated constants. When `features` are disabled all values /// for proposals are fixed at compile time to their defaults. #[derive(Hash, Debug, Copy, Clone, Eq, PartialEq)] pub struct WasmFeatures: u64 { /// The WebAssembly `mutable-global` proposal. pub mutable_global: MUTABLE_GLOBAL(1) = true; /// The WebAssembly `saturating-float-to-int` proposal. pub saturating_float_to_int: SATURATING_FLOAT_TO_INT(1 << 1) = true; /// The WebAssembly `sign-extension-ops` proposal. pub sign_extension: SIGN_EXTENSION(1 << 2) = true; /// The WebAssembly reference types proposal. // // Note that this is two bits, one for the proposal itself and one which // is the overlong call-indirect encoding. This is split across two bits // since the "lime1" feature set below only encompasses some of this // feature, not all of it. Enabling reference types always enabled // call-indirect-overlong, but not vice-versa. // // Also note that this goes against the recommendation of the // `bitflags!` macro since the lone `1 << 3` bit here doesn't actually // correspond to any named flag. That means it's possible to technically // create a `WasmFeatures` that only has the `1 << 3` bit set, which // doesn't actually mean anything, but that in theory shouldn't be too // harmful and is esoteric enough we don't have to worry much about it. pub reference_types: REFERENCE_TYPES((1 << 3) | Self::CALL_INDIRECT_OVERLONG.bits()) = true; /// The WebAssembly multi-value proposal. pub multi_value: MULTI_VALUE(1 << 4) = true; /// The WebAssembly bulk memory operations proposal. // // Note that this proposal is split in two the same way // `REFERENCE_TYPES` is split above. See more words there for why and // rationale. pub bulk_memory: BULK_MEMORY((1 << 5) | Self::BULK_MEMORY_OPT.bits()) = true; /// The WebAssembly SIMD proposal. pub simd: SIMD(1 << 6) = true; /// The WebAssembly Relaxed SIMD proposal. pub relaxed_simd: RELAXED_SIMD(1 << 7) = true; /// The WebAssembly threads proposal. pub threads: THREADS(1 << 8) = true; /// The WebAssembly shared-everything-threads proposal; includes new /// component model built-ins. pub shared_everything_threads: SHARED_EVERYTHING_THREADS(1 << 9) = false; /// The WebAssembly tail-call proposal. pub tail_call: TAIL_CALL(1 << 10) = true; /// Whether or not floating-point instructions are enabled. /// /// This is enabled by default can be used to disallow floating-point /// operators and types. /// /// This does not correspond to a WebAssembly proposal but is instead /// intended for embeddings which have stricter-than-usual requirements /// about execution. Floats in WebAssembly can have different NaN patterns /// across hosts which can lead to host-dependent execution which some /// runtimes may not desire. pub floats: FLOATS(1 << 11) = true; /// The WebAssembly multi memory proposal. pub multi_memory: MULTI_MEMORY(1 << 12) = true; /// The WebAssembly exception handling proposal. pub exceptions: EXCEPTIONS(1 << 13) = true; /// The WebAssembly memory64 proposal. pub memory64: MEMORY64(1 << 14) = true; /// The WebAssembly extended_const proposal. pub extended_const: EXTENDED_CONST(1 << 15) = true; /// The WebAssembly component model proposal. pub component_model: COMPONENT_MODEL(1 << 16) = true; /// The WebAssembly typed function references proposal. pub function_references: FUNCTION_REFERENCES(1 << 17) = true; /// The WebAssembly memory control proposal. pub memory_control: MEMORY_CONTROL(1 << 18) = false; /// The WebAssembly gc proposal. pub gc: GC(1 << 19) = true; /// The WebAssembly [custom-page-sizes /// proposal](https://github.com/WebAssembly/custom-page-sizes). pub custom_page_sizes: CUSTOM_PAGE_SIZES(1 << 20) = false; /// The WebAssembly legacy exception handling proposal (phase 1) /// /// # Note /// /// Support this feature as long as all leading browsers also support it /// pub legacy_exceptions: LEGACY_EXCEPTIONS(1 << 23) = false; /// Whether or not gc types are enabled. /// /// This feature does not correspond to any WebAssembly proposal nor /// concept in the specification itself. This is intended to assist /// embedders in disabling support for GC types at validation time. For /// example if an engine wants to support all of WebAssembly except /// a runtime garbage collector it could disable this feature. /// /// This features is enabled by default and is used to gate types such /// as `externref` or `anyref`. Note that the requisite WebAssembly /// proposal must also be enabled for types like `externref`, meaning /// that it requires both `REFERENCE_TYPES` and `GC_TYPE` to be enabled. /// /// Note that the `funcref` and `exnref` types are not gated by this /// feature. Those are expected to not require a full garbage collector /// so are not gated by this. pub gc_types: GC_TYPES(1 << 24) = true; /// The WebAssembly [stack-switching proposal](https://github.com/WebAssembly/stack-switching). pub stack_switching: STACK_SWITCHING(1 << 25) = false; /// The WebAssembly [wide-arithmetic proposal](https://github.com/WebAssembly/wide-arithmetic). pub wide_arithmetic: WIDE_ARITHMETIC(1 << 26) = false; /// Support for the `value` type in the component model proposal. /// /// Corresponds to the πŸͺ™ character in /// . pub cm_values: CM_VALUES(1 << 21) = false; /// Support for the nested namespaces and projects in component model names. /// /// Corresponds to the πŸͺΊ character in /// . pub cm_nested_names: CM_NESTED_NAMES(1 << 22) = false; /// Support for component model async lift/lower ABI, as well as streams, futures, and errors. /// /// Corresponds to the πŸ”€ character in /// . pub cm_async: CM_ASYNC(1 << 27) = false; /// Gates the "stackful ABI" in the component model async proposal. /// /// Corresponds to the 🚟 character in /// . pub cm_async_stackful: CM_ASYNC_STACKFUL(1 << 28) = false; /// Gates some intrinsics being marked with `async` in the component /// model async proposal. /// /// Corresponds to the 🚝 character in /// . pub cm_async_builtins: CM_ASYNC_BUILTINS(1 << 29) = false; /// Support for threading in the component model proposal. /// /// Corresponds to the 🧡 character in /// . pub cm_threading: CM_THREADING(1 << 30) = false; /// Gates some intrinsics being marked with `error-context` in the component /// model async proposal. /// /// Corresponds to the πŸ“ character in /// . pub cm_error_context: CM_ERROR_CONTEXT(1 << 31) = false; /// Support for fixed size lists /// /// Corresponds to the πŸ”§ character in /// . pub cm_fixed_size_list: CM_FIXED_SIZE_LIST(1 << 32) = false; /// Support for Wasm GC in the component model proposal. /// /// Corresponds to the πŸ›Έ character in /// . pub cm_gc: CM_GC(1 << 33) = false; /// Subset of the reference-types WebAssembly proposal which only /// encompasses the leb-encoding of the table immediate to the /// `call_indirect` instruction, enabling over-long encodings of an /// integer for example. /// /// This is a subcomponent of the "lime1" feature. pub call_indirect_overlong: CALL_INDIRECT_OVERLONG(1 << 34) = true; /// Subset of the bulk-memory proposal covering just the `memory.copy` /// and `memory.fill` instructions. /// /// This is a subcomponent of the "lime1" feature. pub bulk_memory_opt: BULK_MEMORY_OPT(1 << 35) = true; // Custom descriptors proposal. pub custom_descriptors: CUSTOM_DESCRIPTORS(1 << 36) = false; // Compact import section proposal. pub compact_imports: COMPACT_IMPORTS(1 << 37) = false; /// Support for maps in the component model proposal. /// /// Corresponds to the πŸ—ΊοΈ character in /// . pub cm_map: CM_MAP(1 << 38) = false; } } impl WasmFeatures { /// The feature set associated with the MVP release of WebAssembly (its /// first release). // // Note that the features listed here are the wasmparser-specific built-in // features such as "floats" and "gc-types". These don't actually correspond // to any wasm proposals themselves and instead just gate constructs in // wasm. They're listed here so they otherwise don't have to be listed // below, but for example wasm with `externref` will be rejected due to lack // of `externref` first. #[cfg(feature = "features")] pub const MVP: WasmFeatures = WasmFeatures::FLOATS.union(WasmFeatures::GC_TYPES); /// The feature set associated with the 1.0 version of the /// WebAssembly specification circa 2017. /// /// #[cfg(feature = "features")] pub const WASM1: WasmFeatures = WasmFeatures::MVP.union(WasmFeatures::MUTABLE_GLOBAL); /// The feature set associated with the 2.0 version of the /// WebAssembly specification circa 2022. /// /// #[cfg(feature = "features")] pub const WASM2: WasmFeatures = WasmFeatures::WASM1 .union(WasmFeatures::BULK_MEMORY) .union(WasmFeatures::REFERENCE_TYPES) .union(WasmFeatures::SIGN_EXTENSION) .union(WasmFeatures::SATURATING_FLOAT_TO_INT) .union(WasmFeatures::MULTI_VALUE) .union(WasmFeatures::SIMD); /// The feature set associated with the 3.0 version of the /// WebAssembly specification. /// /// Note that as of the time of this writing the 3.0 version of the /// specification is not yet published. The precise set of features set /// here may change as that continues to evolve. /// /// (draft) /// #[cfg(feature = "features")] pub const WASM3: WasmFeatures = WasmFeatures::WASM2 .union(WasmFeatures::GC) .union(WasmFeatures::TAIL_CALL) .union(WasmFeatures::EXTENDED_CONST) .union(WasmFeatures::FUNCTION_REFERENCES) .union(WasmFeatures::MULTI_MEMORY) .union(WasmFeatures::RELAXED_SIMD) .union(WasmFeatures::THREADS) .union(WasmFeatures::EXCEPTIONS) .union(WasmFeatures::MEMORY64); /// The feature set associated with the "lime1" set of features. /// /// Here "lime1" stands for "linear memory version 1" and is a stable set of /// features agreed on by both producers and consumers which is more than /// the MVP but does not include some more weighty engine features such as /// reference types, gc, etc. /// /// #[cfg(feature = "features")] pub const LIME1: WasmFeatures = WasmFeatures::WASM1 .union(WasmFeatures::MULTI_VALUE) .union(WasmFeatures::SIGN_EXTENSION) .union(WasmFeatures::SATURATING_FLOAT_TO_INT) .union(WasmFeatures::BULK_MEMORY_OPT) .union(WasmFeatures::EXTENDED_CONST) .union(WasmFeatures::CALL_INDIRECT_OVERLONG); } #[cfg(feature = "features")] impl From for WasmFeatures { #[inline] fn from(inflated: WasmFeaturesInflated) -> Self { Self::from_inflated(inflated) } } #[cfg(feature = "features")] impl From for WasmFeaturesInflated { #[inline] fn from(features: WasmFeatures) -> Self { features.inflate() } } impl WasmFeatures { /// Returns whether any types considered valid with this set of /// `WasmFeatures` requires any type canonicalization/interning internally. #[cfg(feature = "validate")] pub(crate) fn needs_type_canonicalization(&self) -> bool { #[cfg(feature = "features")] { // Types from the function-references proposal and beyond (namely // GC) need type canonicalization for inter-type references and for // rec-groups to work. Prior proposals have no such inter-type // references structurally and as such can hit various fast paths in // the validator to do a bit less work when processing the type // section. // // None of these proposals below support inter-type references. If // `self` contains any proposal outside of this set then it requires // type canonicalization. const FAST_VALIDATION_FEATURES: WasmFeatures = WasmFeatures::WASM2 .union(WasmFeatures::CUSTOM_PAGE_SIZES) .union(WasmFeatures::EXTENDED_CONST) .union(WasmFeatures::MEMORY64) .union(WasmFeatures::MULTI_MEMORY) .union(WasmFeatures::RELAXED_SIMD) .union(WasmFeatures::TAIL_CALL) .union(WasmFeatures::THREADS) .union(WasmFeatures::WIDE_ARITHMETIC); !FAST_VALIDATION_FEATURES.contains(*self) } #[cfg(not(feature = "features"))] { // GC/function-references are enabled by default, so // canonicalization is required if feature flags aren't enabled at // runtime. true } } }