| 1 | macro_rules! define_wasm_features { |
| 2 | ( |
| 3 | $(#[$outer:meta])* |
| 4 | pub struct WasmFeatures: $repr:ty { |
| 5 | $( |
| 6 | $(#[$inner:ident $($args:tt)*])* |
| 7 | pub $field:ident: $const:ident($flag:expr) = $default:expr; |
| 8 | )* |
| 9 | } |
| 10 | ) => { |
| 11 | #[cfg(feature = "features" )] |
| 12 | bitflags::bitflags! { |
| 13 | $(#[$outer])* |
| 14 | pub struct WasmFeatures: $repr { |
| 15 | $( |
| 16 | $(#[$inner $($args)*])* |
| 17 | #[doc = " \nDefaults to `" ] |
| 18 | #[doc = stringify!($default)] |
| 19 | #[doc = "`. \n" ] |
| 20 | const $const = $flag; |
| 21 | )* |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | /// Enabled WebAssembly proposals and features. |
| 26 | /// |
| 27 | /// This is the disabled zero-size version of this structure because the |
| 28 | /// `features` feature was disabled at compile time of this crate. |
| 29 | #[cfg(not(feature = "features" ))] |
| 30 | #[derive(Clone, Debug, Default, Hash, Copy)] |
| 31 | pub struct WasmFeatures { |
| 32 | _priv: (), |
| 33 | } |
| 34 | |
| 35 | #[cfg(feature = "features" )] |
| 36 | impl Default for WasmFeatures { |
| 37 | #[inline] |
| 38 | fn default() -> Self { |
| 39 | let mut features = WasmFeatures::empty(); |
| 40 | $( |
| 41 | features.set(WasmFeatures::$const, $default); |
| 42 | )* |
| 43 | features |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | impl WasmFeatures { |
| 48 | /// Construct a bit-packed `WasmFeatures` from the inflated struct version. |
| 49 | #[inline] |
| 50 | #[cfg(feature = "features" )] |
| 51 | pub fn from_inflated(inflated: WasmFeaturesInflated) -> Self { |
| 52 | let mut features = WasmFeatures::empty(); |
| 53 | $( |
| 54 | features.set(WasmFeatures::$const, inflated.$field); |
| 55 | )* |
| 56 | features |
| 57 | } |
| 58 | |
| 59 | /// Inflate these bit-packed features into a struct with a field per |
| 60 | /// feature. |
| 61 | /// |
| 62 | /// Although the inflated struct takes up much more memory than the |
| 63 | /// bit-packed version, its fields can be exhaustively matched |
| 64 | /// upon. This makes it useful for temporarily checking against, |
| 65 | /// while keeping the bit-packed version as the method of storing |
| 66 | /// the features for longer periods of time. |
| 67 | #[inline] |
| 68 | #[cfg(feature = "features" )] |
| 69 | pub fn inflate(&self) -> WasmFeaturesInflated { |
| 70 | WasmFeaturesInflated { |
| 71 | $( |
| 72 | $field: self.$field(), |
| 73 | )* |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | $( |
| 78 | /// Returns whether this feature is enabled in this feature set. |
| 79 | #[inline] |
| 80 | pub fn $field(&self) -> bool { |
| 81 | #[cfg(feature = "features" )] |
| 82 | { self.contains(WasmFeatures::$const) } |
| 83 | #[cfg(not(feature = "features" ))] |
| 84 | { $default } |
| 85 | } |
| 86 | )* |
| 87 | } |
| 88 | |
| 89 | /// Inflated version of [`WasmFeatures`][crate::WasmFeatures] that |
| 90 | /// allows for exhaustive matching on fields. |
| 91 | #[cfg(feature = "features" )] |
| 92 | pub struct WasmFeaturesInflated { |
| 93 | $( |
| 94 | $(#[$inner $($args)*])* |
| 95 | #[doc = " \nDefaults to `" ] |
| 96 | #[doc = stringify!($default)] |
| 97 | #[doc = "`. \n" ] |
| 98 | pub $field: bool, |
| 99 | )* |
| 100 | } |
| 101 | |
| 102 | macro_rules! foreach_wasm_feature { |
| 103 | ($f:ident) => { |
| 104 | $($f!($field = $default);)* |
| 105 | } |
| 106 | } |
| 107 | pub(crate) use foreach_wasm_feature; |
| 108 | }; |
| 109 | } |
| 110 | |
| 111 | define_wasm_features! { |
| 112 | /// Flags for features that are enabled for validation. |
| 113 | /// |
| 114 | /// This type controls the set of WebAssembly proposals and features that |
| 115 | /// are active during validation and parsing of WebAssembly binaries. This |
| 116 | /// is used in conjunction with |
| 117 | /// [`Validator::new_with_features`](crate::Validator::new_with_features) |
| 118 | /// for example. |
| 119 | /// |
| 120 | /// The [`Default`] implementation for this structure returns the set of |
| 121 | /// supported WebAssembly proposals this crate implements. All features are |
| 122 | /// required to be in Phase 4 or above in the WebAssembly standardization |
| 123 | /// process. |
| 124 | /// |
| 125 | /// Enabled/disabled features can affect both parsing and validation at this |
| 126 | /// time. When decoding a WebAssembly binary it's generally recommended if |
| 127 | /// possible to enable all features, as is the default with |
| 128 | /// [`BinaryReader::new`](crate::BinaryReader::new). If strict conformance |
| 129 | /// with historical versions of the specification are required then |
| 130 | /// [`BinaryReader::new_features`](crate::BinaryReader::new_features) or |
| 131 | /// [`BinaryReader::set_features`](crate::BinaryReader::set_features) can be |
| 132 | /// used. |
| 133 | /// |
| 134 | /// This crate additionally has a compile-time Cargo feature called |
| 135 | /// `features` which can be used to enable/disable most of this type at |
| 136 | /// compile time. This crate feature is turned on by default and enables |
| 137 | /// this bitflags-representation of this structure. When the `features` |
| 138 | /// feature is disabled then this is a zero-sized structure and no longer |
| 139 | /// has any associated constants. When `features` are disabled all values |
| 140 | /// for proposals are fixed at compile time to their defaults. |
| 141 | #[derive (Hash, Debug, Copy, Clone, Eq, PartialEq)] |
| 142 | pub struct WasmFeatures: u32 { |
| 143 | /// The WebAssembly `mutable-global` proposal. |
| 144 | pub mutable_global: MUTABLE_GLOBAL(1) = true; |
| 145 | /// The WebAssembly `saturating-float-to-int` proposal. |
| 146 | pub saturating_float_to_int: SATURATING_FLOAT_TO_INT(1 << 1) = true; |
| 147 | /// The WebAssembly `sign-extension-ops` proposal. |
| 148 | pub sign_extension: SIGN_EXTENSION(1 << 2) = true; |
| 149 | /// The WebAssembly reference types proposal. |
| 150 | pub reference_types: REFERENCE_TYPES(1 << 3) = true; |
| 151 | /// The WebAssembly multi-value proposal. |
| 152 | pub multi_value: MULTI_VALUE(1 << 4) = true; |
| 153 | /// The WebAssembly bulk memory operations proposal. |
| 154 | pub bulk_memory: BULK_MEMORY(1 << 5) = true; |
| 155 | /// The WebAssembly SIMD proposal. |
| 156 | pub simd: SIMD(1 << 6) = true; |
| 157 | /// The WebAssembly Relaxed SIMD proposal. |
| 158 | pub relaxed_simd: RELAXED_SIMD(1 << 7) = true; |
| 159 | /// The WebAssembly threads proposal. |
| 160 | pub threads: THREADS(1 << 8) = true; |
| 161 | /// The WebAssembly shared-everything-threads proposal; includes new |
| 162 | /// component model built-ins. |
| 163 | pub shared_everything_threads: SHARED_EVERYTHING_THREADS(1 << 9) = false; |
| 164 | /// The WebAssembly tail-call proposal. |
| 165 | pub tail_call: TAIL_CALL(1 << 10) = true; |
| 166 | /// Whether or not floating-point instructions are enabled. |
| 167 | /// |
| 168 | /// This is enabled by default can be used to disallow floating-point |
| 169 | /// operators and types. |
| 170 | /// |
| 171 | /// This does not correspond to a WebAssembly proposal but is instead |
| 172 | /// intended for embeddings which have stricter-than-usual requirements |
| 173 | /// about execution. Floats in WebAssembly can have different NaN patterns |
| 174 | /// across hosts which can lead to host-dependent execution which some |
| 175 | /// runtimes may not desire. |
| 176 | pub floats: FLOATS(1 << 11) = true; |
| 177 | /// The WebAssembly multi memory proposal. |
| 178 | pub multi_memory: MULTI_MEMORY(1 << 12) = true; |
| 179 | /// The WebAssembly exception handling proposal. |
| 180 | pub exceptions: EXCEPTIONS(1 << 13) = true; |
| 181 | /// The WebAssembly memory64 proposal. |
| 182 | pub memory64: MEMORY64(1 << 14) = true; |
| 183 | /// The WebAssembly extended_const proposal. |
| 184 | pub extended_const: EXTENDED_CONST(1 << 15) = true; |
| 185 | /// The WebAssembly component model proposal. |
| 186 | pub component_model: COMPONENT_MODEL(1 << 16) = true; |
| 187 | /// The WebAssembly typed function references proposal. |
| 188 | pub function_references: FUNCTION_REFERENCES(1 << 17) = true; |
| 189 | /// The WebAssembly memory control proposal. |
| 190 | pub memory_control: MEMORY_CONTROL(1 << 18) = false; |
| 191 | /// The WebAssembly gc proposal. |
| 192 | pub gc: GC(1 << 19) = true; |
| 193 | /// The WebAssembly [custom-page-sizes |
| 194 | /// proposal](https://github.com/WebAssembly/custom-page-sizes). |
| 195 | pub custom_page_sizes: CUSTOM_PAGE_SIZES(1 << 20) = false; |
| 196 | /// Support for the `value` type in the component model proposal. |
| 197 | pub component_model_values: COMPONENT_MODEL_VALUES(1 << 21) = false; |
| 198 | /// Support for the nested namespaces and projects in component model names. |
| 199 | pub component_model_nested_names: COMPONENT_MODEL_NESTED_NAMES(1 << 22) = false; |
| 200 | /// Support for more than 32 flags per-type in the component model. |
| 201 | pub component_model_more_flags: COMPONENT_MODEL_MORE_FLAGS(1 << 23) = false; |
| 202 | /// Support for multiple return values in a component model function. |
| 203 | pub component_model_multiple_returns: COMPONENT_MODEL_MULTIPLE_RETURNS(1 << 24) = false; |
| 204 | /// The WebAssembly legacy exception handling proposal (phase 1) |
| 205 | /// |
| 206 | /// # Note |
| 207 | /// |
| 208 | /// Support this feature as long as all leading browsers also support it |
| 209 | /// <https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md> |
| 210 | pub legacy_exceptions: LEGACY_EXCEPTIONS(1 << 25) = false; |
| 211 | /// Whether or not gc types are enabled. |
| 212 | /// |
| 213 | /// This feature does not correspond to any WebAssembly proposal nor |
| 214 | /// concept in the specification itself. This is intended to assist |
| 215 | /// embedders in disabling support for GC types at validation time. For |
| 216 | /// example if an engine wants to support all of WebAssembly except |
| 217 | /// a runtime garbage collector it could disable this feature. |
| 218 | /// |
| 219 | /// This features is enabled by default and is used to gate types such |
| 220 | /// as `externref` or `anyref`. Note that the requisite WebAssembly |
| 221 | /// proposal must also be enabled for types like `externref`, meaning |
| 222 | /// that it requires both `REFERENCE_TYPES` and `GC_TYPE` to be enabled. |
| 223 | /// |
| 224 | /// Note that the `funcref` and `exnref` types are not gated by this |
| 225 | /// feature. Those are expected to not require a full garbage collector |
| 226 | /// so are not gated by this. |
| 227 | pub gc_types: GC_TYPES(1 << 26) = true; |
| 228 | /// The WebAssembly [stack-switching proposal](https://github.com/WebAssembly/stack-switching). |
| 229 | pub stack_switching: STACK_SWITCHING(1 << 27) = false; |
| 230 | /// The WebAssembly [wide-arithmetic proposal](https://github.com/WebAssembly/wide-arithmetic). |
| 231 | pub wide_arithmetic: WIDE_ARITHMETIC(1 << 28) = false; |
| 232 | /// Support for component model async lift/lower ABI, as well as streams, futures, and errors. |
| 233 | pub component_model_async: COMPONENT_MODEL_ASYNC(1 << 29) = false; |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | impl WasmFeatures { |
| 238 | /// The feature set associated with the MVP release of WebAssembly (its |
| 239 | /// first release). |
| 240 | // |
| 241 | // Note that the features listed here are the wasmparser-specific built-in |
| 242 | // features such as "floats" and "gc-types". These don't actually correspond |
| 243 | // to any wasm proposals themselves and instead just gate constructs in |
| 244 | // wasm. They're listed here so they otherwise don't have to be listed |
| 245 | // below, but for example wasm with `externref` will be rejected due to lack |
| 246 | // of `externref` first. |
| 247 | #[cfg (feature = "features" )] |
| 248 | pub const MVP: WasmFeatures = WasmFeatures::FLOATS.union(WasmFeatures::GC_TYPES); |
| 249 | |
| 250 | /// The feature set associated with the 1.0 version of the |
| 251 | /// WebAssembly specification circa 2017. |
| 252 | /// |
| 253 | /// <https://webassembly.github.io/spec/versions/core/WebAssembly-1.0.pdf> |
| 254 | #[cfg (feature = "features" )] |
| 255 | pub const WASM1: WasmFeatures = WasmFeatures::MVP.union(WasmFeatures::MUTABLE_GLOBAL); |
| 256 | |
| 257 | /// The feature set associated with the 2.0 version of the |
| 258 | /// WebAssembly specification circa 2022. |
| 259 | /// |
| 260 | /// <https://webassembly.github.io/spec/versions/core/WebAssembly-2.0.pdf> |
| 261 | #[cfg (feature = "features" )] |
| 262 | pub const WASM2: WasmFeatures = WasmFeatures::WASM1 |
| 263 | .union(WasmFeatures::BULK_MEMORY) |
| 264 | .union(WasmFeatures::REFERENCE_TYPES) |
| 265 | .union(WasmFeatures::SIGN_EXTENSION) |
| 266 | .union(WasmFeatures::SATURATING_FLOAT_TO_INT) |
| 267 | .union(WasmFeatures::MULTI_VALUE) |
| 268 | .union(WasmFeatures::SIMD); |
| 269 | |
| 270 | /// The feature set associated with the 3.0 version of the |
| 271 | /// WebAssembly specification. |
| 272 | /// |
| 273 | /// Note that as of the time of this writing the 3.0 version of the |
| 274 | /// specification is not yet published. The precise set of features set |
| 275 | /// here may change as that continues to evolve. |
| 276 | /// |
| 277 | /// (draft) |
| 278 | /// <https://webassembly.github.io/spec/versions/core/WebAssembly-3.0-draft.pdf> |
| 279 | #[cfg (feature = "features" )] |
| 280 | pub const WASM3: WasmFeatures = WasmFeatures::WASM2 |
| 281 | .union(WasmFeatures::GC) |
| 282 | .union(WasmFeatures::TAIL_CALL) |
| 283 | .union(WasmFeatures::EXTENDED_CONST) |
| 284 | .union(WasmFeatures::FUNCTION_REFERENCES) |
| 285 | .union(WasmFeatures::MULTI_MEMORY) |
| 286 | .union(WasmFeatures::RELAXED_SIMD) |
| 287 | .union(WasmFeatures::THREADS) |
| 288 | .union(WasmFeatures::EXCEPTIONS) |
| 289 | .union(WasmFeatures::MEMORY64); |
| 290 | } |
| 291 | |
| 292 | #[cfg (feature = "features" )] |
| 293 | impl From<WasmFeaturesInflated> for WasmFeatures { |
| 294 | #[inline ] |
| 295 | fn from(inflated: WasmFeaturesInflated) -> Self { |
| 296 | Self::from_inflated(inflated) |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | #[cfg (feature = "features" )] |
| 301 | impl From<WasmFeatures> for WasmFeaturesInflated { |
| 302 | #[inline ] |
| 303 | fn from(features: WasmFeatures) -> Self { |
| 304 | features.inflate() |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | impl WasmFeatures { |
| 309 | /// Returns whether any types considered valid with this set of |
| 310 | /// `WasmFeatures` requires any type canonicalization/interning internally. |
| 311 | #[cfg (feature = "validate" )] |
| 312 | pub(crate) fn needs_type_canonicalization(&self) -> bool { |
| 313 | #[cfg (feature = "features" )] |
| 314 | { |
| 315 | // Types from the function-references proposal and beyond (namely |
| 316 | // GC) need type canonicalization for inter-type references and for |
| 317 | // rec-groups to work. Prior proposals have no such inter-type |
| 318 | // references structurally and as such can hit various fast paths in |
| 319 | // the validator to do a bit less work when processing the type |
| 320 | // section. |
| 321 | // |
| 322 | // None of these proposals below support inter-type references. If |
| 323 | // `self` contains any proposal outside of this set then it requires |
| 324 | // type canonicalization. |
| 325 | const FAST_VALIDATION_FEATURES: WasmFeatures = WasmFeatures::WASM2 |
| 326 | .union(WasmFeatures::CUSTOM_PAGE_SIZES) |
| 327 | .union(WasmFeatures::EXTENDED_CONST) |
| 328 | .union(WasmFeatures::MEMORY64) |
| 329 | .union(WasmFeatures::MULTI_MEMORY) |
| 330 | .union(WasmFeatures::RELAXED_SIMD) |
| 331 | .union(WasmFeatures::TAIL_CALL) |
| 332 | .union(WasmFeatures::THREADS) |
| 333 | .union(WasmFeatures::WIDE_ARITHMETIC); |
| 334 | !FAST_VALIDATION_FEATURES.contains(*self) |
| 335 | } |
| 336 | #[cfg (not(feature = "features" ))] |
| 337 | { |
| 338 | // GC/function-references are enabled by default, so |
| 339 | // canonicalization is required if feature flags aren't enabled at |
| 340 | // runtime. |
| 341 | true |
| 342 | } |
| 343 | } |
| 344 | } |
| 345 | |