1use std::num::NonZeroUsize;
15use std::{fmt, num::NonZeroU32, path::PathBuf, time::Duration};
16use wasmtime::{Config, Engine, Result, WasmBacktraceDetails, WasmFeatures, bail};
17
18pub mod opt;
19
20#[cfg(feature = "logging")]
21fn init_file_per_thread_logger(prefix: &'static str) {
22 file_per_thread_logger::initialize(prefix);
23 file_per_thread_logger::allow_uninitialized();
24
25 #[cfg(feature = "parallel-compilation")]
30 rayon::ThreadPoolBuilder::new()
31 .spawn_handler(move |thread| {
32 let mut b = std::thread::Builder::new();
33 if let Some(name) = thread.name() {
34 b = b.name(name.to_owned());
35 }
36 if let Some(stack_size) = thread.stack_size() {
37 b = b.stack_size(stack_size);
38 }
39 b.spawn(move || {
40 file_per_thread_logger::initialize(prefix);
41 thread.run()
42 })?;
43 Ok(())
44 })
45 .build_global()
46 .unwrap();
47}
48
49wasmtime_option_group! {
50 pub struct OptimizeOptions {
51 #[serde(default)]
53 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
54 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
55 pub opt_level: Option<wasmtime::OptLevel>,
56
57 #[serde(default)]
59 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
60 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
61 pub regalloc_algorithm: Option<wasmtime::RegallocAlgorithm>,
62
63 pub memory_may_move: Option<bool>,
66
67 pub memory_reservation: Option<u64>,
69
70 pub memory_reservation_for_growth: Option<u64>,
72
73 pub memory_guard_size: Option<u64>,
75
76 pub gc_heap_may_move: Option<bool>,
79
80 pub gc_heap_reservation: Option<u64>,
82
83 pub gc_heap_reservation_for_growth: Option<u64>,
85
86 pub gc_heap_guard_size: Option<u64>,
88
89 pub guard_before_linear_memory: Option<bool>,
92
93 pub table_lazy_init: Option<bool>,
98
99 pub pooling_allocator: Option<bool>,
101
102 pub pooling_decommit_batch_size: Option<usize>,
105
106 pub pooling_memory_keep_resident: Option<usize>,
109
110 pub pooling_table_keep_resident: Option<usize>,
113
114 #[serde(default)]
117 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
118 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
119 pub pooling_memory_protection_keys: Option<wasmtime::Enabled>,
120
121 pub pooling_max_memory_protection_keys: Option<usize>,
124
125 pub memory_init_cow: Option<bool>,
128
129 pub memory_guaranteed_dense_image_size: Option<u64>,
132
133 pub pooling_total_core_instances: Option<u32>,
136
137 pub pooling_total_component_instances: Option<u32>,
140
141 pub pooling_total_memories: Option<u32>,
144
145 pub pooling_total_tables: Option<u32>,
148
149 pub pooling_total_stacks: Option<u32>,
152
153 pub pooling_max_memory_size: Option<usize>,
156
157 pub pooling_table_elements: Option<usize>,
160
161 pub pooling_max_core_instance_size: Option<usize>,
164
165 pub pooling_max_unused_warm_slots: Option<u32>,
168
169 pub pooling_async_stack_keep_resident: Option<usize>,
172
173 pub pooling_max_component_instance_size: Option<usize>,
176
177 pub pooling_max_core_instances_per_component: Option<u32>,
180
181 pub pooling_max_memories_per_component: Option<u32>,
184
185 pub pooling_max_tables_per_component: Option<u32>,
188
189 pub pooling_max_tables_per_module: Option<u32>,
191
192 pub pooling_max_memories_per_module: Option<u32>,
194
195 pub pooling_total_gc_heaps: Option<u32>,
197
198 pub signals_based_traps: Option<bool>,
200
201 pub dynamic_memory_guard_size: Option<u64>,
203
204 pub static_memory_guard_size: Option<u64>,
206
207 pub static_memory_forced: Option<bool>,
209
210 pub static_memory_maximum_size: Option<u64>,
212
213 pub dynamic_memory_reserved_for_growth: Option<u64>,
215
216 #[serde(default)]
219 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
220 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
221 pub pooling_pagemap_scan: Option<wasmtime::Enabled>,
222
223 #[doc(hidden)]
225 pub gc_zeal_alloc_counter: Option<NonZeroU32>,
226 }
227
228 enum Optimize {
229 ...
230 }
231}
232
233wasmtime_option_group! {
234 pub struct CodegenOptions {
235 #[serde(default)]
240 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
241 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
242 pub compiler: Option<wasmtime::Strategy>,
243 #[serde(default)]
255 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
256 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
257 pub collector: Option<wasmtime::Collector>,
258 pub cranelift_debug_verifier: Option<bool>,
260 pub cache: Option<bool>,
262 pub cache_config: Option<String>,
264 pub parallel_compilation: Option<bool>,
266 pub native_unwind_info: Option<bool>,
269
270 #[serde(default)]
272 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
273 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
274 pub inlining: Option<wasmtime::Inlining>,
275
276 pub metadata_for_internal_asserts: Option<bool>,
279 pub metadata_for_gc_heap_corruption: Option<bool>,
282
283 #[prefixed = "cranelift"]
284 #[serde(default)]
285 pub cranelift: Vec<(String, Option<String>)>,
288 }
289
290 enum Codegen {
291 ...
292 }
293}
294
295wasmtime_option_group! {
296 pub struct DebugOptions {
297 pub debug_info: Option<bool>,
299 pub guest_debug: Option<bool>,
301 pub address_map: Option<bool>,
303 pub logging: Option<bool>,
305 pub log_to_files: Option<bool>,
307 pub coredump: Option<String>,
309 pub debugger: Option<PathBuf>,
312 #[serde(default)]
315 pub arg: Vec<String>,
316 pub inherit_stdin: Option<bool>,
319 pub inherit_stdout: Option<bool>,
322 pub inherit_stderr: Option<bool>,
325 pub max_backtrace: Option<usize>,
327 pub symbols: Option<bool>,
329 }
330
331 enum Debug {
332 ...
333 }
334}
335
336wasmtime_option_group! {
337 pub struct WasmOptions {
338 pub nan_canonicalization: Option<bool>,
340 pub fuel: Option<u64>,
348 pub epoch_interruption: Option<bool>,
351 pub max_wasm_stack: Option<usize>,
354 pub async_stack_size: Option<usize>,
360 pub async_stack_zeroing: Option<bool>,
363 pub unknown_exports_allow: Option<bool>,
365 pub unknown_imports_trap: Option<bool>,
368 pub unknown_imports_default: Option<bool>,
371 pub wmemcheck: Option<bool>,
373 pub max_memory_size: Option<usize>,
378 pub max_table_elements: Option<usize>,
380 pub max_instances: Option<usize>,
382 pub max_tables: Option<usize>,
384 pub max_memories: Option<usize>,
386 pub trap_on_grow_failure: Option<bool>,
393 pub timeout: Option<Duration>,
395 pub all_proposals: Option<bool>,
397 pub bulk_memory: Option<bool>,
399 pub multi_memory: Option<bool>,
401 pub multi_value: Option<bool>,
403 pub reference_types: Option<bool>,
405 pub simd: Option<bool>,
407 pub relaxed_simd: Option<bool>,
409 pub relaxed_simd_deterministic: Option<bool>,
418 pub tail_call: Option<bool>,
420 pub threads: Option<bool>,
422 pub shared_memory: Option<bool>,
424 pub shared_everything_threads: Option<bool>,
426 pub memory64: Option<bool>,
428 pub component_model: Option<bool>,
430 pub component_model_async: Option<bool>,
432 pub component_model_more_async_builtins: Option<bool>,
435 pub component_model_async_stackful: Option<bool>,
438 pub component_model_threading: Option<bool>,
441 pub component_model_error_context: Option<bool>,
444 pub component_model_gc: Option<bool>,
447 pub component_model_map: Option<bool>,
449 pub function_references: Option<bool>,
451 pub stack_switching: Option<bool>,
453 pub gc: Option<bool>,
455 pub custom_page_sizes: Option<bool>,
457 pub wide_arithmetic: Option<bool>,
459 pub branch_hinting: Option<bool>,
461 pub extended_const: Option<bool>,
463 pub exceptions: Option<bool>,
465 pub gc_support: Option<bool>,
467 pub component_model_fixed_length_lists: Option<bool>,
470 pub component_model_implements: Option<bool>,
473 pub concurrency_support: Option<bool>,
476 }
477
478 enum Wasm {
479 ...
480 }
481}
482
483wasmtime_option_group! {
484 pub struct WasiOptions {
485 pub cli: Option<bool>,
487 pub cli_exit_with_code: Option<bool>,
489 pub common: Option<bool>,
491 pub nn: Option<bool>,
493 pub threads: Option<bool>,
495 pub http: Option<bool>,
497 pub http_outgoing_body_buffer_chunks: Option<usize>,
501 pub http_outgoing_body_chunk_size: Option<usize>,
504 pub config: Option<bool>,
506 pub keyvalue: Option<bool>,
508 pub listenfd: Option<bool>,
512 #[serde(default)]
515 pub tcplisten: Vec<String>,
516 pub tls: Option<bool>,
518 pub preview2: Option<bool>,
521 #[serde(skip)]
530 pub nn_graph: Vec<WasiNnGraph>,
531 pub inherit_network: Option<bool>,
534 pub allow_ip_name_lookup: Option<bool>,
536 pub tcp: Option<bool>,
538 pub udp: Option<bool>,
540 pub network_error_code: Option<bool>,
542 pub preview0: Option<bool>,
544 pub inherit_env: Option<bool>,
548 pub inherit_stdin: Option<bool>,
550 pub inherit_stdout: Option<bool>,
552 pub inherit_stderr: Option<bool>,
554 pub cwd: Option<String>,
556 #[serde(skip)]
558 pub config_var: Vec<KeyValuePair>,
559 #[serde(skip)]
561 pub keyvalue_in_memory_data: Vec<KeyValuePair>,
562 pub p3: Option<bool>,
564 pub max_resources: Option<usize>,
566 pub hostcall_fuel: Option<usize>,
568 pub max_random_size: Option<u64>,
572 pub max_http_fields_size: Option<usize>,
576 }
577
578 enum Wasi {
579 ...
580 }
581}
582
583wasmtime_option_group! {
584 pub struct RecordOptions {
585 pub path: Option<String>,
587 pub validation_metadata: Option<bool>,
590 pub event_window_size: Option<usize>,
593 }
594
595 enum Record {
596 ...
597 }
598}
599
600#[derive(Debug, Clone, PartialEq)]
601pub struct WasiNnGraph {
602 pub format: String,
603 pub dir: String,
604}
605
606#[derive(Debug, Clone, PartialEq)]
607pub struct KeyValuePair {
608 pub key: String,
609 pub value: String,
610}
611
612#[derive(Clone)]
614#[cfg_attr(feature = "clap", derive(clap::Parser))]
615#[cfg_attr(
616 feature = "serde",
617 derive(serde_derive::Deserialize, serde_derive::Serialize)
618)]
619#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
620pub struct CommonOptions {
621 #[cfg_attr(
631 feature = "clap",
632 arg(short = 'O', long = "optimize", value_name = "KEY[=VAL[,..]]")
633 )]
634 #[cfg_attr(feature = "serde", serde(skip))]
635 opts_raw: Vec<opt::CommaSeparated<Optimize>>,
636
637 #[cfg_attr(
639 feature = "clap",
640 arg(short = 'C', long = "codegen", value_name = "KEY[=VAL[,..]]")
641 )]
642 #[cfg_attr(feature = "serde", serde(skip))]
643 codegen_raw: Vec<opt::CommaSeparated<Codegen>>,
644
645 #[cfg_attr(
647 feature = "clap",
648 arg(short = 'D', long = "debug", value_name = "KEY[=VAL[,..]]")
649 )]
650 #[cfg_attr(feature = "serde", serde(skip))]
651 debug_raw: Vec<opt::CommaSeparated<Debug>>,
652
653 #[cfg_attr(
656 feature = "clap",
657 arg(short = 'W', long = "wasm", value_name = "KEY[=VAL[,..]]")
658 )]
659 #[cfg_attr(feature = "serde", serde(skip))]
660 wasm_raw: Vec<opt::CommaSeparated<Wasm>>,
661
662 #[cfg_attr(
664 feature = "clap",
665 arg(short = 'S', long = "wasi", value_name = "KEY[=VAL[,..]]")
666 )]
667 #[cfg_attr(feature = "serde", serde(skip))]
668 wasi_raw: Vec<opt::CommaSeparated<Wasi>>,
669
670 #[cfg_attr(
679 feature = "clap",
680 arg(short = 'R', long = "record", value_name = "KEY[=VAL[,..]]")
681 )]
682 #[cfg_attr(feature = "serde", serde(skip))]
683 record_raw: Vec<opt::CommaSeparated<Record>>,
684
685 #[cfg_attr(feature = "clap", arg(skip))]
688 #[cfg_attr(feature = "serde", serde(skip))]
689 configured: bool,
690
691 #[cfg_attr(feature = "clap", arg(skip))]
692 #[cfg_attr(feature = "serde", serde(rename = "optimize", default))]
693 pub opts: OptimizeOptions,
694
695 #[cfg_attr(feature = "clap", arg(skip))]
696 #[cfg_attr(feature = "serde", serde(default))]
697 pub codegen: CodegenOptions,
698
699 #[cfg_attr(feature = "clap", arg(skip))]
700 #[cfg_attr(feature = "serde", serde(default))]
701 pub debug: DebugOptions,
702
703 #[cfg_attr(feature = "clap", arg(skip))]
704 #[cfg_attr(feature = "serde", serde(default))]
705 pub wasm: WasmOptions,
706
707 #[cfg_attr(feature = "clap", arg(skip))]
708 #[cfg_attr(feature = "serde", serde(default))]
709 pub wasi: WasiOptions,
710
711 #[cfg_attr(feature = "clap", arg(skip))]
712 #[cfg_attr(feature = "serde", serde(default))]
713 pub record: RecordOptions,
714
715 #[cfg_attr(feature = "clap", arg(long, value_name = "TARGET"))]
717 #[cfg_attr(feature = "serde", serde(skip))]
718 pub target: Option<String>,
719
720 #[cfg_attr(feature = "clap", arg(long = "config", value_name = "FILE"))]
727 #[cfg_attr(feature = "serde", serde(skip))]
728 pub config: Option<PathBuf>,
729}
730
731macro_rules! match_feature {
732 (
733 [$feat:tt : $config:expr]
734 $val:ident => $e:expr,
735 $p:pat => err,
736 ) => {
737 #[cfg(feature = $feat)]
738 {
739 if let Some($val) = $config {
740 $e;
741 }
742 }
743 #[cfg(not(feature = $feat))]
744 {
745 if let Some($p) = $config {
746 bail!(concat!("support for ", $feat, " disabled at compile time"));
747 }
748 }
749 };
750}
751
752impl CommonOptions {
753 pub fn new() -> CommonOptions {
755 CommonOptions {
756 opts_raw: Vec::new(),
757 codegen_raw: Vec::new(),
758 debug_raw: Vec::new(),
759 wasm_raw: Vec::new(),
760 wasi_raw: Vec::new(),
761 record_raw: Vec::new(),
762 configured: true,
763 opts: Default::default(),
764 codegen: Default::default(),
765 debug: Default::default(),
766 wasm: Default::default(),
767 wasi: Default::default(),
768 record: Default::default(),
769 target: None,
770 config: None,
771 }
772 }
773
774 fn configure(&mut self) -> Result<()> {
775 if self.configured {
776 return Ok(());
777 }
778 self.configured = true;
779 if let Some(toml_config_path) = &self.config {
780 #[cfg(feature = "toml")]
781 {
782 let toml_options = CommonOptions::from_file(toml_config_path)?;
783 self.opts = toml_options.opts;
784 self.codegen = toml_options.codegen;
785 self.debug = toml_options.debug;
786 self.wasm = toml_options.wasm;
787 self.wasi = toml_options.wasi;
788 self.record = toml_options.record;
789 }
790 #[cfg(not(feature = "toml"))]
791 {
792 bail!(
793 "support for loading a configuration file from \
794 {toml_config_path:?} disabled at compile time"
795 );
796 }
797 }
798 self.opts.configure_with(&self.opts_raw);
799 self.codegen.configure_with(&self.codegen_raw);
800 self.debug.configure_with(&self.debug_raw);
801 self.wasm.configure_with(&self.wasm_raw);
802 self.wasi.configure_with(&self.wasi_raw);
803 self.record.configure_with(&self.record_raw);
804 Ok(())
805 }
806
807 pub fn init_logging(&mut self) -> Result<()> {
808 self.configure()?;
809 if self.debug.logging == Some(false) {
810 return Ok(());
811 }
812 #[cfg(feature = "logging")]
813 if self.debug.log_to_files == Some(true) {
814 let prefix = "wasmtime.dbg.";
815 init_file_per_thread_logger(prefix);
816 } else {
817 use std::io::IsTerminal;
818 use tracing_subscriber::{EnvFilter, FmtSubscriber};
819 let builder = FmtSubscriber::builder()
820 .with_writer(std::io::stderr)
821 .with_env_filter(EnvFilter::from_env("WASMTIME_LOG"))
822 .with_ansi(std::io::stderr().is_terminal());
823 if std::env::var("WASMTIME_LOG_NO_CONTEXT").is_ok_and(|value| value.eq("1")) {
824 builder
825 .with_level(false)
826 .with_target(false)
827 .without_time()
828 .init()
829 } else {
830 builder.init();
831 }
832 }
833 #[cfg(not(feature = "logging"))]
834 if self.debug.log_to_files == Some(true) || self.debug.logging == Some(true) {
835 bail!("support for logging disabled at compile time");
836 }
837 Ok(())
838 }
839
840 pub fn config(&mut self, pooling_allocator_default: Option<bool>) -> Result<Config> {
841 self.configure()?;
842 let mut config = Config::new();
843
844 match_feature! {
845 ["cranelift" : self.codegen.compiler]
846 strategy => config.strategy(strategy),
847 _ => err,
848 }
849 match_feature! {
850 ["gc" : self.codegen.collector]
851 collector => config.collector(collector),
852 _ => err,
853 }
854 if let Some(target) = &self.target {
855 config.target(target)?;
856 }
857 match_feature! {
858 ["cranelift" : self.codegen.cranelift_debug_verifier]
859 enable => config.cranelift_debug_verifier(enable),
860 true => err,
861 }
862 if let Some(enable) = self.debug.debug_info {
863 config.debug_info(enable);
864 }
865 match_feature! {
866 ["debug" : self.debug.guest_debug]
867 enable => config.guest_debug(enable),
868 _ => err,
869 }
870 if self.debug.coredump.is_some() {
871 #[cfg(feature = "coredump")]
872 config.coredump_on_trap(true);
873 #[cfg(not(feature = "coredump"))]
874 bail!("support for coredumps disabled at compile time");
875 }
876 match_feature! {
877 ["cranelift" : self.opts.opt_level]
878 level => config.cranelift_opt_level(level),
879 _ => err,
880 }
881 match_feature! {
882 ["cranelift": self.opts.regalloc_algorithm]
883 algo => config.cranelift_regalloc_algorithm(algo),
884 _ => err,
885 }
886 match_feature! {
887 ["cranelift" : self.wasm.nan_canonicalization]
888 enable => config.cranelift_nan_canonicalization(enable),
889 true => err,
890 }
891
892 self.enable_wasm_features(&mut config)?;
893
894 #[cfg(feature = "cranelift")]
895 for (name, value) in self.codegen.cranelift.iter() {
896 let name = name.replace('-', "_");
897 unsafe {
898 match value {
899 Some(val) => {
900 config.cranelift_flag_set(&name, val);
901 }
902 None => {
903 config.cranelift_flag_enable(&name);
904 }
905 }
906 }
907 }
908 #[cfg(not(feature = "cranelift"))]
909 if !self.codegen.cranelift.is_empty() {
910 bail!("support for cranelift disabled at compile time");
911 }
912
913 #[cfg(feature = "cache")]
914 if self.codegen.cache != Some(false) {
915 use wasmtime::Cache;
916 let cache = match &self.codegen.cache_config {
917 Some(path) => Cache::from_file(Some(std::path::Path::new(path)))?,
918 None => Cache::from_file(None)?,
919 };
920 config.cache(Some(cache));
921 }
922 #[cfg(not(feature = "cache"))]
923 if self.codegen.cache == Some(true) {
924 bail!("support for caching disabled at compile time");
925 }
926
927 match_feature! {
928 ["parallel-compilation" : self.codegen.parallel_compilation]
929 enable => config.parallel_compilation(enable),
930 true => err,
931 }
932
933 let memory_reservation = self
934 .opts
935 .memory_reservation
936 .or(self.opts.static_memory_maximum_size);
937 if let Some(size) = memory_reservation {
938 config.memory_reservation(size);
939 }
940
941 if let Some(enable) = self.opts.static_memory_forced {
942 config.memory_may_move(!enable);
943 }
944 if let Some(enable) = self.opts.memory_may_move {
945 config.memory_may_move(enable);
946 }
947
948 let memory_guard_size = self
949 .opts
950 .static_memory_guard_size
951 .or(self.opts.dynamic_memory_guard_size)
952 .or(self.opts.memory_guard_size);
953 if let Some(size) = memory_guard_size {
954 config.memory_guard_size(size);
955 }
956
957 let mem_for_growth = self
958 .opts
959 .memory_reservation_for_growth
960 .or(self.opts.dynamic_memory_reserved_for_growth);
961 if let Some(size) = mem_for_growth {
962 config.memory_reservation_for_growth(size);
963 }
964 if let Some(enable) = self.opts.guard_before_linear_memory {
965 config.guard_before_linear_memory(enable);
966 }
967
968 if let Some(size) = self.opts.gc_heap_reservation {
969 config.gc_heap_reservation(size);
970 }
971 if let Some(enable) = self.opts.gc_heap_may_move {
972 config.gc_heap_may_move(enable);
973 }
974 if let Some(size) = self.opts.gc_heap_guard_size {
975 config.gc_heap_guard_size(size);
976 }
977 if let Some(size) = self.opts.gc_heap_reservation_for_growth {
978 config.gc_heap_reservation_for_growth(size);
979 }
980 if let Some(enable) = self.opts.table_lazy_init {
981 config.table_lazy_init(enable);
982 }
983
984 if let Some(n) = self.opts.gc_zeal_alloc_counter
985 && (cfg!(gc_zeal) || cfg!(fuzzing))
986 {
987 config.gc_zeal_alloc_counter(Some(n))?;
988 }
989
990 if self.wasm.fuel.is_some() {
992 config.consume_fuel(true);
993 }
994
995 if let Some(enable) = self.wasm.epoch_interruption {
996 config.epoch_interruption(enable);
997 }
998 if let Some(enable) = self.debug.address_map {
999 config.generate_address_map(enable);
1000 }
1001 if let Some(frames) = self.debug.max_backtrace {
1002 match NonZeroUsize::new(frames) {
1003 None => {
1004 config.wasm_backtrace_details(WasmBacktraceDetails::Disable);
1005 }
1006 Some(amt) => {
1007 config.wasm_backtrace_max_frames(Some(amt));
1008 }
1009 }
1010 }
1011 if let Some(enable) = self.debug.symbols {
1012 config.debug_symbols(enable);
1013 }
1014 if let Some(enable) = self.opts.memory_init_cow {
1015 config.memory_init_cow(enable);
1016 }
1017 if let Some(size) = self.opts.memory_guaranteed_dense_image_size {
1018 config.memory_guaranteed_dense_image_size(size);
1019 }
1020 if let Some(enable) = self.opts.signals_based_traps {
1021 config.signals_based_traps(enable);
1022 }
1023 if let Some(enable) = self.codegen.native_unwind_info {
1024 config.native_unwind_info(enable);
1025 }
1026 if let Some(enable) = self.codegen.inlining {
1027 config.compiler_inlining(enable);
1028 }
1029 if let Some(enable) = self.codegen.metadata_for_internal_asserts {
1030 config.metadata_for_internal_asserts(enable);
1031 }
1032 if let Some(enable) = self.codegen.metadata_for_gc_heap_corruption {
1033 config.metadata_for_gc_heap_corruption(enable);
1034 }
1035
1036 #[cfg(any(feature = "async", feature = "stack-switching"))]
1039 {
1040 if let Some(size) = self.wasm.async_stack_size {
1041 config.async_stack_size(size);
1042 }
1043 }
1044 #[cfg(not(any(feature = "async", feature = "stack-switching")))]
1045 {
1046 if let Some(_size) = self.wasm.async_stack_size {
1047 bail!(concat!(
1048 "support for async/stack-switching disabled at compile time"
1049 ));
1050 }
1051 }
1052
1053 match_feature! {
1054 ["pooling-allocator" : self.opts.pooling_allocator.or(pooling_allocator_default)]
1055 enable => {
1056 if enable {
1057 let mut cfg = wasmtime::PoolingAllocationConfig::default();
1058 if let Some(size) = self.opts.pooling_memory_keep_resident {
1059 cfg.linear_memory_keep_resident(size);
1060 }
1061 if let Some(size) = self.opts.pooling_table_keep_resident {
1062 cfg.table_keep_resident(size);
1063 }
1064 if let Some(limit) = self.opts.pooling_total_core_instances {
1065 cfg.total_core_instances(limit);
1066 }
1067 if let Some(limit) = self.opts.pooling_total_component_instances {
1068 cfg.total_component_instances(limit);
1069 }
1070 if let Some(limit) = self.opts.pooling_total_memories {
1071 cfg.total_memories(limit);
1072 }
1073 if let Some(limit) = self.opts.pooling_total_tables {
1074 cfg.total_tables(limit);
1075 }
1076 if let Some(limit) = self.opts.pooling_table_elements
1077 .or(self.wasm.max_table_elements)
1078 {
1079 cfg.table_elements(limit);
1080 }
1081 if let Some(limit) = self.opts.pooling_max_core_instance_size {
1082 cfg.max_core_instance_size(limit);
1083 }
1084 match_feature! {
1085 ["async" : self.opts.pooling_total_stacks]
1086 limit => cfg.total_stacks(limit),
1087 _ => err,
1088 }
1089 if let Some(max) = self.opts.pooling_max_memory_size
1090 .or(self.wasm.max_memory_size)
1091 {
1092 cfg.max_memory_size(max);
1093 }
1094 if let Some(size) = self.opts.pooling_decommit_batch_size {
1095 cfg.decommit_batch_size(size);
1096 }
1097 if let Some(max) = self.opts.pooling_max_unused_warm_slots {
1098 cfg.max_unused_warm_slots(max);
1099 }
1100 match_feature! {
1101 ["async" : self.opts.pooling_async_stack_keep_resident]
1102 size => cfg.async_stack_keep_resident(size),
1103 _ => err,
1104 }
1105 if let Some(max) = self.opts.pooling_max_component_instance_size {
1106 cfg.max_component_instance_size(max);
1107 }
1108 if let Some(max) = self.opts.pooling_max_core_instances_per_component {
1109 cfg.max_core_instances_per_component(max);
1110 }
1111 if let Some(max) = self.opts.pooling_max_memories_per_component {
1112 cfg.max_memories_per_component(max);
1113 }
1114 if let Some(max) = self.opts.pooling_max_tables_per_component {
1115 cfg.max_tables_per_component(max);
1116 }
1117 if let Some(max) = self.opts.pooling_max_tables_per_module {
1118 cfg.max_tables_per_module(max);
1119 }
1120 if let Some(max) = self.opts.pooling_max_memories_per_module {
1121 cfg.max_memories_per_module(max);
1122 }
1123 match_feature! {
1124 ["memory-protection-keys" : self.opts.pooling_memory_protection_keys]
1125 enable => cfg.memory_protection_keys(enable),
1126 _ => err,
1127 }
1128 match_feature! {
1129 ["memory-protection-keys" : self.opts.pooling_max_memory_protection_keys]
1130 max => cfg.max_memory_protection_keys(max),
1131 _ => err,
1132 }
1133 match_feature! {
1134 ["gc" : self.opts.pooling_total_gc_heaps]
1135 max => cfg.total_gc_heaps(max),
1136 _ => err,
1137 }
1138 if let Some(enabled) = self.opts.pooling_pagemap_scan {
1139 cfg.pagemap_scan(enabled);
1140 }
1141 config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling(cfg));
1142 }
1143 },
1144 true => err,
1145 }
1146
1147 if self.opts.pooling_memory_protection_keys.is_some()
1148 && !self.opts.pooling_allocator.unwrap_or(false)
1149 {
1150 bail!("memory protection keys require the pooling allocator");
1151 }
1152
1153 if self.opts.pooling_max_memory_protection_keys.is_some()
1154 && !self.opts.pooling_memory_protection_keys.is_some()
1155 {
1156 bail!("max memory protection keys requires memory protection keys to be enabled");
1157 }
1158
1159 match_feature! {
1160 ["async" : self.wasm.async_stack_zeroing]
1161 enable => config.async_stack_zeroing(enable),
1162 _ => err,
1163 }
1164
1165 if let Some(max) = self.wasm.max_wasm_stack {
1166 config.max_wasm_stack(max);
1167
1168 #[cfg(any(feature = "async", feature = "stack-switching"))]
1172 if self.wasm.async_stack_size.is_none() {
1173 const DEFAULT_HOST_STACK: usize = 512 << 10;
1174 config.async_stack_size(max + DEFAULT_HOST_STACK);
1175 }
1176 }
1177
1178 if let Some(enable) = self.wasm.relaxed_simd_deterministic {
1179 config.relaxed_simd_deterministic(enable);
1180 }
1181 match_feature! {
1182 ["cranelift" : self.wasm.wmemcheck]
1183 enable => config.wmemcheck(enable),
1184 true => err,
1185 }
1186
1187 if let Some(enable) = self.wasm.gc_support {
1188 config.gc_support(enable);
1189 }
1190
1191 if let Some(enable) = self.wasm.concurrency_support {
1192 config.concurrency_support(enable);
1193 }
1194
1195 if let Some(enable) = self.wasm.shared_memory {
1196 config.shared_memory(enable);
1197 }
1198
1199 let record = &self.record;
1200 match_feature! {
1201 ["rr" : &record.path]
1202 _path => {
1203 bail!("recording configuration for `rr` feature is not supported yet");
1204 },
1205 _ => err,
1206 }
1207
1208 Ok(config)
1209 }
1210
1211 pub fn enable_wasm_features(&self, config: &mut Config) -> Result<()> {
1212 let all = self.wasm.all_proposals;
1213
1214 if let Some(enable) = self.wasm.simd.or(all) {
1215 config.wasm_simd(enable);
1216 }
1217 if let Some(enable) = self.wasm.relaxed_simd.or(all) {
1218 config.wasm_relaxed_simd(enable);
1219 }
1220 if let Some(enable) = self.wasm.bulk_memory.or(all) {
1221 config.wasm_bulk_memory(enable);
1222 }
1223 if let Some(enable) = self.wasm.multi_value.or(all) {
1224 config.wasm_multi_value(enable);
1225 }
1226 if let Some(enable) = self.wasm.tail_call.or(all) {
1227 config.wasm_tail_call(enable);
1228 }
1229 if let Some(enable) = self.wasm.multi_memory.or(all) {
1230 config.wasm_multi_memory(enable);
1231 }
1232 if let Some(enable) = self.wasm.memory64.or(all) {
1233 config.wasm_memory64(enable);
1234 }
1235 if let Some(enable) = self.wasm.stack_switching {
1236 config.wasm_stack_switching(enable);
1237 }
1238 if let Some(enable) = self.wasm.custom_page_sizes.or(all) {
1239 config.wasm_custom_page_sizes(enable);
1240 }
1241 if let Some(enable) = self.wasm.wide_arithmetic.or(all) {
1242 config.wasm_wide_arithmetic(enable);
1243 }
1244 if let Some(enable) = self.wasm.branch_hinting {
1246 config.wasm_branch_hinting(enable);
1247 }
1248 if let Some(enable) = self.wasm.extended_const.or(all) {
1249 config.wasm_extended_const(enable);
1250 }
1251
1252 macro_rules! handle_conditionally_compiled {
1253 ($(($feature:tt, $field:tt, $method:tt))*) => ($(
1254 if let Some(enable) = self.wasm.$field.or(all) {
1255 #[cfg(feature = $feature)]
1256 config.$method(enable);
1257 #[cfg(not(feature = $feature))]
1258 if enable && all.is_none() {
1259 bail!("support for {} was disabled at compile-time", $feature);
1260 }
1261 }
1262 )*)
1263 }
1264
1265 handle_conditionally_compiled! {
1266 ("component-model", component_model, wasm_component_model)
1267 ("component-model-async", component_model_async, wasm_component_model_async)
1268 ("component-model-async", component_model_more_async_builtins, wasm_component_model_more_async_builtins)
1269 ("component-model-async", component_model_async_stackful, wasm_component_model_async_stackful)
1270 ("component-model-async", component_model_threading, wasm_component_model_threading)
1271 ("component-model", component_model_error_context, wasm_component_model_error_context)
1272 ("component-model", component_model_map, wasm_component_model_map)
1273 ("component-model", component_model_fixed_length_lists, wasm_component_model_fixed_length_lists)
1274 ("component-model", component_model_implements, wasm_component_model_implements)
1275 ("threads", threads, wasm_threads)
1276 ("gc", gc, wasm_gc)
1277 ("gc", reference_types, wasm_reference_types)
1278 ("gc", function_references, wasm_function_references)
1279 ("gc", exceptions, wasm_exceptions)
1280 ("stack-switching", stack_switching, wasm_stack_switching)
1281 }
1282
1283 if let Some(enable) = self.wasm.component_model_gc {
1284 #[cfg(all(feature = "component-model", feature = "gc"))]
1285 config.wasm_component_model_gc(enable);
1286 #[cfg(not(all(feature = "component-model", feature = "gc")))]
1287 if enable && all.is_none() {
1288 bail!("support for `component-model-gc` was disabled at compile time")
1289 }
1290 }
1291
1292 Ok(())
1293 }
1294
1295 #[cfg(feature = "toml")]
1296 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
1297 use wasmtime::error::Context;
1298
1299 let path_ref = path.as_ref();
1300 let file_contents = std::fs::read_to_string(path_ref)
1301 .with_context(|| format!("failed to read config file: {path_ref:?}"))?;
1302 toml::from_str::<CommonOptions>(&file_contents)
1303 .with_context(|| format!("failed to parse TOML config file {path_ref:?}"))
1304 }
1305
1306 pub fn from_engine(engine: &Engine) -> Self {
1319 let features = engine.get_wasm_features();
1320 let pooling = engine.get_pooling_config();
1321 CommonOptions {
1322 target: engine.get_target(),
1323 opts: OptimizeOptions {
1324 memory_may_move: Some(engine.get_memory_may_move()),
1325 memory_reservation: Some(engine.get_memory_reservation()),
1326 memory_reservation_for_growth: Some(engine.get_memory_reservation_for_growth()),
1327 memory_guard_size: Some(engine.get_memory_guard_size()),
1328 gc_heap_may_move: Some(engine.get_gc_heap_may_move()),
1329 gc_heap_reservation: Some(engine.get_gc_heap_reservation()),
1330 gc_heap_reservation_for_growth: Some(engine.get_gc_heap_reservation_for_growth()),
1331 gc_heap_guard_size: Some(engine.get_gc_heap_guard_size()),
1332 guard_before_linear_memory: Some(engine.get_guard_before_linear_memory()),
1333 table_lazy_init: Some(engine.get_table_lazy_init()),
1334 memory_init_cow: Some(engine.get_memory_init_cow()),
1335 memory_guaranteed_dense_image_size: Some(
1336 engine.get_memory_guaranteed_dense_image_size(),
1337 ),
1338 signals_based_traps: Some(engine.get_signals_based_traps()),
1339 gc_zeal_alloc_counter: engine.get_gc_zeal_alloc_counter(),
1340 opt_level: engine.get_cranelift_opt_level(),
1341 regalloc_algorithm: engine.get_cranelift_regalloc_algorithm(),
1342 pooling_allocator: Some(pooling.is_some()),
1343 pooling_decommit_batch_size: pooling.map(|c| c.get_decommit_batch_size()),
1344 pooling_memory_keep_resident: pooling.map(|c| c.get_memory_keep_resident()),
1345 pooling_table_keep_resident: pooling.map(|c| c.get_table_keep_resident()),
1346 pooling_max_unused_warm_slots: pooling.map(|c| c.get_max_unused_warm_slots()),
1347 pooling_pagemap_scan: pooling.map(|c| c.get_pagemap_scan()),
1348 pooling_total_core_instances: pooling.map(|c| c.get_total_core_instances()),
1349 pooling_total_component_instances: pooling
1350 .map(|c| c.get_total_component_instances()),
1351 pooling_total_memories: pooling.map(|c| c.get_total_memories()),
1352 pooling_total_tables: pooling.map(|c| c.get_total_tables()),
1353 pooling_max_memory_size: pooling.map(|c| c.get_max_memory_size()),
1354 pooling_table_elements: pooling.map(|c| c.get_table_elements()),
1355 pooling_max_core_instance_size: pooling.map(|c| c.get_max_core_instance_size()),
1356 pooling_max_component_instance_size: pooling
1357 .map(|c| c.get_max_component_instance_size()),
1358 pooling_max_core_instances_per_component: pooling
1359 .map(|c| c.get_max_core_instances_per_component()),
1360 pooling_max_memories_per_component: pooling
1361 .map(|c| c.get_max_memories_per_component()),
1362 pooling_max_tables_per_component: pooling.map(|c| c.get_max_tables_per_component()),
1363 pooling_max_tables_per_module: pooling.map(|c| c.get_max_tables_per_module()),
1364 pooling_max_memories_per_module: pooling.map(|c| c.get_max_memories_per_module()),
1365 pooling_async_stack_keep_resident: pooling
1366 .map(|c| c.get_async_stack_keep_resident()),
1367 pooling_total_stacks: pooling.map(|c| c.get_total_stacks()),
1368 pooling_total_gc_heaps: pooling.map(|c| c.get_total_gc_heaps()),
1369 pooling_memory_protection_keys: pooling.map(|c| c.get_memory_protection_keys()),
1370 pooling_max_memory_protection_keys: pooling
1371 .map(|c| c.get_max_memory_protection_keys()),
1372 dynamic_memory_guard_size: None,
1375 static_memory_guard_size: None,
1376 static_memory_forced: None,
1377 static_memory_maximum_size: None,
1378 dynamic_memory_reserved_for_growth: None,
1379 },
1380 codegen: CodegenOptions {
1381 compiler: engine.get_strategy(),
1382 collector: engine.get_collector(),
1383 cranelift_debug_verifier: engine.get_cranelift_debug_verifier(),
1384 inlining: Some(engine.get_compiler_inlining()),
1385 native_unwind_info: engine.get_native_unwind_info(),
1386 parallel_compilation: Some(engine.get_parallel_compilation()),
1387 metadata_for_internal_asserts: Some(engine.get_metadata_for_internal_asserts()),
1388 metadata_for_gc_heap_corruption: Some(engine.get_metadata_for_gc_heap_corruption()),
1389 cranelift: engine
1390 .get_cranelift_flags_set()
1391 .map(|(k, v)| (k.to_string(), Some(v.to_string())))
1392 .chain(
1393 engine
1394 .get_cranelift_flags_enabled()
1395 .map(|k| (k.to_string(), None)),
1396 )
1397 .collect(),
1398
1399 cache: None,
1402 cache_config: None,
1403 },
1404 debug: DebugOptions {
1405 address_map: Some(engine.get_generate_address_map()),
1406 debug_info: Some(engine.get_debug_info()),
1407 guest_debug: Some(engine.get_guest_debug()),
1408 symbols: Some(engine.get_debug_symbols()),
1409 max_backtrace: Some(engine.get_wasm_backtrace_max_frames()),
1410
1411 coredump: None,
1414 debugger: None,
1416 arg: Vec::new(),
1417 inherit_stderr: None,
1418 inherit_stdout: None,
1419 inherit_stdin: None,
1420 log_to_files: None,
1422 logging: None,
1423 },
1424 wasm: WasmOptions {
1425 async_stack_size: Some(engine.get_async_stack_size()),
1426 async_stack_zeroing: Some(engine.get_async_stack_zeroing()),
1427 branch_hinting: Some(engine.get_wasm_branch_hinting()),
1428 bulk_memory: Some(features.contains(WasmFeatures::BULK_MEMORY)),
1429 component_model: Some(features.contains(WasmFeatures::COMPONENT_MODEL)),
1430 component_model_async: Some(features.contains(WasmFeatures::CM_ASYNC)),
1431 component_model_async_stackful: Some(
1432 features.contains(WasmFeatures::CM_ASYNC_STACKFUL),
1433 ),
1434 component_model_error_context: Some(
1435 features.contains(WasmFeatures::CM_ERROR_CONTEXT),
1436 ),
1437 component_model_gc: Some(features.contains(WasmFeatures::CM_GC)),
1438 component_model_fixed_length_lists: Some(
1439 features.contains(WasmFeatures::CM_FIXED_LENGTH_LISTS),
1440 ),
1441 component_model_implements: Some(features.contains(WasmFeatures::CM_IMPLEMENTS)),
1442 component_model_map: Some(features.contains(WasmFeatures::CM_MAP)),
1443 component_model_more_async_builtins: Some(
1444 features.contains(WasmFeatures::CM_MORE_ASYNC_BUILTINS),
1445 ),
1446 component_model_threading: Some(features.contains(WasmFeatures::CM_THREADING)),
1447 custom_page_sizes: Some(features.contains(WasmFeatures::CUSTOM_PAGE_SIZES)),
1448 exceptions: Some(features.contains(WasmFeatures::EXCEPTIONS)),
1449 extended_const: Some(features.contains(WasmFeatures::EXTENDED_CONST)),
1450 function_references: Some(features.contains(WasmFeatures::FUNCTION_REFERENCES)),
1451 gc: Some(features.contains(WasmFeatures::GC)),
1452 gc_support: Some(features.contains(WasmFeatures::GC_TYPES)),
1453 memory64: Some(features.contains(WasmFeatures::MEMORY64)),
1454 multi_memory: Some(features.contains(WasmFeatures::MULTI_MEMORY)),
1455 multi_value: Some(features.contains(WasmFeatures::MULTI_VALUE)),
1456 reference_types: Some(features.contains(WasmFeatures::REFERENCE_TYPES)),
1457 relaxed_simd: Some(features.contains(WasmFeatures::RELAXED_SIMD)),
1458 shared_everything_threads: Some(
1459 features.contains(WasmFeatures::SHARED_EVERYTHING_THREADS),
1460 ),
1461 simd: Some(features.contains(WasmFeatures::SIMD)),
1462 stack_switching: Some(features.contains(WasmFeatures::STACK_SWITCHING)),
1463 tail_call: Some(features.contains(WasmFeatures::TAIL_CALL)),
1464 threads: Some(features.contains(WasmFeatures::THREADS)),
1465 wide_arithmetic: Some(features.contains(WasmFeatures::WIDE_ARITHMETIC)),
1466 concurrency_support: Some(engine.get_concurrency_support()),
1467 epoch_interruption: Some(engine.get_epoch_interruption()),
1468 fuel: if engine.get_consume_fuel() {
1469 Some(1)
1470 } else {
1471 None
1472 },
1473 max_wasm_stack: Some(engine.get_max_wasm_stack()),
1474 nan_canonicalization: engine.get_cranelift_nan_canonicalization(),
1475 relaxed_simd_deterministic: Some(engine.get_relaxed_simd_deterministic()),
1476 shared_memory: Some(engine.get_shared_memory()),
1477
1478 all_proposals: None,
1480 max_instances: None,
1483 max_memories: None,
1484 max_memory_size: None,
1485 max_table_elements: None,
1486 max_tables: None,
1487 timeout: None,
1489 trap_on_grow_failure: None,
1490 unknown_exports_allow: None,
1491 unknown_imports_default: None,
1492 unknown_imports_trap: None,
1493 wmemcheck: None,
1494 },
1495
1496 record: RecordOptions::default(),
1498
1499 wasi: Default::default(),
1501
1502 configured: true,
1504 codegen_raw: Default::default(),
1505 debug_raw: Default::default(),
1506 opts_raw: Default::default(),
1507 record_raw: Default::default(),
1508 wasi_raw: Default::default(),
1509 wasm_raw: Default::default(),
1510
1511 config: None,
1513 }
1518 }
1519}
1520
1521#[cfg(test)]
1522mod tests {
1523 use wasmtime::{OptLevel, RegallocAlgorithm};
1524
1525 use super::*;
1526
1527 #[test]
1528 fn from_toml() {
1529 let empty_toml = "";
1531 let mut common_options: CommonOptions = toml::from_str(empty_toml).unwrap();
1532 common_options.config(None).unwrap();
1533
1534 let basic_toml = r#"
1536 [optimize]
1537 [codegen]
1538 [debug]
1539 [wasm]
1540 [wasi]
1541 [record]
1542 "#;
1543 let mut common_options: CommonOptions = toml::from_str(basic_toml).unwrap();
1544 common_options.config(None).unwrap();
1545
1546 for (opt_value, expected) in [
1548 ("0", Some(OptLevel::None)),
1549 ("1", Some(OptLevel::Speed)),
1550 ("2", Some(OptLevel::Speed)),
1551 ("\"s\"", Some(OptLevel::SpeedAndSize)),
1552 ("\"hello\"", None), ("3", None), ] {
1555 let toml = format!(
1556 r#"
1557 [optimize]
1558 opt-level = {opt_value}
1559 "#,
1560 );
1561 let parsed_opt_level = toml::from_str::<CommonOptions>(&toml)
1562 .ok()
1563 .and_then(|common_options| common_options.opts.opt_level);
1564
1565 assert_eq!(
1566 parsed_opt_level, expected,
1567 "Mismatch for input '{opt_value}'. Parsed: {parsed_opt_level:?}, Expected: {expected:?}"
1568 );
1569 }
1570
1571 for (regalloc_value, expected) in [
1573 ("\"backtracking\"", Some(RegallocAlgorithm::Backtracking)),
1574 ("\"single-pass\"", Some(RegallocAlgorithm::SinglePass)),
1575 ("\"hello\"", None), ("3", None), ("true", None), ] {
1579 let toml = format!(
1580 r#"
1581 [optimize]
1582 regalloc-algorithm = {regalloc_value}
1583 "#,
1584 );
1585 let parsed_regalloc_algorithm = toml::from_str::<CommonOptions>(&toml)
1586 .ok()
1587 .and_then(|common_options| common_options.opts.regalloc_algorithm);
1588 assert_eq!(
1589 parsed_regalloc_algorithm, expected,
1590 "Mismatch for input '{regalloc_value}'. Parsed: {parsed_regalloc_algorithm:?}, Expected: {expected:?}"
1591 );
1592 }
1593
1594 for (strategy_value, expected) in [
1596 ("\"cranelift\"", Some(wasmtime::Strategy::Cranelift)),
1597 ("\"winch\"", Some(wasmtime::Strategy::Winch)),
1598 ("\"hello\"", None), ("5", None), ("true", None), ] {
1602 let toml = format!(
1603 r#"
1604 [codegen]
1605 compiler = {strategy_value}
1606 "#,
1607 );
1608 let parsed_strategy = toml::from_str::<CommonOptions>(&toml)
1609 .ok()
1610 .and_then(|common_options| common_options.codegen.compiler);
1611 assert_eq!(
1612 parsed_strategy, expected,
1613 "Mismatch for input '{strategy_value}'. Parsed: {parsed_strategy:?}, Expected: {expected:?}",
1614 );
1615 }
1616
1617 for (collector_value, expected) in [
1619 (
1620 "\"drc\"",
1621 Some(wasmtime::Collector::DeferredReferenceCounting),
1622 ),
1623 ("\"null\"", Some(wasmtime::Collector::Null)),
1624 ("\"copying\"", Some(wasmtime::Collector::Copying)),
1625 ("\"hello\"", None), ("5", None), ("true", None), ] {
1629 let toml = format!(
1630 r#"
1631 [codegen]
1632 collector = {collector_value}
1633 "#,
1634 );
1635 let parsed_collector = toml::from_str::<CommonOptions>(&toml)
1636 .ok()
1637 .and_then(|common_options| common_options.codegen.collector);
1638 assert_eq!(
1639 parsed_collector, expected,
1640 "Mismatch for input '{collector_value}'. Parsed: {parsed_collector:?}, Expected: {expected:?}",
1641 );
1642 }
1643 }
1644}
1645
1646impl Default for CommonOptions {
1647 fn default() -> CommonOptions {
1648 CommonOptions::new()
1649 }
1650}
1651
1652impl fmt::Display for CommonOptions {
1653 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1654 let CommonOptions {
1655 codegen_raw,
1656 codegen,
1657 debug_raw,
1658 debug,
1659 opts_raw,
1660 opts,
1661 wasm_raw,
1662 wasm,
1663 wasi_raw,
1664 wasi,
1665 record_raw,
1666 record,
1667 configured,
1668 target,
1669 config,
1670 } = self;
1671 if let Some(target) = target {
1672 write!(f, "--target {target} ")?;
1673 }
1674 if let Some(config) = config {
1675 write!(f, "--config {} ", config.display())?;
1676 }
1677
1678 let codegen_flags;
1679 let opts_flags;
1680 let wasi_flags;
1681 let wasm_flags;
1682 let debug_flags;
1683 let record_flags;
1684
1685 if *configured {
1686 codegen_flags = codegen.to_options();
1687 debug_flags = debug.to_options();
1688 wasi_flags = wasi.to_options();
1689 wasm_flags = wasm.to_options();
1690 opts_flags = opts.to_options();
1691 record_flags = record.to_options();
1692 } else {
1693 codegen_flags = codegen_raw
1694 .iter()
1695 .flat_map(|t| t.0.iter())
1696 .cloned()
1697 .collect();
1698 debug_flags = debug_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1699 wasi_flags = wasi_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1700 wasm_flags = wasm_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1701 opts_flags = opts_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1702 record_flags = record_raw
1703 .iter()
1704 .flat_map(|t| t.0.iter())
1705 .cloned()
1706 .collect();
1707 }
1708
1709 for flag in codegen_flags {
1710 write!(f, "-C{flag} ")?;
1711 }
1712 for flag in opts_flags {
1713 write!(f, "-O{flag} ")?;
1714 }
1715 for flag in wasi_flags {
1716 write!(f, "-S{flag} ")?;
1717 }
1718 for flag in wasm_flags {
1719 write!(f, "-W{flag} ")?;
1720 }
1721 for flag in debug_flags {
1722 write!(f, "-D{flag} ")?;
1723 }
1724 for flag in record_flags {
1725 write!(f, "-R{flag} ")?;
1726 }
1727
1728 Ok(())
1729 }
1730}