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