1 use crate::commands::run::{CliInstance, Preloads, RunCommand}; 2 use crate::common::{RunCommon, RunTarget}; 3 use std::fs; 4 use std::io::{self, Read, Write}; 5 use std::path::PathBuf; 6 use wasmtime::{Module, Result, error::Context as _}; 7 use wasmtime_wizer::Wizer; 8 9 #[derive(clap::Parser)] 10 #[expect(missing_docs, reason = "inheriting wizer's docs")] 11 pub struct WizerCommand { 12 #[command(flatten)] 13 run: RunCommon, 14 15 #[command(flatten)] 16 wizer: Wizer, 17 18 /// The input Wasm module's file path. 19 input: PathBuf, 20 21 #[command(flatten)] 22 preloads: Preloads, 23 24 /// The file path to write the output Wasm module to. 25 /// 26 /// If not specified, then `stdout` is used. 27 #[arg(short = 'o', long)] 28 output: Option<PathBuf>, 29 } 30 31 enum WizerInfo<'a> { 32 Core(wasmtime_wizer::ModuleContext<'a>), 33 #[cfg(feature = "component-model")] 34 Component(wasmtime_wizer::ComponentContext<'a>), 35 } 36 37 impl WizerCommand { 38 /// Runs the command. execute(mut self) -> Result<()>39 pub fn execute(mut self) -> Result<()> { 40 self.run.common.init_logging()?; 41 let runtime = tokio::runtime::Builder::new_multi_thread() 42 .enable_time() 43 .enable_io() 44 .build()?; 45 runtime.block_on(self.execute_async()) 46 } 47 execute_async(mut self) -> Result<()>48 async fn execute_async(mut self) -> Result<()> { 49 // By default use deterministic relaxed simd operations to guarantee 50 // that if relaxed simd operations are used in a module that they always 51 // produce the same result. 52 if self.run.common.wasm.relaxed_simd_deterministic.is_none() { 53 self.run.common.wasm.relaxed_simd_deterministic = Some(true); 54 } 55 56 // Don't provide any WASI imports by default to wizened components. The 57 // `run` command provides the "cli" world as a default so turn that off 58 // here if the command line flags don't otherwise say what to do. 59 if self.run.common.wasi.cli.is_none() { 60 self.run.common.wasi.cli = Some(false); 61 } 62 63 // Read the input wasm, possibly from stdin. 64 let mut wasm = Vec::new(); 65 if self.input.to_str() == Some("-") { 66 io::stdin() 67 .read_to_end(&mut wasm) 68 .context("failed to read input Wasm module from stdin")?; 69 } else { 70 wasm = fs::read(&self.input).context("failed to read input Wasm module")?; 71 } 72 73 #[cfg(feature = "wat")] 74 let wasm = wat::parse_bytes(&wasm)?; 75 let is_component = wasmparser::Parser::is_component(&wasm); 76 77 let mut run = RunCommand { 78 run: self.run, 79 argv0: None, 80 invoke: Some(if is_component { 81 format!("{}()", self.wizer.get_init_func()) 82 } else { 83 self.wizer.get_init_func().to_string() 84 }), 85 module_and_args: vec![self.input.clone().into()], 86 preloads: self.preloads.clone(), 87 module_bytes: None, 88 }; 89 let engine = run.new_engine()?; 90 91 // Instrument the input wasm with wizer. 92 let (cx, main) = if is_component { 93 #[cfg(feature = "component-model")] 94 { 95 let (cx, wasm) = self.wizer.instrument_component(&wasm)?; 96 ( 97 WizerInfo::Component(cx), 98 RunTarget::Component(wasmtime::component::Component::new(&engine, &wasm)?), 99 ) 100 } 101 #[cfg(not(feature = "component-model"))] 102 unreachable!(); 103 } else { 104 let (cx, wasm) = self.wizer.instrument(&wasm)?; 105 ( 106 WizerInfo::Core(cx), 107 RunTarget::Core(Module::new(&engine, &wasm)?), 108 ) 109 }; 110 111 // Execute a rough equivalent of 112 // `wasmtime run --invoke <..> <instrumented-wasm>` 113 let (mut store, mut linker) = run.new_store_and_linker(&engine, &main)?; 114 let instance = run 115 .instantiate_and_run(&engine, &mut linker, &main, &mut store) 116 .await?; 117 118 // Use our state to capture a snapshot with Wizer and then serialize 119 // that. 120 let final_wasm = match (cx, instance) { 121 (WizerInfo::Core(cx), CliInstance::Core(instance)) => { 122 self.wizer 123 .snapshot( 124 cx, 125 &mut wasmtime_wizer::WasmtimeWizer { 126 store: &mut store, 127 instance, 128 }, 129 ) 130 .await? 131 } 132 133 #[cfg(feature = "component-model")] 134 (WizerInfo::Component(cx), CliInstance::Component(instance)) => { 135 self.wizer 136 .snapshot_component( 137 cx, 138 &mut wasmtime_wizer::WasmtimeWizerComponent { 139 store: &mut store, 140 instance, 141 }, 142 ) 143 .await? 144 } 145 146 #[cfg(feature = "component-model")] 147 (WizerInfo::Core(_) | WizerInfo::Component(_), _) => unreachable!(), 148 }; 149 150 match &self.output { 151 Some(file) => fs::write(file, &final_wasm).context("failed to write output file")?, 152 None => std::io::stdout() 153 .write_all(&final_wasm) 154 .context("failed to write output to stdout")?, 155 } 156 Ok(()) 157 } 158 } 159