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