1 //! Contains the common Wasmtime command line interface (CLI) flags. 2 3 #![deny(trivial_numeric_casts, unused_extern_crates, unstable_features)] 4 #![warn(unused_import_braces)] 5 #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] 6 #![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] 7 #![cfg_attr( 8 feature = "cargo-clippy", 9 warn( 10 clippy::float_arithmetic, 11 clippy::mut_mut, 12 clippy::nonminimal_bool, 13 clippy::map_unwrap_or, 14 clippy::unicode_not_nfc, 15 clippy::use_self 16 ) 17 )] 18 19 use anyhow::{bail, Result}; 20 use clap::Parser; 21 use std::collections::HashMap; 22 use std::path::PathBuf; 23 use wasmtime::{Config, Strategy}; 24 25 pub const SUPPORTED_WASM_FEATURES: &[(&str, &str)] = &[ 26 ("all", "enables all supported WebAssembly features"), 27 ( 28 "bulk-memory", 29 "enables support for bulk memory instructions", 30 ), 31 ( 32 "multi-memory", 33 "enables support for the multi-memory proposal", 34 ), 35 ("multi-value", "enables support for multi-value functions"), 36 ("reference-types", "enables support for reference types"), 37 ("simd", "enables support for proposed SIMD instructions"), 38 ( 39 "relaxed-simd", 40 "enables support for the relaxed simd proposal", 41 ), 42 ("tail-call", "enables support for WebAssembly tail calls"), 43 ("threads", "enables support for WebAssembly threads"), 44 ("memory64", "enables support for 64-bit memories"), 45 #[cfg(feature = "component-model")] 46 ("component-model", "enables support for the component model"), 47 ( 48 "function-references", 49 "enables support for typed function references", 50 ), 51 ]; 52 53 pub const SUPPORTED_WASI_MODULES: &[(&str, &str)] = &[ 54 ( 55 "default", 56 "enables all stable WASI modules (no experimental modules)", 57 ), 58 ( 59 "wasi-common", 60 "enables support for the WASI common APIs, see https://github.com/WebAssembly/WASI", 61 ), 62 ( 63 "experimental-wasi-nn", 64 "enables support for the WASI neural network API (experimental), see https://github.com/WebAssembly/wasi-nn", 65 ), 66 ( 67 "experimental-wasi-threads", 68 "enables support for the WASI threading API (experimental), see https://github.com/WebAssembly/wasi-threads", 69 ), 70 ( 71 "experimental-wasi-http", 72 "enables support for the WASI HTTP APIs (experimental), see https://github.com/WebAssembly/wasi-http", 73 ), 74 ]; 75 76 fn init_file_per_thread_logger(prefix: &'static str) { 77 file_per_thread_logger::initialize(prefix); 78 79 // Extending behavior of default spawner: 80 // https://docs.rs/rayon/1.1.0/rayon/struct.ThreadPoolBuilder.html#method.spawn_handler 81 // Source code says DefaultSpawner is implementation detail and 82 // shouldn't be used directly. 83 rayon::ThreadPoolBuilder::new() 84 .spawn_handler(move |thread| { 85 let mut b = std::thread::Builder::new(); 86 if let Some(name) = thread.name() { 87 b = b.name(name.to_owned()); 88 } 89 if let Some(stack_size) = thread.stack_size() { 90 b = b.stack_size(stack_size); 91 } 92 b.spawn(move || { 93 file_per_thread_logger::initialize(prefix); 94 thread.run() 95 })?; 96 Ok(()) 97 }) 98 .build_global() 99 .unwrap(); 100 } 101 102 /// Common options for commands that translate WebAssembly modules 103 #[derive(Parser)] 104 #[cfg_attr(test, derive(Debug, PartialEq))] 105 pub struct CommonOptions { 106 /// Use specified configuration file 107 #[clap(long, value_name = "CONFIG_PATH")] 108 pub config: Option<PathBuf>, 109 110 /// Disable logging 111 #[clap(long, conflicts_with = "log_to_files")] 112 pub disable_logging: bool, 113 114 /// Log to per-thread log files instead of stderr 115 #[clap(long)] 116 pub log_to_files: bool, 117 118 /// Generate debug information 119 #[clap(short = 'g')] 120 pub debug_info: bool, 121 122 /// Disable cache system 123 #[clap(long)] 124 pub disable_cache: bool, 125 126 /// Disable parallel compilation 127 #[clap(long)] 128 pub disable_parallel_compilation: bool, 129 130 /// Enable or disable WebAssembly features 131 #[clap(long, value_name = "FEATURE,FEATURE,...", value_parser = parse_wasm_features)] 132 pub wasm_features: Option<WasmFeatures>, 133 134 /// Enable or disable WASI modules 135 #[clap(long, value_name = "MODULE,MODULE,...", value_parser = parse_wasi_modules)] 136 pub wasi_modules: Option<WasiModules>, 137 138 /// Generate jitdump file (supported on --features=profiling build) 139 /// Run optimization passes on translated functions, on by default 140 #[clap(short = 'O', long)] 141 pub optimize: bool, 142 143 /// Optimization level for generated functions 144 /// Supported levels: 0 (none), 1, 2 (most), or s (size); default is "most" 145 #[clap( 146 long, 147 value_name = "LEVEL", 148 value_parser = parse_opt_level, 149 verbatim_doc_comment, 150 )] 151 pub opt_level: Option<wasmtime::OptLevel>, 152 153 /// Set a Cranelift setting to a given value. 154 /// Use `wasmtime settings` to list Cranelift settings for a target. 155 #[clap( 156 long = "cranelift-set", 157 value_name = "NAME=VALUE", 158 number_of_values = 1, 159 verbatim_doc_comment, 160 value_parser = parse_cranelift_flag, 161 )] 162 pub cranelift_set: Vec<(String, String)>, 163 164 /// Enable a Cranelift boolean setting or preset. 165 /// Use `wasmtime settings` to list Cranelift settings for a target. 166 #[clap( 167 long, 168 value_name = "SETTING", 169 number_of_values = 1, 170 verbatim_doc_comment 171 )] 172 pub cranelift_enable: Vec<String>, 173 174 /// Maximum size in bytes of wasm memory before it becomes dynamically 175 /// relocatable instead of up-front-reserved. 176 #[clap(long, value_name = "MAXIMUM")] 177 pub static_memory_maximum_size: Option<u64>, 178 179 /// Force using a "static" style for all wasm memories 180 #[clap(long)] 181 pub static_memory_forced: bool, 182 183 /// Byte size of the guard region after static memories are allocated 184 #[clap(long, value_name = "SIZE")] 185 pub static_memory_guard_size: Option<u64>, 186 187 /// Byte size of the guard region after dynamic memories are allocated 188 #[clap(long, value_name = "SIZE")] 189 pub dynamic_memory_guard_size: Option<u64>, 190 191 /// Bytes to reserve at the end of linear memory for growth for dynamic 192 /// memories. 193 #[clap(long, value_name = "SIZE")] 194 pub dynamic_memory_reserved_for_growth: Option<u64>, 195 196 /// Enable Cranelift's internal debug verifier (expensive) 197 #[clap(long)] 198 pub enable_cranelift_debug_verifier: bool, 199 200 /// Enable Cranelift's internal NaN canonicalization 201 #[clap(long)] 202 pub enable_cranelift_nan_canonicalization: bool, 203 204 /// Enable execution fuel with N units fuel, where execution will trap after 205 /// running out of fuel. 206 /// 207 /// Most WebAssembly instructions consume 1 unit of fuel. Some instructions, 208 /// such as `nop`, `drop`, `block`, and `loop`, consume 0 units, as any 209 /// execution cost associated with them involves other instructions which do 210 /// consume fuel. 211 #[clap(long, value_name = "N")] 212 pub fuel: Option<u64>, 213 214 /// Executing wasm code will yield when a global epoch counter 215 /// changes, allowing for async operation without blocking the 216 /// executor. 217 #[clap(long)] 218 pub epoch_interruption: bool, 219 220 /// Disable the on-by-default address map from native code to wasm code 221 #[clap(long)] 222 pub disable_address_map: bool, 223 224 /// Disable the default of attempting to initialize linear memory via a 225 /// copy-on-write mapping 226 #[clap(long)] 227 pub disable_memory_init_cow: bool, 228 229 /// Enable the pooling allocator, in place of the on-demand 230 /// allocator. 231 #[cfg(feature = "pooling-allocator")] 232 #[clap(long)] 233 pub pooling_allocator: bool, 234 235 /// Maximum stack size, in bytes, that wasm is allowed to consume before a 236 /// stack overflow is reported. 237 #[clap(long)] 238 pub max_wasm_stack: Option<usize>, 239 240 /// Whether or not to force deterministic and host-independent behavior of 241 /// the relaxed-simd instructions. 242 /// 243 /// By default these instructions may have architecture-specific behavior as 244 /// allowed by the specification, but this can be used to force the behavior 245 /// of these instructions to match the deterministic behavior classified in 246 /// the specification. Note that enabling this option may come at a 247 /// performance cost. 248 #[clap(long)] 249 pub relaxed_simd_deterministic: bool, 250 /// Explicitly specify the name of the compiler to use for WebAssembly. 251 /// 252 /// Currently only `cranelift` and `winch` are supported, but not all builds 253 /// of Wasmtime have both built in. 254 #[clap(long)] 255 pub compiler: Option<String>, 256 } 257 258 impl CommonOptions { 259 pub fn init_logging(&self) { 260 if self.disable_logging { 261 return; 262 } 263 if self.log_to_files { 264 let prefix = "wasmtime.dbg."; 265 init_file_per_thread_logger(prefix); 266 } else { 267 pretty_env_logger::init(); 268 } 269 } 270 271 pub fn config(&self, target: Option<&str>) -> Result<Config> { 272 let mut config = Config::new(); 273 274 config.strategy(match self.compiler.as_deref() { 275 None => Strategy::Auto, 276 Some("cranelift") => Strategy::Cranelift, 277 Some("winch") => Strategy::Winch, 278 Some(s) => bail!("unknown compiler: {s}"), 279 }); 280 281 // Set the target before setting any cranelift options, since the 282 // target will reset any target-specific options. 283 if let Some(target) = target { 284 config.target(target)?; 285 } 286 287 config 288 .cranelift_debug_verifier(self.enable_cranelift_debug_verifier) 289 .debug_info(self.debug_info) 290 .cranelift_opt_level(self.opt_level()) 291 .cranelift_nan_canonicalization(self.enable_cranelift_nan_canonicalization); 292 293 self.enable_wasm_features(&mut config); 294 295 for name in &self.cranelift_enable { 296 unsafe { 297 config.cranelift_flag_enable(name); 298 } 299 } 300 301 for (name, value) in &self.cranelift_set { 302 unsafe { 303 config.cranelift_flag_set(name, value); 304 } 305 } 306 307 if !self.disable_cache { 308 match &self.config { 309 Some(path) => { 310 config.cache_config_load(path)?; 311 } 312 None => { 313 config.cache_config_load_default()?; 314 } 315 } 316 } 317 318 if self.disable_parallel_compilation { 319 config.parallel_compilation(false); 320 } 321 322 if let Some(max) = self.static_memory_maximum_size { 323 config.static_memory_maximum_size(max); 324 } 325 326 config.static_memory_forced(self.static_memory_forced); 327 328 if let Some(size) = self.static_memory_guard_size { 329 config.static_memory_guard_size(size); 330 } 331 332 if let Some(size) = self.dynamic_memory_guard_size { 333 config.dynamic_memory_guard_size(size); 334 } 335 if let Some(size) = self.dynamic_memory_reserved_for_growth { 336 config.dynamic_memory_reserved_for_growth(size); 337 } 338 339 // If fuel has been configured, set the `consume fuel` flag on the config. 340 if self.fuel.is_some() { 341 config.consume_fuel(true); 342 } 343 344 config.epoch_interruption(self.epoch_interruption); 345 config.generate_address_map(!self.disable_address_map); 346 config.memory_init_cow(!self.disable_memory_init_cow); 347 348 #[cfg(feature = "pooling-allocator")] 349 { 350 if self.pooling_allocator { 351 config.allocation_strategy(wasmtime::InstanceAllocationStrategy::pooling()); 352 } 353 } 354 355 if let Some(max) = self.max_wasm_stack { 356 config.max_wasm_stack(max); 357 } 358 359 config.relaxed_simd_deterministic(self.relaxed_simd_deterministic); 360 361 Ok(config) 362 } 363 364 pub fn enable_wasm_features(&self, config: &mut Config) { 365 let WasmFeatures { 366 simd, 367 relaxed_simd, 368 bulk_memory, 369 reference_types, 370 multi_value, 371 tail_call, 372 threads, 373 multi_memory, 374 memory64, 375 #[cfg(feature = "component-model")] 376 component_model, 377 function_references, 378 } = self.wasm_features.unwrap_or_default(); 379 380 if let Some(enable) = simd { 381 config.wasm_simd(enable); 382 } 383 if let Some(enable) = relaxed_simd { 384 config.wasm_relaxed_simd(enable); 385 } 386 if let Some(enable) = bulk_memory { 387 config.wasm_bulk_memory(enable); 388 } 389 if let Some(enable) = reference_types { 390 config.wasm_reference_types(enable); 391 } 392 if let Some(enable) = function_references { 393 config.wasm_function_references(enable); 394 } 395 if let Some(enable) = multi_value { 396 config.wasm_multi_value(enable); 397 } 398 if let Some(enable) = tail_call { 399 config.wasm_tail_call(enable); 400 } 401 if let Some(enable) = threads { 402 config.wasm_threads(enable); 403 } 404 if let Some(enable) = multi_memory { 405 config.wasm_multi_memory(enable); 406 } 407 if let Some(enable) = memory64 { 408 config.wasm_memory64(enable); 409 } 410 #[cfg(feature = "component-model")] 411 if let Some(enable) = component_model { 412 config.wasm_component_model(enable); 413 } 414 } 415 416 pub fn opt_level(&self) -> wasmtime::OptLevel { 417 match (self.optimize, self.opt_level.clone()) { 418 (true, _) => wasmtime::OptLevel::Speed, 419 (false, other) => other.unwrap_or(wasmtime::OptLevel::Speed), 420 } 421 } 422 } 423 424 fn parse_opt_level(opt_level: &str) -> Result<wasmtime::OptLevel> { 425 match opt_level { 426 "s" => Ok(wasmtime::OptLevel::SpeedAndSize), 427 "0" => Ok(wasmtime::OptLevel::None), 428 "1" => Ok(wasmtime::OptLevel::Speed), 429 "2" => Ok(wasmtime::OptLevel::Speed), 430 other => bail!( 431 "unknown optimization level `{}`, only 0,1,2,s accepted", 432 other 433 ), 434 } 435 } 436 437 #[derive(Default, Clone, Copy)] 438 #[cfg_attr(test, derive(Debug, PartialEq))] 439 pub struct WasmFeatures { 440 pub reference_types: Option<bool>, 441 pub multi_value: Option<bool>, 442 pub bulk_memory: Option<bool>, 443 pub simd: Option<bool>, 444 pub relaxed_simd: Option<bool>, 445 pub tail_call: Option<bool>, 446 pub threads: Option<bool>, 447 pub multi_memory: Option<bool>, 448 pub memory64: Option<bool>, 449 #[cfg(feature = "component-model")] 450 pub component_model: Option<bool>, 451 pub function_references: Option<bool>, 452 } 453 454 fn parse_wasm_features(features: &str) -> Result<WasmFeatures> { 455 let features = features.trim(); 456 457 let mut all = None; 458 let mut values: HashMap<_, _> = SUPPORTED_WASM_FEATURES 459 .iter() 460 .map(|(name, _)| (name.to_string(), None)) 461 .collect(); 462 463 if features == "all" { 464 all = Some(true); 465 } else if features == "-all" { 466 all = Some(false); 467 } else { 468 for feature in features.split(',') { 469 let feature = feature.trim(); 470 471 if feature.is_empty() { 472 continue; 473 } 474 475 let (feature, value) = if feature.starts_with('-') { 476 (&feature[1..], false) 477 } else { 478 (feature, true) 479 }; 480 481 if feature == "all" { 482 bail!("'all' cannot be specified with other WebAssembly features"); 483 } 484 485 match values.get_mut(feature) { 486 Some(v) => *v = Some(value), 487 None => bail!("unsupported WebAssembly feature '{}'", feature), 488 } 489 } 490 } 491 492 Ok(WasmFeatures { 493 reference_types: all.or(values["reference-types"]), 494 multi_value: all.or(values["multi-value"]), 495 bulk_memory: all.or(values["bulk-memory"]), 496 simd: all.or(values["simd"]), 497 relaxed_simd: all.or(values["relaxed-simd"]), 498 tail_call: all.or(values["tail-call"]), 499 threads: all.or(values["threads"]), 500 multi_memory: all.or(values["multi-memory"]), 501 memory64: all.or(values["memory64"]), 502 #[cfg(feature = "component-model")] 503 component_model: all.or(values["component-model"]), 504 function_references: all.or(values["function-references"]), 505 }) 506 } 507 508 fn parse_wasi_modules(modules: &str) -> Result<WasiModules> { 509 let modules = modules.trim(); 510 match modules { 511 "default" => Ok(WasiModules::default()), 512 "-default" => Ok(WasiModules::none()), 513 _ => { 514 // Starting from the default set of WASI modules, enable or disable a list of 515 // comma-separated modules. 516 let mut wasi_modules = WasiModules::default(); 517 let mut set = |module: &str, enable: bool| match module { 518 "" => Ok(()), 519 "wasi-common" => Ok(wasi_modules.wasi_common = enable), 520 "experimental-wasi-nn" => Ok(wasi_modules.wasi_nn = enable), 521 "experimental-wasi-threads" => Ok(wasi_modules.wasi_threads = enable), 522 "experimental-wasi-http" => Ok(wasi_modules.wasi_http = enable), 523 "default" => bail!("'default' cannot be specified with other WASI modules"), 524 _ => bail!("unsupported WASI module '{}'", module), 525 }; 526 527 for module in modules.split(',') { 528 let module = module.trim(); 529 let (module, value) = if module.starts_with('-') { 530 (&module[1..], false) 531 } else { 532 (module, true) 533 }; 534 set(module, value)?; 535 } 536 537 Ok(wasi_modules) 538 } 539 } 540 } 541 542 /// Select which WASI modules are available at runtime for use by Wasm programs. 543 #[derive(Debug, Clone, Copy, PartialEq)] 544 pub struct WasiModules { 545 /// Enable the wasi-common implementation; eventually this should be split into its separate 546 /// parts once the implementation allows for it (e.g. wasi-fs, wasi-clocks, etc.). 547 pub wasi_common: bool, 548 549 /// Enable the experimental wasi-nn implementation. 550 pub wasi_nn: bool, 551 552 /// Enable the experimental wasi-threads implementation. 553 pub wasi_threads: bool, 554 555 /// Enable the experimental wasi-http implementation 556 pub wasi_http: bool, 557 } 558 559 impl Default for WasiModules { 560 fn default() -> Self { 561 Self { 562 wasi_common: true, 563 wasi_nn: false, 564 wasi_threads: false, 565 wasi_http: false, 566 } 567 } 568 } 569 570 impl WasiModules { 571 /// Enable no modules. 572 pub fn none() -> Self { 573 Self { 574 wasi_common: false, 575 wasi_nn: false, 576 wasi_threads: false, 577 wasi_http: false, 578 } 579 } 580 } 581 582 fn parse_cranelift_flag(name_and_value: &str) -> Result<(String, String)> { 583 let mut split = name_and_value.splitn(2, '='); 584 let name = if let Some(name) = split.next() { 585 name.to_string() 586 } else { 587 bail!("missing name in cranelift flag"); 588 }; 589 let value = if let Some(value) = split.next() { 590 value.to_string() 591 } else { 592 bail!("missing value in cranelift flag"); 593 }; 594 Ok((name, value)) 595 } 596 597 #[cfg(test)] 598 mod test { 599 use super::*; 600 601 #[test] 602 fn test_all_features() -> Result<()> { 603 let options = CommonOptions::try_parse_from(vec!["foo", "--wasm-features=all"])?; 604 605 let WasmFeatures { 606 reference_types, 607 multi_value, 608 bulk_memory, 609 simd, 610 relaxed_simd, 611 tail_call, 612 threads, 613 multi_memory, 614 memory64, 615 function_references, 616 #[cfg(feature = "component-model")] 617 component_model, 618 } = options.wasm_features.unwrap(); 619 620 assert_eq!(reference_types, Some(true)); 621 assert_eq!(multi_value, Some(true)); 622 assert_eq!(bulk_memory, Some(true)); 623 assert_eq!(simd, Some(true)); 624 assert_eq!(tail_call, Some(true)); 625 assert_eq!(threads, Some(true)); 626 assert_eq!(multi_memory, Some(true)); 627 assert_eq!(memory64, Some(true)); 628 assert_eq!(function_references, Some(true)); 629 assert_eq!(relaxed_simd, Some(true)); 630 #[cfg(feature = "component-model")] 631 assert_eq!(component_model, Some(true)); 632 633 Ok(()) 634 } 635 636 #[test] 637 fn test_no_features() -> Result<()> { 638 let options = CommonOptions::try_parse_from(vec!["foo", "--wasm-features=-all"])?; 639 640 let WasmFeatures { 641 reference_types, 642 multi_value, 643 bulk_memory, 644 simd, 645 relaxed_simd, 646 tail_call, 647 threads, 648 multi_memory, 649 memory64, 650 function_references, 651 #[cfg(feature = "component-model")] 652 component_model, 653 } = options.wasm_features.unwrap(); 654 655 assert_eq!(reference_types, Some(false)); 656 assert_eq!(multi_value, Some(false)); 657 assert_eq!(bulk_memory, Some(false)); 658 assert_eq!(simd, Some(false)); 659 assert_eq!(tail_call, Some(false)); 660 assert_eq!(threads, Some(false)); 661 assert_eq!(multi_memory, Some(false)); 662 assert_eq!(memory64, Some(false)); 663 assert_eq!(function_references, Some(false)); 664 assert_eq!(relaxed_simd, Some(false)); 665 #[cfg(feature = "component-model")] 666 assert_eq!(component_model, Some(false)); 667 668 Ok(()) 669 } 670 671 #[test] 672 fn test_multiple_features() -> Result<()> { 673 let options = CommonOptions::try_parse_from(vec![ 674 "foo", 675 "--wasm-features=-reference-types,simd,multi-memory,memory64", 676 ])?; 677 678 let WasmFeatures { 679 reference_types, 680 multi_value, 681 bulk_memory, 682 simd, 683 relaxed_simd, 684 tail_call, 685 threads, 686 multi_memory, 687 memory64, 688 function_references, 689 #[cfg(feature = "component-model")] 690 component_model, 691 } = options.wasm_features.unwrap(); 692 693 assert_eq!(reference_types, Some(false)); 694 assert_eq!(multi_value, None); 695 assert_eq!(bulk_memory, None); 696 assert_eq!(simd, Some(true)); 697 assert_eq!(tail_call, None); 698 assert_eq!(threads, None); 699 assert_eq!(multi_memory, Some(true)); 700 assert_eq!(memory64, Some(true)); 701 assert_eq!(function_references, None); 702 assert_eq!(relaxed_simd, None); 703 #[cfg(feature = "component-model")] 704 assert_eq!(component_model, None); 705 706 Ok(()) 707 } 708 709 macro_rules! feature_test { 710 ($test_name:ident, $name:ident, $flag:literal) => { 711 #[test] 712 fn $test_name() -> Result<()> { 713 let options = 714 CommonOptions::try_parse_from(vec!["foo", concat!("--wasm-features=", $flag)])?; 715 716 let WasmFeatures { $name, .. } = options.wasm_features.unwrap(); 717 718 assert_eq!($name, Some(true)); 719 720 let options = CommonOptions::try_parse_from(vec![ 721 "foo", 722 concat!("--wasm-features=-", $flag), 723 ])?; 724 725 let WasmFeatures { $name, .. } = options.wasm_features.unwrap(); 726 727 assert_eq!($name, Some(false)); 728 729 Ok(()) 730 } 731 }; 732 } 733 734 feature_test!( 735 test_reference_types_feature, 736 reference_types, 737 "reference-types" 738 ); 739 feature_test!(test_multi_value_feature, multi_value, "multi-value"); 740 feature_test!(test_bulk_memory_feature, bulk_memory, "bulk-memory"); 741 feature_test!(test_simd_feature, simd, "simd"); 742 feature_test!(test_relaxed_simd_feature, relaxed_simd, "relaxed-simd"); 743 feature_test!(test_tail_call_feature, tail_call, "tail-call"); 744 feature_test!(test_threads_feature, threads, "threads"); 745 feature_test!(test_multi_memory_feature, multi_memory, "multi-memory"); 746 feature_test!(test_memory64_feature, memory64, "memory64"); 747 748 #[test] 749 fn test_default_modules() { 750 let options = CommonOptions::try_parse_from(vec!["foo", "--wasi-modules=default"]).unwrap(); 751 assert_eq!( 752 options.wasi_modules.unwrap(), 753 WasiModules { 754 wasi_common: true, 755 wasi_nn: false, 756 wasi_threads: false, 757 wasi_http: false, 758 } 759 ); 760 } 761 762 #[test] 763 fn test_empty_modules() { 764 let options = CommonOptions::try_parse_from(vec!["foo", "--wasi-modules="]).unwrap(); 765 assert_eq!( 766 options.wasi_modules.unwrap(), 767 WasiModules { 768 wasi_common: true, 769 wasi_nn: false, 770 wasi_threads: false, 771 wasi_http: false 772 } 773 ); 774 } 775 776 #[test] 777 fn test_some_modules() { 778 let options = CommonOptions::try_parse_from(vec![ 779 "foo", 780 "--wasi-modules=experimental-wasi-nn,-wasi-common", 781 ]) 782 .unwrap(); 783 assert_eq!( 784 options.wasi_modules.unwrap(), 785 WasiModules { 786 wasi_common: false, 787 wasi_nn: true, 788 wasi_threads: false, 789 wasi_http: false, 790 } 791 ); 792 } 793 794 #[test] 795 fn test_no_modules() { 796 let options = 797 CommonOptions::try_parse_from(vec!["foo", "--wasi-modules=-default"]).unwrap(); 798 assert_eq!( 799 options.wasi_modules.unwrap(), 800 WasiModules { 801 wasi_common: false, 802 wasi_nn: false, 803 wasi_threads: false, 804 wasi_http: false, 805 } 806 ); 807 } 808 } 809