mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-16 17:18:57 +00:00
Added inspect subcommand
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -124,6 +124,12 @@ version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "bytesize"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.3"
|
||||
@@ -1483,6 +1489,7 @@ version = "0.16.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
"bytesize",
|
||||
"distance",
|
||||
"glob",
|
||||
"rustc_version",
|
||||
|
||||
@@ -33,7 +33,10 @@ wasmer-cache = { version = "0.16.2", path = "lib/cache", optional = true }
|
||||
atty = "0.2"
|
||||
anyhow = "1.0.28"
|
||||
structopt = { version = "0.3", features = ["suggestions"] }
|
||||
# For the function names autosuggestion
|
||||
distance = "0.4"
|
||||
# For the inspect subcommand
|
||||
bytesize = "1.0.0"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
|
||||
@@ -31,6 +31,11 @@ pub enum IoCompileError {
|
||||
pub struct Module {
|
||||
store: Store,
|
||||
compiled: Arc<CompiledModule>,
|
||||
|
||||
#[cfg(feature = "wat")]
|
||||
#[doc(hidden)]
|
||||
// If the module was compiled from a wat file.
|
||||
pub from_wat: bool,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
@@ -71,13 +76,18 @@ impl Module {
|
||||
pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Module, CompileError> {
|
||||
#[cfg(feature = "wat")]
|
||||
{
|
||||
let might_be_wat = !bytes.as_ref().starts_with(b"\0asm");
|
||||
let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| {
|
||||
CompileError::Wasm(WasmError::Generic(format!(
|
||||
"Error when converting wat: {}",
|
||||
e
|
||||
)))
|
||||
})?;
|
||||
return Module::from_binary(store, bytes.as_ref());
|
||||
let mut module = Module::from_binary(store, bytes.as_ref())?;
|
||||
// We can assume it was a wat file if is not "wasm" looking
|
||||
// and the previous step succeeded.
|
||||
module.from_wat = might_be_wat;
|
||||
return Ok(module);
|
||||
}
|
||||
|
||||
Module::from_binary(store, bytes.as_ref())
|
||||
@@ -177,6 +187,8 @@ impl Module {
|
||||
Module {
|
||||
store: store.clone(),
|
||||
compiled: Arc::new(compiled),
|
||||
#[cfg(feature = "wat")]
|
||||
from_wat: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -339,6 +339,16 @@ impl GlobalType {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GlobalType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let mutability = match self.mutability {
|
||||
Mutability::Const => "constant",
|
||||
Mutability::Var => "mutable",
|
||||
};
|
||||
write!(f, "{} ({})", self.ty, mutability)
|
||||
}
|
||||
}
|
||||
|
||||
/// Globals are initialized via the `const` operators or by referring to another import.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
@@ -414,6 +424,16 @@ impl TableType {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TableType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
if let Some(maximum) = self.maximum {
|
||||
write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
|
||||
} else {
|
||||
write!(f, "{} ({}..)", self.ty, self.minimum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Memory Types
|
||||
|
||||
/// A descriptor for a WebAssembly memory type.
|
||||
@@ -450,6 +470,17 @@ impl MemoryType {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MemoryType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let shared = if self.shared { "shared" } else { "not shared" };
|
||||
if let Some(maximum) = self.maximum {
|
||||
write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
|
||||
} else {
|
||||
write!(f, "{} ({:?}..)", shared, self.minimum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Import Types
|
||||
|
||||
/// A descriptor for an imported value into a wasm module.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::Result;
|
||||
#[cfg(feature = "wast")]
|
||||
use wasmer_bin::commands::Wast;
|
||||
use wasmer_bin::commands::{Cache, Compile, Run, SelfUpdate, Validate};
|
||||
use wasmer_bin::commands::{Cache, Compile, Inspect, Run, SelfUpdate, Validate};
|
||||
|
||||
use structopt::{clap::ErrorKind, StructOpt};
|
||||
|
||||
@@ -29,6 +29,10 @@ enum WasmerCLIOptions {
|
||||
#[structopt(name = "self-update")]
|
||||
SelfUpdate(SelfUpdate),
|
||||
|
||||
/// Inspect a WebAssembly file
|
||||
#[structopt(name = "inspect")]
|
||||
Inspect(Inspect),
|
||||
|
||||
/// Run spec testsuite
|
||||
#[cfg(feature = "wast")]
|
||||
#[structopt(name = "wast")]
|
||||
@@ -43,6 +47,7 @@ impl WasmerCLIOptions {
|
||||
Self::Cache(cache) => cache.execute(),
|
||||
Self::Validate(validate) => validate.execute(),
|
||||
Self::Compile(compile) => compile.execute(),
|
||||
Self::Inspect(inspect) => inspect.execute(),
|
||||
#[cfg(feature = "wast")]
|
||||
Self::Wast(wast) => wast.execute(),
|
||||
}
|
||||
@@ -57,7 +62,9 @@ fn main() -> Result<()> {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let command = args.get(1);
|
||||
let options = match command.unwrap_or(&"".to_string()).as_ref() {
|
||||
"run" | "cache" | "validate" | "compile" | "self-update" => WasmerCLIOptions::from_args(),
|
||||
"run" | "cache" | "validate" | "compile" | "self-update" | "inspect" => {
|
||||
WasmerCLIOptions::from_args()
|
||||
}
|
||||
_ => {
|
||||
WasmerCLIOptions::from_iter_safe(args.iter()).unwrap_or_else(|e| {
|
||||
match e.kind {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! The commands available in the Wasmer binary.
|
||||
mod cache;
|
||||
mod compile;
|
||||
mod inspect;
|
||||
mod run;
|
||||
mod self_update;
|
||||
mod validate;
|
||||
@@ -9,4 +10,4 @@ mod wast;
|
||||
|
||||
#[cfg(feature = "wast")]
|
||||
pub use wast::*;
|
||||
pub use {cache::*, compile::*, run::*, self_update::*, validate::*};
|
||||
pub use {cache::*, compile::*, inspect::*, run::*, self_update::*, validate::*};
|
||||
|
||||
51
src/commands/inspect.rs
Normal file
51
src/commands/inspect.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use crate::store::StoreOptions;
|
||||
use anyhow::{Context, Result};
|
||||
use bytesize::ByteSize;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
use wasmer::*;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
/// The options for the `wasmer validate` subcommand
|
||||
pub struct Inspect {
|
||||
/// File to validate as WebAssembly
|
||||
#[structopt(name = "FILE", parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
|
||||
#[structopt(flatten)]
|
||||
compiler: StoreOptions,
|
||||
}
|
||||
|
||||
impl Inspect {
|
||||
/// Runs logic for the `validate` subcommand
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
self.inner_execute()
|
||||
.context(format!("failed to inspect `{}`", self.path.display()))
|
||||
}
|
||||
fn inner_execute(&self) -> Result<()> {
|
||||
let (store, _compiler_name) = self.compiler.get_store()?;
|
||||
let module_contents = std::fs::read(&self.path)?;
|
||||
let module = Module::new(&store, &module_contents)?;
|
||||
println!("Wat: {}", if module.from_wat { "yes" } else { "no" });
|
||||
println!("Size: {}", ByteSize(module_contents.len() as _));
|
||||
println!("Imports:");
|
||||
println!("Exports:");
|
||||
println!(" Functions:");
|
||||
for f in module.exports().functions() {
|
||||
println!(" \"{}\": {}", f.name(), f.ty());
|
||||
}
|
||||
println!(" Memories:");
|
||||
for f in module.exports().memories() {
|
||||
println!(" \"{}\": {}", f.name(), f.ty());
|
||||
}
|
||||
println!(" Tables:");
|
||||
for f in module.exports().tables() {
|
||||
println!(" \"{}\": {}", f.name(), f.ty());
|
||||
}
|
||||
println!(" Globals:");
|
||||
for f in module.exports().globals() {
|
||||
println!(" \"{}\": {}", f.name(), f.ty());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ impl Run {
|
||||
// Try to instantiate the wasm file, with no provided imports
|
||||
let imports = imports! {};
|
||||
let instance = Instance::new(&module, &imports)?;
|
||||
let start: &Function = instance.exports.get("_start")?;
|
||||
let start: Function = self.try_find_function(&instance, "_start", &vec![])?;
|
||||
start.call(&[])?;
|
||||
|
||||
Ok(())
|
||||
@@ -180,13 +180,16 @@ impl Run {
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
fn invoke_function(
|
||||
fn try_find_function(
|
||||
&self,
|
||||
instance: &Instance,
|
||||
invoke: &str,
|
||||
name: &str,
|
||||
args: &Vec<String>,
|
||||
) -> Result<Box<[Val]>> {
|
||||
let func: &Function = instance.exports.get(&invoke).map_err(|e| {
|
||||
) -> Result<Function> {
|
||||
Ok(instance
|
||||
.exports
|
||||
.get_function(&name)
|
||||
.map_err(|e| {
|
||||
if instance.module().info().functions.len() == 0 {
|
||||
anyhow!("The module has no exported functions to call.")
|
||||
} else {
|
||||
@@ -208,19 +211,27 @@ impl Run {
|
||||
names, suggested_command
|
||||
);
|
||||
match e {
|
||||
ExportError::Missing(_) => anyhow!(
|
||||
"No export `{}` found in the module.\n{}",
|
||||
invoke,
|
||||
suggestion
|
||||
),
|
||||
ExportError::Missing(_) => {
|
||||
anyhow!("No export `{}` found in the module.\n{}", name, suggestion)
|
||||
}
|
||||
ExportError::IncompatibleType => anyhow!(
|
||||
"Export `{}` found, but is not a function.\n{}",
|
||||
invoke,
|
||||
name,
|
||||
suggestion
|
||||
),
|
||||
}
|
||||
}
|
||||
})?;
|
||||
})?
|
||||
.clone())
|
||||
}
|
||||
|
||||
fn invoke_function(
|
||||
&self,
|
||||
instance: &Instance,
|
||||
invoke: &str,
|
||||
args: &Vec<String>,
|
||||
) -> Result<Box<[Val]>> {
|
||||
let func: Function = self.try_find_function(&instance, invoke, args)?;
|
||||
let func_ty = func.ty();
|
||||
let required_arguments = func_ty.params().len();
|
||||
let provided_arguments = args.len();
|
||||
|
||||
Reference in New Issue
Block a user