diff options
Diffstat (limited to 'crates/cli/src/cmd')
| -rw-r--r-- | crates/cli/src/cmd/compile.rs | 15 | ||||
| -rw-r--r-- | crates/cli/src/cmd/completion.rs | 12 | ||||
| -rw-r--r-- | crates/cli/src/cmd/dump.rs | 71 | ||||
| -rw-r--r-- | crates/cli/src/cmd/inspect.rs | 35 | ||||
| -rw-r--r-- | crates/cli/src/cmd/mod.rs | 7 | ||||
| -rw-r--r-- | crates/cli/src/cmd/run.rs | 47 | ||||
| -rw-r--r-- | crates/cli/src/cmd/wast.rs | 12 |
7 files changed, 199 insertions, 0 deletions
diff --git a/crates/cli/src/cmd/compile.rs b/crates/cli/src/cmd/compile.rs new file mode 100644 index 0000000..93e38d5 --- /dev/null +++ b/crates/cli/src/cmd/compile.rs @@ -0,0 +1,15 @@ +use eyre::Result; + +use crate::cli::CompileArgs; +use crate::load::{default_twasm_output_path, load_compilable_module, write_output_bytes}; + +pub fn run(args: CompileArgs) -> Result<()> { + let module = load_compilable_module(&args.input)?; + let twasm = module.serialize_twasm()?; + let output = match args.output { + Some(output) => output, + None => default_twasm_output_path(&args.input)?, + }; + + write_output_bytes(&output, &twasm, args.force) +} diff --git a/crates/cli/src/cmd/completion.rs b/crates/cli/src/cmd/completion.rs new file mode 100644 index 0000000..5815f57 --- /dev/null +++ b/crates/cli/src/cmd/completion.rs @@ -0,0 +1,12 @@ +use std::io; + +use clap::CommandFactory; +use eyre::Result; + +use crate::cli::{Cli, CompletionArgs}; + +pub fn run(args: CompletionArgs) -> Result<()> { + let mut cmd = Cli::command(); + clap_complete::generate(args.shell, &mut cmd, "tinywasm", &mut io::stdout()); + Ok(()) +} diff --git a/crates/cli/src/cmd/dump.rs b/crates/cli/src/cmd/dump.rs new file mode 100644 index 0000000..78fb28b --- /dev/null +++ b/crates/cli/src/cmd/dump.rs @@ -0,0 +1,71 @@ +use anstream::println; +use eyre::Result; +use owo_colors::OwoColorize; +use tinywasm::types::{ExternalKind, ImportKind}; + +use crate::cli::ModuleInputArgs; +use crate::load::load_module; + +pub fn run(args: ModuleInputArgs) -> Result<()> { + let loaded = load_module(&args.module)?; + let module = loaded.module; + + let imported_func_count = + module.imports.iter().filter(|import| matches!(import.kind, ImportKind::Function(_))).count() as u32; + + for (func_idx, func) in module.funcs.iter().enumerate() { + let global_idx = imported_func_count + func_idx as u32; + + let exports = module + .exports + .iter() + .filter(|export| export.kind == ExternalKind::Func && export.index == global_idx) + .map(|export| export.name.as_ref()) + .collect::<Vec<_>>(); + + let header = format!("func[{func_idx}]").blue().bold().to_string(); + if exports.is_empty() { + println!("{header}"); + } else { + println!("{header} {}", format!("exports={}", format!("{exports:?}").cyan()).bright_black()); + } + + for (ip, instr) in func.instructions.iter().enumerate() { + let instr = print_instr(instr); + println!(" {}: {}", print_ip(ip), instr); + } + println!(); + } + + Ok(()) +} + +fn print_ip(ip: usize) -> String { + let s = format!("{ip:04}"); + let first_non_zero = s.find(|c| c != '0').unwrap_or(s.len() - 1); + + format!( + "{}{}", + &s[..first_non_zero].to_string().bright_black().dimmed(), + &s[first_non_zero..].to_string().bright_black() + ) +} + +fn print_instr(instr: &tinywasm::types::Instruction) -> String { + let instr = format!("{instr:?}"); + let Some(split) = instr.find(['(', ' ', '{']) else { + return instr.bold().to_string(); + }; + + let (name, rest) = instr.split_at(split); + + let rest = rest + .replace('(', &"(".bright_black().to_string()) + .replace(')', &")".bright_black().to_string()) + .replace('{', &"{".bright_black().to_string()) + .replace('}', &"}".bright_black().to_string()) + .replace(',', &",".bright_black().to_string()) + .replace(':', &":".bright_black().to_string()); + + format!("{}{}", name.bold(), rest) +} diff --git a/crates/cli/src/cmd/inspect.rs b/crates/cli/src/cmd/inspect.rs new file mode 100644 index 0000000..ea2cec7 --- /dev/null +++ b/crates/cli/src/cmd/inspect.rs @@ -0,0 +1,35 @@ +use eyre::Result; + +use crate::cli::ModuleInputArgs; +use crate::load::load_module; +use crate::output::{format_export_type, format_import_type}; +use anstream::println; +use owo_colors::OwoColorize; + +pub fn run(args: ModuleInputArgs) -> Result<()> { + let loaded = load_module(&args.module)?; + let module = loaded.module; + + println!("{}", "Imports".bold()); + let mut import_count = 0usize; + for import in module.imports() { + import_count += 1; + println!(" {}.{}: {}", import.module.blue(), import.name.cyan(), format_import_type(import.ty).yellow()); + } + if import_count == 0 { + println!(" {}", "(none)".yellow()); + } + + println!(); + println!("{}", "Exports".bold()); + let mut export_count = 0usize; + for export in module.exports() { + export_count += 1; + println!(" {}: {}", export.name.green(), format_export_type(export.ty).yellow()); + } + if export_count == 0 { + println!(" {}", "(none)".yellow()); + } + + Ok(()) +} diff --git a/crates/cli/src/cmd/mod.rs b/crates/cli/src/cmd/mod.rs new file mode 100644 index 0000000..0861af2 --- /dev/null +++ b/crates/cli/src/cmd/mod.rs @@ -0,0 +1,7 @@ +pub mod compile; +pub mod completion; +pub mod dump; +pub mod inspect; +pub mod run; +#[cfg(feature = "wast")] +pub mod wast; diff --git a/crates/cli/src/cmd/run.rs b/crates/cli/src/cmd/run.rs new file mode 100644 index 0000000..b2f5e03 --- /dev/null +++ b/crates/cli/src/cmd/run.rs @@ -0,0 +1,47 @@ +use eyre::{Result, bail}; +use tinywasm::types::ExportType; +use tinywasm::{ModuleInstance, Store}; + +use crate::cli::RunArgs; +use crate::load::load_module; +use crate::output::print_results; +use crate::value_parse::parse_invocation_args; + +pub fn run(args: RunArgs) -> Result<()> { + let module_path = args.module.as_deref().ok_or_else(|| eyre::eyre!("missing module path"))?; + let loaded = load_module(module_path)?; + let mut store = Store::new(args.engine.build_engine()?); + let instance = ModuleInstance::instantiate_no_start(&mut store, &loaded.module, None)?; + + match args.invoke.as_deref() { + Some(export) => { + if loaded.module.start_func.is_some() { + let _ = instance.start(&mut store)?; + } + + let func_ty = loaded + .module + .exports() + .find_map(|item| match (item.name == export, item.ty) { + (true, ExportType::Func(ty)) => Some(ty), + _ => None, + }) + .ok_or_else(|| eyre::eyre!("export is not a function: {export}"))?; + let func = instance.func_untyped(&store, export)?; + let params = parse_invocation_args(func_ty, &args.args)?; + let results = func.call(&mut store, ¶ms)?; + print_results(&results); + Ok(()) + } + None => { + if instance.start_func(&store)?.is_none() { + bail!( + "module has no start function or `_start` export; use `tinywasm inspect {module_path}` or `tinywasm run --invoke <export> {module_path}`" + ) + } + + let _ = instance.start(&mut store)?; + Ok(()) + } + } +} diff --git a/crates/cli/src/cmd/wast.rs b/crates/cli/src/cmd/wast.rs new file mode 100644 index 0000000..ef9c263 --- /dev/null +++ b/crates/cli/src/cmd/wast.rs @@ -0,0 +1,12 @@ +use std::path::PathBuf; + +use eyre::Result; + +use crate::cli::WastArgs; +use crate::wast_runner::WastRunner; + +pub fn run(args: WastArgs) -> Result<()> { + let paths = args.paths.into_iter().map(PathBuf::from).collect::<Vec<_>>(); + let mut runner = WastRunner::new(); + runner.run_paths(&paths) +} |
