wasmparser/features.rs
1macro_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
111define_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: u64 {
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 //
151 // Note that this is two bits, one for the proposal itself and one which
152 // is the overlong call-indirect encoding. This is split across two bits
153 // since the "lime1" feature set below only encompasses some of this
154 // feature, not all of it. Enabling reference types always enabled
155 // call-indirect-overlong, but not vice-versa.
156 //
157 // Also note that this goes against the recommendation of the
158 // `bitflags!` macro since the lone `1 << 3` bit here doesn't actually
159 // correspond to any named flag. That means it's possible to technically
160 // create a `WasmFeatures` that only has the `1 << 3` bit set, which
161 // doesn't actually mean anything, but that in theory shouldn't be too
162 // harmful and is esoteric enough we don't have to worry much about it.
163 pub reference_types: REFERENCE_TYPES((1 << 3) | Self::CALL_INDIRECT_OVERLONG.bits()) = true;
164 /// The WebAssembly multi-value proposal.
165 pub multi_value: MULTI_VALUE(1 << 4) = true;
166 /// The WebAssembly bulk memory operations proposal.
167 //
168 // Note that this proposal is split in two the same way
169 // `REFERENCE_TYPES` is split above. See more words there for why and
170 // rationale.
171 pub bulk_memory: BULK_MEMORY((1 << 5) | Self::BULK_MEMORY_OPT.bits()) = true;
172 /// The WebAssembly SIMD proposal.
173 pub simd: SIMD(1 << 6) = true;
174 /// The WebAssembly Relaxed SIMD proposal.
175 pub relaxed_simd: RELAXED_SIMD(1 << 7) = true;
176 /// The WebAssembly threads proposal.
177 pub threads: THREADS(1 << 8) = true;
178 /// The WebAssembly shared-everything-threads proposal; includes new
179 /// component model built-ins.
180 pub shared_everything_threads: SHARED_EVERYTHING_THREADS(1 << 9) = false;
181 /// The WebAssembly tail-call proposal.
182 pub tail_call: TAIL_CALL(1 << 10) = true;
183 /// Whether or not floating-point instructions are enabled.
184 ///
185 /// This is enabled by default can be used to disallow floating-point
186 /// operators and types.
187 ///
188 /// This does not correspond to a WebAssembly proposal but is instead
189 /// intended for embeddings which have stricter-than-usual requirements
190 /// about execution. Floats in WebAssembly can have different NaN patterns
191 /// across hosts which can lead to host-dependent execution which some
192 /// runtimes may not desire.
193 pub floats: FLOATS(1 << 11) = true;
194 /// The WebAssembly multi memory proposal.
195 pub multi_memory: MULTI_MEMORY(1 << 12) = true;
196 /// The WebAssembly exception handling proposal.
197 pub exceptions: EXCEPTIONS(1 << 13) = true;
198 /// The WebAssembly memory64 proposal.
199 pub memory64: MEMORY64(1 << 14) = true;
200 /// The WebAssembly extended_const proposal.
201 pub extended_const: EXTENDED_CONST(1 << 15) = true;
202 /// The WebAssembly component model proposal.
203 pub component_model: COMPONENT_MODEL(1 << 16) = true;
204 /// The WebAssembly typed function references proposal.
205 pub function_references: FUNCTION_REFERENCES(1 << 17) = true;
206 /// The WebAssembly memory control proposal.
207 pub memory_control: MEMORY_CONTROL(1 << 18) = false;
208 /// The WebAssembly gc proposal.
209 pub gc: GC(1 << 19) = true;
210 /// The WebAssembly [custom-page-sizes
211 /// proposal](https://github.com/WebAssembly/custom-page-sizes).
212 pub custom_page_sizes: CUSTOM_PAGE_SIZES(1 << 20) = false;
213 /// The WebAssembly legacy exception handling proposal (phase 1)
214 ///
215 /// # Note
216 ///
217 /// Support this feature as long as all leading browsers also support it
218 /// <https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md>
219 pub legacy_exceptions: LEGACY_EXCEPTIONS(1 << 23) = false;
220 /// Whether or not gc types are enabled.
221 ///
222 /// This feature does not correspond to any WebAssembly proposal nor
223 /// concept in the specification itself. This is intended to assist
224 /// embedders in disabling support for GC types at validation time. For
225 /// example if an engine wants to support all of WebAssembly except
226 /// a runtime garbage collector it could disable this feature.
227 ///
228 /// This features is enabled by default and is used to gate types such
229 /// as `externref` or `anyref`. Note that the requisite WebAssembly
230 /// proposal must also be enabled for types like `externref`, meaning
231 /// that it requires both `REFERENCE_TYPES` and `GC_TYPE` to be enabled.
232 ///
233 /// Note that the `funcref` and `exnref` types are not gated by this
234 /// feature. Those are expected to not require a full garbage collector
235 /// so are not gated by this.
236 pub gc_types: GC_TYPES(1 << 24) = true;
237 /// The WebAssembly [stack-switching proposal](https://github.com/WebAssembly/stack-switching).
238 pub stack_switching: STACK_SWITCHING(1 << 25) = false;
239 /// The WebAssembly [wide-arithmetic proposal](https://github.com/WebAssembly/wide-arithmetic).
240 pub wide_arithmetic: WIDE_ARITHMETIC(1 << 26) = false;
241
242 /// Support for the `value` type in the component model proposal.
243 ///
244 /// Corresponds to the 🪙 character in
245 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
246 pub cm_values: CM_VALUES(1 << 21) = false;
247 /// Support for the nested namespaces and projects in component model names.
248 ///
249 /// Corresponds to the 🪺 character in
250 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
251 pub cm_nested_names: CM_NESTED_NAMES(1 << 22) = false;
252 /// Support for component model async lift/lower ABI, as well as streams, futures, and errors.
253 ///
254 /// Corresponds to the 🔀 character in
255 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
256 pub cm_async: CM_ASYNC(1 << 27) = false;
257 /// Gates the "stackful ABI" in the component model async proposal.
258 ///
259 /// Corresponds to the 🚟 character in
260 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
261 pub cm_async_stackful: CM_ASYNC_STACKFUL(1 << 28) = false;
262 /// Gates some intrinsics being marked with `async` in the component
263 /// model async proposal.
264 ///
265 /// Corresponds to the 🚝 character in
266 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
267 pub cm_async_builtins: CM_ASYNC_BUILTINS(1 << 29) = false;
268 /// Gates some intrinsics being marked with `error-context` in the component
269 /// model async proposal.
270 ///
271 /// Corresponds to the 📝 character in
272 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
273 pub cm_error_context: CM_ERROR_CONTEXT(1 << 30) = false;
274 /// Support for fixed size lists
275 ///
276 /// Corresponds to the 🔧 character in
277 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
278 pub cm_fixed_size_list: CM_FIXED_SIZE_LIST(1 << 31) = false;
279 /// Support for Wasm GC in the component model proposal.
280 ///
281 /// Corresponds to the 🛸 character in
282 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
283 pub cm_gc: CM_GC(1 << 32) = false;
284
285 /// Subset of the reference-types WebAssembly proposal which only
286 /// encompasses the leb-encoding of the table immediate to the
287 /// `call_indirect` instruction, enabling over-long encodings of an
288 /// integer for example.
289 ///
290 /// This is a subcomponent of the "lime1" feature.
291 pub call_indirect_overlong: CALL_INDIRECT_OVERLONG(1 << 33) = true;
292
293 /// Subset of the bulk-memory proposal covering just the `memory.copy`
294 /// and `memory.fill` instructions.
295 ///
296 /// This is a subcomponent of the "lime1" feature.
297 pub bulk_memory_opt: BULK_MEMORY_OPT(1 << 34) = true;
298 }
299}
300
301impl WasmFeatures {
302 /// The feature set associated with the MVP release of WebAssembly (its
303 /// first release).
304 //
305 // Note that the features listed here are the wasmparser-specific built-in
306 // features such as "floats" and "gc-types". These don't actually correspond
307 // to any wasm proposals themselves and instead just gate constructs in
308 // wasm. They're listed here so they otherwise don't have to be listed
309 // below, but for example wasm with `externref` will be rejected due to lack
310 // of `externref` first.
311 #[cfg(feature = "features")]
312 pub const MVP: WasmFeatures = WasmFeatures::FLOATS.union(WasmFeatures::GC_TYPES);
313
314 /// The feature set associated with the 1.0 version of the
315 /// WebAssembly specification circa 2017.
316 ///
317 /// <https://webassembly.github.io/spec/versions/core/WebAssembly-1.0.pdf>
318 #[cfg(feature = "features")]
319 pub const WASM1: WasmFeatures = WasmFeatures::MVP.union(WasmFeatures::MUTABLE_GLOBAL);
320
321 /// The feature set associated with the 2.0 version of the
322 /// WebAssembly specification circa 2022.
323 ///
324 /// <https://webassembly.github.io/spec/versions/core/WebAssembly-2.0.pdf>
325 #[cfg(feature = "features")]
326 pub const WASM2: WasmFeatures = WasmFeatures::WASM1
327 .union(WasmFeatures::BULK_MEMORY)
328 .union(WasmFeatures::REFERENCE_TYPES)
329 .union(WasmFeatures::SIGN_EXTENSION)
330 .union(WasmFeatures::SATURATING_FLOAT_TO_INT)
331 .union(WasmFeatures::MULTI_VALUE)
332 .union(WasmFeatures::SIMD);
333
334 /// The feature set associated with the 3.0 version of the
335 /// WebAssembly specification.
336 ///
337 /// Note that as of the time of this writing the 3.0 version of the
338 /// specification is not yet published. The precise set of features set
339 /// here may change as that continues to evolve.
340 ///
341 /// (draft)
342 /// <https://webassembly.github.io/spec/versions/core/WebAssembly-3.0-draft.pdf>
343 #[cfg(feature = "features")]
344 pub const WASM3: WasmFeatures = WasmFeatures::WASM2
345 .union(WasmFeatures::GC)
346 .union(WasmFeatures::TAIL_CALL)
347 .union(WasmFeatures::EXTENDED_CONST)
348 .union(WasmFeatures::FUNCTION_REFERENCES)
349 .union(WasmFeatures::MULTI_MEMORY)
350 .union(WasmFeatures::RELAXED_SIMD)
351 .union(WasmFeatures::THREADS)
352 .union(WasmFeatures::EXCEPTIONS)
353 .union(WasmFeatures::MEMORY64);
354
355 /// The feature set associated with the "lime1" set of features.
356 ///
357 /// Here "lime1" stands for "linear memory version 1" and is a stable set of
358 /// features agreed on by both producers and consumers which is more than
359 /// the MVP but does not include some more weighty engine features such as
360 /// reference types, gc, etc.
361 ///
362 /// <https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md>
363 #[cfg(feature = "features")]
364 pub const LIME1: WasmFeatures = WasmFeatures::WASM1
365 .union(WasmFeatures::MULTI_VALUE)
366 .union(WasmFeatures::SIGN_EXTENSION)
367 .union(WasmFeatures::SATURATING_FLOAT_TO_INT)
368 .union(WasmFeatures::BULK_MEMORY_OPT)
369 .union(WasmFeatures::EXTENDED_CONST)
370 .union(WasmFeatures::CALL_INDIRECT_OVERLONG);
371}
372
373#[cfg(feature = "features")]
374impl From<WasmFeaturesInflated> for WasmFeatures {
375 #[inline]
376 fn from(inflated: WasmFeaturesInflated) -> Self {
377 Self::from_inflated(inflated)
378 }
379}
380
381#[cfg(feature = "features")]
382impl From<WasmFeatures> for WasmFeaturesInflated {
383 #[inline]
384 fn from(features: WasmFeatures) -> Self {
385 features.inflate()
386 }
387}
388
389impl WasmFeatures {
390 /// Returns whether any types considered valid with this set of
391 /// `WasmFeatures` requires any type canonicalization/interning internally.
392 #[cfg(feature = "validate")]
393 pub(crate) fn needs_type_canonicalization(&self) -> bool {
394 #[cfg(feature = "features")]
395 {
396 // Types from the function-references proposal and beyond (namely
397 // GC) need type canonicalization for inter-type references and for
398 // rec-groups to work. Prior proposals have no such inter-type
399 // references structurally and as such can hit various fast paths in
400 // the validator to do a bit less work when processing the type
401 // section.
402 //
403 // None of these proposals below support inter-type references. If
404 // `self` contains any proposal outside of this set then it requires
405 // type canonicalization.
406 const FAST_VALIDATION_FEATURES: WasmFeatures = WasmFeatures::WASM2
407 .union(WasmFeatures::CUSTOM_PAGE_SIZES)
408 .union(WasmFeatures::EXTENDED_CONST)
409 .union(WasmFeatures::MEMORY64)
410 .union(WasmFeatures::MULTI_MEMORY)
411 .union(WasmFeatures::RELAXED_SIMD)
412 .union(WasmFeatures::TAIL_CALL)
413 .union(WasmFeatures::THREADS)
414 .union(WasmFeatures::WIDE_ARITHMETIC);
415 !FAST_VALIDATION_FEATURES.contains(*self)
416 }
417 #[cfg(not(feature = "features"))]
418 {
419 // GC/function-references are enabled by default, so
420 // canonicalization is required if feature flags aren't enabled at
421 // runtime.
422 true
423 }
424 }
425}