Skip to main content

wasmtime_cli_flags/
lib.rs

1//! A utility crate for managing Wasmtime configuration options in a
2//! serializable format.
3//!
4//! This crate primarily provides a [`CommonOptions`] structure which can be
5//! parsed from CLI flags via [`clap`] or with [`serde::Deserialize`]. This
6//! structure can additionally be created from a [`wasmtime::Engine`] to
7//! serialize some of the significant configuration options affecting an engine.
8//!
9//! This is intended to be a shared way for embedders to configure Wasmtime with
10//! serializable configuration. The source of truth for Wasmtime's configuration
11//! is the [`Config`] type but this additionally serves as a means of creating
12//! that type.
13
14use crate::opt::NonzeroDuration;
15use std::num::NonZeroUsize;
16use std::{fmt, num::NonZeroU32, path::PathBuf};
17use wasmtime::{Config, Engine, Result, WasmBacktraceDetails, WasmFeatures, bail};
18
19pub mod opt;
20
21#[cfg(feature = "logging")]
22fn init_file_per_thread_logger(prefix: &'static str) {
23    file_per_thread_logger::initialize(prefix);
24    file_per_thread_logger::allow_uninitialized();
25
26    // Extending behavior of default spawner:
27    // https://docs.rs/rayon/1.1.0/rayon/struct.ThreadPoolBuilder.html#method.spawn_handler
28    // Source code says DefaultSpawner is implementation detail and
29    // shouldn't be used directly.
30    #[cfg(feature = "parallel-compilation")]
31    rayon::ThreadPoolBuilder::new()
32        .spawn_handler(move |thread| {
33            let mut b = std::thread::Builder::new();
34            if let Some(name) = thread.name() {
35                b = b.name(name.to_owned());
36            }
37            if let Some(stack_size) = thread.stack_size() {
38                b = b.stack_size(stack_size);
39            }
40            b.spawn(move || {
41                file_per_thread_logger::initialize(prefix);
42                thread.run()
43            })?;
44            Ok(())
45        })
46        .build_global()
47        .unwrap();
48}
49
50wasmtime_option_group! {
51    #[env = "OPTIMIZE"]
52    pub struct OptimizeOptions {
53        /// Optimization level of generated code (0-2, s; default: 2)
54        #[serde(default)]
55        #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
56        #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
57        pub opt_level: Option<wasmtime::OptLevel>,
58
59        /// Register allocator algorithm choice.
60        #[serde(default)]
61        #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
62        #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
63        pub regalloc_algorithm: Option<wasmtime::RegallocAlgorithm>,
64
65        /// Do not allow Wasm linear memories to move in the host process's
66        /// address space.
67        pub memory_may_move: Option<bool>,
68
69        /// Initial virtual memory allocation size for memories.
70        pub memory_reservation: Option<u64>,
71
72        /// Bytes to reserve at the end of linear memory for growth into.
73        pub memory_reservation_for_growth: Option<u64>,
74
75        /// Size, in bytes, of guard pages for linear memories.
76        pub memory_guard_size: Option<u64>,
77
78        /// Do not allow the GC heap to move in the host process's address
79        /// space.
80        pub gc_heap_may_move: Option<bool>,
81
82        /// Initial virtual memory allocation size for the GC heap.
83        pub gc_heap_reservation: Option<u64>,
84
85        /// Bytes to reserve at the end of the GC heap for growth into.
86        pub gc_heap_reservation_for_growth: Option<u64>,
87
88        /// Size, in bytes, of guard pages for the GC heap.
89        pub gc_heap_guard_size: Option<u64>,
90
91        /// Initial allocated size for the GC heap.
92        pub gc_heap_initial_size: Option<u64>,
93
94        /// Indicates whether an unmapped region of memory is placed before all
95        /// linear memories.
96        pub guard_before_linear_memory: Option<bool>,
97
98        /// Whether to initialize tables lazily, so that instantiation is
99        /// fast but indirect calls are a little slower. If no, tables are
100        /// initialized eagerly from any active element segments that apply to
101        /// them during instantiation. (default: yes)
102        pub table_lazy_init: Option<bool>,
103
104        /// Enable the pooling allocator, in place of the on-demand allocator.
105        pub pooling_allocator: Option<bool>,
106
107        /// The number of decommits to do per batch. A batch size of 1
108        /// effectively disables decommit batching. (default: 1)
109        pub pooling_decommit_batch_size: Option<usize>,
110
111        /// How many bytes to keep resident between instantiations for the
112        /// pooling allocator in linear memories.
113        pub pooling_memory_keep_resident: Option<usize>,
114
115        /// How many bytes to keep resident between instantiations for the
116        /// pooling allocator in tables.
117        pub pooling_table_keep_resident: Option<usize>,
118
119        /// Enable memory protection keys for the pooling allocator; this can
120        /// optimize the size of memory slots.
121        #[serde(default)]
122        #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
123        #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
124        pub pooling_memory_protection_keys: Option<wasmtime::Enabled>,
125
126        /// Sets an upper limit on how many memory protection keys (MPK) Wasmtime
127        /// will use. (default: 16)
128        pub pooling_max_memory_protection_keys: Option<usize>,
129
130        /// Configure attempting to initialize linear memory via a
131        /// copy-on-write mapping (default: yes)
132        pub memory_init_cow: Option<bool>,
133
134        /// Threshold below which CoW images are guaranteed to be used and be
135        /// dense.
136        pub memory_guaranteed_dense_image_size: Option<u64>,
137
138        /// The maximum number of WebAssembly instances which can be created
139        /// with the pooling allocator.
140        pub pooling_total_core_instances: Option<u32>,
141
142        /// The maximum number of WebAssembly components which can be created
143        /// with the pooling allocator.
144        pub pooling_total_component_instances: Option<u32>,
145
146        /// The maximum number of WebAssembly memories which can be created with
147        /// the pooling allocator.
148        pub pooling_total_memories: Option<u32>,
149
150        /// The maximum number of WebAssembly tables which can be created with
151        /// the pooling allocator.
152        pub pooling_total_tables: Option<u32>,
153
154        /// The maximum number of WebAssembly stacks which can be created with
155        /// the pooling allocator.
156        pub pooling_total_stacks: Option<u32>,
157
158        /// The maximum runtime size of each linear memory in the pooling
159        /// allocator, in bytes.
160        pub pooling_max_memory_size: Option<usize>,
161
162        /// The maximum table elements for any table defined in a module when
163        /// using the pooling allocator.
164        pub pooling_table_elements: Option<usize>,
165
166        /// The maximum size, in bytes, allocated for a core instance's metadata
167        /// when using the pooling allocator.
168        pub pooling_max_core_instance_size: Option<usize>,
169
170        /// Configures the maximum number of "unused warm slots" to retain in the
171        /// pooling allocator. (default: 100)
172        pub pooling_max_unused_warm_slots: Option<u32>,
173
174        /// How much memory, in bytes, to keep resident for async stacks allocated
175        /// with the pooling allocator. (default: 0)
176        pub pooling_async_stack_keep_resident: Option<usize>,
177
178        /// The maximum size, in bytes, allocated for a component instance's
179        /// `VMComponentContext` metadata. (default: 1MiB)
180        pub pooling_max_component_instance_size: Option<usize>,
181
182        /// The maximum number of core instances a single component may contain
183        /// (default is unlimited).
184        pub pooling_max_core_instances_per_component: Option<u32>,
185
186        /// The maximum number of Wasm linear memories that a single component may
187        /// transitively contain (default is unlimited).
188        pub pooling_max_memories_per_component: Option<u32>,
189
190        /// The maximum number of tables that a single component may transitively
191        /// contain (default is unlimited).
192        pub pooling_max_tables_per_component: Option<u32>,
193
194        /// The maximum number of defined tables for a core module. (default: 1)
195        pub pooling_max_tables_per_module: Option<u32>,
196
197        /// The maximum number of defined linear memories for a module. (default: 1)
198        pub pooling_max_memories_per_module: Option<u32>,
199
200        /// The maximum number of concurrent GC heaps supported. (default: 1000)
201        pub pooling_total_gc_heaps: Option<u32>,
202
203        /// Enable or disable the use of host signal handlers for traps.
204        pub signals_based_traps: Option<bool>,
205
206        /// DEPRECATED: Use `-Cmemory-guard-size=N` instead.
207        pub dynamic_memory_guard_size: Option<u64>,
208
209        /// DEPRECATED: Use `-Cmemory-guard-size=N` instead.
210        pub static_memory_guard_size: Option<u64>,
211
212        /// DEPRECATED: Use `-Cmemory-may-move` instead.
213        pub static_memory_forced: Option<bool>,
214
215        /// DEPRECATED: Use `-Cmemory-reservation=N` instead.
216        pub static_memory_maximum_size: Option<u64>,
217
218        /// DEPRECATED: Use `-Cmemory-reservation-for-growth=N` instead.
219        pub dynamic_memory_reserved_for_growth: Option<u64>,
220
221        /// Whether or not `PAGEMAP_SCAN` ioctls are used to reset linear
222        /// memory.
223        #[serde(default)]
224        #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
225        #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
226        pub pooling_pagemap_scan: Option<wasmtime::Enabled>,
227
228        /// XXX: For internal fuzzing and debugging use only!
229        #[doc(hidden)]
230        pub gc_zeal_alloc_counter: Option<NonZeroU32>,
231    }
232
233    enum Optimize {
234        ...
235    }
236}
237
238wasmtime_option_group! {
239    #[env = "CODEGEN"]
240    pub struct CodegenOptions {
241        /// Either `cranelift` or `winch`.
242        ///
243        /// Currently only `cranelift` and `winch` are supported, but not all
244        /// builds of Wasmtime have both built in.
245        #[serde(default)]
246        #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
247        #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
248        pub compiler: Option<wasmtime::Strategy>,
249        /// Which garbage collector to use: `drc`, `null`, or `copying`.
250        ///
251        /// `drc` is the deferred reference-counting collector.
252        ///
253        /// `null` is the null garbage collector, which does not collect any
254        /// garbage.
255        ///
256        /// `copying` is the copying garbage collector (not yet implemented).
257        ///
258        /// Note that not all builds of Wasmtime will have support for garbage
259        /// collection included.
260        #[serde(default)]
261        #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
262        #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
263        pub collector: Option<wasmtime::Collector>,
264        /// Enable Cranelift's internal debug verifier (expensive)
265        pub cranelift_debug_verifier: Option<bool>,
266        /// Whether or not to enable caching of compiled modules.
267        pub cache: Option<bool>,
268        /// Configuration for compiled module caching.
269        pub cache_config: Option<String>,
270        /// Whether or not to enable parallel compilation of modules.
271        pub parallel_compilation: Option<bool>,
272        /// Controls whether native unwind information is present in compiled
273        /// object files.
274        pub native_unwind_info: Option<bool>,
275        /// Whether compiled code includes the hooks needed to run in a
276        /// Wasmtime built with AddressSanitizer (defaults to whether this
277        /// Wasmtime was built with AddressSanitizer).
278        pub asan_compatible_code: Option<bool>,
279
280        /// Whether to perform function inlining during compilation.
281        #[serde(default)]
282        #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
283        #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
284        pub inlining: Option<wasmtime::Inlining>,
285
286        /// Whether or not trap metadata is present for wasm internal assertions
287        /// in compiled code.
288        pub metadata_for_internal_asserts: Option<bool>,
289        /// Whether or not trap metadata is present for detection of gc
290        /// corruption in compiled code.
291        pub metadata_for_gc_heap_corruption: Option<bool>,
292
293        /// Expose Wasmtime's unsafe intrinsics to the Wasm being compiled under
294        /// the given instance import name, which defaults to
295        /// `unsafe-intrinsics` when the name is omitted.
296        ///
297        /// The unsafe intrinsics are a set of raw, unchecked load and store
298        /// operations on the host's address space, along with the address of
299        /// the store's data. They may only be used with components, not core
300        /// Wasm modules.
301        ///
302        /// This is wildly unsafe: the Wasm is given the ability to read and
303        /// write arbitrary host memory. Only use this with Wasm that you trust
304        /// as much as you trust the CLI itself.
305        ///
306        /// When `-C compile-time-builtin` is also given, the intrinsics are
307        /// exposed only to the compile-time builtins and not to the main Wasm
308        /// program.
309        ///
310        /// See the API documentation for
311        /// `CodeBuilder::expose_unsafe_intrinsics` for more details.
312        #[serde(default)]
313        #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
314        #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
315        pub unsafe_intrinsics: Option<UnsafeIntrinsicsImport>,
316
317        /// Define a compile-time builtin: satisfy the `<name>` instance import
318        /// of the main component with the component at `<path>` at compile
319        /// time, rather than with a host-defined import at instantiation time.
320        ///
321        /// May be specified multiple times, once per builtin. Compile-time
322        /// builtins may only be used with components, not core Wasm modules,
323        /// and require `-C unsafe-intrinsics`.
324        ///
325        /// The `<name>=` prefix may be omitted, in which case `<name>` defaults
326        /// to the file name of `<path>` without its extension. For example
327        /// `-C compile-time-builtin=path/to/my-host-api.wat` satisfies the
328        /// `my-host-api` import. Note that a `<path>` which itself contains an
329        /// `=` is interpreted as the `<name>=<path>` form; pass the explicit
330        /// form to disambiguate.
331        ///
332        /// Compile-time builtins are part of your trusted compute base: they
333        /// are given access to the unsafe intrinsics described above. Calls
334        /// into them become direct calls, so pass `-C inlining=y` to let them
335        /// be inlined into their callers.
336        ///
337        /// See the API documentation for
338        /// `CodeBuilder::compile_time_builtin_binary` for more details.
339        #[serde(skip)]
340        pub compile_time_builtin: Vec<CompileTimeBuiltin>,
341
342        #[prefixed = "cranelift"]
343        #[serde(default)]
344        /// Set a cranelift-specific option. Use `wasmtime settings` to see
345        /// all.
346        pub cranelift: Vec<(String, Option<String>)>,
347    }
348
349    enum Codegen {
350        ...
351    }
352}
353
354wasmtime_option_group! {
355    #[env = "DEBUG"]
356    pub struct DebugOptions {
357        /// Enable generation of DWARF debug information in compiled code.
358        pub debug_info: Option<bool>,
359        /// Enable guest debugging insrumentation.
360        pub guest_debug: Option<bool>,
361        /// Configure whether compiled code can map native addresses to wasm.
362        pub address_map: Option<bool>,
363        /// Configure whether logging is enabled.
364        pub logging: Option<bool>,
365        /// Configure whether logs are emitted to files
366        pub log_to_files: Option<bool>,
367        /// Enable coredump generation to this file after a WebAssembly trap.
368        pub coredump: Option<String>,
369        /// Load the given debugger component and attach it to the
370        /// main module or component.
371        pub debugger: Option<PathBuf>,
372        /// Pass the given command-line arguments to the debugger
373        /// component. May be specified multiple times.
374        #[serde(default)]
375        pub arg: Vec<String>,
376        /// Allow the debugger component to inherit stdin.Off by
377        /// default.
378        pub inherit_stdin: Option<bool>,
379        /// Allow the debugger component to inherit stdout. Off by
380        /// default.
381        pub inherit_stdout: Option<bool>,
382        /// Allow the debugger component to inherit stderr. Off by
383        /// default.
384        pub inherit_stderr: Option<bool>,
385        /// Maximum number of frames to capture in backtraces.
386        pub max_backtrace: Option<usize>,
387        /// Whether or not `*.cwasm` files have symbols in them.
388        pub symbols: Option<bool>,
389    }
390
391    enum Debug {
392        ...
393    }
394}
395
396wasmtime_option_group! {
397    #[env = "WASM"]
398    pub struct WasmOptions {
399        /// Enable canonicalization of all NaN values.
400        pub nan_canonicalization: Option<bool>,
401        /// Enable execution fuel with N units fuel, trapping after running out
402        /// of fuel.
403        ///
404        /// Most WebAssembly instructions consume 1 unit of fuel. Some
405        /// instructions, such as `nop`, `drop`, `block`, and `loop`, consume 0
406        /// units, as any execution cost associated with them involves other
407        /// instructions which do consume fuel.
408        pub fuel: Option<u64>,
409        /// Yield when a global epoch counter changes, allowing for async
410        /// operation without blocking the executor.
411        pub epoch_interruption: Option<bool>,
412        /// Maximum stack size, in bytes, that wasm is allowed to consume before a
413        /// stack overflow is reported.
414        pub max_wasm_stack: Option<usize>,
415        /// Stack size, in bytes, that will be allocated for async stacks.
416        ///
417        /// Note that this must be larger than `max-wasm-stack` and the
418        /// difference between the two is how much stack the host has to execute
419        /// on.
420        pub async_stack_size: Option<usize>,
421        /// Configures whether or not stacks used for async futures are zeroed
422        /// before (re)use as a defense-in-depth mechanism. (default: false)
423        pub async_stack_zeroing: Option<bool>,
424        /// Allow unknown exports when running commands.
425        pub unknown_exports_allow: Option<bool>,
426        /// Allow the main module to import unknown functions, using an
427        /// implementation that immediately traps, when running commands.
428        pub unknown_imports_trap: Option<bool>,
429        /// Allow the main module to import unknown functions, using an
430        /// implementation that returns default values, when running commands.
431        pub unknown_imports_default: Option<bool>,
432        /// Enables memory error checking. (see wmemcheck.md for more info)
433        pub wmemcheck: Option<bool>,
434        /// Maximum size, in bytes, that a linear memory is allowed to reach.
435        ///
436        /// Growth beyond this limit will cause `memory.grow` instructions in
437        /// WebAssembly modules to return -1 and fail.
438        pub max_memory_size: Option<usize>,
439        /// Maximum size, in table elements, that a table is allowed to reach.
440        pub max_table_elements: Option<usize>,
441        /// Maximum number of WebAssembly instances allowed to be created.
442        pub max_instances: Option<usize>,
443        /// Maximum number of WebAssembly tables allowed to be created.
444        pub max_tables: Option<usize>,
445        /// Maximum number of WebAssembly linear memories allowed to be created.
446        pub max_memories: Option<usize>,
447        /// Force a trap to be raised on `memory.grow` and `table.grow` failure
448        /// instead of returning -1 from these instructions.
449        ///
450        /// This is not necessarily a spec-compliant option to enable but can be
451        /// useful for tracking down a backtrace of what is requesting so much
452        /// memory, for example.
453        pub trap_on_grow_failure: Option<bool>,
454        /// Maximum execution time of wasm code before timing out (1, 2s, 100ms, etc).
455        ///
456        /// Note that when this option is not set, malicious wasm code may
457        /// cause the host process to become unresponsive to inputs including
458        /// SIGTERM.
459        pub timeout: Option<NonzeroDuration>,
460        /// Configures support for all WebAssembly proposals implemented.
461        pub all_proposals: Option<bool>,
462        /// Configure support for the bulk memory proposal.
463        pub bulk_memory: Option<bool>,
464        /// Configure support for the multi-memory proposal.
465        pub multi_memory: Option<bool>,
466        /// Configure support for the multi-value proposal.
467        pub multi_value: Option<bool>,
468        /// Configure support for the reference-types proposal.
469        pub reference_types: Option<bool>,
470        /// Configure support for the simd proposal.
471        pub simd: Option<bool>,
472        /// Configure support for the relaxed-simd proposal.
473        pub relaxed_simd: Option<bool>,
474        /// Configure forcing deterministic and host-independent behavior of
475        /// the relaxed-simd instructions.
476        ///
477        /// By default these instructions may have architecture-specific behavior as
478        /// allowed by the specification, but this can be used to force the behavior
479        /// of these instructions to match the deterministic behavior classified in
480        /// the specification. Note that enabling this option may come at a
481        /// performance cost.
482        pub relaxed_simd_deterministic: Option<bool>,
483        /// Configure support for the tail-call proposal.
484        pub tail_call: Option<bool>,
485        /// Configure support for the threads proposal.
486        pub threads: Option<bool>,
487        /// Configure the ability to create a `shared` memory.
488        pub shared_memory: Option<bool>,
489        /// Configure support for the memory64 proposal.
490        pub memory64: Option<bool>,
491        /// Configure support for the component-model proposal.
492        pub component_model: Option<bool>,
493        /// Component model support for async lifting/lowering.
494        pub component_model_async: Option<bool>,
495        /// Component model support for async lifting/lowering: this corresponds
496        /// to the 🚝 emoji in the component model specification.
497        pub component_model_more_async_builtins: Option<bool>,
498        /// Component model support for async lifting/lowering: this corresponds
499        /// to the 🚟 emoji in the component model specification.
500        pub component_model_async_stackful: Option<bool>,
501        /// Component model support for threading: this corresponds
502        /// to the 🧡 emoji in the component model specification.
503        pub component_model_threading: Option<bool>,
504        /// Component model support for `error-context`: this corresponds
505        /// to the πŸ“ emoji in the component model specification.
506        pub component_model_error_context: Option<bool>,
507        /// GC support in the component model: this corresponds to the πŸ›Έ emoji
508        /// in the component model specification.
509        pub component_model_gc: Option<bool>,
510        /// Map support in the component model: this corresponds to the πŸ—ΊοΈ
511        /// emoji in the component model specification.
512        pub component_model_map: Option<bool>,
513        /// Component model support for `memory64`: this corresponds
514        /// to the 🐘 emoji in the component model specification.
515        pub component_model_memory64: Option<bool>,
516        /// Configure support for the function-references proposal.
517        pub function_references: Option<bool>,
518        /// Configure support for the stack-switching proposal.
519        pub stack_switching: Option<bool>,
520        /// Configure support for the GC proposal.
521        pub gc: Option<bool>,
522        /// Configure support for the custom-page-sizes proposal.
523        pub custom_page_sizes: Option<bool>,
524        /// Configure support for the wide-arithmetic proposal.
525        pub wide_arithmetic: Option<bool>,
526        /// Configure support for the branch-hinting proposal.
527        pub branch_hinting: Option<bool>,
528        /// Configure support for compact imports.
529        pub compact_imports: Option<bool>,
530        /// Configure support for the extended-const proposal.
531        pub extended_const: Option<bool>,
532        /// Configure support for the exceptions proposal.
533        pub exceptions: Option<bool>,
534        /// Configure support for the legacy exceptions proposal.
535        ///
536        /// This only exists for Wasmtime's own testing of the spec test suite
537        /// and may be removed at any time.
538        pub legacy_exceptions: Option<bool>,
539        /// Whether or not any GC infrastructure in Wasmtime is enabled or not.
540        pub gc_support: Option<bool>,
541        /// Component model support for fixed-length lists: this corresponds
542        /// to the πŸ”§ emoji in the component model specification
543        pub component_model_fixed_length_lists: Option<bool>,
544        /// Component model support for `(implements ...)` and
545        /// `(external-id ...)`, corresponds to the 🏷️ emoji in the upstream
546        /// spec.
547        pub component_model_implements: Option<bool>,
548        /// Component model support for canonical names, corresponds to the
549        /// πŸ”— emoji in the upstream spec.
550        pub component_model_canonical_names: Option<bool>,
551        /// Component model support for `[get]` and `[set]` accessors,
552        /// corresponds to the πŸ“‘ emoji in the upstream spec.
553        pub component_model_accessors: Option<bool>,
554        /// Whether or not any concurrency infrastructure in Wasmtime is
555        /// enabled or not.
556        pub concurrency_support: Option<bool>,
557    }
558
559    enum Wasm {
560        ...
561    }
562}
563
564wasmtime_option_group! {
565    #[env = "WASI"]
566    pub struct WasiOptions {
567        /// Enable support for WASI CLI APIs, including filesystems, sockets, clocks, and random.
568        pub cli: Option<bool>,
569        /// Enable WASI APIs marked as: @unstable(feature = cli-exit-with-code)
570        pub cli_exit_with_code: Option<bool>,
571        /// Deprecated alias for `cli`
572        pub common: Option<bool>,
573        /// Enable support for WASI neural network imports (experimental)
574        pub nn: Option<bool>,
575        /// Enable support for WASI threading imports (experimental). Implies preview2=false.
576        pub threads: Option<bool>,
577        /// Enable support for WASI HTTP imports
578        pub http: Option<bool>,
579        /// Number of distinct write calls to the outgoing body's output-stream
580        /// that the implementation will buffer.
581        /// Default: 1.
582        pub http_outgoing_body_buffer_chunks: Option<NonZeroUsize>,
583        /// Maximum size allowed in a write call to the outgoing body's output-stream.
584        /// Default: 1024 * 1024.
585        pub http_outgoing_body_chunk_size: Option<NonZeroUsize>,
586        /// Enable support for WASI config imports (experimental)
587        pub config: Option<bool>,
588        /// Enable support for WASI key-value imports (experimental)
589        pub keyvalue: Option<bool>,
590        /// Grant access to the given TCP listen socket (experimental, legacy
591        /// wasip1 implementation only)
592        #[serde(default)]
593        pub tcplisten: Vec<String>,
594        /// Enable support for WASI TLS (Transport Layer Security) imports (experimental)
595        pub tls: Option<bool>,
596        /// Implement WASI Preview1 using new Preview2 implementation (true, default) or legacy
597        /// implementation (false)
598        pub preview2: Option<bool>,
599        /// Pre-load machine learning graphs (i.e., models) for use by wasi-nn.
600        ///
601        /// Each use of the flag will preload a ML model from the host directory
602        /// using the given model encoding. The model will be mapped to the
603        /// directory name: e.g., `--wasi-nn-graph openvino:/foo/bar` will preload
604        /// an OpenVINO model named `bar`. Note that which model encodings are
605        /// available is dependent on the backends implemented in the
606        /// `wasmtime_wasi_nn` crate.
607        #[serde(skip)]
608        pub nn_graph: Vec<WasiNnGraph>,
609        /// Flag for WASI preview2 to inherit the host's network within the
610        /// guest so it has full access to all addresses/ports/etc.
611        pub inherit_network: Option<bool>,
612        /// Indicates whether `wasi:sockets/ip-name-lookup` is enabled or not.
613        pub allow_ip_name_lookup: Option<bool>,
614        /// Indicates whether `wasi:sockets` TCP support is enabled or not.
615        pub tcp: Option<bool>,
616        /// Indicates whether `wasi:sockets` UDP support is enabled or not.
617        pub udp: Option<bool>,
618        /// Enable WASI APIs marked as: @unstable(feature = network-error-code)
619        pub network_error_code: Option<bool>,
620        /// Allows imports from the `wasi_unstable` core wasm module. Disabled by default.
621        pub preview0: Option<bool>,
622        /// Inherit all environment variables from the parent process.
623        ///
624        /// This option can be further overwritten with `--env` flags.
625        pub inherit_env: Option<bool>,
626        /// Inherit stdin from the parent process. On by default.
627        pub inherit_stdin: Option<bool>,
628        /// Inherit stdout from the parent process. On by default.
629        pub inherit_stdout: Option<bool>,
630        /// Inherit stderr from the parent process. On by default.
631        pub inherit_stderr: Option<bool>,
632        /// Initial current working directory reported through `wasi:cli/environment`.
633        pub cwd: Option<String>,
634        /// Pass a wasi config variable to the program.
635        #[serde(skip)]
636        pub config_var: Vec<KeyValuePair>,
637        /// Preset data for the In-Memory provider of WASI key-value API.
638        #[serde(skip)]
639        pub keyvalue_in_memory_data: Vec<KeyValuePair>,
640        /// Enable support for WASIp3 APIs.
641        pub p3: Option<bool>,
642        /// Maximum resources the guest is allowed to create simultaneously.
643        pub max_resources: Option<usize>,
644        /// Fuel to use for all hostcalls to limit guest<->host data transfer.
645        pub hostcall_fuel: Option<usize>,
646        /// Maximum size, in bytes, of a single WASI filesystem or socket read
647        /// or write, which bounds the host buffer allocated per I/O operation.
648        /// (default: 2MiB)
649        pub max_io_size: Option<NonZeroUsize>,
650        /// Maximum number of component-model handle-table slots (e.g. for
651        /// resources, streams, futures) per store; allocating past it traps.
652        /// (default: 100000)
653        pub max_component_handles: Option<usize>,
654        /// Maximum number of simultaneously-live fibers per store.
655        pub max_fibers: Option<u32>,
656        /// Maximum value, in bytes, for a wasi-random 0.2
657        /// `get{,-insecure}-random-bytes` `len` parameter. Calls with a value
658        /// exceeding this limit will trap.
659        pub max_random_size: Option<u64>,
660        /// Maximum value, in bytes, for the contents of a wasi-http 0.2
661        /// `fields` resource (aka `headers` and `trailers`). `fields` methods
662        /// which cause the contents to exceed this size limit will trap.
663        pub max_http_fields_size: Option<usize>,
664    }
665
666    enum Wasi {
667        ...
668    }
669}
670
671wasmtime_option_group! {
672    #[env = "RECORD"]
673    pub struct RecordOptions {
674        /// Filename for the recorded execution trace (or empty string to skip writing a file).
675        pub path: Option<String>,
676        /// Include (optional) signatures to facilitate validation checks during replay
677        /// (see `wasmtime replay` for details).
678        pub validation_metadata: Option<bool>,
679        /// Window size of internal buffering for record events (large windows offer more opportunities
680        /// for coalescing events at the cost of memory usage).
681        pub event_window_size: Option<usize>,
682    }
683
684    enum Record {
685        ...
686    }
687}
688
689#[derive(Debug, Clone, PartialEq)]
690pub struct WasiNnGraph {
691    pub format: String,
692    pub dir: String,
693}
694
695#[derive(Debug, Clone, PartialEq)]
696pub struct KeyValuePair {
697    pub key: String,
698    pub value: String,
699}
700
701/// The instance import name under which Wasmtime's unsafe intrinsics are
702/// exposed to the Wasm being compiled.
703#[derive(Debug, Clone, PartialEq)]
704pub struct UnsafeIntrinsicsImport(pub String);
705
706/// A compile-time builtin.
707#[derive(Debug, Clone, PartialEq)]
708pub struct CompileTimeBuiltin {
709    /// The instance import name that this satisfies in the main component.
710    pub name: String,
711    /// The path to the component that implements this builtin.
712    pub path: PathBuf,
713}
714
715/// Common options for commands that translate WebAssembly modules
716#[derive(Clone)]
717#[cfg_attr(feature = "clap", derive(clap::Parser))]
718#[cfg_attr(
719    feature = "serde",
720    derive(serde_derive::Deserialize, serde_derive::Serialize)
721)]
722#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
723pub struct CommonOptions {
724    // These options groups are used to parse `-O` and such options but aren't
725    // the raw form consumed by the CLI. Instead they're pushed into the `pub`
726    // fields below as part of the `configure` method.
727    //
728    // Ideally clap would support `pub opts: OptimizeOptions` and parse directly
729    // into that but it does not appear to do so for multiple `-O` flags for
730    // now.
731    /// Optimization and tuning related options for wasm performance, `-O help` to
732    /// see all.
733    #[cfg_attr(
734        feature = "clap",
735        arg(short = 'O', long = "optimize", value_name = "KEY[=VAL[,..]]")
736    )]
737    #[cfg_attr(feature = "serde", serde(skip))]
738    opts_raw: Vec<opt::CommaSeparated<Optimize>>,
739
740    /// Codegen-related configuration options, `-C help` to see all.
741    #[cfg_attr(
742        feature = "clap",
743        arg(short = 'C', long = "codegen", value_name = "KEY[=VAL[,..]]")
744    )]
745    #[cfg_attr(feature = "serde", serde(skip))]
746    codegen_raw: Vec<opt::CommaSeparated<Codegen>>,
747
748    /// Debug-related configuration options, `-D help` to see all.
749    #[cfg_attr(
750        feature = "clap",
751        arg(short = 'D', long = "debug", value_name = "KEY[=VAL[,..]]")
752    )]
753    #[cfg_attr(feature = "serde", serde(skip))]
754    debug_raw: Vec<opt::CommaSeparated<Debug>>,
755
756    /// Options for configuring semantic execution of WebAssembly, `-W help` to see
757    /// all.
758    #[cfg_attr(
759        feature = "clap",
760        arg(short = 'W', long = "wasm", value_name = "KEY[=VAL[,..]]")
761    )]
762    #[cfg_attr(feature = "serde", serde(skip))]
763    wasm_raw: Vec<opt::CommaSeparated<Wasm>>,
764
765    /// Options for configuring WASI and its proposals, `-S help` to see all.
766    #[cfg_attr(
767        feature = "clap",
768        arg(short = 'S', long = "wasi", value_name = "KEY[=VAL[,..]]")
769    )]
770    #[cfg_attr(feature = "serde", serde(skip))]
771    wasi_raw: Vec<opt::CommaSeparated<Wasi>>,
772
773    /// Options to enable and configure execution recording, `-R help` to see all.
774    ///
775    /// Generates a serialized trace of the Wasm module execution that captures all
776    /// non-determinism observable by the module. This trace can subsequently be
777    /// re-executed in a deterministic, embedding-agnostic manner (see the `wasmtime replay` command).
778    ///
779    /// Note: Minimal configuration options for deterministic Wasm semantics will be
780    /// enforced during recording by default (NaN canonicalization, deterministic relaxed SIMD).
781    #[cfg_attr(
782        feature = "clap",
783        arg(short = 'R', long = "record", value_name = "KEY[=VAL[,..]]")
784    )]
785    #[cfg_attr(feature = "serde", serde(skip))]
786    record_raw: Vec<opt::CommaSeparated<Record>>,
787
788    // These fields are filled in by the `configure` method below via the
789    // options parsed from the CLI above. This is what the CLI should use.
790    #[cfg_attr(feature = "clap", arg(skip))]
791    #[cfg_attr(feature = "serde", serde(skip))]
792    configured: bool,
793
794    /// Whether to ignore `WASMTIME_*` environment variables, which are
795    /// otherwise read as options in addition to those specified here.
796    #[cfg_attr(feature = "clap", arg(skip))]
797    #[cfg_attr(feature = "serde", serde(skip))]
798    pub ignore_env: bool,
799
800    #[cfg_attr(feature = "clap", arg(skip))]
801    #[cfg_attr(feature = "serde", serde(rename = "optimize", default))]
802    pub opts: OptimizeOptions,
803
804    #[cfg_attr(feature = "clap", arg(skip))]
805    #[cfg_attr(feature = "serde", serde(default))]
806    pub codegen: CodegenOptions,
807
808    #[cfg_attr(feature = "clap", arg(skip))]
809    #[cfg_attr(feature = "serde", serde(default))]
810    pub debug: DebugOptions,
811
812    #[cfg_attr(feature = "clap", arg(skip))]
813    #[cfg_attr(feature = "serde", serde(default))]
814    pub wasm: WasmOptions,
815
816    #[cfg_attr(feature = "clap", arg(skip))]
817    #[cfg_attr(feature = "serde", serde(default))]
818    pub wasi: WasiOptions,
819
820    #[cfg_attr(feature = "clap", arg(skip))]
821    #[cfg_attr(feature = "serde", serde(default))]
822    pub record: RecordOptions,
823
824    /// The target triple; default is the host triple
825    #[cfg_attr(feature = "clap", arg(long, value_name = "TARGET"))]
826    #[cfg_attr(feature = "serde", serde(skip))]
827    pub target: Option<String>,
828
829    /// Use the specified TOML configuration file.
830    /// This TOML configuration file can provide same configuration options as the
831    /// `--optimize`, `--codegen`, `--debug`, `--wasm`, `--wasi` CLI options, with a couple exceptions.
832    ///
833    /// Additional options specified on the command line will take precedent over options loaded from
834    /// this TOML file.
835    #[cfg_attr(feature = "clap", arg(long = "config", value_name = "FILE"))]
836    #[cfg_attr(feature = "serde", serde(skip))]
837    pub config: Option<PathBuf>,
838}
839
840type F = WasmFeatures;
841
842/// The options in [`WasmOptions`] that enable or disable a [`WasmFeatures`]
843/// flag.
844///
845/// Each entry is the flag, the option, and the option's name if it's not the
846/// flag's name in lowercase with dashes (`CM_ASYNC` is `cm-async`).
847static WASM_FEATURE_OPTIONS: &[(WasmFeatures, fn(&WasmOptions) -> Option<bool>, Option<&str>)] = &[
848    (F::SIMD, |w| w.simd, None),
849    (F::RELAXED_SIMD, |w| w.relaxed_simd, None),
850    (F::BULK_MEMORY, |w| w.bulk_memory, None),
851    (F::MULTI_VALUE, |w| w.multi_value, None),
852    (F::TAIL_CALL, |w| w.tail_call, None),
853    (F::MULTI_MEMORY, |w| w.multi_memory, None),
854    (F::MEMORY64, |w| w.memory64, None),
855    (F::CUSTOM_PAGE_SIZES, |w| w.custom_page_sizes, None),
856    (F::WIDE_ARITHMETIC, |w| w.wide_arithmetic, None),
857    (F::COMPACT_IMPORTS, |w| w.compact_imports, None),
858    (F::EXTENDED_CONST, |w| w.extended_const, None),
859    (F::THREADS, |w| w.threads, None),
860    (F::GC, |w| w.gc, None),
861    (F::GC_TYPES, |w| w.gc_support, Some("gc-support")),
862    (F::REFERENCE_TYPES, |w| w.reference_types, None),
863    (F::FUNCTION_REFERENCES, |w| w.function_references, None),
864    (F::EXCEPTIONS, |w| w.exceptions, None),
865    (F::LEGACY_EXCEPTIONS, |w| w.legacy_exceptions, None),
866    (F::STACK_SWITCHING, |w| w.stack_switching, None),
867    (F::COMPONENT_MODEL, |w| w.component_model, None),
868    (
869        F::CM_ASYNC,
870        |w| w.component_model_async,
871        Some("component-model-async"),
872    ),
873    (
874        F::CM_MORE_ASYNC_BUILTINS,
875        |w| w.component_model_more_async_builtins,
876        Some("component-model-more-async-builtins"),
877    ),
878    (
879        F::CM_ASYNC_STACKFUL,
880        |w| w.component_model_async_stackful,
881        Some("component-model-async-stackful"),
882    ),
883    (
884        F::CM_THREADING,
885        |w| w.component_model_threading,
886        Some("component-model-threading"),
887    ),
888    (
889        F::CM_ERROR_CONTEXT,
890        |w| w.component_model_error_context,
891        Some("component-model-error-context"),
892    ),
893    (
894        F::CM_MAP,
895        |w| w.component_model_map,
896        Some("component-model-map"),
897    ),
898    (
899        F::CM_FIXED_LENGTH_LISTS,
900        |w| w.component_model_fixed_length_lists,
901        Some("component-model-fixed-length-lists"),
902    ),
903    (
904        F::CM_IMPLEMENTS,
905        |w| w.component_model_implements,
906        Some("component-model-implements"),
907    ),
908    (
909        F::CM_CANON_NAMES,
910        |w| w.component_model_canonical_names,
911        Some("component-model-canonical-names"),
912    ),
913    (
914        F::CM_ACCESSORS,
915        |w| w.component_model_accessors,
916        Some("component-model-accessors"),
917    ),
918    (
919        F::CM64,
920        |w| w.component_model_memory64,
921        Some("component-model-memory64"),
922    ),
923    (
924        F::CM_GC,
925        |w| w.component_model_gc,
926        Some("component-model-gc"),
927    ),
928];
929
930macro_rules! match_feature {
931    (
932        [$feat:tt : $config:expr]
933        $val:ident => $e:expr,
934        $p:pat => err,
935    ) => {
936        #[cfg(feature = $feat)]
937        {
938            if let Some($val) = $config {
939                $e;
940            }
941        }
942        #[cfg(not(feature = $feat))]
943        {
944            if let Some($p) = $config {
945                bail!(concat!("support for ", $feat, " disabled at compile time"));
946            }
947        }
948    };
949}
950
951impl CommonOptions {
952    /// Creates a blank new set of [`CommonOptions`] that can be configured.
953    pub fn new() -> CommonOptions {
954        CommonOptions {
955            opts_raw: Vec::new(),
956            codegen_raw: Vec::new(),
957            debug_raw: Vec::new(),
958            wasm_raw: Vec::new(),
959            wasi_raw: Vec::new(),
960            record_raw: Vec::new(),
961            configured: true,
962            ignore_env: false,
963            opts: Default::default(),
964            codegen: Default::default(),
965            debug: Default::default(),
966            wasm: Default::default(),
967            wasi: Default::default(),
968            record: Default::default(),
969            target: None,
970            config: None,
971        }
972    }
973
974    fn configure(&mut self) -> Result<()> {
975        if self.configured {
976            return Ok(());
977        }
978        self.configured = true;
979        if let Some(toml_config_path) = &self.config {
980            #[cfg(feature = "toml")]
981            {
982                let toml_options = CommonOptions::from_file(toml_config_path)?;
983                self.opts = toml_options.opts;
984                self.codegen = toml_options.codegen;
985                self.debug = toml_options.debug;
986                self.wasm = toml_options.wasm;
987                self.wasi = toml_options.wasi;
988                self.record = toml_options.record;
989            }
990            #[cfg(not(feature = "toml"))]
991            {
992                bail!(
993                    "support for loading a configuration file from \
994                     {toml_config_path:?} disabled at compile time"
995                );
996            }
997        }
998        self.opts.configure_with(&self.opts_raw, !self.ignore_env)?;
999        self.codegen
1000            .configure_with(&self.codegen_raw, !self.ignore_env)?;
1001        self.debug
1002            .configure_with(&self.debug_raw, !self.ignore_env)?;
1003        self.wasm.configure_with(&self.wasm_raw, !self.ignore_env)?;
1004        self.wasi.configure_with(&self.wasi_raw, !self.ignore_env)?;
1005        self.record
1006            .configure_with(&self.record_raw, !self.ignore_env)?;
1007        Ok(())
1008    }
1009
1010    /// Layers `other` on top of `self`.
1011    ///
1012    /// Each option that `other` specifies overrides the one in `self`, and
1013    /// options that `other` doesn't specify are left as they are in `self`.
1014    pub fn merge(&mut self, other: &mut CommonOptions) -> Result<()> {
1015        self.configure()?;
1016        other.configure()?;
1017        let CommonOptions {
1018            // Processed into the fields below by `configure`.
1019            opts_raw: _,
1020            codegen_raw: _,
1021            debug_raw: _,
1022            wasm_raw: _,
1023            wasi_raw: _,
1024            record_raw: _,
1025            configured: _,
1026            ignore_env: _,
1027            config: _,
1028
1029            opts,
1030            codegen,
1031            debug,
1032            wasm,
1033            wasi,
1034            record,
1035            target,
1036        } = other;
1037        for opt in opts.to_options() {
1038            self.opts.configure(&opt);
1039        }
1040        for opt in codegen.to_options() {
1041            self.codegen.configure(&opt);
1042        }
1043        for opt in debug.to_options() {
1044            self.debug.configure(&opt);
1045        }
1046        for opt in wasm.to_options() {
1047            self.wasm.configure(&opt);
1048        }
1049        for opt in wasi.to_options() {
1050            self.wasi.configure(&opt);
1051        }
1052        for opt in record.to_options() {
1053            self.record.configure(&opt);
1054        }
1055        if target.is_some() {
1056            self.target = target.clone();
1057        }
1058        Ok(())
1059    }
1060
1061    pub fn init_logging(&mut self) -> Result<()> {
1062        self.configure()?;
1063        if self.debug.logging == Some(false) {
1064            return Ok(());
1065        }
1066        #[cfg(feature = "logging")]
1067        if self.debug.log_to_files == Some(true) {
1068            let prefix = "wasmtime.dbg.";
1069            init_file_per_thread_logger(prefix);
1070        } else {
1071            use std::io::IsTerminal;
1072            use tracing_subscriber::{EnvFilter, FmtSubscriber};
1073            let builder = FmtSubscriber::builder()
1074                .with_writer(std::io::stderr)
1075                .with_env_filter(EnvFilter::from_env("WASMTIME_LOG"))
1076                .with_ansi(std::io::stderr().is_terminal());
1077            if std::env::var("WASMTIME_LOG_NO_CONTEXT").is_ok_and(|value| value.eq("1")) {
1078                builder
1079                    .with_level(false)
1080                    .with_target(false)
1081                    .without_time()
1082                    .init()
1083            } else {
1084                builder.init();
1085            }
1086        }
1087        #[cfg(not(feature = "logging"))]
1088        if self.debug.log_to_files == Some(true) || self.debug.logging == Some(true) {
1089            bail!("support for logging disabled at compile time");
1090        }
1091        Ok(())
1092    }
1093
1094    /// Returns a fresh new [`Config`] which reflects all configuration settings
1095    /// in `self`.
1096    pub fn config(&mut self, pooling_allocator_default: Option<bool>) -> Result<Config> {
1097        let mut config = Config::new();
1098        self.apply_config(&mut config, pooling_allocator_default)?;
1099        Ok(config)
1100    }
1101
1102    /// Applies the options in `self` to an existing `config`.
1103    pub fn apply_config(
1104        &mut self,
1105        config: &mut Config,
1106        pooling_allocator_default: Option<bool>,
1107    ) -> Result<()> {
1108        self.configure()?;
1109
1110        match_feature! {
1111            ["cranelift" : self.codegen.compiler]
1112            strategy => config.strategy(strategy),
1113            _ => err,
1114        }
1115        match_feature! {
1116            ["gc" : self.codegen.collector]
1117            collector => config.collector(collector),
1118            _ => err,
1119        }
1120        if let Some(target) = &self.target {
1121            config.target(target)?;
1122        }
1123        match_feature! {
1124            ["cranelift" : self.codegen.cranelift_debug_verifier]
1125            enable => config.cranelift_debug_verifier(enable),
1126            true => err,
1127        }
1128        if let Some(enable) = self.debug.debug_info {
1129            config.debug_info(enable);
1130        }
1131        match_feature! {
1132            ["debug" : self.debug.guest_debug]
1133            enable => config.guest_debug(enable),
1134            _ => err,
1135        }
1136        if self.debug.coredump.is_some() {
1137            #[cfg(feature = "coredump")]
1138            config.coredump_on_trap(true);
1139            #[cfg(not(feature = "coredump"))]
1140            bail!("support for coredumps disabled at compile time");
1141        }
1142        match_feature! {
1143            ["cranelift" : self.opts.opt_level]
1144            level => config.cranelift_opt_level(level),
1145            _ => err,
1146        }
1147        match_feature! {
1148            ["cranelift": self.opts.regalloc_algorithm]
1149            algo => config.cranelift_regalloc_algorithm(algo),
1150            _ => err,
1151        }
1152        match_feature! {
1153            ["cranelift" : self.wasm.nan_canonicalization]
1154            enable => config.cranelift_nan_canonicalization(enable),
1155            true => err,
1156        }
1157
1158        self.enable_wasm_features(config)?;
1159
1160        #[cfg(feature = "cranelift")]
1161        for (name, value) in self.codegen.cranelift.iter() {
1162            let name = name.replace('-', "_");
1163            unsafe {
1164                match value {
1165                    Some(val) => {
1166                        config.cranelift_flag_set(&name, val);
1167                    }
1168                    None => {
1169                        config.cranelift_flag_enable(&name);
1170                    }
1171                }
1172            }
1173        }
1174        #[cfg(not(feature = "cranelift"))]
1175        if !self.codegen.cranelift.is_empty() {
1176            bail!("support for cranelift disabled at compile time");
1177        }
1178
1179        #[cfg(feature = "cache")]
1180        if self.codegen.cache != Some(false) {
1181            use wasmtime::Cache;
1182            let cache = match &self.codegen.cache_config {
1183                Some(path) => Cache::from_file(Some(std::path::Path::new(path)))?,
1184                None => Cache::from_file(None)?,
1185            };
1186            config.cache(Some(cache));
1187        }
1188        #[cfg(not(feature = "cache"))]
1189        if self.codegen.cache == Some(true) {
1190            bail!("support for caching disabled at compile time");
1191        }
1192
1193        match_feature! {
1194            ["parallel-compilation" : self.codegen.parallel_compilation]
1195            enable => config.parallel_compilation(enable),
1196            true => err,
1197        }
1198
1199        let memory_reservation = self
1200            .opts
1201            .memory_reservation
1202            .or(self.opts.static_memory_maximum_size);
1203        if let Some(size) = memory_reservation {
1204            config.memory_reservation(size);
1205        }
1206
1207        if let Some(enable) = self.opts.static_memory_forced {
1208            config.memory_may_move(!enable);
1209        }
1210        if let Some(enable) = self.opts.memory_may_move {
1211            config.memory_may_move(enable);
1212        }
1213
1214        let memory_guard_size = self
1215            .opts
1216            .static_memory_guard_size
1217            .or(self.opts.dynamic_memory_guard_size)
1218            .or(self.opts.memory_guard_size);
1219        if let Some(size) = memory_guard_size {
1220            config.memory_guard_size(size);
1221        }
1222
1223        let mem_for_growth = self
1224            .opts
1225            .memory_reservation_for_growth
1226            .or(self.opts.dynamic_memory_reserved_for_growth);
1227        if let Some(size) = mem_for_growth {
1228            config.memory_reservation_for_growth(size);
1229        }
1230        if let Some(enable) = self.opts.guard_before_linear_memory {
1231            config.guard_before_linear_memory(enable);
1232        }
1233
1234        if let Some(size) = self.opts.gc_heap_reservation {
1235            config.gc_heap_reservation(size);
1236        }
1237        if let Some(enable) = self.opts.gc_heap_may_move {
1238            config.gc_heap_may_move(enable);
1239        }
1240        if let Some(size) = self.opts.gc_heap_guard_size {
1241            config.gc_heap_guard_size(size);
1242        }
1243        if let Some(size) = self.opts.gc_heap_reservation_for_growth {
1244            config.gc_heap_reservation_for_growth(size);
1245        }
1246
1247        if let Some(size) = self.opts.gc_heap_initial_size {
1248            config.gc_heap_initial_size(size);
1249        }
1250
1251        if let Some(enable) = self.opts.table_lazy_init {
1252            config.table_lazy_init(enable);
1253        }
1254
1255        if let Some(n) = self.opts.gc_zeal_alloc_counter
1256            && (cfg!(gc_zeal) || cfg!(fuzzing))
1257        {
1258            config.gc_zeal_alloc_counter(Some(n))?;
1259        }
1260
1261        // If fuel has been configured, set the `consume fuel` flag on the config.
1262        if self.wasm.fuel.is_some() {
1263            config.consume_fuel(true);
1264        }
1265
1266        if let Some(enable) = self.wasm.epoch_interruption {
1267            config.epoch_interruption(enable);
1268        }
1269        if let Some(enable) = self.debug.address_map {
1270            config.generate_address_map(enable);
1271        }
1272        if let Some(frames) = self.debug.max_backtrace {
1273            match NonZeroUsize::new(frames) {
1274                None => {
1275                    config.wasm_backtrace_details(WasmBacktraceDetails::Disable);
1276                }
1277                Some(amt) => {
1278                    config.wasm_backtrace_max_frames(Some(amt));
1279                }
1280            }
1281        }
1282        if let Some(enable) = self.debug.symbols {
1283            config.debug_symbols(enable);
1284        }
1285        if let Some(enable) = self.opts.memory_init_cow {
1286            config.memory_init_cow(enable);
1287        }
1288        if let Some(size) = self.opts.memory_guaranteed_dense_image_size {
1289            config.memory_guaranteed_dense_image_size(size);
1290        }
1291        if let Some(enable) = self.opts.signals_based_traps {
1292            config.signals_based_traps(enable);
1293        }
1294        if let Some(enable) = self.codegen.native_unwind_info {
1295            config.native_unwind_info(enable);
1296        }
1297        if let Some(enable) = self.codegen.asan_compatible_code {
1298            config.asan_compatible_code(enable);
1299        }
1300        if let Some(enable) = self.codegen.inlining {
1301            config.compiler_inlining(enable);
1302        }
1303        if let Some(enable) = self.codegen.metadata_for_internal_asserts {
1304            config.metadata_for_internal_asserts(enable);
1305        }
1306        if let Some(enable) = self.codegen.metadata_for_gc_heap_corruption {
1307            config.metadata_for_gc_heap_corruption(enable);
1308        }
1309
1310        // async_stack_size enabled by either async or stack-switching, so
1311        // cannot directly use match_feature!
1312        #[cfg(any(feature = "async", feature = "stack-switching"))]
1313        {
1314            if let Some(size) = self.wasm.async_stack_size {
1315                config.async_stack_size(size);
1316            }
1317        }
1318        #[cfg(not(any(feature = "async", feature = "stack-switching")))]
1319        {
1320            if let Some(_size) = self.wasm.async_stack_size {
1321                bail!(concat!(
1322                    "support for async/stack-switching disabled at compile time"
1323                ));
1324            }
1325        }
1326
1327        match_feature! {
1328            ["pooling-allocator" : self.opts.pooling_allocator.or(pooling_allocator_default)]
1329            enable => {
1330                if enable {
1331                    let mut cfg = wasmtime::PoolingAllocationConfig::default();
1332                    if let Some(size) = self.opts.pooling_memory_keep_resident {
1333                        cfg.linear_memory_keep_resident(size);
1334                    }
1335                    if let Some(size) = self.opts.pooling_table_keep_resident {
1336                        cfg.table_keep_resident(size);
1337                    }
1338                    if let Some(limit) = self.opts.pooling_total_core_instances {
1339                        cfg.total_core_instances(limit);
1340                    }
1341                    if let Some(limit) = self.opts.pooling_total_component_instances {
1342                        cfg.total_component_instances(limit);
1343                    }
1344                    if let Some(limit) = self.opts.pooling_total_memories {
1345                        cfg.total_memories(limit);
1346                    }
1347                    if let Some(limit) = self.opts.pooling_total_tables {
1348                        cfg.total_tables(limit);
1349                    }
1350                    if let Some(limit) = self.opts.pooling_table_elements
1351                        .or(self.wasm.max_table_elements)
1352                    {
1353                        cfg.table_elements(limit);
1354                    }
1355                    if let Some(limit) = self.opts.pooling_max_core_instance_size {
1356                        cfg.max_core_instance_size(limit);
1357                    }
1358                    match_feature! {
1359                        ["async" : self.opts.pooling_total_stacks]
1360                        limit => cfg.total_stacks(limit),
1361                        _ => err,
1362                    }
1363                    if let Some(max) = self.opts.pooling_max_memory_size
1364                        .or(self.wasm.max_memory_size)
1365                    {
1366                        cfg.max_memory_size(max);
1367                    }
1368                    if let Some(size) = self.opts.pooling_decommit_batch_size {
1369                        cfg.decommit_batch_size(size);
1370                    }
1371                    if let Some(max) = self.opts.pooling_max_unused_warm_slots {
1372                        cfg.max_unused_warm_slots(max);
1373                    }
1374                    match_feature! {
1375                        ["async" : self.opts.pooling_async_stack_keep_resident]
1376                        size => cfg.async_stack_keep_resident(size),
1377                        _ => err,
1378                    }
1379                    if let Some(max) = self.opts.pooling_max_component_instance_size {
1380                        cfg.max_component_instance_size(max);
1381                    }
1382                    if let Some(max) = self.opts.pooling_max_core_instances_per_component {
1383                        cfg.max_core_instances_per_component(max);
1384                    }
1385                    if let Some(max) = self.opts.pooling_max_memories_per_component {
1386                        cfg.max_memories_per_component(max);
1387                    }
1388                    if let Some(max) = self.opts.pooling_max_tables_per_component {
1389                        cfg.max_tables_per_component(max);
1390                    }
1391                    if let Some(max) = self.opts.pooling_max_tables_per_module {
1392                        cfg.max_tables_per_module(max);
1393                    }
1394                    if let Some(max) = self.opts.pooling_max_memories_per_module {
1395                        cfg.max_memories_per_module(max);
1396                    }
1397                    match_feature! {
1398                        ["memory-protection-keys" : self.opts.pooling_memory_protection_keys]
1399                        enable => cfg.memory_protection_keys(enable),
1400                        _ => err,
1401                    }
1402                    match_feature! {
1403                        ["memory-protection-keys" : self.opts.pooling_max_memory_protection_keys]
1404                        max => cfg.max_memory_protection_keys(max),
1405                        _ => err,
1406                    }
1407                    match_feature! {
1408                        ["gc" : self.opts.pooling_total_gc_heaps]
1409                        max => cfg.total_gc_heaps(max),
1410                        _ => err,
1411                    }
1412                    if let Some(enabled) = self.opts.pooling_pagemap_scan {
1413                        cfg.pagemap_scan(enabled);
1414                    }
1415                    config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling(cfg));
1416                }
1417            },
1418            true => err,
1419        }
1420
1421        if self.opts.pooling_memory_protection_keys.is_some()
1422            && !self.opts.pooling_allocator.unwrap_or(false)
1423        {
1424            bail!("memory protection keys require the pooling allocator");
1425        }
1426
1427        if self.opts.pooling_max_memory_protection_keys.is_some()
1428            && !self.opts.pooling_memory_protection_keys.is_some()
1429        {
1430            bail!("max memory protection keys requires memory protection keys to be enabled");
1431        }
1432
1433        match_feature! {
1434            ["async" : self.wasm.async_stack_zeroing]
1435            enable => config.async_stack_zeroing(enable),
1436            _ => err,
1437        }
1438
1439        if let Some(max) = self.wasm.max_wasm_stack {
1440            config.max_wasm_stack(max);
1441
1442            // If `-Wasync-stack-size` isn't passed then automatically adjust it
1443            // to the wasm stack size provided here too. That prevents the need
1444            // to pass both when one can generally be inferred from the other.
1445            #[cfg(any(feature = "async", feature = "stack-switching"))]
1446            if self.wasm.async_stack_size.is_none() {
1447                const DEFAULT_HOST_STACK: usize = 512 << 10;
1448                config.async_stack_size(max.saturating_add(DEFAULT_HOST_STACK));
1449            }
1450        }
1451
1452        if let Some(enable) = self.wasm.relaxed_simd_deterministic {
1453            config.relaxed_simd_deterministic(enable);
1454        }
1455        match_feature! {
1456            ["cranelift" : self.wasm.wmemcheck]
1457            enable => config.wmemcheck(enable),
1458            true => err,
1459        }
1460
1461        if let Some(enable) = self.wasm.concurrency_support {
1462            config.concurrency_support(enable);
1463        }
1464
1465        if let Some(enable) = self.wasm.shared_memory {
1466            config.shared_memory(enable);
1467        }
1468
1469        let record = &self.record;
1470        match_feature! {
1471            ["rr" : &record.path]
1472            _path => {
1473                bail!("recording configuration for `rr` feature is not supported yet");
1474            },
1475            _ => err,
1476        }
1477
1478        Ok(())
1479    }
1480
1481    /// Applies the per-`Store` options in `self` to `store`.
1482    ///
1483    /// Note that [`CommonOptions::config`] or [`CommonOptions::apply_config`]
1484    /// should be called before this to ensure options are processed internally
1485    /// correctly.
1486    #[cfg(feature = "runtime")]
1487    pub fn configure_store<T>(&self, store: &mut wasmtime::Store<T>) -> Result<()> {
1488        debug_assert!(self.configured, "`config` or `apply_config` not called");
1489        #[cfg(feature = "component-model")]
1490        {
1491            if let Some(fuel) = self.wasi.hostcall_fuel {
1492                store.set_hostcall_fuel(fuel);
1493            }
1494            if let Some(max) = self.wasi.max_component_handles {
1495                store.set_max_component_handles(max);
1496            }
1497        }
1498        #[cfg(feature = "async")]
1499        if let Some(max) = self.wasi.max_fibers {
1500            store.set_max_fibers(max);
1501        }
1502        if let Some(fuel) = self.wasm.fuel {
1503            store.set_fuel(fuel)?;
1504        }
1505        Ok(())
1506    }
1507
1508    fn enable_wasm_features(&self, config: &mut Config) -> Result<()> {
1509        let all = self.wasm.all_proposals;
1510        for (feature, option, _) in WASM_FEATURE_OPTIONS {
1511            let mut value = option(&self.wasm);
1512            // Exempt legacy exceptions from `-Wall-proposals`.
1513            if *feature != F::LEGACY_EXCEPTIONS {
1514                value = value.or(all);
1515            }
1516            if let Some(enable) = value {
1517                config.wasm_features(*feature, enable);
1518            }
1519        }
1520
1521        // Branch hinting is a setting of the compiler rather than a
1522        // `WasmFeatures` flag.
1523        if let Some(enable) = self.wasm.branch_hinting.or(all) {
1524            config.wasm_branch_hinting(enable);
1525        }
1526
1527        Ok(())
1528    }
1529
1530    /// Returns the names of the specified options that enable or disable a
1531    /// WebAssembly feature, such as `simd` for `-W simd=y`.
1532    ///
1533    /// This includes `all-proposals` for `-W all-proposals`.
1534    pub fn wasm_features_specified(&self) -> Vec<String> {
1535        let mut ret = Vec::new();
1536        for (feature, option, name) in WASM_FEATURE_OPTIONS {
1537            if option(&self.wasm).is_none() {
1538                continue;
1539            }
1540            ret.push(match name {
1541                Some(name) => name.to_string(),
1542                None => {
1543                    let (name, _) = feature.iter_names().next().unwrap();
1544                    name.to_lowercase().replace('_', "-")
1545                }
1546            });
1547        }
1548        if self.wasm.all_proposals.is_some() {
1549            ret.push("all-proposals".to_string());
1550        }
1551        ret
1552    }
1553
1554    #[cfg(feature = "toml")]
1555    pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
1556        use wasmtime::error::Context;
1557
1558        let path_ref = path.as_ref();
1559        let file_contents = std::fs::read_to_string(path_ref)
1560            .with_context(|| format!("failed to read config file: {path_ref:?}"))?;
1561        toml::from_str::<CommonOptions>(&file_contents)
1562            .with_context(|| format!("failed to parse TOML config file {path_ref:?}"))
1563    }
1564
1565    /// Creates an instance of [`CommonOptions`] by reflecting on the
1566    /// configuration present in the provided [`Engine`].
1567    ///
1568    /// This will extract all configuration values that can be set by this
1569    /// [`CommonOptions`] into a [`Config`] into the returned structure to the
1570    /// best of its ability. Not all configuration options present on [`Engine`]
1571    /// can be reflected back into [`CommonOptions`], and not all options on
1572    /// [`CommonOptions`] can be set on an [`Engine`]. This means that this
1573    /// is a lossy operation that doesn't fully capture 100% of the
1574    /// configuration of [`Engine`]. That being said, however, it can capture
1575    /// almost all of the configuration related to codegen and major other
1576    /// configuration options.
1577    pub fn from_engine(engine: &Engine) -> Self {
1578        let features = engine.get_wasm_features();
1579        let pooling = engine.get_pooling_config();
1580        CommonOptions {
1581            target: engine.get_target(),
1582            opts: OptimizeOptions {
1583                memory_may_move: Some(engine.get_memory_may_move()),
1584                memory_reservation: Some(engine.get_memory_reservation()),
1585                memory_reservation_for_growth: Some(engine.get_memory_reservation_for_growth()),
1586                memory_guard_size: Some(engine.get_memory_guard_size()),
1587                gc_heap_may_move: Some(engine.get_gc_heap_may_move()),
1588                gc_heap_reservation: Some(engine.get_gc_heap_reservation()),
1589                gc_heap_reservation_for_growth: Some(engine.get_gc_heap_reservation_for_growth()),
1590                gc_heap_guard_size: Some(engine.get_gc_heap_guard_size()),
1591                gc_heap_initial_size: Some(engine.get_gc_heap_initial_size()),
1592                guard_before_linear_memory: Some(engine.get_guard_before_linear_memory()),
1593                table_lazy_init: Some(engine.get_table_lazy_init()),
1594                memory_init_cow: Some(engine.get_memory_init_cow()),
1595                memory_guaranteed_dense_image_size: Some(
1596                    engine.get_memory_guaranteed_dense_image_size(),
1597                ),
1598                signals_based_traps: Some(engine.get_signals_based_traps()),
1599                gc_zeal_alloc_counter: engine.get_gc_zeal_alloc_counter(),
1600                opt_level: engine.get_cranelift_opt_level(),
1601                regalloc_algorithm: engine.get_cranelift_regalloc_algorithm(),
1602                pooling_allocator: Some(pooling.is_some()),
1603                pooling_decommit_batch_size: pooling.map(|c| c.get_decommit_batch_size()),
1604                pooling_memory_keep_resident: pooling.map(|c| c.get_memory_keep_resident()),
1605                pooling_table_keep_resident: pooling.map(|c| c.get_table_keep_resident()),
1606                pooling_max_unused_warm_slots: pooling.map(|c| c.get_max_unused_warm_slots()),
1607                pooling_pagemap_scan: pooling.map(|c| c.get_pagemap_scan()),
1608                pooling_total_core_instances: pooling.map(|c| c.get_total_core_instances()),
1609                pooling_total_component_instances: pooling
1610                    .map(|c| c.get_total_component_instances()),
1611                pooling_total_memories: pooling.map(|c| c.get_total_memories()),
1612                pooling_total_tables: pooling.map(|c| c.get_total_tables()),
1613                pooling_max_memory_size: pooling.map(|c| c.get_max_memory_size()),
1614                pooling_table_elements: pooling.map(|c| c.get_table_elements()),
1615                pooling_max_core_instance_size: pooling.map(|c| c.get_max_core_instance_size()),
1616                pooling_max_component_instance_size: pooling
1617                    .map(|c| c.get_max_component_instance_size()),
1618                pooling_max_core_instances_per_component: pooling
1619                    .map(|c| c.get_max_core_instances_per_component()),
1620                pooling_max_memories_per_component: pooling
1621                    .map(|c| c.get_max_memories_per_component()),
1622                pooling_max_tables_per_component: pooling.map(|c| c.get_max_tables_per_component()),
1623                pooling_max_tables_per_module: pooling.map(|c| c.get_max_tables_per_module()),
1624                pooling_max_memories_per_module: pooling.map(|c| c.get_max_memories_per_module()),
1625                pooling_async_stack_keep_resident: pooling
1626                    .map(|c| c.get_async_stack_keep_resident()),
1627                pooling_total_stacks: pooling.map(|c| c.get_total_stacks()),
1628                pooling_total_gc_heaps: pooling.map(|c| c.get_total_gc_heaps()),
1629                pooling_memory_protection_keys: pooling.map(|c| c.get_memory_protection_keys()),
1630                pooling_max_memory_protection_keys: pooling
1631                    .map(|c| c.get_max_memory_protection_keys()),
1632                // Deprecated aliases for the above options; intentionally left
1633                // unset in favor of their replacements.
1634                dynamic_memory_guard_size: None,
1635                static_memory_guard_size: None,
1636                static_memory_forced: None,
1637                static_memory_maximum_size: None,
1638                dynamic_memory_reserved_for_growth: None,
1639            },
1640            codegen: CodegenOptions {
1641                compiler: engine.get_strategy(),
1642                collector: engine.get_collector(),
1643                cranelift_debug_verifier: engine.get_cranelift_debug_verifier(),
1644                inlining: Some(engine.get_compiler_inlining()),
1645                native_unwind_info: engine.get_native_unwind_info(),
1646                asan_compatible_code: Some(engine.get_asan_compatible_code()),
1647                parallel_compilation: Some(engine.get_parallel_compilation()),
1648                metadata_for_internal_asserts: Some(engine.get_metadata_for_internal_asserts()),
1649                metadata_for_gc_heap_corruption: Some(engine.get_metadata_for_gc_heap_corruption()),
1650                cranelift: engine
1651                    .get_cranelift_flags_set()
1652                    .map(|(k, v)| (k.to_string(), Some(v.to_string())))
1653                    .chain(
1654                        engine
1655                            .get_cranelift_flags_enabled()
1656                            .map(|k| (k.to_string(), None)),
1657                    )
1658                    .collect(),
1659
1660                // not easily extractable from `Engine` since that supports
1661                // arbitrary code-defined caches.
1662                cache: None,
1663                cache_config: None,
1664
1665                // These are configured per-`CodeBuilder`, so they cannot be
1666                // recovered here.
1667                unsafe_intrinsics: None,
1668                compile_time_builtin: Vec::new(),
1669            },
1670            debug: DebugOptions {
1671                address_map: Some(engine.get_generate_address_map()),
1672                debug_info: Some(engine.get_debug_info()),
1673                guest_debug: Some(engine.get_guest_debug()),
1674                symbols: Some(engine.get_debug_symbols()),
1675                max_backtrace: Some(engine.get_wasm_backtrace_max_frames()),
1676
1677                // Can't infer a path to emit a core dump to from engine
1678                // configuration.
1679                coredump: None,
1680                // debugger configuration not part of engine config
1681                debugger: None,
1682                arg: Vec::new(),
1683                inherit_stderr: None,
1684                inherit_stdout: None,
1685                inherit_stdin: None,
1686                // logging not part of engine config
1687                log_to_files: None,
1688                logging: None,
1689            },
1690            wasm: WasmOptions {
1691                async_stack_size: Some(engine.get_async_stack_size()),
1692                async_stack_zeroing: Some(engine.get_async_stack_zeroing()),
1693                branch_hinting: Some(engine.get_wasm_branch_hinting()),
1694                compact_imports: Some(features.contains(WasmFeatures::COMPACT_IMPORTS)),
1695                bulk_memory: Some(features.contains(WasmFeatures::BULK_MEMORY)),
1696                component_model: Some(features.contains(WasmFeatures::COMPONENT_MODEL)),
1697                component_model_async: Some(features.contains(WasmFeatures::CM_ASYNC)),
1698                component_model_async_stackful: Some(
1699                    features.contains(WasmFeatures::CM_ASYNC_STACKFUL),
1700                ),
1701                component_model_error_context: Some(
1702                    features.contains(WasmFeatures::CM_ERROR_CONTEXT),
1703                ),
1704                component_model_gc: Some(features.contains(WasmFeatures::CM_GC)),
1705                component_model_fixed_length_lists: Some(
1706                    features.contains(WasmFeatures::CM_FIXED_LENGTH_LISTS),
1707                ),
1708                component_model_implements: Some(features.contains(WasmFeatures::CM_IMPLEMENTS)),
1709                component_model_canonical_names: Some(
1710                    features.contains(WasmFeatures::CM_CANON_NAMES),
1711                ),
1712                component_model_accessors: Some(features.contains(WasmFeatures::CM_ACCESSORS)),
1713                component_model_map: Some(features.contains(WasmFeatures::CM_MAP)),
1714                component_model_memory64: Some(features.contains(WasmFeatures::CM64)),
1715                component_model_more_async_builtins: Some(
1716                    features.contains(WasmFeatures::CM_MORE_ASYNC_BUILTINS),
1717                ),
1718                component_model_threading: Some(features.contains(WasmFeatures::CM_THREADING)),
1719                custom_page_sizes: Some(features.contains(WasmFeatures::CUSTOM_PAGE_SIZES)),
1720                exceptions: Some(features.contains(WasmFeatures::EXCEPTIONS)),
1721                legacy_exceptions: Some(features.contains(WasmFeatures::LEGACY_EXCEPTIONS)),
1722                extended_const: Some(features.contains(WasmFeatures::EXTENDED_CONST)),
1723                function_references: Some(features.contains(WasmFeatures::FUNCTION_REFERENCES)),
1724                gc: Some(features.contains(WasmFeatures::GC)),
1725                gc_support: Some(features.contains(WasmFeatures::GC_TYPES)),
1726                memory64: Some(features.contains(WasmFeatures::MEMORY64)),
1727                multi_memory: Some(features.contains(WasmFeatures::MULTI_MEMORY)),
1728                multi_value: Some(features.contains(WasmFeatures::MULTI_VALUE)),
1729                reference_types: Some(features.contains(WasmFeatures::REFERENCE_TYPES)),
1730                relaxed_simd: Some(features.contains(WasmFeatures::RELAXED_SIMD)),
1731                simd: Some(features.contains(WasmFeatures::SIMD)),
1732                stack_switching: Some(features.contains(WasmFeatures::STACK_SWITCHING)),
1733                tail_call: Some(features.contains(WasmFeatures::TAIL_CALL)),
1734                threads: Some(features.contains(WasmFeatures::THREADS)),
1735                wide_arithmetic: Some(features.contains(WasmFeatures::WIDE_ARITHMETIC)),
1736                concurrency_support: Some(engine.get_concurrency_support()),
1737                epoch_interruption: Some(engine.get_epoch_interruption()),
1738                fuel: if engine.get_consume_fuel() {
1739                    Some(1)
1740                } else {
1741                    None
1742                },
1743                max_wasm_stack: Some(engine.get_max_wasm_stack()),
1744                nan_canonicalization: engine.get_cranelift_nan_canonicalization(),
1745                relaxed_simd_deterministic: Some(engine.get_relaxed_simd_deterministic()),
1746                shared_memory: Some(engine.get_shared_memory()),
1747
1748                // This is covered by individual `WasmFeatures::*` flags above.
1749                all_proposals: None,
1750                // These aren't set in `Config` any more, they're just
1751                // historical
1752                max_instances: None,
1753                max_memories: None,
1754                max_memory_size: None,
1755                max_table_elements: None,
1756                max_tables: None,
1757                // not part of engine config, specific to `run` command
1758                timeout: None,
1759                trap_on_grow_failure: None,
1760                unknown_exports_allow: None,
1761                unknown_imports_default: None,
1762                unknown_imports_trap: None,
1763                wmemcheck: None,
1764            },
1765
1766            // Not currently reflected in `Engine`.
1767            record: RecordOptions::default(),
1768
1769            // WASI options aren't reflected in an `Engine`.
1770            wasi: Default::default(),
1771
1772            // CLI flags are represented above, they have default values here.
1773            configured: true,
1774            ignore_env: false,
1775            codegen_raw: Default::default(),
1776            debug_raw: Default::default(),
1777            opts_raw: Default::default(),
1778            record_raw: Default::default(),
1779            wasi_raw: Default::default(),
1780            wasm_raw: Default::default(),
1781
1782            // No external configuration file, it's all above.
1783            config: None,
1784            //
1785            // Note that an exhaustive listing is explicitly used to avoid
1786            // forgetting to configure a field. Please don't add `..something`
1787            // here at the end.
1788        }
1789    }
1790}
1791
1792#[cfg(test)]
1793mod tests {
1794    use wasmtime::{OptLevel, RegallocAlgorithm};
1795
1796    use super::*;
1797
1798    #[test]
1799    fn from_toml() {
1800        // empty toml
1801        let empty_toml = "";
1802        let mut common_options: CommonOptions = toml::from_str(empty_toml).unwrap();
1803        common_options.config(None).unwrap();
1804
1805        // basic toml
1806        let basic_toml = r#"
1807            [optimize]
1808            [codegen]
1809            [debug]
1810            [wasm]
1811            [wasi]
1812            [record]
1813        "#;
1814        let mut common_options: CommonOptions = toml::from_str(basic_toml).unwrap();
1815        common_options.config(None).unwrap();
1816
1817        // toml with custom deserialization to match CLI flag parsing
1818        for (opt_value, expected) in [
1819            ("0", Some(OptLevel::None)),
1820            ("1", Some(OptLevel::Speed)),
1821            ("2", Some(OptLevel::Speed)),
1822            ("\"s\"", Some(OptLevel::SpeedAndSize)),
1823            ("\"hello\"", None), // should fail
1824            ("3", None),         // should fail
1825        ] {
1826            let toml = format!(
1827                r#"
1828                    [optimize]
1829                    opt-level = {opt_value}
1830                "#,
1831            );
1832            let parsed_opt_level = toml::from_str::<CommonOptions>(&toml)
1833                .ok()
1834                .and_then(|common_options| common_options.opts.opt_level);
1835
1836            assert_eq!(
1837                parsed_opt_level, expected,
1838                "Mismatch for input '{opt_value}'. Parsed: {parsed_opt_level:?}, Expected: {expected:?}"
1839            );
1840        }
1841
1842        // Regalloc algorithm
1843        for (regalloc_value, expected) in [
1844            ("\"backtracking\"", Some(RegallocAlgorithm::Backtracking)),
1845            ("\"single-pass\"", Some(RegallocAlgorithm::SinglePass)),
1846            ("\"hello\"", None), // should fail
1847            ("3", None),         // should fail
1848            ("true", None),      // should fail
1849        ] {
1850            let toml = format!(
1851                r#"
1852                    [optimize]
1853                    regalloc-algorithm = {regalloc_value}
1854                "#,
1855            );
1856            let parsed_regalloc_algorithm = toml::from_str::<CommonOptions>(&toml)
1857                .ok()
1858                .and_then(|common_options| common_options.opts.regalloc_algorithm);
1859            assert_eq!(
1860                parsed_regalloc_algorithm, expected,
1861                "Mismatch for input '{regalloc_value}'. Parsed: {parsed_regalloc_algorithm:?}, Expected: {expected:?}"
1862            );
1863        }
1864
1865        // Strategy
1866        for (strategy_value, expected) in [
1867            ("\"cranelift\"", Some(wasmtime::Strategy::Cranelift)),
1868            ("\"winch\"", Some(wasmtime::Strategy::Winch)),
1869            ("\"hello\"", None), // should fail
1870            ("5", None),         // should fail
1871            ("true", None),      // should fail
1872        ] {
1873            let toml = format!(
1874                r#"
1875                    [codegen]
1876                    compiler = {strategy_value}
1877                "#,
1878            );
1879            let parsed_strategy = toml::from_str::<CommonOptions>(&toml)
1880                .ok()
1881                .and_then(|common_options| common_options.codegen.compiler);
1882            assert_eq!(
1883                parsed_strategy, expected,
1884                "Mismatch for input '{strategy_value}'. Parsed: {parsed_strategy:?}, Expected: {expected:?}",
1885            );
1886        }
1887
1888        // Collector
1889        for (collector_value, expected) in [
1890            (
1891                "\"drc\"",
1892                Some(wasmtime::Collector::DeferredReferenceCounting),
1893            ),
1894            ("\"null\"", Some(wasmtime::Collector::Null)),
1895            ("\"copying\"", Some(wasmtime::Collector::Copying)),
1896            ("\"hello\"", None), // should fail
1897            ("5", None),         // should fail
1898            ("true", None),      // should fail
1899        ] {
1900            let toml = format!(
1901                r#"
1902                    [codegen]
1903                    collector = {collector_value}
1904                "#,
1905            );
1906            let parsed_collector = toml::from_str::<CommonOptions>(&toml)
1907                .ok()
1908                .and_then(|common_options| common_options.codegen.collector);
1909            assert_eq!(
1910                parsed_collector, expected,
1911                "Mismatch for input '{collector_value}'. Parsed: {parsed_collector:?}, Expected: {expected:?}",
1912            );
1913        }
1914
1915        // Timeout: zero is rejected, as it is on the command line
1916        for (timeout_value, expected) in [
1917            (
1918                "{ secs = 5, nanos = 0 }",
1919                Some(std::time::Duration::from_secs(5)),
1920            ),
1921            (
1922                "{ secs = 0, nanos = 1 }",
1923                Some(std::time::Duration::from_nanos(1)),
1924            ),
1925            ("{ secs = 0, nanos = 0 }", None), // should fail
1926        ] {
1927            let toml = format!(
1928                r#"
1929                    [wasm]
1930                    timeout = {timeout_value}
1931                "#,
1932            );
1933            let parsed_timeout = toml::from_str::<CommonOptions>(&toml)
1934                .ok()
1935                .and_then(|common_options| common_options.wasm.timeout)
1936                .map(|timeout| *timeout);
1937            assert_eq!(
1938                parsed_timeout, expected,
1939                "Mismatch for input '{timeout_value}'. Parsed: {parsed_timeout:?}, Expected: {expected:?}",
1940            );
1941        }
1942    }
1943}
1944
1945impl Default for CommonOptions {
1946    fn default() -> CommonOptions {
1947        CommonOptions::new()
1948    }
1949}
1950
1951impl fmt::Display for CommonOptions {
1952    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1953        let CommonOptions {
1954            codegen_raw,
1955            codegen,
1956            debug_raw,
1957            debug,
1958            opts_raw,
1959            opts,
1960            wasm_raw,
1961            wasm,
1962            wasi_raw,
1963            wasi,
1964            record_raw,
1965            record,
1966            configured,
1967            ignore_env: _,
1968            target,
1969            config,
1970        } = self;
1971        if let Some(target) = target {
1972            write!(f, "--target {target} ")?;
1973        }
1974        if let Some(config) = config {
1975            write!(f, "--config {} ", config.display())?;
1976        }
1977
1978        let codegen_flags;
1979        let opts_flags;
1980        let wasi_flags;
1981        let wasm_flags;
1982        let debug_flags;
1983        let record_flags;
1984
1985        if *configured {
1986            codegen_flags = codegen.to_options();
1987            debug_flags = debug.to_options();
1988            wasi_flags = wasi.to_options();
1989            wasm_flags = wasm.to_options();
1990            opts_flags = opts.to_options();
1991            record_flags = record.to_options();
1992        } else {
1993            codegen_flags = codegen_raw
1994                .iter()
1995                .flat_map(|t| t.0.iter())
1996                .cloned()
1997                .collect();
1998            debug_flags = debug_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1999            wasi_flags = wasi_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
2000            wasm_flags = wasm_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
2001            opts_flags = opts_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
2002            record_flags = record_raw
2003                .iter()
2004                .flat_map(|t| t.0.iter())
2005                .cloned()
2006                .collect();
2007        }
2008
2009        for flag in codegen_flags {
2010            write!(f, "-C{flag} ")?;
2011        }
2012        for flag in opts_flags {
2013            write!(f, "-O{flag} ")?;
2014        }
2015        for flag in wasi_flags {
2016            write!(f, "-S{flag} ")?;
2017        }
2018        for flag in wasm_flags {
2019            write!(f, "-W{flag} ")?;
2020        }
2021        for flag in debug_flags {
2022            write!(f, "-D{flag} ")?;
2023        }
2024        for flag in record_flags {
2025            write!(f, "-R{flag} ")?;
2026        }
2027
2028        Ok(())
2029    }
2030}