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 ("component-model", component_model_memory64, wasm_component_model_memory64)
1287 ("threads", threads, wasm_threads)
1288 ("gc", gc, wasm_gc)
1289 ("gc", reference_types, wasm_reference_types)
1290 ("gc", function_references, wasm_function_references)
1291 ("gc", exceptions, wasm_exceptions)
1292 ("stack-switching", stack_switching, wasm_stack_switching)
1293 }
1294
1295 if let Some(enable) = self.wasm.component_model_gc {
1296 #[cfg(all(feature = "component-model", feature = "gc"))]
1297 config.wasm_component_model_gc(enable);
1298 #[cfg(not(all(feature = "component-model", feature = "gc")))]
1299 if enable && all.is_none() {
1300 bail!("support for `component-model-gc` was disabled at compile time")
1301 }
1302 }
1303
1304 Ok(())
1305 }
1306
1307 #[cfg(feature = "toml")]
1308 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
1309 use wasmtime::error::Context;
1310
1311 let path_ref = path.as_ref();
1312 let file_contents = std::fs::read_to_string(path_ref)
1313 .with_context(|| format!("failed to read config file: {path_ref:?}"))?;
1314 toml::from_str::<CommonOptions>(&file_contents)
1315 .with_context(|| format!("failed to parse TOML config file {path_ref:?}"))
1316 }
1317
1318 pub fn from_engine(engine: &Engine) -> Self {
1331 let features = engine.get_wasm_features();
1332 let pooling = engine.get_pooling_config();
1333 CommonOptions {
1334 target: engine.get_target(),
1335 opts: OptimizeOptions {
1336 memory_may_move: Some(engine.get_memory_may_move()),
1337 memory_reservation: Some(engine.get_memory_reservation()),
1338 memory_reservation_for_growth: Some(engine.get_memory_reservation_for_growth()),
1339 memory_guard_size: Some(engine.get_memory_guard_size()),
1340 gc_heap_may_move: Some(engine.get_gc_heap_may_move()),
1341 gc_heap_reservation: Some(engine.get_gc_heap_reservation()),
1342 gc_heap_reservation_for_growth: Some(engine.get_gc_heap_reservation_for_growth()),
1343 gc_heap_guard_size: Some(engine.get_gc_heap_guard_size()),
1344 gc_heap_initial_size: Some(engine.get_gc_heap_initial_size()),
1345 guard_before_linear_memory: Some(engine.get_guard_before_linear_memory()),
1346 table_lazy_init: Some(engine.get_table_lazy_init()),
1347 memory_init_cow: Some(engine.get_memory_init_cow()),
1348 memory_guaranteed_dense_image_size: Some(
1349 engine.get_memory_guaranteed_dense_image_size(),
1350 ),
1351 signals_based_traps: Some(engine.get_signals_based_traps()),
1352 gc_zeal_alloc_counter: engine.get_gc_zeal_alloc_counter(),
1353 opt_level: engine.get_cranelift_opt_level(),
1354 regalloc_algorithm: engine.get_cranelift_regalloc_algorithm(),
1355 pooling_allocator: Some(pooling.is_some()),
1356 pooling_decommit_batch_size: pooling.map(|c| c.get_decommit_batch_size()),
1357 pooling_memory_keep_resident: pooling.map(|c| c.get_memory_keep_resident()),
1358 pooling_table_keep_resident: pooling.map(|c| c.get_table_keep_resident()),
1359 pooling_max_unused_warm_slots: pooling.map(|c| c.get_max_unused_warm_slots()),
1360 pooling_pagemap_scan: pooling.map(|c| c.get_pagemap_scan()),
1361 pooling_total_core_instances: pooling.map(|c| c.get_total_core_instances()),
1362 pooling_total_component_instances: pooling
1363 .map(|c| c.get_total_component_instances()),
1364 pooling_total_memories: pooling.map(|c| c.get_total_memories()),
1365 pooling_total_tables: pooling.map(|c| c.get_total_tables()),
1366 pooling_max_memory_size: pooling.map(|c| c.get_max_memory_size()),
1367 pooling_table_elements: pooling.map(|c| c.get_table_elements()),
1368 pooling_max_core_instance_size: pooling.map(|c| c.get_max_core_instance_size()),
1369 pooling_max_component_instance_size: pooling
1370 .map(|c| c.get_max_component_instance_size()),
1371 pooling_max_core_instances_per_component: pooling
1372 .map(|c| c.get_max_core_instances_per_component()),
1373 pooling_max_memories_per_component: pooling
1374 .map(|c| c.get_max_memories_per_component()),
1375 pooling_max_tables_per_component: pooling.map(|c| c.get_max_tables_per_component()),
1376 pooling_max_tables_per_module: pooling.map(|c| c.get_max_tables_per_module()),
1377 pooling_max_memories_per_module: pooling.map(|c| c.get_max_memories_per_module()),
1378 pooling_async_stack_keep_resident: pooling
1379 .map(|c| c.get_async_stack_keep_resident()),
1380 pooling_total_stacks: pooling.map(|c| c.get_total_stacks()),
1381 pooling_total_gc_heaps: pooling.map(|c| c.get_total_gc_heaps()),
1382 pooling_memory_protection_keys: pooling.map(|c| c.get_memory_protection_keys()),
1383 pooling_max_memory_protection_keys: pooling
1384 .map(|c| c.get_max_memory_protection_keys()),
1385 dynamic_memory_guard_size: None,
1388 static_memory_guard_size: None,
1389 static_memory_forced: None,
1390 static_memory_maximum_size: None,
1391 dynamic_memory_reserved_for_growth: None,
1392 },
1393 codegen: CodegenOptions {
1394 compiler: engine.get_strategy(),
1395 collector: engine.get_collector(),
1396 cranelift_debug_verifier: engine.get_cranelift_debug_verifier(),
1397 inlining: Some(engine.get_compiler_inlining()),
1398 native_unwind_info: engine.get_native_unwind_info(),
1399 parallel_compilation: Some(engine.get_parallel_compilation()),
1400 metadata_for_internal_asserts: Some(engine.get_metadata_for_internal_asserts()),
1401 metadata_for_gc_heap_corruption: Some(engine.get_metadata_for_gc_heap_corruption()),
1402 cranelift: engine
1403 .get_cranelift_flags_set()
1404 .map(|(k, v)| (k.to_string(), Some(v.to_string())))
1405 .chain(
1406 engine
1407 .get_cranelift_flags_enabled()
1408 .map(|k| (k.to_string(), None)),
1409 )
1410 .collect(),
1411
1412 cache: None,
1415 cache_config: None,
1416 },
1417 debug: DebugOptions {
1418 address_map: Some(engine.get_generate_address_map()),
1419 debug_info: Some(engine.get_debug_info()),
1420 guest_debug: Some(engine.get_guest_debug()),
1421 symbols: Some(engine.get_debug_symbols()),
1422 max_backtrace: Some(engine.get_wasm_backtrace_max_frames()),
1423
1424 coredump: None,
1427 debugger: None,
1429 arg: Vec::new(),
1430 inherit_stderr: None,
1431 inherit_stdout: None,
1432 inherit_stdin: None,
1433 log_to_files: None,
1435 logging: None,
1436 },
1437 wasm: WasmOptions {
1438 async_stack_size: Some(engine.get_async_stack_size()),
1439 async_stack_zeroing: Some(engine.get_async_stack_zeroing()),
1440 branch_hinting: Some(engine.get_wasm_branch_hinting()),
1441 bulk_memory: Some(features.contains(WasmFeatures::BULK_MEMORY)),
1442 component_model: Some(features.contains(WasmFeatures::COMPONENT_MODEL)),
1443 component_model_async: Some(features.contains(WasmFeatures::CM_ASYNC)),
1444 component_model_async_stackful: Some(
1445 features.contains(WasmFeatures::CM_ASYNC_STACKFUL),
1446 ),
1447 component_model_error_context: Some(
1448 features.contains(WasmFeatures::CM_ERROR_CONTEXT),
1449 ),
1450 component_model_gc: Some(features.contains(WasmFeatures::CM_GC)),
1451 component_model_fixed_length_lists: Some(
1452 features.contains(WasmFeatures::CM_FIXED_LENGTH_LISTS),
1453 ),
1454 component_model_implements: Some(features.contains(WasmFeatures::CM_IMPLEMENTS)),
1455 component_model_map: Some(features.contains(WasmFeatures::CM_MAP)),
1456 component_model_memory64: Some(features.contains(WasmFeatures::CM64)),
1457 component_model_more_async_builtins: Some(
1458 features.contains(WasmFeatures::CM_MORE_ASYNC_BUILTINS),
1459 ),
1460 component_model_threading: Some(features.contains(WasmFeatures::CM_THREADING)),
1461 custom_page_sizes: Some(features.contains(WasmFeatures::CUSTOM_PAGE_SIZES)),
1462 exceptions: Some(features.contains(WasmFeatures::EXCEPTIONS)),
1463 extended_const: Some(features.contains(WasmFeatures::EXTENDED_CONST)),
1464 function_references: Some(features.contains(WasmFeatures::FUNCTION_REFERENCES)),
1465 gc: Some(features.contains(WasmFeatures::GC)),
1466 gc_support: Some(features.contains(WasmFeatures::GC_TYPES)),
1467 memory64: Some(features.contains(WasmFeatures::MEMORY64)),
1468 multi_memory: Some(features.contains(WasmFeatures::MULTI_MEMORY)),
1469 multi_value: Some(features.contains(WasmFeatures::MULTI_VALUE)),
1470 reference_types: Some(features.contains(WasmFeatures::REFERENCE_TYPES)),
1471 relaxed_simd: Some(features.contains(WasmFeatures::RELAXED_SIMD)),
1472 shared_everything_threads: Some(
1473 features.contains(WasmFeatures::SHARED_EVERYTHING_THREADS),
1474 ),
1475 simd: Some(features.contains(WasmFeatures::SIMD)),
1476 stack_switching: Some(features.contains(WasmFeatures::STACK_SWITCHING)),
1477 tail_call: Some(features.contains(WasmFeatures::TAIL_CALL)),
1478 threads: Some(features.contains(WasmFeatures::THREADS)),
1479 wide_arithmetic: Some(features.contains(WasmFeatures::WIDE_ARITHMETIC)),
1480 concurrency_support: Some(engine.get_concurrency_support()),
1481 epoch_interruption: Some(engine.get_epoch_interruption()),
1482 fuel: if engine.get_consume_fuel() {
1483 Some(1)
1484 } else {
1485 None
1486 },
1487 max_wasm_stack: Some(engine.get_max_wasm_stack()),
1488 nan_canonicalization: engine.get_cranelift_nan_canonicalization(),
1489 relaxed_simd_deterministic: Some(engine.get_relaxed_simd_deterministic()),
1490 shared_memory: Some(engine.get_shared_memory()),
1491
1492 all_proposals: None,
1494 max_instances: None,
1497 max_memories: None,
1498 max_memory_size: None,
1499 max_table_elements: None,
1500 max_tables: None,
1501 timeout: None,
1503 trap_on_grow_failure: None,
1504 unknown_exports_allow: None,
1505 unknown_imports_default: None,
1506 unknown_imports_trap: None,
1507 wmemcheck: None,
1508 },
1509
1510 record: RecordOptions::default(),
1512
1513 wasi: Default::default(),
1515
1516 configured: true,
1518 codegen_raw: Default::default(),
1519 debug_raw: Default::default(),
1520 opts_raw: Default::default(),
1521 record_raw: Default::default(),
1522 wasi_raw: Default::default(),
1523 wasm_raw: Default::default(),
1524
1525 config: None,
1527 }
1532 }
1533}
1534
1535#[cfg(test)]
1536mod tests {
1537 use wasmtime::{OptLevel, RegallocAlgorithm};
1538
1539 use super::*;
1540
1541 #[test]
1542 fn from_toml() {
1543 let empty_toml = "";
1545 let mut common_options: CommonOptions = toml::from_str(empty_toml).unwrap();
1546 common_options.config(None).unwrap();
1547
1548 let basic_toml = r#"
1550 [optimize]
1551 [codegen]
1552 [debug]
1553 [wasm]
1554 [wasi]
1555 [record]
1556 "#;
1557 let mut common_options: CommonOptions = toml::from_str(basic_toml).unwrap();
1558 common_options.config(None).unwrap();
1559
1560 for (opt_value, expected) in [
1562 ("0", Some(OptLevel::None)),
1563 ("1", Some(OptLevel::Speed)),
1564 ("2", Some(OptLevel::Speed)),
1565 ("\"s\"", Some(OptLevel::SpeedAndSize)),
1566 ("\"hello\"", None), ("3", None), ] {
1569 let toml = format!(
1570 r#"
1571 [optimize]
1572 opt-level = {opt_value}
1573 "#,
1574 );
1575 let parsed_opt_level = toml::from_str::<CommonOptions>(&toml)
1576 .ok()
1577 .and_then(|common_options| common_options.opts.opt_level);
1578
1579 assert_eq!(
1580 parsed_opt_level, expected,
1581 "Mismatch for input '{opt_value}'. Parsed: {parsed_opt_level:?}, Expected: {expected:?}"
1582 );
1583 }
1584
1585 for (regalloc_value, expected) in [
1587 ("\"backtracking\"", Some(RegallocAlgorithm::Backtracking)),
1588 ("\"single-pass\"", Some(RegallocAlgorithm::SinglePass)),
1589 ("\"hello\"", None), ("3", None), ("true", None), ] {
1593 let toml = format!(
1594 r#"
1595 [optimize]
1596 regalloc-algorithm = {regalloc_value}
1597 "#,
1598 );
1599 let parsed_regalloc_algorithm = toml::from_str::<CommonOptions>(&toml)
1600 .ok()
1601 .and_then(|common_options| common_options.opts.regalloc_algorithm);
1602 assert_eq!(
1603 parsed_regalloc_algorithm, expected,
1604 "Mismatch for input '{regalloc_value}'. Parsed: {parsed_regalloc_algorithm:?}, Expected: {expected:?}"
1605 );
1606 }
1607
1608 for (strategy_value, expected) in [
1610 ("\"cranelift\"", Some(wasmtime::Strategy::Cranelift)),
1611 ("\"winch\"", Some(wasmtime::Strategy::Winch)),
1612 ("\"hello\"", None), ("5", None), ("true", None), ] {
1616 let toml = format!(
1617 r#"
1618 [codegen]
1619 compiler = {strategy_value}
1620 "#,
1621 );
1622 let parsed_strategy = toml::from_str::<CommonOptions>(&toml)
1623 .ok()
1624 .and_then(|common_options| common_options.codegen.compiler);
1625 assert_eq!(
1626 parsed_strategy, expected,
1627 "Mismatch for input '{strategy_value}'. Parsed: {parsed_strategy:?}, Expected: {expected:?}",
1628 );
1629 }
1630
1631 for (collector_value, expected) in [
1633 (
1634 "\"drc\"",
1635 Some(wasmtime::Collector::DeferredReferenceCounting),
1636 ),
1637 ("\"null\"", Some(wasmtime::Collector::Null)),
1638 ("\"copying\"", Some(wasmtime::Collector::Copying)),
1639 ("\"hello\"", None), ("5", None), ("true", None), ] {
1643 let toml = format!(
1644 r#"
1645 [codegen]
1646 collector = {collector_value}
1647 "#,
1648 );
1649 let parsed_collector = toml::from_str::<CommonOptions>(&toml)
1650 .ok()
1651 .and_then(|common_options| common_options.codegen.collector);
1652 assert_eq!(
1653 parsed_collector, expected,
1654 "Mismatch for input '{collector_value}'. Parsed: {parsed_collector:?}, Expected: {expected:?}",
1655 );
1656 }
1657 }
1658}
1659
1660impl Default for CommonOptions {
1661 fn default() -> CommonOptions {
1662 CommonOptions::new()
1663 }
1664}
1665
1666impl fmt::Display for CommonOptions {
1667 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1668 let CommonOptions {
1669 codegen_raw,
1670 codegen,
1671 debug_raw,
1672 debug,
1673 opts_raw,
1674 opts,
1675 wasm_raw,
1676 wasm,
1677 wasi_raw,
1678 wasi,
1679 record_raw,
1680 record,
1681 configured,
1682 target,
1683 config,
1684 } = self;
1685 if let Some(target) = target {
1686 write!(f, "--target {target} ")?;
1687 }
1688 if let Some(config) = config {
1689 write!(f, "--config {} ", config.display())?;
1690 }
1691
1692 let codegen_flags;
1693 let opts_flags;
1694 let wasi_flags;
1695 let wasm_flags;
1696 let debug_flags;
1697 let record_flags;
1698
1699 if *configured {
1700 codegen_flags = codegen.to_options();
1701 debug_flags = debug.to_options();
1702 wasi_flags = wasi.to_options();
1703 wasm_flags = wasm.to_options();
1704 opts_flags = opts.to_options();
1705 record_flags = record.to_options();
1706 } else {
1707 codegen_flags = codegen_raw
1708 .iter()
1709 .flat_map(|t| t.0.iter())
1710 .cloned()
1711 .collect();
1712 debug_flags = debug_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1713 wasi_flags = wasi_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1714 wasm_flags = wasm_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1715 opts_flags = opts_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1716 record_flags = record_raw
1717 .iter()
1718 .flat_map(|t| t.0.iter())
1719 .cloned()
1720 .collect();
1721 }
1722
1723 for flag in codegen_flags {
1724 write!(f, "-C{flag} ")?;
1725 }
1726 for flag in opts_flags {
1727 write!(f, "-O{flag} ")?;
1728 }
1729 for flag in wasi_flags {
1730 write!(f, "-S{flag} ")?;
1731 }
1732 for flag in wasm_flags {
1733 write!(f, "-W{flag} ")?;
1734 }
1735 for flag in debug_flags {
1736 write!(f, "-D{flag} ")?;
1737 }
1738 for flag in record_flags {
1739 write!(f, "-R{flag} ")?;
1740 }
1741
1742 Ok(())
1743 }
1744}