1use crate::opt::NonzeroDuration;
15use std::num::NonZeroUsize;
16use std::{fmt, num::NonZeroU32, path::PathBuf};
17use wasmtime::{Config, Engine, Result, WasmBacktraceDetails, WasmFeatures, bail};
18
19pub mod opt;
20
21#[cfg(feature = "logging")]
22fn init_file_per_thread_logger(prefix: &'static str) {
23 file_per_thread_logger::initialize(prefix);
24 file_per_thread_logger::allow_uninitialized();
25
26 #[cfg(feature = "parallel-compilation")]
31 rayon::ThreadPoolBuilder::new()
32 .spawn_handler(move |thread| {
33 let mut b = std::thread::Builder::new();
34 if let Some(name) = thread.name() {
35 b = b.name(name.to_owned());
36 }
37 if let Some(stack_size) = thread.stack_size() {
38 b = b.stack_size(stack_size);
39 }
40 b.spawn(move || {
41 file_per_thread_logger::initialize(prefix);
42 thread.run()
43 })?;
44 Ok(())
45 })
46 .build_global()
47 .unwrap();
48}
49
50wasmtime_option_group! {
51 #[env = "OPTIMIZE"]
52 pub struct OptimizeOptions {
53 #[serde(default)]
55 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
56 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
57 pub opt_level: Option<wasmtime::OptLevel>,
58
59 #[serde(default)]
61 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
62 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
63 pub regalloc_algorithm: Option<wasmtime::RegallocAlgorithm>,
64
65 pub memory_may_move: Option<bool>,
68
69 pub memory_reservation: Option<u64>,
71
72 pub memory_reservation_for_growth: Option<u64>,
74
75 pub memory_guard_size: Option<u64>,
77
78 pub gc_heap_may_move: Option<bool>,
81
82 pub gc_heap_reservation: Option<u64>,
84
85 pub gc_heap_reservation_for_growth: Option<u64>,
87
88 pub gc_heap_guard_size: Option<u64>,
90
91 pub gc_heap_initial_size: Option<u64>,
93
94 pub guard_before_linear_memory: Option<bool>,
97
98 pub table_lazy_init: Option<bool>,
103
104 pub pooling_allocator: Option<bool>,
106
107 pub pooling_decommit_batch_size: Option<usize>,
110
111 pub pooling_memory_keep_resident: Option<usize>,
114
115 pub pooling_table_keep_resident: Option<usize>,
118
119 #[serde(default)]
122 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
123 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
124 pub pooling_memory_protection_keys: Option<wasmtime::Enabled>,
125
126 pub pooling_max_memory_protection_keys: Option<usize>,
129
130 pub memory_init_cow: Option<bool>,
133
134 pub memory_guaranteed_dense_image_size: Option<u64>,
137
138 pub pooling_total_core_instances: Option<u32>,
141
142 pub pooling_total_component_instances: Option<u32>,
145
146 pub pooling_total_memories: Option<u32>,
149
150 pub pooling_total_tables: Option<u32>,
153
154 pub pooling_total_stacks: Option<u32>,
157
158 pub pooling_max_memory_size: Option<usize>,
161
162 pub pooling_table_elements: Option<usize>,
165
166 pub pooling_max_core_instance_size: Option<usize>,
169
170 pub pooling_max_unused_warm_slots: Option<u32>,
173
174 pub pooling_async_stack_keep_resident: Option<usize>,
177
178 pub pooling_max_component_instance_size: Option<usize>,
181
182 pub pooling_max_core_instances_per_component: Option<u32>,
185
186 pub pooling_max_memories_per_component: Option<u32>,
189
190 pub pooling_max_tables_per_component: Option<u32>,
193
194 pub pooling_max_tables_per_module: Option<u32>,
196
197 pub pooling_max_memories_per_module: Option<u32>,
199
200 pub pooling_total_gc_heaps: Option<u32>,
202
203 pub signals_based_traps: Option<bool>,
205
206 pub dynamic_memory_guard_size: Option<u64>,
208
209 pub static_memory_guard_size: Option<u64>,
211
212 pub static_memory_forced: Option<bool>,
214
215 pub static_memory_maximum_size: Option<u64>,
217
218 pub dynamic_memory_reserved_for_growth: Option<u64>,
220
221 #[serde(default)]
224 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
225 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
226 pub pooling_pagemap_scan: Option<wasmtime::Enabled>,
227
228 #[doc(hidden)]
230 pub gc_zeal_alloc_counter: Option<NonZeroU32>,
231 }
232
233 enum Optimize {
234 ...
235 }
236}
237
238wasmtime_option_group! {
239 #[env = "CODEGEN"]
240 pub struct CodegenOptions {
241 #[serde(default)]
246 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
247 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
248 pub compiler: Option<wasmtime::Strategy>,
249 #[serde(default)]
261 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
262 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
263 pub collector: Option<wasmtime::Collector>,
264 pub cranelift_debug_verifier: Option<bool>,
266 pub cache: Option<bool>,
268 pub cache_config: Option<String>,
270 pub parallel_compilation: Option<bool>,
272 pub native_unwind_info: Option<bool>,
275 pub asan_compatible_code: Option<bool>,
279
280 #[serde(default)]
282 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
283 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
284 pub inlining: Option<wasmtime::Inlining>,
285
286 pub metadata_for_internal_asserts: Option<bool>,
289 pub metadata_for_gc_heap_corruption: Option<bool>,
292
293 #[serde(default)]
313 #[serde(deserialize_with = "crate::opt::deserialize_cli_parse_wrapper")]
314 #[serde(serialize_with = "crate::opt::serialize_cli_parse_wrapper")]
315 pub unsafe_intrinsics: Option<UnsafeIntrinsicsImport>,
316
317 #[serde(skip)]
340 pub compile_time_builtin: Vec<CompileTimeBuiltin>,
341
342 #[prefixed = "cranelift"]
343 #[serde(default)]
344 pub cranelift: Vec<(String, Option<String>)>,
347 }
348
349 enum Codegen {
350 ...
351 }
352}
353
354wasmtime_option_group! {
355 #[env = "DEBUG"]
356 pub struct DebugOptions {
357 pub debug_info: Option<bool>,
359 pub guest_debug: Option<bool>,
361 pub address_map: Option<bool>,
363 pub logging: Option<bool>,
365 pub log_to_files: Option<bool>,
367 pub coredump: Option<String>,
369 pub debugger: Option<PathBuf>,
372 #[serde(default)]
375 pub arg: Vec<String>,
376 pub inherit_stdin: Option<bool>,
379 pub inherit_stdout: Option<bool>,
382 pub inherit_stderr: Option<bool>,
385 pub max_backtrace: Option<usize>,
387 pub symbols: Option<bool>,
389 }
390
391 enum Debug {
392 ...
393 }
394}
395
396wasmtime_option_group! {
397 #[env = "WASM"]
398 pub struct WasmOptions {
399 pub nan_canonicalization: Option<bool>,
401 pub fuel: Option<u64>,
409 pub epoch_interruption: Option<bool>,
412 pub max_wasm_stack: Option<usize>,
415 pub async_stack_size: Option<usize>,
421 pub async_stack_zeroing: Option<bool>,
424 pub unknown_exports_allow: Option<bool>,
426 pub unknown_imports_trap: Option<bool>,
429 pub unknown_imports_default: Option<bool>,
432 pub wmemcheck: Option<bool>,
434 pub max_memory_size: Option<usize>,
439 pub max_table_elements: Option<usize>,
441 pub max_instances: Option<usize>,
443 pub max_tables: Option<usize>,
445 pub max_memories: Option<usize>,
447 pub trap_on_grow_failure: Option<bool>,
454 pub timeout: Option<NonzeroDuration>,
460 pub all_proposals: Option<bool>,
462 pub bulk_memory: Option<bool>,
464 pub multi_memory: Option<bool>,
466 pub multi_value: Option<bool>,
468 pub reference_types: Option<bool>,
470 pub simd: Option<bool>,
472 pub relaxed_simd: Option<bool>,
474 pub relaxed_simd_deterministic: Option<bool>,
483 pub tail_call: Option<bool>,
485 pub threads: Option<bool>,
487 pub shared_memory: Option<bool>,
489 pub memory64: Option<bool>,
491 pub component_model: Option<bool>,
493 pub component_model_async: Option<bool>,
495 pub component_model_more_async_builtins: Option<bool>,
498 pub component_model_async_stackful: Option<bool>,
501 pub component_model_threading: Option<bool>,
504 pub component_model_error_context: Option<bool>,
507 pub component_model_gc: Option<bool>,
510 pub component_model_map: Option<bool>,
513 pub component_model_memory64: Option<bool>,
516 pub function_references: Option<bool>,
518 pub stack_switching: Option<bool>,
520 pub gc: Option<bool>,
522 pub custom_page_sizes: Option<bool>,
524 pub wide_arithmetic: Option<bool>,
526 pub branch_hinting: Option<bool>,
528 pub compact_imports: Option<bool>,
530 pub extended_const: Option<bool>,
532 pub exceptions: Option<bool>,
534 pub legacy_exceptions: Option<bool>,
539 pub gc_support: Option<bool>,
541 pub component_model_fixed_length_lists: Option<bool>,
544 pub component_model_implements: Option<bool>,
548 pub component_model_canonical_names: Option<bool>,
551 pub component_model_accessors: Option<bool>,
554 pub concurrency_support: Option<bool>,
557 }
558
559 enum Wasm {
560 ...
561 }
562}
563
564wasmtime_option_group! {
565 #[env = "WASI"]
566 pub struct WasiOptions {
567 pub cli: Option<bool>,
569 pub cli_exit_with_code: Option<bool>,
571 pub common: Option<bool>,
573 pub nn: Option<bool>,
575 pub threads: Option<bool>,
577 pub http: Option<bool>,
579 pub http_outgoing_body_buffer_chunks: Option<NonZeroUsize>,
583 pub http_outgoing_body_chunk_size: Option<NonZeroUsize>,
586 pub config: Option<bool>,
588 pub keyvalue: Option<bool>,
590 #[serde(default)]
593 pub tcplisten: Vec<String>,
594 pub tls: Option<bool>,
596 pub preview2: Option<bool>,
599 #[serde(skip)]
608 pub nn_graph: Vec<WasiNnGraph>,
609 pub inherit_network: Option<bool>,
612 pub allow_ip_name_lookup: Option<bool>,
614 pub tcp: Option<bool>,
616 pub udp: Option<bool>,
618 pub network_error_code: Option<bool>,
620 pub preview0: Option<bool>,
622 pub inherit_env: Option<bool>,
626 pub inherit_stdin: Option<bool>,
628 pub inherit_stdout: Option<bool>,
630 pub inherit_stderr: Option<bool>,
632 pub cwd: Option<String>,
634 #[serde(skip)]
636 pub config_var: Vec<KeyValuePair>,
637 #[serde(skip)]
639 pub keyvalue_in_memory_data: Vec<KeyValuePair>,
640 pub p3: Option<bool>,
642 pub max_resources: Option<usize>,
644 pub hostcall_fuel: Option<usize>,
646 pub max_io_size: Option<NonZeroUsize>,
650 pub max_component_handles: Option<usize>,
654 pub max_fibers: Option<u32>,
656 pub max_random_size: Option<u64>,
660 pub max_http_fields_size: Option<usize>,
664 }
665
666 enum Wasi {
667 ...
668 }
669}
670
671wasmtime_option_group! {
672 #[env = "RECORD"]
673 pub struct RecordOptions {
674 pub path: Option<String>,
676 pub validation_metadata: Option<bool>,
679 pub event_window_size: Option<usize>,
682 }
683
684 enum Record {
685 ...
686 }
687}
688
689#[derive(Debug, Clone, PartialEq)]
690pub struct WasiNnGraph {
691 pub format: String,
692 pub dir: String,
693}
694
695#[derive(Debug, Clone, PartialEq)]
696pub struct KeyValuePair {
697 pub key: String,
698 pub value: String,
699}
700
701#[derive(Debug, Clone, PartialEq)]
704pub struct UnsafeIntrinsicsImport(pub String);
705
706#[derive(Debug, Clone, PartialEq)]
708pub struct CompileTimeBuiltin {
709 pub name: String,
711 pub path: PathBuf,
713}
714
715#[derive(Clone)]
717#[cfg_attr(feature = "clap", derive(clap::Parser))]
718#[cfg_attr(
719 feature = "serde",
720 derive(serde_derive::Deserialize, serde_derive::Serialize)
721)]
722#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
723pub struct CommonOptions {
724 #[cfg_attr(
734 feature = "clap",
735 arg(short = 'O', long = "optimize", value_name = "KEY[=VAL[,..]]")
736 )]
737 #[cfg_attr(feature = "serde", serde(skip))]
738 opts_raw: Vec<opt::CommaSeparated<Optimize>>,
739
740 #[cfg_attr(
742 feature = "clap",
743 arg(short = 'C', long = "codegen", value_name = "KEY[=VAL[,..]]")
744 )]
745 #[cfg_attr(feature = "serde", serde(skip))]
746 codegen_raw: Vec<opt::CommaSeparated<Codegen>>,
747
748 #[cfg_attr(
750 feature = "clap",
751 arg(short = 'D', long = "debug", value_name = "KEY[=VAL[,..]]")
752 )]
753 #[cfg_attr(feature = "serde", serde(skip))]
754 debug_raw: Vec<opt::CommaSeparated<Debug>>,
755
756 #[cfg_attr(
759 feature = "clap",
760 arg(short = 'W', long = "wasm", value_name = "KEY[=VAL[,..]]")
761 )]
762 #[cfg_attr(feature = "serde", serde(skip))]
763 wasm_raw: Vec<opt::CommaSeparated<Wasm>>,
764
765 #[cfg_attr(
767 feature = "clap",
768 arg(short = 'S', long = "wasi", value_name = "KEY[=VAL[,..]]")
769 )]
770 #[cfg_attr(feature = "serde", serde(skip))]
771 wasi_raw: Vec<opt::CommaSeparated<Wasi>>,
772
773 #[cfg_attr(
782 feature = "clap",
783 arg(short = 'R', long = "record", value_name = "KEY[=VAL[,..]]")
784 )]
785 #[cfg_attr(feature = "serde", serde(skip))]
786 record_raw: Vec<opt::CommaSeparated<Record>>,
787
788 #[cfg_attr(feature = "clap", arg(skip))]
791 #[cfg_attr(feature = "serde", serde(skip))]
792 configured: bool,
793
794 #[cfg_attr(feature = "clap", arg(skip))]
797 #[cfg_attr(feature = "serde", serde(skip))]
798 pub ignore_env: bool,
799
800 #[cfg_attr(feature = "clap", arg(skip))]
801 #[cfg_attr(feature = "serde", serde(rename = "optimize", default))]
802 pub opts: OptimizeOptions,
803
804 #[cfg_attr(feature = "clap", arg(skip))]
805 #[cfg_attr(feature = "serde", serde(default))]
806 pub codegen: CodegenOptions,
807
808 #[cfg_attr(feature = "clap", arg(skip))]
809 #[cfg_attr(feature = "serde", serde(default))]
810 pub debug: DebugOptions,
811
812 #[cfg_attr(feature = "clap", arg(skip))]
813 #[cfg_attr(feature = "serde", serde(default))]
814 pub wasm: WasmOptions,
815
816 #[cfg_attr(feature = "clap", arg(skip))]
817 #[cfg_attr(feature = "serde", serde(default))]
818 pub wasi: WasiOptions,
819
820 #[cfg_attr(feature = "clap", arg(skip))]
821 #[cfg_attr(feature = "serde", serde(default))]
822 pub record: RecordOptions,
823
824 #[cfg_attr(feature = "clap", arg(long, value_name = "TARGET"))]
826 #[cfg_attr(feature = "serde", serde(skip))]
827 pub target: Option<String>,
828
829 #[cfg_attr(feature = "clap", arg(long = "config", value_name = "FILE"))]
836 #[cfg_attr(feature = "serde", serde(skip))]
837 pub config: Option<PathBuf>,
838}
839
840type F = WasmFeatures;
841
842static WASM_FEATURE_OPTIONS: &[(WasmFeatures, fn(&WasmOptions) -> Option<bool>, Option<&str>)] = &[
848 (F::SIMD, |w| w.simd, None),
849 (F::RELAXED_SIMD, |w| w.relaxed_simd, None),
850 (F::BULK_MEMORY, |w| w.bulk_memory, None),
851 (F::MULTI_VALUE, |w| w.multi_value, None),
852 (F::TAIL_CALL, |w| w.tail_call, None),
853 (F::MULTI_MEMORY, |w| w.multi_memory, None),
854 (F::MEMORY64, |w| w.memory64, None),
855 (F::CUSTOM_PAGE_SIZES, |w| w.custom_page_sizes, None),
856 (F::WIDE_ARITHMETIC, |w| w.wide_arithmetic, None),
857 (F::COMPACT_IMPORTS, |w| w.compact_imports, None),
858 (F::EXTENDED_CONST, |w| w.extended_const, None),
859 (F::THREADS, |w| w.threads, None),
860 (F::GC, |w| w.gc, None),
861 (F::GC_TYPES, |w| w.gc_support, Some("gc-support")),
862 (F::REFERENCE_TYPES, |w| w.reference_types, None),
863 (F::FUNCTION_REFERENCES, |w| w.function_references, None),
864 (F::EXCEPTIONS, |w| w.exceptions, None),
865 (F::LEGACY_EXCEPTIONS, |w| w.legacy_exceptions, None),
866 (F::STACK_SWITCHING, |w| w.stack_switching, None),
867 (F::COMPONENT_MODEL, |w| w.component_model, None),
868 (
869 F::CM_ASYNC,
870 |w| w.component_model_async,
871 Some("component-model-async"),
872 ),
873 (
874 F::CM_MORE_ASYNC_BUILTINS,
875 |w| w.component_model_more_async_builtins,
876 Some("component-model-more-async-builtins"),
877 ),
878 (
879 F::CM_ASYNC_STACKFUL,
880 |w| w.component_model_async_stackful,
881 Some("component-model-async-stackful"),
882 ),
883 (
884 F::CM_THREADING,
885 |w| w.component_model_threading,
886 Some("component-model-threading"),
887 ),
888 (
889 F::CM_ERROR_CONTEXT,
890 |w| w.component_model_error_context,
891 Some("component-model-error-context"),
892 ),
893 (
894 F::CM_MAP,
895 |w| w.component_model_map,
896 Some("component-model-map"),
897 ),
898 (
899 F::CM_FIXED_LENGTH_LISTS,
900 |w| w.component_model_fixed_length_lists,
901 Some("component-model-fixed-length-lists"),
902 ),
903 (
904 F::CM_IMPLEMENTS,
905 |w| w.component_model_implements,
906 Some("component-model-implements"),
907 ),
908 (
909 F::CM_CANON_NAMES,
910 |w| w.component_model_canonical_names,
911 Some("component-model-canonical-names"),
912 ),
913 (
914 F::CM_ACCESSORS,
915 |w| w.component_model_accessors,
916 Some("component-model-accessors"),
917 ),
918 (
919 F::CM64,
920 |w| w.component_model_memory64,
921 Some("component-model-memory64"),
922 ),
923 (
924 F::CM_GC,
925 |w| w.component_model_gc,
926 Some("component-model-gc"),
927 ),
928];
929
930macro_rules! match_feature {
931 (
932 [$feat:tt : $config:expr]
933 $val:ident => $e:expr,
934 $p:pat => err,
935 ) => {
936 #[cfg(feature = $feat)]
937 {
938 if let Some($val) = $config {
939 $e;
940 }
941 }
942 #[cfg(not(feature = $feat))]
943 {
944 if let Some($p) = $config {
945 bail!(concat!("support for ", $feat, " disabled at compile time"));
946 }
947 }
948 };
949}
950
951impl CommonOptions {
952 pub fn new() -> CommonOptions {
954 CommonOptions {
955 opts_raw: Vec::new(),
956 codegen_raw: Vec::new(),
957 debug_raw: Vec::new(),
958 wasm_raw: Vec::new(),
959 wasi_raw: Vec::new(),
960 record_raw: Vec::new(),
961 configured: true,
962 ignore_env: false,
963 opts: Default::default(),
964 codegen: Default::default(),
965 debug: Default::default(),
966 wasm: Default::default(),
967 wasi: Default::default(),
968 record: Default::default(),
969 target: None,
970 config: None,
971 }
972 }
973
974 fn configure(&mut self) -> Result<()> {
975 if self.configured {
976 return Ok(());
977 }
978 self.configured = true;
979 if let Some(toml_config_path) = &self.config {
980 #[cfg(feature = "toml")]
981 {
982 let toml_options = CommonOptions::from_file(toml_config_path)?;
983 self.opts = toml_options.opts;
984 self.codegen = toml_options.codegen;
985 self.debug = toml_options.debug;
986 self.wasm = toml_options.wasm;
987 self.wasi = toml_options.wasi;
988 self.record = toml_options.record;
989 }
990 #[cfg(not(feature = "toml"))]
991 {
992 bail!(
993 "support for loading a configuration file from \
994 {toml_config_path:?} disabled at compile time"
995 );
996 }
997 }
998 self.opts.configure_with(&self.opts_raw, !self.ignore_env)?;
999 self.codegen
1000 .configure_with(&self.codegen_raw, !self.ignore_env)?;
1001 self.debug
1002 .configure_with(&self.debug_raw, !self.ignore_env)?;
1003 self.wasm.configure_with(&self.wasm_raw, !self.ignore_env)?;
1004 self.wasi.configure_with(&self.wasi_raw, !self.ignore_env)?;
1005 self.record
1006 .configure_with(&self.record_raw, !self.ignore_env)?;
1007 Ok(())
1008 }
1009
1010 pub fn merge(&mut self, other: &mut CommonOptions) -> Result<()> {
1015 self.configure()?;
1016 other.configure()?;
1017 let CommonOptions {
1018 opts_raw: _,
1020 codegen_raw: _,
1021 debug_raw: _,
1022 wasm_raw: _,
1023 wasi_raw: _,
1024 record_raw: _,
1025 configured: _,
1026 ignore_env: _,
1027 config: _,
1028
1029 opts,
1030 codegen,
1031 debug,
1032 wasm,
1033 wasi,
1034 record,
1035 target,
1036 } = other;
1037 for opt in opts.to_options() {
1038 self.opts.configure(&opt);
1039 }
1040 for opt in codegen.to_options() {
1041 self.codegen.configure(&opt);
1042 }
1043 for opt in debug.to_options() {
1044 self.debug.configure(&opt);
1045 }
1046 for opt in wasm.to_options() {
1047 self.wasm.configure(&opt);
1048 }
1049 for opt in wasi.to_options() {
1050 self.wasi.configure(&opt);
1051 }
1052 for opt in record.to_options() {
1053 self.record.configure(&opt);
1054 }
1055 if target.is_some() {
1056 self.target = target.clone();
1057 }
1058 Ok(())
1059 }
1060
1061 pub fn init_logging(&mut self) -> Result<()> {
1062 self.configure()?;
1063 if self.debug.logging == Some(false) {
1064 return Ok(());
1065 }
1066 #[cfg(feature = "logging")]
1067 if self.debug.log_to_files == Some(true) {
1068 let prefix = "wasmtime.dbg.";
1069 init_file_per_thread_logger(prefix);
1070 } else {
1071 use std::io::IsTerminal;
1072 use tracing_subscriber::{EnvFilter, FmtSubscriber};
1073 let builder = FmtSubscriber::builder()
1074 .with_writer(std::io::stderr)
1075 .with_env_filter(EnvFilter::from_env("WASMTIME_LOG"))
1076 .with_ansi(std::io::stderr().is_terminal());
1077 if std::env::var("WASMTIME_LOG_NO_CONTEXT").is_ok_and(|value| value.eq("1")) {
1078 builder
1079 .with_level(false)
1080 .with_target(false)
1081 .without_time()
1082 .init()
1083 } else {
1084 builder.init();
1085 }
1086 }
1087 #[cfg(not(feature = "logging"))]
1088 if self.debug.log_to_files == Some(true) || self.debug.logging == Some(true) {
1089 bail!("support for logging disabled at compile time");
1090 }
1091 Ok(())
1092 }
1093
1094 pub fn config(&mut self, pooling_allocator_default: Option<bool>) -> Result<Config> {
1097 let mut config = Config::new();
1098 self.apply_config(&mut config, pooling_allocator_default)?;
1099 Ok(config)
1100 }
1101
1102 pub fn apply_config(
1104 &mut self,
1105 config: &mut Config,
1106 pooling_allocator_default: Option<bool>,
1107 ) -> Result<()> {
1108 self.configure()?;
1109
1110 match_feature! {
1111 ["cranelift" : self.codegen.compiler]
1112 strategy => config.strategy(strategy),
1113 _ => err,
1114 }
1115 match_feature! {
1116 ["gc" : self.codegen.collector]
1117 collector => config.collector(collector),
1118 _ => err,
1119 }
1120 if let Some(target) = &self.target {
1121 config.target(target)?;
1122 }
1123 match_feature! {
1124 ["cranelift" : self.codegen.cranelift_debug_verifier]
1125 enable => config.cranelift_debug_verifier(enable),
1126 true => err,
1127 }
1128 if let Some(enable) = self.debug.debug_info {
1129 config.debug_info(enable);
1130 }
1131 match_feature! {
1132 ["debug" : self.debug.guest_debug]
1133 enable => config.guest_debug(enable),
1134 _ => err,
1135 }
1136 if self.debug.coredump.is_some() {
1137 #[cfg(feature = "coredump")]
1138 config.coredump_on_trap(true);
1139 #[cfg(not(feature = "coredump"))]
1140 bail!("support for coredumps disabled at compile time");
1141 }
1142 match_feature! {
1143 ["cranelift" : self.opts.opt_level]
1144 level => config.cranelift_opt_level(level),
1145 _ => err,
1146 }
1147 match_feature! {
1148 ["cranelift": self.opts.regalloc_algorithm]
1149 algo => config.cranelift_regalloc_algorithm(algo),
1150 _ => err,
1151 }
1152 match_feature! {
1153 ["cranelift" : self.wasm.nan_canonicalization]
1154 enable => config.cranelift_nan_canonicalization(enable),
1155 true => err,
1156 }
1157
1158 self.enable_wasm_features(config)?;
1159
1160 #[cfg(feature = "cranelift")]
1161 for (name, value) in self.codegen.cranelift.iter() {
1162 let name = name.replace('-', "_");
1163 unsafe {
1164 match value {
1165 Some(val) => {
1166 config.cranelift_flag_set(&name, val);
1167 }
1168 None => {
1169 config.cranelift_flag_enable(&name);
1170 }
1171 }
1172 }
1173 }
1174 #[cfg(not(feature = "cranelift"))]
1175 if !self.codegen.cranelift.is_empty() {
1176 bail!("support for cranelift disabled at compile time");
1177 }
1178
1179 #[cfg(feature = "cache")]
1180 if self.codegen.cache != Some(false) {
1181 use wasmtime::Cache;
1182 let cache = match &self.codegen.cache_config {
1183 Some(path) => Cache::from_file(Some(std::path::Path::new(path)))?,
1184 None => Cache::from_file(None)?,
1185 };
1186 config.cache(Some(cache));
1187 }
1188 #[cfg(not(feature = "cache"))]
1189 if self.codegen.cache == Some(true) {
1190 bail!("support for caching disabled at compile time");
1191 }
1192
1193 match_feature! {
1194 ["parallel-compilation" : self.codegen.parallel_compilation]
1195 enable => config.parallel_compilation(enable),
1196 true => err,
1197 }
1198
1199 let memory_reservation = self
1200 .opts
1201 .memory_reservation
1202 .or(self.opts.static_memory_maximum_size);
1203 if let Some(size) = memory_reservation {
1204 config.memory_reservation(size);
1205 }
1206
1207 if let Some(enable) = self.opts.static_memory_forced {
1208 config.memory_may_move(!enable);
1209 }
1210 if let Some(enable) = self.opts.memory_may_move {
1211 config.memory_may_move(enable);
1212 }
1213
1214 let memory_guard_size = self
1215 .opts
1216 .static_memory_guard_size
1217 .or(self.opts.dynamic_memory_guard_size)
1218 .or(self.opts.memory_guard_size);
1219 if let Some(size) = memory_guard_size {
1220 config.memory_guard_size(size);
1221 }
1222
1223 let mem_for_growth = self
1224 .opts
1225 .memory_reservation_for_growth
1226 .or(self.opts.dynamic_memory_reserved_for_growth);
1227 if let Some(size) = mem_for_growth {
1228 config.memory_reservation_for_growth(size);
1229 }
1230 if let Some(enable) = self.opts.guard_before_linear_memory {
1231 config.guard_before_linear_memory(enable);
1232 }
1233
1234 if let Some(size) = self.opts.gc_heap_reservation {
1235 config.gc_heap_reservation(size);
1236 }
1237 if let Some(enable) = self.opts.gc_heap_may_move {
1238 config.gc_heap_may_move(enable);
1239 }
1240 if let Some(size) = self.opts.gc_heap_guard_size {
1241 config.gc_heap_guard_size(size);
1242 }
1243 if let Some(size) = self.opts.gc_heap_reservation_for_growth {
1244 config.gc_heap_reservation_for_growth(size);
1245 }
1246
1247 if let Some(size) = self.opts.gc_heap_initial_size {
1248 config.gc_heap_initial_size(size);
1249 }
1250
1251 if let Some(enable) = self.opts.table_lazy_init {
1252 config.table_lazy_init(enable);
1253 }
1254
1255 if let Some(n) = self.opts.gc_zeal_alloc_counter
1256 && (cfg!(gc_zeal) || cfg!(fuzzing))
1257 {
1258 config.gc_zeal_alloc_counter(Some(n))?;
1259 }
1260
1261 if self.wasm.fuel.is_some() {
1263 config.consume_fuel(true);
1264 }
1265
1266 if let Some(enable) = self.wasm.epoch_interruption {
1267 config.epoch_interruption(enable);
1268 }
1269 if let Some(enable) = self.debug.address_map {
1270 config.generate_address_map(enable);
1271 }
1272 if let Some(frames) = self.debug.max_backtrace {
1273 match NonZeroUsize::new(frames) {
1274 None => {
1275 config.wasm_backtrace_details(WasmBacktraceDetails::Disable);
1276 }
1277 Some(amt) => {
1278 config.wasm_backtrace_max_frames(Some(amt));
1279 }
1280 }
1281 }
1282 if let Some(enable) = self.debug.symbols {
1283 config.debug_symbols(enable);
1284 }
1285 if let Some(enable) = self.opts.memory_init_cow {
1286 config.memory_init_cow(enable);
1287 }
1288 if let Some(size) = self.opts.memory_guaranteed_dense_image_size {
1289 config.memory_guaranteed_dense_image_size(size);
1290 }
1291 if let Some(enable) = self.opts.signals_based_traps {
1292 config.signals_based_traps(enable);
1293 }
1294 if let Some(enable) = self.codegen.native_unwind_info {
1295 config.native_unwind_info(enable);
1296 }
1297 if let Some(enable) = self.codegen.asan_compatible_code {
1298 config.asan_compatible_code(enable);
1299 }
1300 if let Some(enable) = self.codegen.inlining {
1301 config.compiler_inlining(enable);
1302 }
1303 if let Some(enable) = self.codegen.metadata_for_internal_asserts {
1304 config.metadata_for_internal_asserts(enable);
1305 }
1306 if let Some(enable) = self.codegen.metadata_for_gc_heap_corruption {
1307 config.metadata_for_gc_heap_corruption(enable);
1308 }
1309
1310 #[cfg(any(feature = "async", feature = "stack-switching"))]
1313 {
1314 if let Some(size) = self.wasm.async_stack_size {
1315 config.async_stack_size(size);
1316 }
1317 }
1318 #[cfg(not(any(feature = "async", feature = "stack-switching")))]
1319 {
1320 if let Some(_size) = self.wasm.async_stack_size {
1321 bail!(concat!(
1322 "support for async/stack-switching disabled at compile time"
1323 ));
1324 }
1325 }
1326
1327 match_feature! {
1328 ["pooling-allocator" : self.opts.pooling_allocator.or(pooling_allocator_default)]
1329 enable => {
1330 if enable {
1331 let mut cfg = wasmtime::PoolingAllocationConfig::default();
1332 if let Some(size) = self.opts.pooling_memory_keep_resident {
1333 cfg.linear_memory_keep_resident(size);
1334 }
1335 if let Some(size) = self.opts.pooling_table_keep_resident {
1336 cfg.table_keep_resident(size);
1337 }
1338 if let Some(limit) = self.opts.pooling_total_core_instances {
1339 cfg.total_core_instances(limit);
1340 }
1341 if let Some(limit) = self.opts.pooling_total_component_instances {
1342 cfg.total_component_instances(limit);
1343 }
1344 if let Some(limit) = self.opts.pooling_total_memories {
1345 cfg.total_memories(limit);
1346 }
1347 if let Some(limit) = self.opts.pooling_total_tables {
1348 cfg.total_tables(limit);
1349 }
1350 if let Some(limit) = self.opts.pooling_table_elements
1351 .or(self.wasm.max_table_elements)
1352 {
1353 cfg.table_elements(limit);
1354 }
1355 if let Some(limit) = self.opts.pooling_max_core_instance_size {
1356 cfg.max_core_instance_size(limit);
1357 }
1358 match_feature! {
1359 ["async" : self.opts.pooling_total_stacks]
1360 limit => cfg.total_stacks(limit),
1361 _ => err,
1362 }
1363 if let Some(max) = self.opts.pooling_max_memory_size
1364 .or(self.wasm.max_memory_size)
1365 {
1366 cfg.max_memory_size(max);
1367 }
1368 if let Some(size) = self.opts.pooling_decommit_batch_size {
1369 cfg.decommit_batch_size(size);
1370 }
1371 if let Some(max) = self.opts.pooling_max_unused_warm_slots {
1372 cfg.max_unused_warm_slots(max);
1373 }
1374 match_feature! {
1375 ["async" : self.opts.pooling_async_stack_keep_resident]
1376 size => cfg.async_stack_keep_resident(size),
1377 _ => err,
1378 }
1379 if let Some(max) = self.opts.pooling_max_component_instance_size {
1380 cfg.max_component_instance_size(max);
1381 }
1382 if let Some(max) = self.opts.pooling_max_core_instances_per_component {
1383 cfg.max_core_instances_per_component(max);
1384 }
1385 if let Some(max) = self.opts.pooling_max_memories_per_component {
1386 cfg.max_memories_per_component(max);
1387 }
1388 if let Some(max) = self.opts.pooling_max_tables_per_component {
1389 cfg.max_tables_per_component(max);
1390 }
1391 if let Some(max) = self.opts.pooling_max_tables_per_module {
1392 cfg.max_tables_per_module(max);
1393 }
1394 if let Some(max) = self.opts.pooling_max_memories_per_module {
1395 cfg.max_memories_per_module(max);
1396 }
1397 match_feature! {
1398 ["memory-protection-keys" : self.opts.pooling_memory_protection_keys]
1399 enable => cfg.memory_protection_keys(enable),
1400 _ => err,
1401 }
1402 match_feature! {
1403 ["memory-protection-keys" : self.opts.pooling_max_memory_protection_keys]
1404 max => cfg.max_memory_protection_keys(max),
1405 _ => err,
1406 }
1407 match_feature! {
1408 ["gc" : self.opts.pooling_total_gc_heaps]
1409 max => cfg.total_gc_heaps(max),
1410 _ => err,
1411 }
1412 if let Some(enabled) = self.opts.pooling_pagemap_scan {
1413 cfg.pagemap_scan(enabled);
1414 }
1415 config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling(cfg));
1416 }
1417 },
1418 true => err,
1419 }
1420
1421 if self.opts.pooling_memory_protection_keys.is_some()
1422 && !self.opts.pooling_allocator.unwrap_or(false)
1423 {
1424 bail!("memory protection keys require the pooling allocator");
1425 }
1426
1427 if self.opts.pooling_max_memory_protection_keys.is_some()
1428 && !self.opts.pooling_memory_protection_keys.is_some()
1429 {
1430 bail!("max memory protection keys requires memory protection keys to be enabled");
1431 }
1432
1433 match_feature! {
1434 ["async" : self.wasm.async_stack_zeroing]
1435 enable => config.async_stack_zeroing(enable),
1436 _ => err,
1437 }
1438
1439 if let Some(max) = self.wasm.max_wasm_stack {
1440 config.max_wasm_stack(max);
1441
1442 #[cfg(any(feature = "async", feature = "stack-switching"))]
1446 if self.wasm.async_stack_size.is_none() {
1447 const DEFAULT_HOST_STACK: usize = 512 << 10;
1448 config.async_stack_size(max.saturating_add(DEFAULT_HOST_STACK));
1449 }
1450 }
1451
1452 if let Some(enable) = self.wasm.relaxed_simd_deterministic {
1453 config.relaxed_simd_deterministic(enable);
1454 }
1455 match_feature! {
1456 ["cranelift" : self.wasm.wmemcheck]
1457 enable => config.wmemcheck(enable),
1458 true => err,
1459 }
1460
1461 if let Some(enable) = self.wasm.concurrency_support {
1462 config.concurrency_support(enable);
1463 }
1464
1465 if let Some(enable) = self.wasm.shared_memory {
1466 config.shared_memory(enable);
1467 }
1468
1469 let record = &self.record;
1470 match_feature! {
1471 ["rr" : &record.path]
1472 _path => {
1473 bail!("recording configuration for `rr` feature is not supported yet");
1474 },
1475 _ => err,
1476 }
1477
1478 Ok(())
1479 }
1480
1481 #[cfg(feature = "runtime")]
1487 pub fn configure_store<T>(&self, store: &mut wasmtime::Store<T>) -> Result<()> {
1488 debug_assert!(self.configured, "`config` or `apply_config` not called");
1489 #[cfg(feature = "component-model")]
1490 {
1491 if let Some(fuel) = self.wasi.hostcall_fuel {
1492 store.set_hostcall_fuel(fuel);
1493 }
1494 if let Some(max) = self.wasi.max_component_handles {
1495 store.set_max_component_handles(max);
1496 }
1497 }
1498 #[cfg(feature = "async")]
1499 if let Some(max) = self.wasi.max_fibers {
1500 store.set_max_fibers(max);
1501 }
1502 if let Some(fuel) = self.wasm.fuel {
1503 store.set_fuel(fuel)?;
1504 }
1505 Ok(())
1506 }
1507
1508 fn enable_wasm_features(&self, config: &mut Config) -> Result<()> {
1509 let all = self.wasm.all_proposals;
1510 for (feature, option, _) in WASM_FEATURE_OPTIONS {
1511 let mut value = option(&self.wasm);
1512 if *feature != F::LEGACY_EXCEPTIONS {
1514 value = value.or(all);
1515 }
1516 if let Some(enable) = value {
1517 config.wasm_features(*feature, enable);
1518 }
1519 }
1520
1521 if let Some(enable) = self.wasm.branch_hinting.or(all) {
1524 config.wasm_branch_hinting(enable);
1525 }
1526
1527 Ok(())
1528 }
1529
1530 pub fn wasm_features_specified(&self) -> Vec<String> {
1535 let mut ret = Vec::new();
1536 for (feature, option, name) in WASM_FEATURE_OPTIONS {
1537 if option(&self.wasm).is_none() {
1538 continue;
1539 }
1540 ret.push(match name {
1541 Some(name) => name.to_string(),
1542 None => {
1543 let (name, _) = feature.iter_names().next().unwrap();
1544 name.to_lowercase().replace('_', "-")
1545 }
1546 });
1547 }
1548 if self.wasm.all_proposals.is_some() {
1549 ret.push("all-proposals".to_string());
1550 }
1551 ret
1552 }
1553
1554 #[cfg(feature = "toml")]
1555 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
1556 use wasmtime::error::Context;
1557
1558 let path_ref = path.as_ref();
1559 let file_contents = std::fs::read_to_string(path_ref)
1560 .with_context(|| format!("failed to read config file: {path_ref:?}"))?;
1561 toml::from_str::<CommonOptions>(&file_contents)
1562 .with_context(|| format!("failed to parse TOML config file {path_ref:?}"))
1563 }
1564
1565 pub fn from_engine(engine: &Engine) -> Self {
1578 let features = engine.get_wasm_features();
1579 let pooling = engine.get_pooling_config();
1580 CommonOptions {
1581 target: engine.get_target(),
1582 opts: OptimizeOptions {
1583 memory_may_move: Some(engine.get_memory_may_move()),
1584 memory_reservation: Some(engine.get_memory_reservation()),
1585 memory_reservation_for_growth: Some(engine.get_memory_reservation_for_growth()),
1586 memory_guard_size: Some(engine.get_memory_guard_size()),
1587 gc_heap_may_move: Some(engine.get_gc_heap_may_move()),
1588 gc_heap_reservation: Some(engine.get_gc_heap_reservation()),
1589 gc_heap_reservation_for_growth: Some(engine.get_gc_heap_reservation_for_growth()),
1590 gc_heap_guard_size: Some(engine.get_gc_heap_guard_size()),
1591 gc_heap_initial_size: Some(engine.get_gc_heap_initial_size()),
1592 guard_before_linear_memory: Some(engine.get_guard_before_linear_memory()),
1593 table_lazy_init: Some(engine.get_table_lazy_init()),
1594 memory_init_cow: Some(engine.get_memory_init_cow()),
1595 memory_guaranteed_dense_image_size: Some(
1596 engine.get_memory_guaranteed_dense_image_size(),
1597 ),
1598 signals_based_traps: Some(engine.get_signals_based_traps()),
1599 gc_zeal_alloc_counter: engine.get_gc_zeal_alloc_counter(),
1600 opt_level: engine.get_cranelift_opt_level(),
1601 regalloc_algorithm: engine.get_cranelift_regalloc_algorithm(),
1602 pooling_allocator: Some(pooling.is_some()),
1603 pooling_decommit_batch_size: pooling.map(|c| c.get_decommit_batch_size()),
1604 pooling_memory_keep_resident: pooling.map(|c| c.get_memory_keep_resident()),
1605 pooling_table_keep_resident: pooling.map(|c| c.get_table_keep_resident()),
1606 pooling_max_unused_warm_slots: pooling.map(|c| c.get_max_unused_warm_slots()),
1607 pooling_pagemap_scan: pooling.map(|c| c.get_pagemap_scan()),
1608 pooling_total_core_instances: pooling.map(|c| c.get_total_core_instances()),
1609 pooling_total_component_instances: pooling
1610 .map(|c| c.get_total_component_instances()),
1611 pooling_total_memories: pooling.map(|c| c.get_total_memories()),
1612 pooling_total_tables: pooling.map(|c| c.get_total_tables()),
1613 pooling_max_memory_size: pooling.map(|c| c.get_max_memory_size()),
1614 pooling_table_elements: pooling.map(|c| c.get_table_elements()),
1615 pooling_max_core_instance_size: pooling.map(|c| c.get_max_core_instance_size()),
1616 pooling_max_component_instance_size: pooling
1617 .map(|c| c.get_max_component_instance_size()),
1618 pooling_max_core_instances_per_component: pooling
1619 .map(|c| c.get_max_core_instances_per_component()),
1620 pooling_max_memories_per_component: pooling
1621 .map(|c| c.get_max_memories_per_component()),
1622 pooling_max_tables_per_component: pooling.map(|c| c.get_max_tables_per_component()),
1623 pooling_max_tables_per_module: pooling.map(|c| c.get_max_tables_per_module()),
1624 pooling_max_memories_per_module: pooling.map(|c| c.get_max_memories_per_module()),
1625 pooling_async_stack_keep_resident: pooling
1626 .map(|c| c.get_async_stack_keep_resident()),
1627 pooling_total_stacks: pooling.map(|c| c.get_total_stacks()),
1628 pooling_total_gc_heaps: pooling.map(|c| c.get_total_gc_heaps()),
1629 pooling_memory_protection_keys: pooling.map(|c| c.get_memory_protection_keys()),
1630 pooling_max_memory_protection_keys: pooling
1631 .map(|c| c.get_max_memory_protection_keys()),
1632 dynamic_memory_guard_size: None,
1635 static_memory_guard_size: None,
1636 static_memory_forced: None,
1637 static_memory_maximum_size: None,
1638 dynamic_memory_reserved_for_growth: None,
1639 },
1640 codegen: CodegenOptions {
1641 compiler: engine.get_strategy(),
1642 collector: engine.get_collector(),
1643 cranelift_debug_verifier: engine.get_cranelift_debug_verifier(),
1644 inlining: Some(engine.get_compiler_inlining()),
1645 native_unwind_info: engine.get_native_unwind_info(),
1646 asan_compatible_code: Some(engine.get_asan_compatible_code()),
1647 parallel_compilation: Some(engine.get_parallel_compilation()),
1648 metadata_for_internal_asserts: Some(engine.get_metadata_for_internal_asserts()),
1649 metadata_for_gc_heap_corruption: Some(engine.get_metadata_for_gc_heap_corruption()),
1650 cranelift: engine
1651 .get_cranelift_flags_set()
1652 .map(|(k, v)| (k.to_string(), Some(v.to_string())))
1653 .chain(
1654 engine
1655 .get_cranelift_flags_enabled()
1656 .map(|k| (k.to_string(), None)),
1657 )
1658 .collect(),
1659
1660 cache: None,
1663 cache_config: None,
1664
1665 unsafe_intrinsics: None,
1668 compile_time_builtin: Vec::new(),
1669 },
1670 debug: DebugOptions {
1671 address_map: Some(engine.get_generate_address_map()),
1672 debug_info: Some(engine.get_debug_info()),
1673 guest_debug: Some(engine.get_guest_debug()),
1674 symbols: Some(engine.get_debug_symbols()),
1675 max_backtrace: Some(engine.get_wasm_backtrace_max_frames()),
1676
1677 coredump: None,
1680 debugger: None,
1682 arg: Vec::new(),
1683 inherit_stderr: None,
1684 inherit_stdout: None,
1685 inherit_stdin: None,
1686 log_to_files: None,
1688 logging: None,
1689 },
1690 wasm: WasmOptions {
1691 async_stack_size: Some(engine.get_async_stack_size()),
1692 async_stack_zeroing: Some(engine.get_async_stack_zeroing()),
1693 branch_hinting: Some(engine.get_wasm_branch_hinting()),
1694 compact_imports: Some(features.contains(WasmFeatures::COMPACT_IMPORTS)),
1695 bulk_memory: Some(features.contains(WasmFeatures::BULK_MEMORY)),
1696 component_model: Some(features.contains(WasmFeatures::COMPONENT_MODEL)),
1697 component_model_async: Some(features.contains(WasmFeatures::CM_ASYNC)),
1698 component_model_async_stackful: Some(
1699 features.contains(WasmFeatures::CM_ASYNC_STACKFUL),
1700 ),
1701 component_model_error_context: Some(
1702 features.contains(WasmFeatures::CM_ERROR_CONTEXT),
1703 ),
1704 component_model_gc: Some(features.contains(WasmFeatures::CM_GC)),
1705 component_model_fixed_length_lists: Some(
1706 features.contains(WasmFeatures::CM_FIXED_LENGTH_LISTS),
1707 ),
1708 component_model_implements: Some(features.contains(WasmFeatures::CM_IMPLEMENTS)),
1709 component_model_canonical_names: Some(
1710 features.contains(WasmFeatures::CM_CANON_NAMES),
1711 ),
1712 component_model_accessors: Some(features.contains(WasmFeatures::CM_ACCESSORS)),
1713 component_model_map: Some(features.contains(WasmFeatures::CM_MAP)),
1714 component_model_memory64: Some(features.contains(WasmFeatures::CM64)),
1715 component_model_more_async_builtins: Some(
1716 features.contains(WasmFeatures::CM_MORE_ASYNC_BUILTINS),
1717 ),
1718 component_model_threading: Some(features.contains(WasmFeatures::CM_THREADING)),
1719 custom_page_sizes: Some(features.contains(WasmFeatures::CUSTOM_PAGE_SIZES)),
1720 exceptions: Some(features.contains(WasmFeatures::EXCEPTIONS)),
1721 legacy_exceptions: Some(features.contains(WasmFeatures::LEGACY_EXCEPTIONS)),
1722 extended_const: Some(features.contains(WasmFeatures::EXTENDED_CONST)),
1723 function_references: Some(features.contains(WasmFeatures::FUNCTION_REFERENCES)),
1724 gc: Some(features.contains(WasmFeatures::GC)),
1725 gc_support: Some(features.contains(WasmFeatures::GC_TYPES)),
1726 memory64: Some(features.contains(WasmFeatures::MEMORY64)),
1727 multi_memory: Some(features.contains(WasmFeatures::MULTI_MEMORY)),
1728 multi_value: Some(features.contains(WasmFeatures::MULTI_VALUE)),
1729 reference_types: Some(features.contains(WasmFeatures::REFERENCE_TYPES)),
1730 relaxed_simd: Some(features.contains(WasmFeatures::RELAXED_SIMD)),
1731 simd: Some(features.contains(WasmFeatures::SIMD)),
1732 stack_switching: Some(features.contains(WasmFeatures::STACK_SWITCHING)),
1733 tail_call: Some(features.contains(WasmFeatures::TAIL_CALL)),
1734 threads: Some(features.contains(WasmFeatures::THREADS)),
1735 wide_arithmetic: Some(features.contains(WasmFeatures::WIDE_ARITHMETIC)),
1736 concurrency_support: Some(engine.get_concurrency_support()),
1737 epoch_interruption: Some(engine.get_epoch_interruption()),
1738 fuel: if engine.get_consume_fuel() {
1739 Some(1)
1740 } else {
1741 None
1742 },
1743 max_wasm_stack: Some(engine.get_max_wasm_stack()),
1744 nan_canonicalization: engine.get_cranelift_nan_canonicalization(),
1745 relaxed_simd_deterministic: Some(engine.get_relaxed_simd_deterministic()),
1746 shared_memory: Some(engine.get_shared_memory()),
1747
1748 all_proposals: None,
1750 max_instances: None,
1753 max_memories: None,
1754 max_memory_size: None,
1755 max_table_elements: None,
1756 max_tables: None,
1757 timeout: None,
1759 trap_on_grow_failure: None,
1760 unknown_exports_allow: None,
1761 unknown_imports_default: None,
1762 unknown_imports_trap: None,
1763 wmemcheck: None,
1764 },
1765
1766 record: RecordOptions::default(),
1768
1769 wasi: Default::default(),
1771
1772 configured: true,
1774 ignore_env: false,
1775 codegen_raw: Default::default(),
1776 debug_raw: Default::default(),
1777 opts_raw: Default::default(),
1778 record_raw: Default::default(),
1779 wasi_raw: Default::default(),
1780 wasm_raw: Default::default(),
1781
1782 config: None,
1784 }
1789 }
1790}
1791
1792#[cfg(test)]
1793mod tests {
1794 use wasmtime::{OptLevel, RegallocAlgorithm};
1795
1796 use super::*;
1797
1798 #[test]
1799 fn from_toml() {
1800 let empty_toml = "";
1802 let mut common_options: CommonOptions = toml::from_str(empty_toml).unwrap();
1803 common_options.config(None).unwrap();
1804
1805 let basic_toml = r#"
1807 [optimize]
1808 [codegen]
1809 [debug]
1810 [wasm]
1811 [wasi]
1812 [record]
1813 "#;
1814 let mut common_options: CommonOptions = toml::from_str(basic_toml).unwrap();
1815 common_options.config(None).unwrap();
1816
1817 for (opt_value, expected) in [
1819 ("0", Some(OptLevel::None)),
1820 ("1", Some(OptLevel::Speed)),
1821 ("2", Some(OptLevel::Speed)),
1822 ("\"s\"", Some(OptLevel::SpeedAndSize)),
1823 ("\"hello\"", None), ("3", None), ] {
1826 let toml = format!(
1827 r#"
1828 [optimize]
1829 opt-level = {opt_value}
1830 "#,
1831 );
1832 let parsed_opt_level = toml::from_str::<CommonOptions>(&toml)
1833 .ok()
1834 .and_then(|common_options| common_options.opts.opt_level);
1835
1836 assert_eq!(
1837 parsed_opt_level, expected,
1838 "Mismatch for input '{opt_value}'. Parsed: {parsed_opt_level:?}, Expected: {expected:?}"
1839 );
1840 }
1841
1842 for (regalloc_value, expected) in [
1844 ("\"backtracking\"", Some(RegallocAlgorithm::Backtracking)),
1845 ("\"single-pass\"", Some(RegallocAlgorithm::SinglePass)),
1846 ("\"hello\"", None), ("3", None), ("true", None), ] {
1850 let toml = format!(
1851 r#"
1852 [optimize]
1853 regalloc-algorithm = {regalloc_value}
1854 "#,
1855 );
1856 let parsed_regalloc_algorithm = toml::from_str::<CommonOptions>(&toml)
1857 .ok()
1858 .and_then(|common_options| common_options.opts.regalloc_algorithm);
1859 assert_eq!(
1860 parsed_regalloc_algorithm, expected,
1861 "Mismatch for input '{regalloc_value}'. Parsed: {parsed_regalloc_algorithm:?}, Expected: {expected:?}"
1862 );
1863 }
1864
1865 for (strategy_value, expected) in [
1867 ("\"cranelift\"", Some(wasmtime::Strategy::Cranelift)),
1868 ("\"winch\"", Some(wasmtime::Strategy::Winch)),
1869 ("\"hello\"", None), ("5", None), ("true", None), ] {
1873 let toml = format!(
1874 r#"
1875 [codegen]
1876 compiler = {strategy_value}
1877 "#,
1878 );
1879 let parsed_strategy = toml::from_str::<CommonOptions>(&toml)
1880 .ok()
1881 .and_then(|common_options| common_options.codegen.compiler);
1882 assert_eq!(
1883 parsed_strategy, expected,
1884 "Mismatch for input '{strategy_value}'. Parsed: {parsed_strategy:?}, Expected: {expected:?}",
1885 );
1886 }
1887
1888 for (collector_value, expected) in [
1890 (
1891 "\"drc\"",
1892 Some(wasmtime::Collector::DeferredReferenceCounting),
1893 ),
1894 ("\"null\"", Some(wasmtime::Collector::Null)),
1895 ("\"copying\"", Some(wasmtime::Collector::Copying)),
1896 ("\"hello\"", None), ("5", None), ("true", None), ] {
1900 let toml = format!(
1901 r#"
1902 [codegen]
1903 collector = {collector_value}
1904 "#,
1905 );
1906 let parsed_collector = toml::from_str::<CommonOptions>(&toml)
1907 .ok()
1908 .and_then(|common_options| common_options.codegen.collector);
1909 assert_eq!(
1910 parsed_collector, expected,
1911 "Mismatch for input '{collector_value}'. Parsed: {parsed_collector:?}, Expected: {expected:?}",
1912 );
1913 }
1914
1915 for (timeout_value, expected) in [
1917 (
1918 "{ secs = 5, nanos = 0 }",
1919 Some(std::time::Duration::from_secs(5)),
1920 ),
1921 (
1922 "{ secs = 0, nanos = 1 }",
1923 Some(std::time::Duration::from_nanos(1)),
1924 ),
1925 ("{ secs = 0, nanos = 0 }", None), ] {
1927 let toml = format!(
1928 r#"
1929 [wasm]
1930 timeout = {timeout_value}
1931 "#,
1932 );
1933 let parsed_timeout = toml::from_str::<CommonOptions>(&toml)
1934 .ok()
1935 .and_then(|common_options| common_options.wasm.timeout)
1936 .map(|timeout| *timeout);
1937 assert_eq!(
1938 parsed_timeout, expected,
1939 "Mismatch for input '{timeout_value}'. Parsed: {parsed_timeout:?}, Expected: {expected:?}",
1940 );
1941 }
1942 }
1943}
1944
1945impl Default for CommonOptions {
1946 fn default() -> CommonOptions {
1947 CommonOptions::new()
1948 }
1949}
1950
1951impl fmt::Display for CommonOptions {
1952 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1953 let CommonOptions {
1954 codegen_raw,
1955 codegen,
1956 debug_raw,
1957 debug,
1958 opts_raw,
1959 opts,
1960 wasm_raw,
1961 wasm,
1962 wasi_raw,
1963 wasi,
1964 record_raw,
1965 record,
1966 configured,
1967 ignore_env: _,
1968 target,
1969 config,
1970 } = self;
1971 if let Some(target) = target {
1972 write!(f, "--target {target} ")?;
1973 }
1974 if let Some(config) = config {
1975 write!(f, "--config {} ", config.display())?;
1976 }
1977
1978 let codegen_flags;
1979 let opts_flags;
1980 let wasi_flags;
1981 let wasm_flags;
1982 let debug_flags;
1983 let record_flags;
1984
1985 if *configured {
1986 codegen_flags = codegen.to_options();
1987 debug_flags = debug.to_options();
1988 wasi_flags = wasi.to_options();
1989 wasm_flags = wasm.to_options();
1990 opts_flags = opts.to_options();
1991 record_flags = record.to_options();
1992 } else {
1993 codegen_flags = codegen_raw
1994 .iter()
1995 .flat_map(|t| t.0.iter())
1996 .cloned()
1997 .collect();
1998 debug_flags = debug_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
1999 wasi_flags = wasi_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
2000 wasm_flags = wasm_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
2001 opts_flags = opts_raw.iter().flat_map(|t| t.0.iter()).cloned().collect();
2002 record_flags = record_raw
2003 .iter()
2004 .flat_map(|t| t.0.iter())
2005 .cloned()
2006 .collect();
2007 }
2008
2009 for flag in codegen_flags {
2010 write!(f, "-C{flag} ")?;
2011 }
2012 for flag in opts_flags {
2013 write!(f, "-O{flag} ")?;
2014 }
2015 for flag in wasi_flags {
2016 write!(f, "-S{flag} ")?;
2017 }
2018 for flag in wasm_flags {
2019 write!(f, "-W{flag} ")?;
2020 }
2021 for flag in debug_flags {
2022 write!(f, "-D{flag} ")?;
2023 }
2024 for flag in record_flags {
2025 write!(f, "-R{flag} ")?;
2026 }
2027
2028 Ok(())
2029 }
2030}