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 gc_heap_initial_size: Option<u64>,
91
92 pub guard_before_linear_memory: Option<bool>,
95
96 pub table_lazy_init: Option<bool>,
101
102 pub pooling_allocator: Option<bool>,
104
105 pub pooling_decommit_batch_size: Option<usize>,
108
109 pub pooling_memory_keep_resident: Option<usize>,
112
113 pub pooling_table_keep_resident: Option<usize>,
116
117 #[serde(default)]
120 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
121 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
122 pub pooling_memory_protection_keys: Option<wasmtime::Enabled>,
123
124 pub pooling_max_memory_protection_keys: Option<usize>,
127
128 pub memory_init_cow: Option<bool>,
131
132 pub memory_guaranteed_dense_image_size: Option<u64>,
135
136 pub pooling_total_core_instances: Option<u32>,
139
140 pub pooling_total_component_instances: Option<u32>,
143
144 pub pooling_total_memories: Option<u32>,
147
148 pub pooling_total_tables: Option<u32>,
151
152 pub pooling_total_stacks: Option<u32>,
155
156 pub pooling_max_memory_size: Option<usize>,
159
160 pub pooling_table_elements: Option<usize>,
163
164 pub pooling_max_core_instance_size: Option<usize>,
167
168 pub pooling_max_unused_warm_slots: Option<u32>,
171
172 pub pooling_async_stack_keep_resident: Option<usize>,
175
176 pub pooling_max_component_instance_size: Option<usize>,
179
180 pub pooling_max_core_instances_per_component: Option<u32>,
183
184 pub pooling_max_memories_per_component: Option<u32>,
187
188 pub pooling_max_tables_per_component: Option<u32>,
191
192 pub pooling_max_tables_per_module: Option<u32>,
194
195 pub pooling_max_memories_per_module: Option<u32>,
197
198 pub pooling_total_gc_heaps: Option<u32>,
200
201 pub signals_based_traps: Option<bool>,
203
204 pub dynamic_memory_guard_size: Option<u64>,
206
207 pub static_memory_guard_size: Option<u64>,
209
210 pub static_memory_forced: Option<bool>,
212
213 pub static_memory_maximum_size: Option<u64>,
215
216 pub dynamic_memory_reserved_for_growth: Option<u64>,
218
219 #[serde(default)]
222 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
223 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
224 pub pooling_pagemap_scan: Option<wasmtime::Enabled>,
225
226 #[doc(hidden)]
228 pub gc_zeal_alloc_counter: Option<NonZeroU32>,
229 }
230
231 enum Optimize {
232 ...
233 }
234}
235
236wasmtime_option_group! {
237 pub struct CodegenOptions {
238 #[serde(default)]
243 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
244 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
245 pub compiler: Option<wasmtime::Strategy>,
246 #[serde(default)]
258 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
259 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
260 pub collector: Option<wasmtime::Collector>,
261 pub cranelift_debug_verifier: Option<bool>,
263 pub cache: Option<bool>,
265 pub cache_config: Option<String>,
267 pub parallel_compilation: Option<bool>,
269 pub native_unwind_info: Option<bool>,
272
273 #[serde(default)]
275 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
276 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
277 pub inlining: Option<wasmtime::Inlining>,
278
279 pub metadata_for_internal_asserts: Option<bool>,
282 pub metadata_for_gc_heap_corruption: Option<bool>,
285
286 #[prefixed = "cranelift"]
287 #[serde(default)]
288 pub cranelift: Vec<(String, Option<String>)>,
291 }
292
293 enum Codegen {
294 ...
295 }
296}
297
298wasmtime_option_group! {
299 pub struct DebugOptions {
300 pub debug_info: Option<bool>,
302 pub guest_debug: Option<bool>,
304 pub address_map: Option<bool>,
306 pub logging: Option<bool>,
308 pub log_to_files: Option<bool>,
310 pub coredump: Option<String>,
312 pub debugger: Option<PathBuf>,
315 #[serde(default)]
318 pub arg: Vec<String>,
319 pub inherit_stdin: Option<bool>,
322 pub inherit_stdout: Option<bool>,
325 pub inherit_stderr: Option<bool>,
328 pub max_backtrace: Option<usize>,
330 pub symbols: Option<bool>,
332 }
333
334 enum Debug {
335 ...
336 }
337}
338
339wasmtime_option_group! {
340 pub struct WasmOptions {
341 pub nan_canonicalization: Option<bool>,
343 pub fuel: Option<u64>,
351 pub epoch_interruption: Option<bool>,
354 pub max_wasm_stack: Option<usize>,
357 pub async_stack_size: Option<usize>,
363 pub async_stack_zeroing: Option<bool>,
366 pub unknown_exports_allow: Option<bool>,
368 pub unknown_imports_trap: Option<bool>,
371 pub unknown_imports_default: Option<bool>,
374 pub wmemcheck: Option<bool>,
376 pub max_memory_size: Option<usize>,
381 pub max_table_elements: Option<usize>,
383 pub max_instances: Option<usize>,
385 pub max_tables: Option<usize>,
387 pub max_memories: Option<usize>,
389 pub trap_on_grow_failure: Option<bool>,
396 pub timeout: Option<Duration>,
398 pub all_proposals: Option<bool>,
400 pub bulk_memory: Option<bool>,
402 pub multi_memory: Option<bool>,
404 pub multi_value: Option<bool>,
406 pub reference_types: Option<bool>,
408 pub simd: Option<bool>,
410 pub relaxed_simd: Option<bool>,
412 pub relaxed_simd_deterministic: Option<bool>,
421 pub tail_call: Option<bool>,
423 pub threads: Option<bool>,
425 pub shared_memory: Option<bool>,
427 pub shared_everything_threads: Option<bool>,
429 pub memory64: Option<bool>,
431 pub component_model: Option<bool>,
433 pub component_model_async: Option<bool>,
435 pub component_model_more_async_builtins: Option<bool>,
438 pub component_model_async_stackful: Option<bool>,
441 pub component_model_threading: Option<bool>,
444 pub component_model_error_context: Option<bool>,
447 pub component_model_gc: Option<bool>,
450 pub component_model_map: Option<bool>,
452 pub function_references: Option<bool>,
454 pub stack_switching: Option<bool>,
456 pub gc: Option<bool>,
458 pub custom_page_sizes: Option<bool>,
460 pub wide_arithmetic: Option<bool>,
462 pub branch_hinting: Option<bool>,
464 pub extended_const: Option<bool>,
466 pub exceptions: Option<bool>,
468 pub gc_support: Option<bool>,
470 pub component_model_fixed_length_lists: Option<bool>,
473 pub component_model_implements: Option<bool>,
476 pub concurrency_support: Option<bool>,
479 }
480
481 enum Wasm {
482 ...
483 }
484}
485
486wasmtime_option_group! {
487 pub struct WasiOptions {
488 pub cli: Option<bool>,
490 pub cli_exit_with_code: Option<bool>,
492 pub common: Option<bool>,
494 pub nn: Option<bool>,
496 pub threads: Option<bool>,
498 pub http: Option<bool>,
500 pub http_outgoing_body_buffer_chunks: Option<usize>,
504 pub http_outgoing_body_chunk_size: Option<usize>,
507 pub config: Option<bool>,
509 pub keyvalue: Option<bool>,
511 pub listenfd: Option<bool>,
515 #[serde(default)]
518 pub tcplisten: Vec<String>,
519 pub tls: Option<bool>,
521 pub preview2: Option<bool>,
524 #[serde(skip)]
533 pub nn_graph: Vec<WasiNnGraph>,
534 pub inherit_network: Option<bool>,
537 pub allow_ip_name_lookup: Option<bool>,
539 pub tcp: Option<bool>,
541 pub udp: Option<bool>,
543 pub network_error_code: Option<bool>,
545 pub preview0: Option<bool>,
547 pub inherit_env: Option<bool>,
551 pub inherit_stdin: Option<bool>,
553 pub inherit_stdout: Option<bool>,
555 pub inherit_stderr: Option<bool>,
557 pub cwd: Option<String>,
559 #[serde(skip)]
561 pub config_var: Vec<KeyValuePair>,
562 #[serde(skip)]
564 pub keyvalue_in_memory_data: Vec<KeyValuePair>,
565 pub p3: Option<bool>,
567 pub max_resources: Option<usize>,
569 pub hostcall_fuel: Option<usize>,
571 pub max_random_size: Option<u64>,
575 pub max_http_fields_size: Option<usize>,
579 }
580
581 enum Wasi {
582 ...
583 }
584}
585
586wasmtime_option_group! {
587 pub struct RecordOptions {
588 pub path: Option<String>,
590 pub validation_metadata: Option<bool>,
593 pub event_window_size: Option<usize>,
596 }
597
598 enum Record {
599 ...
600 }
601}
602
603#[derive(Debug, Clone, PartialEq)]
604pub struct WasiNnGraph {
605 pub format: String,
606 pub dir: String,
607}
608
609#[derive(Debug, Clone, PartialEq)]
610pub struct KeyValuePair {
611 pub key: String,
612 pub value: String,
613}
614
615#[derive(Clone)]
617#[cfg_attr(feature = "clap", derive(clap::Parser))]
618#[cfg_attr(
619 feature = "serde",
620 derive(serde_derive::Deserialize, serde_derive::Serialize)
621)]
622#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
623pub struct CommonOptions {
624 #[cfg_attr(
634 feature = "clap",
635 arg(short = 'O', long = "optimize", value_name = "KEY[=VAL[,..]]")
636 )]
637 #[cfg_attr(feature = "serde", serde(skip))]
638 opts_raw: Vec<opt::CommaSeparated<Optimize>>,
639
640 #[cfg_attr(
642 feature = "clap",
643 arg(short = 'C', long = "codegen", value_name = "KEY[=VAL[,..]]")
644 )]
645 #[cfg_attr(feature = "serde", serde(skip))]
646 codegen_raw: Vec<opt::CommaSeparated<Codegen>>,
647
648 #[cfg_attr(
650 feature = "clap",
651 arg(short = 'D', long = "debug", value_name = "KEY[=VAL[,..]]")
652 )]
653 #[cfg_attr(feature = "serde", serde(skip))]
654 debug_raw: Vec<opt::CommaSeparated<Debug>>,
655
656 #[cfg_attr(
659 feature = "clap",
660 arg(short = 'W', long = "wasm", value_name = "KEY[=VAL[,..]]")
661 )]
662 #[cfg_attr(feature = "serde", serde(skip))]
663 wasm_raw: Vec<opt::CommaSeparated<Wasm>>,
664
665 #[cfg_attr(
667 feature = "clap",
668 arg(short = 'S', long = "wasi", value_name = "KEY[=VAL[,..]]")
669 )]
670 #[cfg_attr(feature = "serde", serde(skip))]
671 wasi_raw: Vec<opt::CommaSeparated<Wasi>>,
672
673 #[cfg_attr(
682 feature = "clap",
683 arg(short = 'R', long = "record", value_name = "KEY[=VAL[,..]]")
684 )]
685 #[cfg_attr(feature = "serde", serde(skip))]
686 record_raw: Vec<opt::CommaSeparated<Record>>,
687
688 #[cfg_attr(feature = "clap", arg(skip))]
691 #[cfg_attr(feature = "serde", serde(skip))]
692 configured: bool,
693
694 #[cfg_attr(feature = "clap", arg(skip))]
695 #[cfg_attr(feature = "serde", serde(rename = "optimize", default))]
696 pub opts: OptimizeOptions,
697
698 #[cfg_attr(feature = "clap", arg(skip))]
699 #[cfg_attr(feature = "serde", serde(default))]
700 pub codegen: CodegenOptions,
701
702 #[cfg_attr(feature = "clap", arg(skip))]
703 #[cfg_attr(feature = "serde", serde(default))]
704 pub debug: DebugOptions,
705
706 #[cfg_attr(feature = "clap", arg(skip))]
707 #[cfg_attr(feature = "serde", serde(default))]
708 pub wasm: WasmOptions,
709
710 #[cfg_attr(feature = "clap", arg(skip))]
711 #[cfg_attr(feature = "serde", serde(default))]
712 pub wasi: WasiOptions,
713
714 #[cfg_attr(feature = "clap", arg(skip))]
715 #[cfg_attr(feature = "serde", serde(default))]
716 pub record: RecordOptions,
717
718 #[cfg_attr(feature = "clap", arg(long, value_name = "TARGET"))]
720 #[cfg_attr(feature = "serde", serde(skip))]
721 pub target: Option<String>,
722
723 #[cfg_attr(feature = "clap", arg(long = "config", value_name = "FILE"))]
730 #[cfg_attr(feature = "serde", serde(skip))]
731 pub config: Option<PathBuf>,
732}
733
734macro_rules! match_feature {
735 (
736 [$feat:tt : $config:expr]
737 $val:ident => $e:expr,
738 $p:pat => err,
739 ) => {
740 #[cfg(feature = $feat)]
741 {
742 if let Some($val) = $config {
743 $e;
744 }
745 }
746 #[cfg(not(feature = $feat))]
747 {
748 if let Some($p) = $config {
749 bail!(concat!("support for ", $feat, " disabled at compile time"));
750 }
751 }
752 };
753}
754
755impl CommonOptions {
756 pub fn new() -> CommonOptions {
758 CommonOptions {
759 opts_raw: Vec::new(),
760 codegen_raw: Vec::new(),
761 debug_raw: Vec::new(),
762 wasm_raw: Vec::new(),
763 wasi_raw: Vec::new(),
764 record_raw: Vec::new(),
765 configured: true,
766 opts: Default::default(),
767 codegen: Default::default(),
768 debug: Default::default(),
769 wasm: Default::default(),
770 wasi: Default::default(),
771 record: Default::default(),
772 target: None,
773 config: None,
774 }
775 }
776
777 fn configure(&mut self) -> Result<()> {
778 if self.configured {
779 return Ok(());
780 }
781 self.configured = true;
782 if let Some(toml_config_path) = &self.config {
783 #[cfg(feature = "toml")]
784 {
785 let toml_options = CommonOptions::from_file(toml_config_path)?;
786 self.opts = toml_options.opts;
787 self.codegen = toml_options.codegen;
788 self.debug = toml_options.debug;
789 self.wasm = toml_options.wasm;
790 self.wasi = toml_options.wasi;
791 self.record = toml_options.record;
792 }
793 #[cfg(not(feature = "toml"))]
794 {
795 bail!(
796 "support for loading a configuration file from \
797 {toml_config_path:?} disabled at compile time"
798 );
799 }
800 }
801 self.opts.configure_with(&self.opts_raw);
802 self.codegen.configure_with(&self.codegen_raw);
803 self.debug.configure_with(&self.debug_raw);
804 self.wasm.configure_with(&self.wasm_raw);
805 self.wasi.configure_with(&self.wasi_raw);
806 self.record.configure_with(&self.record_raw);
807 Ok(())
808 }
809
810 pub fn init_logging(&mut self) -> Result<()> {
811 self.configure()?;
812 if self.debug.logging == Some(false) {
813 return Ok(());
814 }
815 #[cfg(feature = "logging")]
816 if self.debug.log_to_files == Some(true) {
817 let prefix = "wasmtime.dbg.";
818 init_file_per_thread_logger(prefix);
819 } else {
820 use std::io::IsTerminal;
821 use tracing_subscriber::{EnvFilter, FmtSubscriber};
822 let builder = FmtSubscriber::builder()
823 .with_writer(std::io::stderr)
824 .with_env_filter(EnvFilter::from_env("WASMTIME_LOG"))
825 .with_ansi(std::io::stderr().is_terminal());
826 if std::env::var("WASMTIME_LOG_NO_CONTEXT").is_ok_and(|value| value.eq("1")) {
827 builder
828 .with_level(false)
829 .with_target(false)
830 .without_time()
831 .init()
832 } else {
833 builder.init();
834 }
835 }
836 #[cfg(not(feature = "logging"))]
837 if self.debug.log_to_files == Some(true) || self.debug.logging == Some(true) {
838 bail!("support for logging disabled at compile time");
839 }
840 Ok(())
841 }
842
843 pub fn config(&mut self, pooling_allocator_default: Option<bool>) -> Result<Config> {
844 self.configure()?;
845 let mut config = Config::new();
846
847 match_feature! {
848 ["cranelift" : self.codegen.compiler]
849 strategy => config.strategy(strategy),
850 _ => err,
851 }
852 match_feature! {
853 ["gc" : self.codegen.collector]
854 collector => config.collector(collector),
855 _ => err,
856 }
857 if let Some(target) = &self.target {
858 config.target(target)?;
859 }
860 match_feature! {
861 ["cranelift" : self.codegen.cranelift_debug_verifier]
862 enable => config.cranelift_debug_verifier(enable),
863 true => err,
864 }
865 if let Some(enable) = self.debug.debug_info {
866 config.debug_info(enable);
867 }
868 match_feature! {
869 ["debug" : self.debug.guest_debug]
870 enable => config.guest_debug(enable),
871 _ => err,
872 }
873 if self.debug.coredump.is_some() {
874 #[cfg(feature = "coredump")]
875 config.coredump_on_trap(true);
876 #[cfg(not(feature = "coredump"))]
877 bail!("support for coredumps disabled at compile time");
878 }
879 match_feature! {
880 ["cranelift" : self.opts.opt_level]
881 level => config.cranelift_opt_level(level),
882 _ => err,
883 }
884 match_feature! {
885 ["cranelift": self.opts.regalloc_algorithm]
886 algo => config.cranelift_regalloc_algorithm(algo),
887 _ => err,
888 }
889 match_feature! {
890 ["cranelift" : self.wasm.nan_canonicalization]
891 enable => config.cranelift_nan_canonicalization(enable),
892 true => err,
893 }
894
895 self.enable_wasm_features(&mut config)?;
896
897 #[cfg(feature = "cranelift")]
898 for (name, value) in self.codegen.cranelift.iter() {
899 let name = name.replace('-', "_");
900 unsafe {
901 match value {
902 Some(val) => {
903 config.cranelift_flag_set(&name, val);
904 }
905 None => {
906 config.cranelift_flag_enable(&name);
907 }
908 }
909 }
910 }
911 #[cfg(not(feature = "cranelift"))]
912 if !self.codegen.cranelift.is_empty() {
913 bail!("support for cranelift disabled at compile time");
914 }
915
916 #[cfg(feature = "cache")]
917 if self.codegen.cache != Some(false) {
918 use wasmtime::Cache;
919 let cache = match &self.codegen.cache_config {
920 Some(path) => Cache::from_file(Some(std::path::Path::new(path)))?,
921 None => Cache::from_file(None)?,
922 };
923 config.cache(Some(cache));
924 }
925 #[cfg(not(feature = "cache"))]
926 if self.codegen.cache == Some(true) {
927 bail!("support for caching disabled at compile time");
928 }
929
930 match_feature! {
931 ["parallel-compilation" : self.codegen.parallel_compilation]
932 enable => config.parallel_compilation(enable),
933 true => err,
934 }
935
936 let memory_reservation = self
937 .opts
938 .memory_reservation
939 .or(self.opts.static_memory_maximum_size);
940 if let Some(size) = memory_reservation {
941 config.memory_reservation(size);
942 }
943
944 if let Some(enable) = self.opts.static_memory_forced {
945 config.memory_may_move(!enable);
946 }
947 if let Some(enable) = self.opts.memory_may_move {
948 config.memory_may_move(enable);
949 }
950
951 let memory_guard_size = self
952 .opts
953 .static_memory_guard_size
954 .or(self.opts.dynamic_memory_guard_size)
955 .or(self.opts.memory_guard_size);
956 if let Some(size) = memory_guard_size {
957 config.memory_guard_size(size);
958 }
959
960 let mem_for_growth = self
961 .opts
962 .memory_reservation_for_growth
963 .or(self.opts.dynamic_memory_reserved_for_growth);
964 if let Some(size) = mem_for_growth {
965 config.memory_reservation_for_growth(size);
966 }
967 if let Some(enable) = self.opts.guard_before_linear_memory {
968 config.guard_before_linear_memory(enable);
969 }
970
971 if let Some(size) = self.opts.gc_heap_reservation {
972 config.gc_heap_reservation(size);
973 }
974 if let Some(enable) = self.opts.gc_heap_may_move {
975 config.gc_heap_may_move(enable);
976 }
977 if let Some(size) = self.opts.gc_heap_guard_size {
978 config.gc_heap_guard_size(size);
979 }
980 if let Some(size) = self.opts.gc_heap_reservation_for_growth {
981 config.gc_heap_reservation_for_growth(size);
982 }
983
984 if let Some(size) = self.opts.gc_heap_initial_size {
985 config.gc_heap_initial_size(size);
986 }
987
988 if let Some(enable) = self.opts.table_lazy_init {
989 config.table_lazy_init(enable);
990 }
991
992 if let Some(n) = self.opts.gc_zeal_alloc_counter
993 && (cfg!(gc_zeal) || cfg!(fuzzing))
994 {
995 config.gc_zeal_alloc_counter(Some(n))?;
996 }
997
998 if self.wasm.fuel.is_some() {
1000 config.consume_fuel(true);
1001 }
1002
1003 if let Some(enable) = self.wasm.epoch_interruption {
1004 config.epoch_interruption(enable);
1005 }
1006 if let Some(enable) = self.debug.address_map {
1007 config.generate_address_map(enable);
1008 }
1009 if let Some(frames) = self.debug.max_backtrace {
1010 match NonZeroUsize::new(frames) {
1011 None => {
1012 config.wasm_backtrace_details(WasmBacktraceDetails::Disable);
1013 }
1014 Some(amt) => {
1015 config.wasm_backtrace_max_frames(Some(amt));
1016 }
1017 }
1018 }
1019 if let Some(enable) = self.debug.symbols {
1020 config.debug_symbols(enable);
1021 }
1022 if let Some(enable) = self.opts.memory_init_cow {
1023 config.memory_init_cow(enable);
1024 }
1025 if let Some(size) = self.opts.memory_guaranteed_dense_image_size {
1026 config.memory_guaranteed_dense_image_size(size);
1027 }
1028 if let Some(enable) = self.opts.signals_based_traps {
1029 config.signals_based_traps(enable);
1030 }
1031 if let Some(enable) = self.codegen.native_unwind_info {
1032 config.native_unwind_info(enable);
1033 }
1034 if let Some(enable) = self.codegen.inlining {
1035 config.compiler_inlining(enable);
1036 }
1037 if let Some(enable) = self.codegen.metadata_for_internal_asserts {
1038 config.metadata_for_internal_asserts(enable);
1039 }
1040 if let Some(enable) = self.codegen.metadata_for_gc_heap_corruption {
1041 config.metadata_for_gc_heap_corruption(enable);
1042 }
1043
1044 #[cfg(any(feature = "async", feature = "stack-switching"))]
1047 {
1048 if let Some(size) = self.wasm.async_stack_size {
1049 config.async_stack_size(size);
1050 }
1051 }
1052 #[cfg(not(any(feature = "async", feature = "stack-switching")))]
1053 {
1054 if let Some(_size) = self.wasm.async_stack_size {
1055 bail!(concat!(
1056 "support for async/stack-switching disabled at compile time"
1057 ));
1058 }
1059 }
1060
1061 match_feature! {
1062 ["pooling-allocator" : self.opts.pooling_allocator.or(pooling_allocator_default)]
1063 enable => {
1064 if enable {
1065 let mut cfg = wasmtime::PoolingAllocationConfig::default();
1066 if let Some(size) = self.opts.pooling_memory_keep_resident {
1067 cfg.linear_memory_keep_resident(size);
1068 }
1069 if let Some(size) = self.opts.pooling_table_keep_resident {
1070 cfg.table_keep_resident(size);
1071 }
1072 if let Some(limit) = self.opts.pooling_total_core_instances {
1073 cfg.total_core_instances(limit);
1074 }
1075 if let Some(limit) = self.opts.pooling_total_component_instances {
1076 cfg.total_component_instances(limit);
1077 }
1078 if let Some(limit) = self.opts.pooling_total_memories {
1079 cfg.total_memories(limit);
1080 }
1081 if let Some(limit) = self.opts.pooling_total_tables {
1082 cfg.total_tables(limit);
1083 }
1084 if let Some(limit) = self.opts.pooling_table_elements
1085 .or(self.wasm.max_table_elements)
1086 {
1087 cfg.table_elements(limit);
1088 }
1089 if let Some(limit) = self.opts.pooling_max_core_instance_size {
1090 cfg.max_core_instance_size(limit);
1091 }
1092 match_feature! {
1093 ["async" : self.opts.pooling_total_stacks]
1094 limit => cfg.total_stacks(limit),
1095 _ => err,
1096 }
1097 if let Some(max) = self.opts.pooling_max_memory_size
1098 .or(self.wasm.max_memory_size)
1099 {
1100 cfg.max_memory_size(max);
1101 }
1102 if let Some(size) = self.opts.pooling_decommit_batch_size {
1103 cfg.decommit_batch_size(size);
1104 }
1105 if let Some(max) = self.opts.pooling_max_unused_warm_slots {
1106 cfg.max_unused_warm_slots(max);
1107 }
1108 match_feature! {
1109 ["async" : self.opts.pooling_async_stack_keep_resident]
1110 size => cfg.async_stack_keep_resident(size),
1111 _ => err,
1112 }
1113 if let Some(max) = self.opts.pooling_max_component_instance_size {
1114 cfg.max_component_instance_size(max);
1115 }
1116 if let Some(max) = self.opts.pooling_max_core_instances_per_component {
1117 cfg.max_core_instances_per_component(max);
1118 }
1119 if let Some(max) = self.opts.pooling_max_memories_per_component {
1120 cfg.max_memories_per_component(max);
1121 }
1122 if let Some(max) = self.opts.pooling_max_tables_per_component {
1123 cfg.max_tables_per_component(max);
1124 }
1125 if let Some(max) = self.opts.pooling_max_tables_per_module {
1126 cfg.max_tables_per_module(max);
1127 }
1128 if let Some(max) = self.opts.pooling_max_memories_per_module {
1129 cfg.max_memories_per_module(max);
1130 }
1131 match_feature! {
1132 ["memory-protection-keys" : self.opts.pooling_memory_protection_keys]
1133 enable => cfg.memory_protection_keys(enable),
1134 _ => err,
1135 }
1136 match_feature! {
1137 ["memory-protection-keys" : self.opts.pooling_max_memory_protection_keys]
1138 max => cfg.max_memory_protection_keys(max),
1139 _ => err,
1140 }
1141 match_feature! {
1142 ["gc" : self.opts.pooling_total_gc_heaps]
1143 max => cfg.total_gc_heaps(max),
1144 _ => err,
1145 }
1146 if let Some(enabled) = self.opts.pooling_pagemap_scan {
1147 cfg.pagemap_scan(enabled);
1148 }
1149 config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling(cfg));
1150 }
1151 },
1152 true => err,
1153 }
1154
1155 if self.opts.pooling_memory_protection_keys.is_some()
1156 && !self.opts.pooling_allocator.unwrap_or(false)
1157 {
1158 bail!("memory protection keys require the pooling allocator");
1159 }
1160
1161 if self.opts.pooling_max_memory_protection_keys.is_some()
1162 && !self.opts.pooling_memory_protection_keys.is_some()
1163 {
1164 bail!("max memory protection keys requires memory protection keys to be enabled");
1165 }
1166
1167 match_feature! {
1168 ["async" : self.wasm.async_stack_zeroing]
1169 enable => config.async_stack_zeroing(enable),
1170 _ => err,
1171 }
1172
1173 if let Some(max) = self.wasm.max_wasm_stack {
1174 config.max_wasm_stack(max);
1175
1176 #[cfg(any(feature = "async", feature = "stack-switching"))]
1180 if self.wasm.async_stack_size.is_none() {
1181 const DEFAULT_HOST_STACK: usize = 512 << 10;
1182 config.async_stack_size(max + DEFAULT_HOST_STACK);
1183 }
1184 }
1185
1186 if let Some(enable) = self.wasm.relaxed_simd_deterministic {
1187 config.relaxed_simd_deterministic(enable);
1188 }
1189 match_feature! {
1190 ["cranelift" : self.wasm.wmemcheck]
1191 enable => config.wmemcheck(enable),
1192 true => err,
1193 }
1194
1195 if let Some(enable) = self.wasm.gc_support {
1196 config.gc_support(enable);
1197 }
1198
1199 if let Some(enable) = self.wasm.concurrency_support {
1200 config.concurrency_support(enable);
1201 }
1202
1203 if let Some(enable) = self.wasm.shared_memory {
1204 config.shared_memory(enable);
1205 }
1206
1207 let record = &self.record;
1208 match_feature! {
1209 ["rr" : &record.path]
1210 _path => {
1211 bail!("recording configuration for `rr` feature is not supported yet");
1212 },
1213 _ => err,
1214 }
1215
1216 Ok(config)
1217 }
1218
1219 pub fn enable_wasm_features(&self, config: &mut Config) -> Result<()> {
1220 let all = self.wasm.all_proposals;
1221
1222 if let Some(enable) = self.wasm.simd.or(all) {
1223 config.wasm_simd(enable);
1224 }
1225 if let Some(enable) = self.wasm.relaxed_simd.or(all) {
1226 config.wasm_relaxed_simd(enable);
1227 }
1228 if let Some(enable) = self.wasm.bulk_memory.or(all) {
1229 config.wasm_bulk_memory(enable);
1230 }
1231 if let Some(enable) = self.wasm.multi_value.or(all) {
1232 config.wasm_multi_value(enable);
1233 }
1234 if let Some(enable) = self.wasm.tail_call.or(all) {
1235 config.wasm_tail_call(enable);
1236 }
1237 if let Some(enable) = self.wasm.multi_memory.or(all) {
1238 config.wasm_multi_memory(enable);
1239 }
1240 if let Some(enable) = self.wasm.memory64.or(all) {
1241 config.wasm_memory64(enable);
1242 }
1243 if let Some(enable) = self.wasm.stack_switching {
1244 config.wasm_stack_switching(enable);
1245 }
1246 if let Some(enable) = self.wasm.custom_page_sizes.or(all) {
1247 config.wasm_custom_page_sizes(enable);
1248 }
1249 if let Some(enable) = self.wasm.wide_arithmetic.or(all) {
1250 config.wasm_wide_arithmetic(enable);
1251 }
1252 if let Some(enable) = self.wasm.branch_hinting {
1254 config.wasm_branch_hinting(enable);
1255 }
1256 if let Some(enable) = self.wasm.extended_const.or(all) {
1257 config.wasm_extended_const(enable);
1258 }
1259
1260 macro_rules! handle_conditionally_compiled {
1261 ($(($feature:tt, $field:tt, $method:tt))*) => ($(
1262 if let Some(enable) = self.wasm.$field.or(all) {
1263 #[cfg(feature = $feature)]
1264 config.$method(enable);
1265 #[cfg(not(feature = $feature))]
1266 if enable && all.is_none() {
1267 bail!("support for {} was disabled at compile-time", $feature);
1268 }
1269 }
1270 )*)
1271 }
1272
1273 handle_conditionally_compiled! {
1274 ("component-model", component_model, wasm_component_model)
1275 ("component-model-async", component_model_async, wasm_component_model_async)
1276 ("component-model-async", component_model_more_async_builtins, wasm_component_model_more_async_builtins)
1277 ("component-model-async", component_model_async_stackful, wasm_component_model_async_stackful)
1278 ("component-model-async", component_model_threading, wasm_component_model_threading)
1279 ("component-model", component_model_error_context, wasm_component_model_error_context)
1280 ("component-model", component_model_map, wasm_component_model_map)
1281 ("component-model", component_model_fixed_length_lists, wasm_component_model_fixed_length_lists)
1282 ("component-model", component_model_implements, wasm_component_model_implements)
1283 ("threads", threads, wasm_threads)
1284 ("gc", gc, wasm_gc)
1285 ("gc", reference_types, wasm_reference_types)
1286 ("gc", function_references, wasm_function_references)
1287 ("gc", exceptions, wasm_exceptions)
1288 ("stack-switching", stack_switching, wasm_stack_switching)
1289 }
1290
1291 if let Some(enable) = self.wasm.component_model_gc {
1292 #[cfg(all(feature = "component-model", feature = "gc"))]
1293 config.wasm_component_model_gc(enable);
1294 #[cfg(not(all(feature = "component-model", feature = "gc")))]
1295 if enable && all.is_none() {
1296 bail!("support for `component-model-gc` was disabled at compile time")
1297 }
1298 }
1299
1300 Ok(())
1301 }
1302
1303 #[cfg(feature = "toml")]
1304 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
1305 use wasmtime::error::Context;
1306
1307 let path_ref = path.as_ref();
1308 let file_contents = std::fs::read_to_string(path_ref)
1309 .with_context(|| format!("failed to read config file: {path_ref:?}"))?;
1310 toml::from_str::<CommonOptions>(&file_contents)
1311 .with_context(|| format!("failed to parse TOML config file {path_ref:?}"))
1312 }
1313
1314 pub fn from_engine(engine: &Engine) -> Self {
1327 let features = engine.get_wasm_features();
1328 let pooling = engine.get_pooling_config();
1329 CommonOptions {
1330 target: engine.get_target(),
1331 opts: OptimizeOptions {
1332 memory_may_move: Some(engine.get_memory_may_move()),
1333 memory_reservation: Some(engine.get_memory_reservation()),
1334 memory_reservation_for_growth: Some(engine.get_memory_reservation_for_growth()),
1335 memory_guard_size: Some(engine.get_memory_guard_size()),
1336 gc_heap_may_move: Some(engine.get_gc_heap_may_move()),
1337 gc_heap_reservation: Some(engine.get_gc_heap_reservation()),
1338 gc_heap_reservation_for_growth: Some(engine.get_gc_heap_reservation_for_growth()),
1339 gc_heap_guard_size: Some(engine.get_gc_heap_guard_size()),
1340 gc_heap_initial_size: Some(engine.get_gc_heap_initial_size()),
1341 guard_before_linear_memory: Some(engine.get_guard_before_linear_memory()),
1342 table_lazy_init: Some(engine.get_table_lazy_init()),
1343 memory_init_cow: Some(engine.get_memory_init_cow()),
1344 memory_guaranteed_dense_image_size: Some(
1345 engine.get_memory_guaranteed_dense_image_size(),
1346 ),
1347 signals_based_traps: Some(engine.get_signals_based_traps()),
1348 gc_zeal_alloc_counter: engine.get_gc_zeal_alloc_counter(),
1349 opt_level: engine.get_cranelift_opt_level(),
1350 regalloc_algorithm: engine.get_cranelift_regalloc_algorithm(),
1351 pooling_allocator: Some(pooling.is_some()),
1352 pooling_decommit_batch_size: pooling.map(|c| c.get_decommit_batch_size()),
1353 pooling_memory_keep_resident: pooling.map(|c| c.get_memory_keep_resident()),
1354 pooling_table_keep_resident: pooling.map(|c| c.get_table_keep_resident()),
1355 pooling_max_unused_warm_slots: pooling.map(|c| c.get_max_unused_warm_slots()),
1356 pooling_pagemap_scan: pooling.map(|c| c.get_pagemap_scan()),
1357 pooling_total_core_instances: pooling.map(|c| c.get_total_core_instances()),
1358 pooling_total_component_instances: pooling
1359 .map(|c| c.get_total_component_instances()),
1360 pooling_total_memories: pooling.map(|c| c.get_total_memories()),
1361 pooling_total_tables: pooling.map(|c| c.get_total_tables()),
1362 pooling_max_memory_size: pooling.map(|c| c.get_max_memory_size()),
1363 pooling_table_elements: pooling.map(|c| c.get_table_elements()),
1364 pooling_max_core_instance_size: pooling.map(|c| c.get_max_core_instance_size()),
1365 pooling_max_component_instance_size: pooling
1366 .map(|c| c.get_max_component_instance_size()),
1367 pooling_max_core_instances_per_component: pooling
1368 .map(|c| c.get_max_core_instances_per_component()),
1369 pooling_max_memories_per_component: pooling
1370 .map(|c| c.get_max_memories_per_component()),
1371 pooling_max_tables_per_component: pooling.map(|c| c.get_max_tables_per_component()),
1372 pooling_max_tables_per_module: pooling.map(|c| c.get_max_tables_per_module()),
1373 pooling_max_memories_per_module: pooling.map(|c| c.get_max_memories_per_module()),
1374 pooling_async_stack_keep_resident: pooling
1375 .map(|c| c.get_async_stack_keep_resident()),
1376 pooling_total_stacks: pooling.map(|c| c.get_total_stacks()),
1377 pooling_total_gc_heaps: pooling.map(|c| c.get_total_gc_heaps()),
1378 pooling_memory_protection_keys: pooling.map(|c| c.get_memory_protection_keys()),
1379 pooling_max_memory_protection_keys: pooling
1380 .map(|c| c.get_max_memory_protection_keys()),
1381 dynamic_memory_guard_size: None,
1384 static_memory_guard_size: None,
1385 static_memory_forced: None,
1386 static_memory_maximum_size: None,
1387 dynamic_memory_reserved_for_growth: None,
1388 },
1389 codegen: CodegenOptions {
1390 compiler: engine.get_strategy(),
1391 collector: engine.get_collector(),
1392 cranelift_debug_verifier: engine.get_cranelift_debug_verifier(),
1393 inlining: Some(engine.get_compiler_inlining()),
1394 native_unwind_info: engine.get_native_unwind_info(),
1395 parallel_compilation: Some(engine.get_parallel_compilation()),
1396 metadata_for_internal_asserts: Some(engine.get_metadata_for_internal_asserts()),
1397 metadata_for_gc_heap_corruption: Some(engine.get_metadata_for_gc_heap_corruption()),
1398 cranelift: engine
1399 .get_cranelift_flags_set()
1400 .map(|(k, v)| (k.to_string(), Some(v.to_string())))
1401 .chain(
1402 engine
1403 .get_cranelift_flags_enabled()
1404 .map(|k| (k.to_string(), None)),
1405 )
1406 .collect(),
1407
1408 cache: None,
1411 cache_config: None,
1412 },
1413 debug: DebugOptions {
1414 address_map: Some(engine.get_generate_address_map()),
1415 debug_info: Some(engine.get_debug_info()),
1416 guest_debug: Some(engine.get_guest_debug()),
1417 symbols: Some(engine.get_debug_symbols()),
1418 max_backtrace: Some(engine.get_wasm_backtrace_max_frames()),
1419
1420 coredump: None,
1423 debugger: None,
1425 arg: Vec::new(),
1426 inherit_stderr: None,
1427 inherit_stdout: None,
1428 inherit_stdin: None,
1429 log_to_files: None,
1431 logging: None,
1432 },
1433 wasm: WasmOptions {
1434 async_stack_size: Some(engine.get_async_stack_size()),
1435 async_stack_zeroing: Some(engine.get_async_stack_zeroing()),
1436 branch_hinting: Some(engine.get_wasm_branch_hinting()),
1437 bulk_memory: Some(features.contains(WasmFeatures::BULK_MEMORY)),
1438 component_model: Some(features.contains(WasmFeatures::COMPONENT_MODEL)),
1439 component_model_async: Some(features.contains(WasmFeatures::CM_ASYNC)),
1440 component_model_async_stackful: Some(
1441 features.contains(WasmFeatures::CM_ASYNC_STACKFUL),
1442 ),
1443 component_model_error_context: Some(
1444 features.contains(WasmFeatures::CM_ERROR_CONTEXT),
1445 ),
1446 component_model_gc: Some(features.contains(WasmFeatures::CM_GC)),
1447 component_model_fixed_length_lists: Some(
1448 features.contains(WasmFeatures::CM_FIXED_LENGTH_LISTS),
1449 ),
1450 component_model_implements: Some(features.contains(WasmFeatures::CM_IMPLEMENTS)),
1451 component_model_map: Some(features.contains(WasmFeatures::CM_MAP)),
1452 component_model_more_async_builtins: Some(
1453 features.contains(WasmFeatures::CM_MORE_ASYNC_BUILTINS),
1454 ),
1455 component_model_threading: Some(features.contains(WasmFeatures::CM_THREADING)),
1456 custom_page_sizes: Some(features.contains(WasmFeatures::CUSTOM_PAGE_SIZES)),
1457 exceptions: Some(features.contains(WasmFeatures::EXCEPTIONS)),
1458 extended_const: Some(features.contains(WasmFeatures::EXTENDED_CONST)),
1459 function_references: Some(features.contains(WasmFeatures::FUNCTION_REFERENCES)),
1460 gc: Some(features.contains(WasmFeatures::GC)),
1461 gc_support: Some(features.contains(WasmFeatures::GC_TYPES)),
1462 memory64: Some(features.contains(WasmFeatures::MEMORY64)),
1463 multi_memory: Some(features.contains(WasmFeatures::MULTI_MEMORY)),
1464 multi_value: Some(features.contains(WasmFeatures::MULTI_VALUE)),
1465 reference_types: Some(features.contains(WasmFeatures::REFERENCE_TYPES)),
1466 relaxed_simd: Some(features.contains(WasmFeatures::RELAXED_SIMD)),
1467 shared_everything_threads: Some(
1468 features.contains(WasmFeatures::SHARED_EVERYTHING_THREADS),
1469 ),
1470 simd: Some(features.contains(WasmFeatures::SIMD)),
1471 stack_switching: Some(features.contains(WasmFeatures::STACK_SWITCHING)),
1472 tail_call: Some(features.contains(WasmFeatures::TAIL_CALL)),
1473 threads: Some(features.contains(WasmFeatures::THREADS)),
1474 wide_arithmetic: Some(features.contains(WasmFeatures::WIDE_ARITHMETIC)),
1475 concurrency_support: Some(engine.get_concurrency_support()),
1476 epoch_interruption: Some(engine.get_epoch_interruption()),
1477 fuel: if engine.get_consume_fuel() {
1478 Some(1)
1479 } else {
1480 None
1481 },
1482 max_wasm_stack: Some(engine.get_max_wasm_stack()),
1483 nan_canonicalization: engine.get_cranelift_nan_canonicalization(),
1484 relaxed_simd_deterministic: Some(engine.get_relaxed_simd_deterministic()),
1485 shared_memory: Some(engine.get_shared_memory()),
1486
1487 all_proposals: None,
1489 max_instances: None,
1492 max_memories: None,
1493 max_memory_size: None,
1494 max_table_elements: None,
1495 max_tables: None,
1496 timeout: None,
1498 trap_on_grow_failure: None,
1499 unknown_exports_allow: None,
1500 unknown_imports_default: None,
1501 unknown_imports_trap: None,
1502 wmemcheck: None,
1503 },
1504
1505 record: RecordOptions::default(),
1507
1508 wasi: Default::default(),
1510
1511 configured: true,
1513 codegen_raw: Default::default(),
1514 debug_raw: Default::default(),
1515 opts_raw: Default::default(),
1516 record_raw: Default::default(),
1517 wasi_raw: Default::default(),
1518 wasm_raw: Default::default(),
1519
1520 config: None,
1522 }
1527 }
1528}
1529
1530#[cfg(test)]
1531mod tests {
1532 use wasmtime::{OptLevel, RegallocAlgorithm};
1533
1534 use super::*;
1535
1536 #[test]
1537 fn from_toml() {
1538 let empty_toml = "";
1540 let mut common_options: CommonOptions = toml::from_str(empty_toml).unwrap();
1541 common_options.config(None).unwrap();
1542
1543 let basic_toml = r#"
1545 [optimize]
1546 [codegen]
1547 [debug]
1548 [wasm]
1549 [wasi]
1550 [record]
1551 "#;
1552 let mut common_options: CommonOptions = toml::from_str(basic_toml).unwrap();
1553 common_options.config(None).unwrap();
1554
1555 for (opt_value, expected) in [
1557 ("0", Some(OptLevel::None)),
1558 ("1", Some(OptLevel::Speed)),
1559 ("2", Some(OptLevel::Speed)),
1560 ("\"s\"", Some(OptLevel::SpeedAndSize)),
1561 ("\"hello\"", None), ("3", None), ] {
1564 let toml = format!(
1565 r#"
1566 [optimize]
1567 opt-level = {opt_value}
1568 "#,
1569 );
1570 let parsed_opt_level = toml::from_str::<CommonOptions>(&toml)
1571 .ok()
1572 .and_then(|common_options| common_options.opts.opt_level);
1573
1574 assert_eq!(
1575 parsed_opt_level, expected,
1576 "Mismatch for input '{opt_value}'. Parsed: {parsed_opt_level:?}, Expected: {expected:?}"
1577 );
1578 }
1579
1580 for (regalloc_value, expected) in [
1582 ("\"backtracking\"", Some(RegallocAlgorithm::Backtracking)),
1583 ("\"single-pass\"", Some(RegallocAlgorithm::SinglePass)),
1584 ("\"hello\"", None), ("3", None), ("true", None), ] {
1588 let toml = format!(
1589 r#"
1590 [optimize]
1591 regalloc-algorithm = {regalloc_value}
1592 "#,
1593 );
1594 let parsed_regalloc_algorithm = toml::from_str::<CommonOptions>(&toml)
1595 .ok()
1596 .and_then(|common_options| common_options.opts.regalloc_algorithm);
1597 assert_eq!(
1598 parsed_regalloc_algorithm, expected,
1599 "Mismatch for input '{regalloc_value}'. Parsed: {parsed_regalloc_algorithm:?}, Expected: {expected:?}"
1600 );
1601 }
1602
1603 for (strategy_value, expected) in [
1605 ("\"cranelift\"", Some(wasmtime::Strategy::Cranelift)),
1606 ("\"winch\"", Some(wasmtime::Strategy::Winch)),
1607 ("\"hello\"", None), ("5", None), ("true", None), ] {
1611 let toml = format!(
1612 r#"
1613 [codegen]
1614 compiler = {strategy_value}
1615 "#,
1616 );
1617 let parsed_strategy = toml::from_str::<CommonOptions>(&toml)
1618 .ok()
1619 .and_then(|common_options| common_options.codegen.compiler);
1620 assert_eq!(
1621 parsed_strategy, expected,
1622 "Mismatch for input '{strategy_value}'. Parsed: {parsed_strategy:?}, Expected: {expected:?}",
1623 );
1624 }
1625
1626 for (collector_value, expected) in [
1628 (
1629 "\"drc\"",
1630 Some(wasmtime::Collector::DeferredReferenceCounting),
1631 ),
1632 ("\"null\"", Some(wasmtime::Collector::Null)),
1633 ("\"copying\"", Some(wasmtime::Collector::Copying)),
1634 ("\"hello\"", None), ("5", None), ("true", None), ] {
1638 let toml = format!(
1639 r#"
1640 [codegen]
1641 collector = {collector_value}
1642 "#,
1643 );
1644 let parsed_collector = toml::from_str::<CommonOptions>(&toml)
1645 .ok()
1646 .and_then(|common_options| common_options.codegen.collector);
1647 assert_eq!(
1648 parsed_collector, expected,
1649 "Mismatch for input '{collector_value}'. Parsed: {parsed_collector:?}, Expected: {expected:?}",
1650 );
1651 }
1652 }
1653}
1654
1655impl Default for CommonOptions {
1656 fn default() -> CommonOptions {
1657 CommonOptions::new()
1658 }
1659}
1660
1661impl fmt::Display for CommonOptions {
1662 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1663 let CommonOptions {
1664 codegen_raw,
1665 codegen,
1666 debug_raw,
1667 debug,
1668 opts_raw,
1669 opts,
1670 wasm_raw,
1671 wasm,
1672 wasi_raw,
1673 wasi,
1674 record_raw,
1675 record,
1676 configured,
1677 target,
1678 config,
1679 } = self;
1680 if let Some(target) = target {
1681 write!(f, "--target {target} ")?;
1682 }
1683 if let Some(config) = config {
1684 write!(f, "--config {} ", config.display())?;
1685 }
1686
1687 let codegen_flags;
1688 let opts_flags;
1689 let wasi_flags;
1690 let wasm_flags;
1691 let debug_flags;
1692 let record_flags;
1693
1694 if *configured {
1695 codegen_flags = codegen.to_options();
1696 debug_flags = debug.to_options();
1697 wasi_flags = wasi.to_options();
1698 wasm_flags = wasm.to_options();
1699 opts_flags = opts.to_options();
1700 record_flags = record.to_options();
1701 } else {
1702 codegen_flags = codegen_raw
1703 .iter()
1704 .flat_map(|t| t.0.iter())
1705 .cloned()
1706 .collect();
1707 debug_flags = debug_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1708 wasi_flags = wasi_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1709 wasm_flags = wasm_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1710 opts_flags = opts_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1711 record_flags = record_raw
1712 .iter()
1713 .flat_map(|t| t.0.iter())
1714 .cloned()
1715 .collect();
1716 }
1717
1718 for flag in codegen_flags {
1719 write!(f, "-C{flag} ")?;
1720 }
1721 for flag in opts_flags {
1722 write!(f, "-O{flag} ")?;
1723 }
1724 for flag in wasi_flags {
1725 write!(f, "-S{flag} ")?;
1726 }
1727 for flag in wasm_flags {
1728 write!(f, "-W{flag} ")?;
1729 }
1730 for flag in debug_flags {
1731 write!(f, "-D{flag} ")?;
1732 }
1733 for flag in record_flags {
1734 write!(f, "-R{flag} ")?;
1735 }
1736
1737 Ok(())
1738 }
1739}