Skip to main content

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}