Merge branch 'master' into engine

# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	lib/api/src/module.rs
This commit is contained in:
Syrus
2020-05-04 18:39:40 -07:00
13 changed files with 392 additions and 33 deletions

14
Cargo.lock generated
View File

@@ -124,6 +124,12 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "bytesize"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010"
[[package]] [[package]]
name = "cast" name = "cast"
version = "0.2.3" version = "0.2.3"
@@ -353,6 +359,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "distance"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240"
[[package]] [[package]]
name = "downcast-rs" name = "downcast-rs"
version = "1.1.1" version = "1.1.1"
@@ -1484,6 +1496,8 @@ version = "0.16.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"atty", "atty",
"bytesize",
"distance",
"glob", "glob",
"rustc_version", "rustc_version",
"structopt", "structopt",

View File

@@ -34,6 +34,10 @@ wasmer-cache = { version = "0.16.2", path = "lib/cache", optional = true }
atty = "0.2" atty = "0.2"
anyhow = "1.0.28" anyhow = "1.0.28"
structopt = { version = "0.3", features = ["suggestions"] } structopt = { version = "0.3", features = ["suggestions"] }
# For the function names autosuggestion
distance = "0.4"
# For the inspect subcommand
bytesize = "1.0.0"
[workspace] [workspace]
members = [ members = [
@@ -52,8 +56,15 @@ test-utils = { path = "tests/lib/test-utils" }
[features] [features]
# Don't add the backend features in default, please add them on the Makefile # Don't add the backend features in default, please add them on the Makefile
# since we might want to autoconfigure them depending on the availability on the host. # since we might want to autoconfigure them depending on the availability on the host.
# default = ["wasi"] default = [
default = ["wast", "wasi", "wat", "experimental-io-devices", "compiler-cranelift", "cache", "engine-jit"] "wast",
"wasi",
"wat",
"experimental-io-devices",
"compiler-cranelift",
"cache",
"engine-jit"
]
wat = ["wasmer/wat"] wat = ["wasmer/wat"]
engine-jit = [ engine-jit = [
"wasmer-engine-jit", "wasmer-engine-jit",
@@ -66,7 +77,8 @@ compiler = [
"wasmer-engine-jit/compiler" "wasmer-engine-jit/compiler"
] ]
experimental-io-devices = [ experimental-io-devices = [
"wasmer-wasi-experimental-io-devices" "wasmer-wasi-experimental-io-devices",
"wasi"
] ]
compiler-singlepass = [ compiler-singlepass = [
"test-utils/compiler-singlepass", "test-utils/compiler-singlepass",

View File

@@ -7,7 +7,7 @@ use std::sync::Arc;
use thiserror::Error; use thiserror::Error;
use wasmer_compiler::{CompileError, WasmError}; use wasmer_compiler::{CompileError, WasmError};
use wasmer_engine::{CompiledModule, DeserializeError, Engine, Resolver, SerializeError}; use wasmer_engine::{CompiledModule, DeserializeError, Engine, Resolver, SerializeError};
use wasmer_runtime::InstanceHandle; use wasmer_runtime::{ExportsIterator, ImportsIterator, InstanceHandle, Module as ModuleInfo};
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum IoCompileError { pub enum IoCompileError {
@@ -25,12 +25,17 @@ pub enum IoCompileError {
/// ///
/// ## Cloning a module /// ## Cloning a module
/// ///
/// Cloning a moudle is cheap: it does a shallow copy of the compiled /// Cloning a module is cheap: it does a shallow copy of the compiled
/// contents rather than a deep copy. /// contents rather than a deep copy.
#[derive(Clone)] #[derive(Clone)]
pub struct Module { pub struct Module {
store: Store, store: Store,
compiled: Arc<dyn CompiledModule>, compiled: Arc<dyn CompiledModule>,
#[cfg(feature = "wat")]
#[doc(hidden)]
// If the module was compiled from a wat file.
pub from_wat: bool,
} }
impl Module { impl Module {
@@ -71,13 +76,18 @@ impl Module {
pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Module, CompileError> { pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Module, CompileError> {
#[cfg(feature = "wat")] #[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| { let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| {
CompileError::Wasm(WasmError::Generic(format!( CompileError::Wasm(WasmError::Generic(format!(
"Error when converting wat: {}", "Error when converting wat: {}",
e 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()) Module::from_binary(store, bytes.as_ref())
@@ -177,6 +187,8 @@ impl Module {
Module { Module {
store: store.clone(), store: store.clone(),
compiled, compiled,
#[cfg(feature = "wat")]
from_wat: false,
} }
} }
@@ -251,7 +263,7 @@ impl Module {
/// import.ty(); /// import.ty();
/// } /// }
/// ``` /// ```
pub fn imports<'a>(&'a self) -> impl Iterator<Item = ImportType> + 'a { pub fn imports<'a>(&'a self) -> ImportsIterator<impl Iterator<Item = ImportType> + 'a> {
self.compiled.module().imports() self.compiled.module().imports()
} }
@@ -274,11 +286,21 @@ impl Module {
/// export.ty(); /// export.ty();
/// } /// }
/// ``` /// ```
pub fn exports<'a>(&'a self) -> impl Iterator<Item = ExportType> + 'a { pub fn exports<'a>(&'a self) -> ExportsIterator<impl Iterator<Item = ExportType> + 'a> {
self.compiled.module().exports() self.compiled.module().exports()
} }
pub fn store(&self) -> &Store { pub fn store(&self) -> &Store {
&self.store &self.store
} }
// The ABI of the ModuleInfo is very unstable, we refactor it very often.
// This funciton is public because in some cases it can be useful to get some
// extra information from the module.
//
// However, the usage is highly discouraged.
#[doc(hidden)]
pub fn info(&self) -> &ModuleInfo {
&self.compiled.module()
}
} }

View File

@@ -339,7 +339,7 @@ impl Instance {
/// Return an iterator over the exports of this instance. /// Return an iterator over the exports of this instance.
/// ///
/// Specifically, it provides access to the key-value pairs, where they keys /// Specifically, it provides access to the key-value pairs, where the keys
/// are export names, and the values are export declarations which can be /// are export names, and the values are export declarations which can be
/// resolved `lookup_by_declaration`. /// resolved `lookup_by_declaration`.
pub fn exports(&self) -> indexmap::map::Iter<String, ExportIndex> { pub fn exports(&self) -> indexmap::map::Iter<String, ExportIndex> {

View File

@@ -41,7 +41,10 @@ pub use crate::imports::Imports;
pub use crate::instance::InstanceHandle; pub use crate::instance::InstanceHandle;
pub use crate::memory::LinearMemory; pub use crate::memory::LinearMemory;
pub use crate::mmap::Mmap; pub use crate::mmap::Mmap;
pub use crate::module::{MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle}; pub use crate::module::{
ExportsIterator, ImportsIterator, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan,
TableStyle,
};
pub use crate::probestack::PROBESTACK; pub use crate::probestack::PROBESTACK;
pub use crate::sig_registry::SignatureRegistry; pub use crate::sig_registry::SignatureRegistry;
pub use crate::table::Table; pub use crate::table::Table;

View File

@@ -5,6 +5,7 @@ use indexmap::IndexMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::iter::ExactSizeIterator;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::Arc; use std::sync::Arc;
use wasm_common::entity::{EntityRef, PrimaryMap}; use wasm_common::entity::{EntityRef, PrimaryMap};
@@ -134,7 +135,7 @@ pub struct Module {
/// WebAssembly function signatures. /// WebAssembly function signatures.
pub signatures: PrimaryMap<SignatureIndex, FunctionType>, pub signatures: PrimaryMap<SignatureIndex, FunctionType>,
/// Types of functions (imported and local). /// WebAssembly functions (imported and local).
pub functions: PrimaryMap<FunctionIndex, SignatureIndex>, pub functions: PrimaryMap<FunctionIndex, SignatureIndex>,
/// WebAssembly tables (imported and local). /// WebAssembly tables (imported and local).
@@ -206,8 +207,8 @@ impl Module {
} }
/// Get the export types of the module /// Get the export types of the module
pub fn exports<'a>(&'a self) -> impl Iterator<Item = ExportType> + 'a { pub fn exports<'a>(&'a self) -> ExportsIterator<impl Iterator<Item = ExportType> + 'a> {
self.exports.iter().map(move |(name, export_index)| { let iter = self.exports.iter().map(move |(name, export_index)| {
let extern_type = match export_index { let extern_type = match export_index {
ExportIndex::Function(i) => { ExportIndex::Function(i) => {
let signature = self.functions.get(i.clone()).unwrap(); let signature = self.functions.get(i.clone()).unwrap();
@@ -228,12 +229,17 @@ impl Module {
} }
}; };
ExportType::new(name, extern_type) ExportType::new(name, extern_type)
}) });
ExportsIterator {
iter,
size: self.exports.len(),
}
} }
/// Get the export types of the module /// Get the export types of the module
pub fn imports<'a>(&'a self) -> impl Iterator<Item = ImportType> + 'a { pub fn imports<'a>(&'a self) -> ImportsIterator<impl Iterator<Item = ImportType> + 'a> {
self.imports let iter = self
.imports
.iter() .iter()
.map(move |((module, field, _), import_index)| { .map(move |((module, field, _), import_index)| {
let extern_type = match import_index { let extern_type = match import_index {
@@ -256,7 +262,11 @@ impl Module {
} }
}; };
ImportType::new(module, field, extern_type) ImportType::new(module, field, extern_type)
}) });
ImportsIterator {
iter,
size: self.imports.len(),
}
} }
/// Convert a `LocalFunctionIndex` into a `FunctionIndex`. /// Convert a `LocalFunctionIndex` into a `FunctionIndex`.
@@ -348,3 +358,126 @@ impl fmt::Display for Module {
write!(f, "{}", self.name()) write!(f, "{}", self.name())
} }
} }
// Code inspired from
// https://www.reddit.com/r/rust/comments/9vspv4/extending_iterators_ergonomically/
/// This iterator allows us to iterate over the exports
/// and offer nice API ergonomics over it.
pub struct ExportsIterator<I: Iterator<Item = ExportType> + Sized> {
iter: I,
size: usize,
}
impl<I: Iterator<Item = ExportType> + Sized> ExactSizeIterator for ExportsIterator<I> {
// We can easily calculate the remaining number of iterations.
fn len(&self) -> usize {
self.size
}
}
impl<I: Iterator<Item = ExportType> + Sized> ExportsIterator<I> {
/// Get only the functions
pub fn functions(self) -> impl Iterator<Item = ExportType<FunctionType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Function(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
/// Get only the memories
pub fn memories(self) -> impl Iterator<Item = ExportType<MemoryType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Memory(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
/// Get only the tables
pub fn tables(self) -> impl Iterator<Item = ExportType<TableType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Table(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
/// Get only the globals
pub fn globals(self) -> impl Iterator<Item = ExportType<GlobalType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Global(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
}
impl<I: Iterator<Item = ExportType> + Sized> Iterator for ExportsIterator<I> {
type Item = ExportType;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
/// This iterator allows us to iterate over the imports
/// and offer nice API ergonomics over it.
pub struct ImportsIterator<I: Iterator<Item = ImportType> + Sized> {
iter: I,
size: usize,
}
impl<I: Iterator<Item = ImportType> + Sized> ExactSizeIterator for ImportsIterator<I> {
// We can easily calculate the remaining number of iterations.
fn len(&self) -> usize {
self.size
}
}
impl<I: Iterator<Item = ImportType> + Sized> ImportsIterator<I> {
/// Get only the functions
pub fn functions(self) -> impl Iterator<Item = ImportType<FunctionType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Function(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
/// Get only the memories
pub fn memories(self) -> impl Iterator<Item = ImportType<MemoryType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Memory(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
/// Get only the tables
pub fn tables(self) -> impl Iterator<Item = ImportType<TableType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Table(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
/// Get only the globals
pub fn globals(self) -> impl Iterator<Item = ImportType<GlobalType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Global(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
}
impl<I: Iterator<Item = ImportType> + Sized> Iterator for ImportsIterator<I> {
type Item = ImportType;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}

View File

@@ -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. /// Globals are initialized via the `const` operators or by referring to another import.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[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 // Memory Types
/// A descriptor for a WebAssembly memory type. /// 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 // Import Types
/// A descriptor for an imported value into a wasm module. /// A descriptor for an imported value into a wasm module.
@@ -460,17 +491,17 @@ impl MemoryType {
/// imported from as well as the type of item that's being imported. /// imported from as well as the type of item that's being imported.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ImportType { pub struct ImportType<T = ExternType> {
module: String, module: String,
name: String, name: String,
ty: ExternType, ty: T,
} }
impl ImportType { impl<T> ImportType<T> {
/// Creates a new import descriptor which comes from `module` and `name` and /// Creates a new import descriptor which comes from `module` and `name` and
/// is of type `ty`. /// is of type `ty`.
pub fn new(module: &str, name: &str, ty: ExternType) -> ImportType { pub fn new(module: &str, name: &str, ty: T) -> Self {
ImportType { Self {
module: module.to_owned(), module: module.to_owned(),
name: name.to_owned(), name: name.to_owned(),
ty, ty,
@@ -489,7 +520,7 @@ impl ImportType {
} }
/// Returns the expected type of this import. /// Returns the expected type of this import.
pub fn ty(&self) -> &ExternType { pub fn ty(&self) -> &T {
&self.ty &self.ty
} }
} }
@@ -502,17 +533,21 @@ impl ImportType {
/// [`Module::exports`](crate::Module::exports) accessor and describes what /// [`Module::exports`](crate::Module::exports) accessor and describes what
/// names are exported from a wasm module and the type of the item that is /// names are exported from a wasm module and the type of the item that is
/// exported. /// exported.
///
/// The `<T>` refefers to `ExternType`, however it can also refer to use
/// `MemoryType`, `TableType`, `FunctionType` and `GlobalType` for ease of
/// use.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ExportType { pub struct ExportType<T = ExternType> {
name: String, name: String,
ty: ExternType, ty: T,
} }
impl ExportType { impl<T> ExportType<T> {
/// Creates a new export which is exported with the given `name` and has the /// Creates a new export which is exported with the given `name` and has the
/// given `ty`. /// given `ty`.
pub fn new(name: &str, ty: ExternType) -> ExportType { pub fn new(name: &str, ty: T) -> Self {
ExportType { ExportType {
name: name.to_string(), name: name.to_string(),
ty, ty,
@@ -525,7 +560,7 @@ impl ExportType {
} }
/// Returns the type of this export. /// Returns the type of this export.
pub fn ty(&self) -> &ExternType { pub fn ty(&self) -> &T {
&self.ty &self.ty
} }
} }

View File

@@ -1,7 +1,7 @@
use anyhow::Result; use anyhow::Result;
#[cfg(feature = "wast")] #[cfg(feature = "wast")]
use wasmer_bin::commands::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}; use structopt::{clap::ErrorKind, StructOpt};
@@ -29,6 +29,10 @@ enum WasmerCLIOptions {
#[structopt(name = "self-update")] #[structopt(name = "self-update")]
SelfUpdate(SelfUpdate), SelfUpdate(SelfUpdate),
/// Inspect a WebAssembly file
#[structopt(name = "inspect")]
Inspect(Inspect),
/// Run spec testsuite /// Run spec testsuite
#[cfg(feature = "wast")] #[cfg(feature = "wast")]
#[structopt(name = "wast")] #[structopt(name = "wast")]
@@ -43,6 +47,7 @@ impl WasmerCLIOptions {
Self::Cache(cache) => cache.execute(), Self::Cache(cache) => cache.execute(),
Self::Validate(validate) => validate.execute(), Self::Validate(validate) => validate.execute(),
Self::Compile(compile) => compile.execute(), Self::Compile(compile) => compile.execute(),
Self::Inspect(inspect) => inspect.execute(),
#[cfg(feature = "wast")] #[cfg(feature = "wast")]
Self::Wast(wast) => wast.execute(), Self::Wast(wast) => wast.execute(),
} }
@@ -57,7 +62,9 @@ fn main() -> Result<()> {
let args = std::env::args().collect::<Vec<_>>(); let args = std::env::args().collect::<Vec<_>>();
let command = args.get(1); let command = args.get(1);
let options = match command.unwrap_or(&"".to_string()).as_ref() { 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| { WasmerCLIOptions::from_iter_safe(args.iter()).unwrap_or_else(|e| {
match e.kind { match e.kind {

View File

@@ -1,6 +1,7 @@
//! The commands available in the Wasmer binary. //! The commands available in the Wasmer binary.
mod cache; mod cache;
mod compile; mod compile;
mod inspect;
mod run; mod run;
mod self_update; mod self_update;
mod validate; mod validate;
@@ -9,4 +10,4 @@ mod wast;
#[cfg(feature = "wast")] #[cfg(feature = "wast")]
pub use wast::*; pub use wast::*;
pub use {cache::*, compile::*, run::*, self_update::*, validate::*}; pub use {cache::*, compile::*, inspect::*, run::*, self_update::*, validate::*};

67
src/commands/inspect.rs Normal file
View File

@@ -0,0 +1,67 @@
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!("Type: {}", if module.from_wat { "wat" } else { "wasm" });
println!("Size: {}", ByteSize(module_contents.len() as _));
println!("Imports:");
println!(" Functions:");
for f in module.imports().functions() {
println!(" \"{}\".\"{}\": {}", f.module(), f.name(), f.ty());
}
println!(" Memories:");
for f in module.imports().memories() {
println!(" \"{}\".\"{}\": {}", f.module(), f.name(), f.ty());
}
println!(" Tables:");
for f in module.imports().tables() {
println!(" \"{}\".\"{}\": {}", f.module(), f.name(), f.ty());
}
println!(" Globals:");
for f in module.imports().globals() {
println!(" \"{}\".\"{}\": {}", f.module(), f.name(), f.ty());
}
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(())
}
}

View File

@@ -1,5 +1,6 @@
use crate::common::get_cache_dir; use crate::common::get_cache_dir;
use crate::store::StoreOptions; use crate::store::StoreOptions;
use crate::suggestions::suggest_function_exports;
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
@@ -123,7 +124,7 @@ impl Run {
// Try to instantiate the wasm file, with no provided imports // Try to instantiate the wasm file, with no provided imports
let imports = imports! {}; let imports = imports! {};
let instance = Instance::new(&module, &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(&[])?; start.call(&[])?;
Ok(()) Ok(())
@@ -183,13 +184,58 @@ impl Run {
Ok(module) Ok(module)
} }
fn try_find_function(
&self,
instance: &Instance,
name: &str,
args: &Vec<String>,
) -> 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 {
let suggested_functions = suggest_function_exports(instance.module(), "");
let names = suggested_functions
.iter()
.take(3)
.map(|arg| format!("`{}`", arg))
.collect::<Vec<_>>()
.join(", ");
let suggested_command = format!(
"wasmer {} -i {} {}",
self.path.display(),
suggested_functions.get(0).unwrap(),
args.join(" ")
);
let suggestion = format!(
"Similar functions found: {}\nTry with: {}",
names, suggested_command
);
match e {
ExportError::Missing(_) => {
anyhow!("No export `{}` found in the module.\n{}", name, suggestion)
}
ExportError::IncompatibleType => anyhow!(
"Export `{}` found, but is not a function.\n{}",
name,
suggestion
),
}
}
})?
.clone())
}
fn invoke_function( fn invoke_function(
&self, &self,
instance: &Instance, instance: &Instance,
invoke: &str, invoke: &str,
args: &Vec<String>, args: &Vec<String>,
) -> Result<Box<[Val]>> { ) -> Result<Box<[Val]>> {
let func: &Function = instance.exports.get(&invoke)?; let func: Function = self.try_find_function(&instance, invoke, args)?;
let func_ty = func.ty(); let func_ty = func.ty();
let required_arguments = func_ty.params().len(); let required_arguments = func_ty.params().len();
let provided_arguments = args.len(); let provided_arguments = args.len();

View File

@@ -18,6 +18,7 @@ pub mod common;
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
pub mod logging; pub mod logging;
pub mod store; pub mod store;
pub mod suggestions;
pub mod utils; pub mod utils;
/// Version number of this crate. /// Version number of this crate.

18
src/suggestions.rs Normal file
View File

@@ -0,0 +1,18 @@
//! This file provides suggestions for the user, to help them on the
//! usage of WebAssembly
use distance::damerau_levenshtein;
use wasmer::Module;
/// Suggest function exports for the module
pub fn suggest_function_exports(module: &Module, query: &str) -> Vec<String> {
let mut function_names = module
.exports()
.functions()
.map(|extern_fn| {
let name = extern_fn.name();
name.to_string()
})
.collect::<Vec<_>>();
function_names.sort_by_key(|name| damerau_levenshtein(name, query));
function_names
}