mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-13 05:48:45 +00:00
Merge branch 'master' into engine
# Conflicts: # Cargo.lock # Cargo.toml # lib/api/src/module.rs
This commit is contained in:
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -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",
|
||||||
|
|||||||
18
Cargo.toml
18
Cargo.toml
@@ -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",
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
67
src/commands/inspect.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
18
src/suggestions.rs
Normal 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user