use std::process;
use colored::Colorize;
pub(super) fn cmd_run_wasip2(
file: &str,
module_root_override: Option<&str>,
record_dir: Option<&str>,
program_args: Vec<String>,
) {
#[cfg(not(feature = "wasip2"))]
{
let _ = (file, module_root_override, record_dir, program_args);
eprintln!(
"{}",
"--wasip2 requires --features wasip2 (rebuild with: cargo build --features wasip2)"
.red()
);
process::exit(1);
}
#[cfg(feature = "wasip2")]
{
if record_dir.is_some() {
eprintln!(
"{}",
"--record is not yet supported on --wasip2: the recorder needs a \
separate plumbing pass against the canonical-ABI WASI imports. \
Use `aver run --wasm-gc --record` until that lands."
.red()
);
process::exit(1);
}
let component_bytes = match build_component_bytes(file, module_root_override) {
Ok(b) => b,
Err(e) => {
eprintln!("{}", e.red());
process::exit(1);
}
};
match run_component(&component_bytes, &program_args) {
Ok(()) => {}
Err(e) => {
eprintln!(
"{}",
format!("--wasip2: component execution failed — {e}").red()
);
process::exit(1);
}
}
}
}
#[cfg(feature = "wasip2")]
fn build_component_bytes(
file: &str,
module_root_override: Option<&str>,
) -> Result<Vec<u8>, String> {
use aver::codegen::{wasip2 as wasip2_codegen, wasm_gc};
use aver::ir::{NeutralAllocPolicy, PipelineConfig, TypecheckMode};
let module_root = super::shared::resolve_module_root_for_entry(file, module_root_override);
let source = super::shared::read_file(file).map_err(|e| e.to_string())?;
let mut items = super::shared::parse_file(&source).map_err(|e| e.to_string())?;
let neutral_policy = NeutralAllocPolicy;
let result = aver::ir::pipeline::run(
&mut items,
PipelineConfig {
typecheck: Some(TypecheckMode::Full {
base_dir: Some(&module_root),
}),
alloc_policy: Some(&neutral_policy),
run_interp_lower: false,
run_buffer_build: false,
..Default::default()
},
);
if let Some(tc) = &result.typecheck
&& !tc.errors.is_empty()
{
return Err(super::shared::format_type_errors(&tc.errors));
}
let dep_modules = super::commands::load_compile_deps(&items, &module_root, false, false);
aver::codegen::wasm_gc::flatten_multimodule(&mut items, &dep_modules);
aver::ir::pipeline::resolve(&mut items);
if let Err(unsupported) = wasip2_codegen::check_supported_effects(&items) {
let mut out = format!(
"error[target-effect-unsupported]: {} effect site(s) cannot be lowered by \
`--wasip2`\n",
unsupported.len()
);
out.push_str(&wasip2_codegen::render_errors(&unsupported));
out.push_str(
"\n See docs/wasip2.md (\"Why X is rejected, not stubbed\") for the \
static-target vs dynamic-host axis.",
);
return Err(out);
}
let core_bytes = wasm_gc::compile_to_wasm_gc_for_wasip2(&items, result.analysis.as_ref())
.map_err(|e| format!("wasm-gc lowering: {e}"))?;
let (component_bytes, _wit) =
wasip2_codegen::compile_to_component(&core_bytes, wasip2_codegen::Wasip2World::CliCommand)
.map_err(|e| format!("component wrap: {e}"))?;
Ok(component_bytes)
}
#[cfg(feature = "wasip2")]
fn run_component(component_bytes: &[u8], program_args: &[String]) -> wasmtime::Result<()> {
use wasmtime::component::{Component, Linker, ResourceTable};
use wasmtime::{Config, Engine, Store};
use wasmtime_wasi::p2::bindings::sync::Command;
use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView};
let mut config = Config::new();
config.wasm_component_model(true);
config.wasm_gc(true);
config.wasm_function_references(true);
let engine = Engine::new(&config)?;
let component = Component::from_binary(&engine, component_bytes)?;
let mut ctx_builder = WasiCtxBuilder::new();
ctx_builder.inherit_stdio();
ctx_builder.arg(std::path::Path::new("aver-wasip2").display().to_string());
for a in program_args {
ctx_builder.arg(a);
}
ctx_builder.inherit_env();
ctx_builder
.preopened_dir(
".",
".",
wasmtime_wasi::DirPerms::all(),
wasmtime_wasi::FilePerms::all(),
)
.map_err(|e| wasmtime::Error::msg(format!("preopen `.`: {e}")))?;
let ctx = ctx_builder.build();
struct Host {
ctx: WasiCtx,
table: ResourceTable,
}
impl WasiView for Host {
fn ctx(&mut self) -> WasiCtxView<'_> {
WasiCtxView {
ctx: &mut self.ctx,
table: &mut self.table,
}
}
}
let mut store = Store::new(
&engine,
Host {
ctx,
table: ResourceTable::new(),
},
);
let mut linker = Linker::<Host>::new(&engine);
wasmtime_wasi::p2::add_to_linker_sync(&mut linker)?;
let command = Command::instantiate(&mut store, &component, &linker)?;
match command.wasi_cli_run().call_run(&mut store)? {
Ok(()) => Ok(()),
Err(()) => {
eprintln!("{}", "--wasip2: program returned non-zero exit code".red());
std::process::exit(1);
}
}
}