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