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 | |