walrus/module/config.rs
1use crate::error::Result;
2use crate::ir::InstrLocId;
3use crate::module::Module;
4use crate::parse::IndicesToIds;
5use std::fmt;
6use std::path::Path;
7use wasmparser::WasmFeatures;
8
9/// Type alias for the on_parse callback function.
10type OnParseFn = Box<dyn Fn(&mut Module, &IndicesToIds) -> Result<()> + Sync + Send + 'static>;
11
12/// Type alias for the on_instr_loc callback function.
13type OnInstrLocFn = Box<dyn Fn(&usize) -> InstrLocId + Sync + Send + 'static>;
14
15/// Configuration for a `Module` which currently affects parsing.
16#[derive(Default)]
17pub struct ModuleConfig {
18 pub(crate) generate_dwarf: bool,
19 pub(crate) generate_synthetic_names_for_anonymous_items: bool,
20 pub(crate) only_stable_features: bool,
21 pub(crate) skip_strict_validate: bool,
22 pub(crate) skip_producers_section: bool,
23 pub(crate) skip_name_section: bool,
24 pub(crate) preserve_code_transform: bool,
25 pub(crate) on_parse: Option<OnParseFn>,
26 pub(crate) on_instr_loc: Option<OnInstrLocFn>,
27}
28
29impl Clone for ModuleConfig {
30 fn clone(&self) -> ModuleConfig {
31 ModuleConfig {
32 // These are all cloned...
33 generate_dwarf: self.generate_dwarf,
34 generate_synthetic_names_for_anonymous_items: self
35 .generate_synthetic_names_for_anonymous_items,
36 only_stable_features: self.only_stable_features,
37 skip_strict_validate: self.skip_strict_validate,
38 skip_producers_section: self.skip_producers_section,
39 skip_name_section: self.skip_name_section,
40 preserve_code_transform: self.preserve_code_transform,
41
42 // ... and this is left empty.
43 on_parse: None,
44 on_instr_loc: None,
45 }
46 }
47}
48
49impl fmt::Debug for ModuleConfig {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 // Destructure `self` so that we get compilation errors if we forget to
52 // add new fields to the debug here.
53 let ModuleConfig {
54 ref generate_dwarf,
55 ref generate_synthetic_names_for_anonymous_items,
56 ref only_stable_features,
57 ref skip_strict_validate,
58 ref skip_producers_section,
59 ref skip_name_section,
60 ref preserve_code_transform,
61 ref on_parse,
62 ref on_instr_loc,
63 } = self;
64
65 f.debug_struct("ModuleConfig")
66 .field("generate_dwarf", generate_dwarf)
67 .field(
68 "generate_synthetic_names_for_anonymous_items",
69 generate_synthetic_names_for_anonymous_items,
70 )
71 .field("only_stable_features", only_stable_features)
72 .field("skip_strict_validate", skip_strict_validate)
73 .field("skip_producers_section", skip_producers_section)
74 .field("skip_name_section", skip_name_section)
75 .field("preserve_code_transform", preserve_code_transform)
76 .field("on_parse", &on_parse.as_ref().map(|_| ".."))
77 .field("on_instr_loc", &on_instr_loc.as_ref().map(|_| ".."))
78 .finish()
79 }
80}
81
82impl ModuleConfig {
83 /// Creates a fresh new configuration with default settings.
84 pub fn new() -> ModuleConfig {
85 ModuleConfig::default()
86 }
87
88 /// Sets a flag to whether DWARF debug sections are generated for this
89 /// module.
90 ///
91 /// By default this flag is `false`. Note that any emitted DWARF is
92 /// currently wildly incorrect and buggy, and is also larger than the wasm
93 /// itself!
94 pub fn generate_dwarf(&mut self, generate: bool) -> &mut ModuleConfig {
95 self.generate_dwarf = generate;
96 // generate_dwarf implies preserve_code_transform
97 self.preserve_code_transform = generate || self.preserve_code_transform;
98 self
99 }
100
101 /// Sets a flag to whether the custom "name" section is generated for this
102 /// module.
103 ///
104 /// The "name" section contains symbol names for the module, functions,
105 /// locals, types, memories, tables, data, elements and globals.
106 /// When enabled, stack traces will use these names, instead of
107 /// `wasm-function[123]`.
108 ///
109 /// By default this flag is `true`.
110 pub fn generate_name_section(&mut self, generate: bool) -> &mut ModuleConfig {
111 self.skip_name_section = !generate;
112 self
113 }
114
115 /// Sets a flag to whether synthetic debugging names are generated for
116 /// anonymous locals/functions/etc when parsing and running passes for this
117 /// module.
118 ///
119 /// By default this flag is `false`, and it will generate quite a few names
120 /// if enabled!
121 pub fn generate_synthetic_names_for_anonymous_items(
122 &mut self,
123 generate: bool,
124 ) -> &mut ModuleConfig {
125 self.generate_synthetic_names_for_anonymous_items = generate;
126 self
127 }
128
129 /// Indicates whether the module, after parsing, performs strict validation
130 /// of the wasm module to adhere with the current version of the wasm
131 /// specification.
132 ///
133 /// This can be expensive for some modules and strictly isn't required to
134 /// create a `Module` from a wasm file. This includes checks such as "atomic
135 /// instructions require a shared memory".
136 ///
137 /// By default this flag is `true`
138 pub fn strict_validate(&mut self, strict: bool) -> &mut ModuleConfig {
139 self.skip_strict_validate = !strict;
140 self
141 }
142
143 /// Indicates whether the module will have the "producers" custom section
144 /// which preserves the original producers and also includes `walrus`.
145 ///
146 /// This is generally used for telemetry in browsers, but for otherwise tiny
147 /// wasm binaries can add some size to the binary.
148 ///
149 /// By default this flag is `true`
150 pub fn generate_producers_section(&mut self, generate: bool) -> &mut ModuleConfig {
151 self.skip_producers_section = !generate;
152 self
153 }
154
155 /// Indicates whether this module is allowed to use only stable WebAssembly
156 /// features or not.
157 ///
158 /// This is currently used to disable some validity checks required by the
159 /// WebAssembly specification. It's not religiously adhered to throughout
160 /// the codebase, even if set to `true` some unstable features may still be
161 /// allowed.
162 ///
163 /// By default this flag is `false`.
164 pub fn only_stable_features(&mut self, only: bool) -> &mut ModuleConfig {
165 self.only_stable_features = only;
166 self
167 }
168
169 /// Returns a `wasmparser::WasmFeatures` based on the enabled proposals
170 /// which should be used for `wasmparser::Parser`` and `wasmparser::Validator`.
171 pub(crate) fn get_wasmparser_wasm_features(&self) -> WasmFeatures {
172 // Use wasmparser's default feature set (all phase 4+ proposals)
173 // This matches what wasm-tools uses and avoids validation bugs
174 let mut features = WasmFeatures::default();
175
176 // Enable legacy exception handling to support older wasm modules
177 // This is the phase 1 exception handling proposal that uses try/catch/delegate
178 // Support this as long as browsers also support it
179 features.insert(WasmFeatures::LEGACY_EXCEPTIONS);
180
181 // Enable wide arithmetic proposal (i64.add128, i64.sub128, i64.mul_wide_s, i64.mul_wide_u)
182 // This is a pre-Phase 4 proposal, so only enable when unstable features are allowed.
183 if !self.only_stable_features {
184 features.insert(WasmFeatures::WIDE_ARITHMETIC);
185 }
186
187 features
188 }
189
190 /// Provide a function that is invoked after successfully parsing a module,
191 /// and gets access to data structures that only exist at parse time, such
192 /// as the map from indices in the original Wasm to the new walrus IDs.
193 ///
194 /// This is a good place to parse custom sections that reference things by
195 /// index.
196 ///
197 /// This will never be invoked for modules that are created from scratch,
198 /// and are not parsed from an existing Wasm binary.
199 ///
200 /// Note that only one `on_parse` function may be registered and subsequent
201 /// registrations will override the old ones.
202 ///
203 /// Note that cloning a `ModuleConfig` will result in a config that does not
204 /// have an `on_parse` function, even if the original did.
205 pub fn on_parse<F>(&mut self, f: F) -> &mut ModuleConfig
206 where
207 F: Fn(&mut Module, &IndicesToIds) -> Result<()> + Send + Sync + 'static,
208 {
209 self.on_parse = Some(Box::new(f) as _);
210 self
211 }
212
213 /// Provide a function that is invoked on source location ID step.
214 ///
215 /// Note that cloning a `ModuleConfig` will result in a config that does not
216 /// have an `on_instr_loc` function, even if the original did.
217 pub fn on_instr_loc<F>(&mut self, f: F) -> &mut ModuleConfig
218 where
219 F: Fn(&usize) -> InstrLocId + Send + Sync + 'static,
220 {
221 self.on_instr_loc = Some(Box::new(f) as _);
222 self
223 }
224
225 /// Sets a flag to whether code transform is preverved during parsing.
226 ///
227 /// By default this flag is `false`.
228 pub fn preserve_code_transform(&mut self, preserve: bool) -> &mut ModuleConfig {
229 self.preserve_code_transform = preserve;
230 self
231 }
232
233 /// Parses an in-memory WebAssembly file into a `Module` using this
234 /// configuration.
235 pub fn parse(&self, wasm: &[u8]) -> Result<Module> {
236 Module::parse(wasm, self)
237 }
238
239 /// Parses a WebAssembly file into a `Module` using this configuration.
240 pub fn parse_file<P>(&self, path: P) -> Result<Module>
241 where
242 P: AsRef<Path>,
243 {
244 Module::from_file_with_config(path, self)
245 }
246}