mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 06:08:29 +00:00
Merge branch 'master' into cranelift-update
This commit is contained in:
11
Makefile
11
Makefile
@@ -130,6 +130,15 @@ test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-ems
|
|||||||
# Packaging #
|
# Packaging #
|
||||||
#############
|
#############
|
||||||
|
|
||||||
|
package-wapm:
|
||||||
|
mkdir -p "package/bin"
|
||||||
|
ifeq ($(OS), Windows_NT)
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "#!/bin/bash\nwapm execute \"\$$@\"" > package/bin/wax
|
||||||
|
chmod +x package/bin/wax
|
||||||
|
endif
|
||||||
|
|
||||||
package-wasmer:
|
package-wasmer:
|
||||||
mkdir -p "package/bin"
|
mkdir -p "package/bin"
|
||||||
ifeq ($(OS), Windows_NT)
|
ifeq ($(OS), Windows_NT)
|
||||||
@@ -171,7 +180,7 @@ package-docs: build-docs build-docs-capi
|
|||||||
echo '<!-- Build $(SOURCE_VERSION) --><meta http-equiv="refresh" content="0; url=rust/wasmer_runtime/index.html">' > package/docs/index.html
|
echo '<!-- Build $(SOURCE_VERSION) --><meta http-equiv="refresh" content="0; url=rust/wasmer_runtime/index.html">' > package/docs/index.html
|
||||||
echo '<!-- Build $(SOURCE_VERSION) --><meta http-equiv="refresh" content="0; url=wasmer_runtime/index.html">' > package/docs/crates/index.html
|
echo '<!-- Build $(SOURCE_VERSION) --><meta http-equiv="refresh" content="0; url=wasmer_runtime/index.html">' > package/docs/crates/index.html
|
||||||
|
|
||||||
package: package-wasmer package-capi
|
package: package-wapm package-wasmer package-capi
|
||||||
cp LICENSE package/LICENSE
|
cp LICENSE package/LICENSE
|
||||||
cp ATTRIBUTIONS.md package/ATTRIBUTIONS
|
cp ATTRIBUTIONS.md package/ATTRIBUTIONS
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -8,12 +8,12 @@
|
|||||||
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
|
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
|
||||||
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
|
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://slack.wasmer.io">
|
|
||||||
<img src="https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square" alt="Slack channel">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
|
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
|
||||||
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
|
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://slack.wasmer.io">
|
||||||
|
<img src="https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square" alt="Slack channel">
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
@@ -21,18 +21,14 @@
|
|||||||
<span> • </span>
|
<span> • </span>
|
||||||
<a href="https://docs.wasmer.io">Docs</a>
|
<a href="https://docs.wasmer.io">Docs</a>
|
||||||
<span> • </span>
|
<span> • </span>
|
||||||
<a href="https://medium.com/wasmer/">Blog</a>
|
<a href="https://slack.wasmer.io/">Chat</a>
|
||||||
<span> • </span>
|
|
||||||
<a href="https://slack.wasmer.io/">Slack</a>
|
|
||||||
<span> • </span>
|
|
||||||
<a href="https://twitter.com/wasmerio">Twitter</a>
|
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
[Wasmer](https://wasmer.io/) is a standalone [WebAssembly](https://webassembly.org/) runtime:
|
[Wasmer](https://wasmer.io/) is the fastest and most popular [WebAssembly](https://webassembly.org/) runtime:
|
||||||
* **Universal**: Wasmer is available in *Linux, macOS and Windows* (for both Desktop and [ARM](https://medium.com/wasmer/running-webassembly-on-arm-7d365ed0e50c))
|
* **Universal**: Wasmer is available in *Linux, macOS and Windows* (for both Desktop and [ARM](https://medium.com/wasmer/running-webassembly-on-arm-7d365ed0e50c))
|
||||||
* **Fast**: Wasmer aims to run WebAssembly at near-native speed
|
* **Fast**: Wasmer aims to run WebAssembly at near-native speed
|
||||||
* **Pluggable**: Wasmer can be used from almost **any programming language**
|
* **Pluggable**: Wasmer can be used from almost **any programming language**
|
||||||
|
|||||||
@@ -184,8 +184,8 @@ impl IntoIterator for ImportObject {
|
|||||||
///
|
///
|
||||||
/// [`ImportObject`]: struct.ImportObject.html
|
/// [`ImportObject`]: struct.ImportObject.html
|
||||||
///
|
///
|
||||||
|
/// # Usage
|
||||||
///
|
///
|
||||||
/// # Usage:
|
|
||||||
/// ```
|
/// ```
|
||||||
/// # use wasmer::{Function, Store};
|
/// # use wasmer::{Function, Store};
|
||||||
/// # let store = Store::default();
|
/// # let store = Store::default();
|
||||||
@@ -194,7 +194,7 @@ impl IntoIterator for ImportObject {
|
|||||||
/// let import_object = imports! {
|
/// let import_object = imports! {
|
||||||
/// "env" => {
|
/// "env" => {
|
||||||
/// "foo" => Function::new(&store, foo)
|
/// "foo" => Function::new(&store, foo)
|
||||||
/// }
|
/// },
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// fn foo(n: i32) -> i32 {
|
/// fn foo(n: i32) -> i32 {
|
||||||
@@ -202,43 +202,45 @@ impl IntoIterator for ImportObject {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
// TOOD: port of lost fixes of imports macro from wasmer master/imports macro tests
|
|
||||||
macro_rules! imports {
|
macro_rules! imports {
|
||||||
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
|
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {
|
||||||
use $crate::ImportObject;
|
{
|
||||||
|
let mut import_object = $crate::ImportObject::new();
|
||||||
|
|
||||||
let mut import_object = ImportObject::new();
|
$({
|
||||||
|
let namespace = $crate::import_namespace!($ns);
|
||||||
|
|
||||||
$({
|
import_object.register($ns_name, namespace);
|
||||||
let ns = $crate::import_namespace!($ns);
|
})*
|
||||||
|
|
||||||
import_object.register($ns_name, ns);
|
import_object
|
||||||
})*
|
}
|
||||||
|
};
|
||||||
import_object
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! namespace {
|
macro_rules! namespace {
|
||||||
($( $imp_name:expr => $import_item:expr ),* $(,)? ) => {
|
($( $import_name:expr => $import_item:expr ),* $(,)? ) => {
|
||||||
$crate::import_namespace!({ $( $imp_name => $import_item, )* })
|
$crate::import_namespace!( { $( $import_name => $import_item, )* } )
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! import_namespace {
|
macro_rules! import_namespace {
|
||||||
( { $( $imp_name:expr => $import_item:expr ),* $(,)? } ) => {{
|
( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {{
|
||||||
let mut ns = $crate::Exports::new();
|
let mut namespace = $crate::Exports::new();
|
||||||
|
|
||||||
$(
|
$(
|
||||||
ns.insert($imp_name, $import_item);
|
namespace.insert($import_name, $import_item);
|
||||||
)*
|
)*
|
||||||
ns
|
|
||||||
|
namespace
|
||||||
}};
|
}};
|
||||||
($ns:ident) => {
|
|
||||||
$ns
|
( $namespace:ident ) => {
|
||||||
|
$namespace
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,4 +356,56 @@ mod test {
|
|||||||
false
|
false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn imports_macro_allows_trailing_comma_and_none() {
|
||||||
|
use crate::Function;
|
||||||
|
|
||||||
|
let store = Default::default();
|
||||||
|
|
||||||
|
fn func(arg: i32) -> i32 {
|
||||||
|
arg + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = imports! {
|
||||||
|
"env" => {
|
||||||
|
"func" => Function::new(&store, func),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let _ = imports! {
|
||||||
|
"env" => {
|
||||||
|
"func" => Function::new(&store, func),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _ = imports! {
|
||||||
|
"env" => {
|
||||||
|
"func" => Function::new(&store, func),
|
||||||
|
},
|
||||||
|
"abc" => {
|
||||||
|
"def" => Function::new(&store, func),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _ = imports! {
|
||||||
|
"env" => {
|
||||||
|
"func" => Function::new(&store, func)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let _ = imports! {
|
||||||
|
"env" => {
|
||||||
|
"func" => Function::new(&store, func)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _ = imports! {
|
||||||
|
"env" => {
|
||||||
|
"func1" => Function::new(&store, func),
|
||||||
|
"func2" => Function::new(&store, func)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _ = imports! {
|
||||||
|
"env" => {
|
||||||
|
"func1" => Function::new(&store, func),
|
||||||
|
"func2" => Function::new(&store, func),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,26 +69,6 @@ impl LLVMConfig {
|
|||||||
/// Creates a new configuration object with the default configuration
|
/// Creates a new configuration object with the default configuration
|
||||||
/// specified.
|
/// specified.
|
||||||
pub fn new(features: Features, target: Target) -> Self {
|
pub fn new(features: Features, target: Target) -> Self {
|
||||||
let operating_system =
|
|
||||||
if target.triple().operating_system == wasmer_compiler::OperatingSystem::Darwin {
|
|
||||||
// LLVM detects static relocation + darwin + 64-bit and
|
|
||||||
// force-enables PIC because MachO doesn't support that
|
|
||||||
// combination. They don't check whether they're targeting
|
|
||||||
// MachO, they check whether the OS is set to Darwin.
|
|
||||||
//
|
|
||||||
// Since both linux and darwin use SysV ABI, this should work.
|
|
||||||
wasmer_compiler::OperatingSystem::Linux
|
|
||||||
} else {
|
|
||||||
target.triple().operating_system
|
|
||||||
};
|
|
||||||
let triple = Triple {
|
|
||||||
architecture: target.triple().architecture,
|
|
||||||
vendor: target.triple().vendor.clone(),
|
|
||||||
operating_system,
|
|
||||||
environment: target.triple().environment,
|
|
||||||
binary_format: target_lexicon::BinaryFormat::Elf,
|
|
||||||
};
|
|
||||||
let target = Target::new(triple, *target.cpu_features());
|
|
||||||
Self {
|
Self {
|
||||||
enable_nan_canonicalization: true,
|
enable_nan_canonicalization: true,
|
||||||
enable_verifier: false,
|
enable_verifier: false,
|
||||||
@@ -112,7 +92,27 @@ impl LLVMConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn target_triple(&self) -> TargetTriple {
|
pub fn target_triple(&self) -> TargetTriple {
|
||||||
TargetTriple::create(&self.target().triple().to_string())
|
let target = self.target();
|
||||||
|
let operating_system =
|
||||||
|
if target.triple().operating_system == wasmer_compiler::OperatingSystem::Darwin {
|
||||||
|
// LLVM detects static relocation + darwin + 64-bit and
|
||||||
|
// force-enables PIC because MachO doesn't support that
|
||||||
|
// combination. They don't check whether they're targeting
|
||||||
|
// MachO, they check whether the OS is set to Darwin.
|
||||||
|
//
|
||||||
|
// Since both linux and darwin use SysV ABI, this should work.
|
||||||
|
wasmer_compiler::OperatingSystem::Linux
|
||||||
|
} else {
|
||||||
|
target.triple().operating_system
|
||||||
|
};
|
||||||
|
let triple = Triple {
|
||||||
|
architecture: target.triple().architecture,
|
||||||
|
vendor: target.triple().vendor.clone(),
|
||||||
|
operating_system,
|
||||||
|
environment: target.triple().environment,
|
||||||
|
binary_format: target_lexicon::BinaryFormat::Elf,
|
||||||
|
};
|
||||||
|
TargetTriple::create(&triple.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the target machine for the current target
|
/// Generates the target machine for the current target
|
||||||
@@ -149,10 +149,11 @@ impl LLVMConfig {
|
|||||||
.map(|feature| format!("+{}", feature.to_string()))
|
.map(|feature| format!("+{}", feature.to_string()))
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|
||||||
let llvm_target = InkwellTarget::from_triple(&self.target_triple()).unwrap();
|
let target_triple = self.target_triple();
|
||||||
|
let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap();
|
||||||
llvm_target
|
llvm_target
|
||||||
.create_target_machine(
|
.create_target_machine(
|
||||||
&self.target_triple(),
|
&target_triple,
|
||||||
"generic",
|
"generic",
|
||||||
&llvm_cpu_features,
|
&llvm_cpu_features,
|
||||||
self.opt_level,
|
self.opt_level,
|
||||||
|
|||||||
@@ -294,38 +294,38 @@ impl NativeArtifact {
|
|||||||
|
|
||||||
let shared_file = NamedTempFile::new().map_err(to_compile_error)?;
|
let shared_file = NamedTempFile::new().map_err(to_compile_error)?;
|
||||||
let (_file, shared_filepath) = shared_file.keep().map_err(to_compile_error)?;
|
let (_file, shared_filepath) = shared_file.keep().map_err(to_compile_error)?;
|
||||||
let wasmer_symbols = libcalls
|
|
||||||
.iter()
|
|
||||||
.map(|libcall| {
|
|
||||||
match target_triple.binary_format {
|
|
||||||
BinaryFormat::Macho => format!("-Wl,-U,_{}", libcall),
|
|
||||||
BinaryFormat::Elf => format!("-Wl,--undefined={}", libcall),
|
|
||||||
_ => {
|
|
||||||
// We should already be filtering only valid binary formats before
|
|
||||||
// so this should never happen.
|
|
||||||
unreachable!("Incorrect binary format")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
let is_cross_compiling = target_triple != Triple::host();
|
let host_target = Triple::host();
|
||||||
|
let is_cross_compiling = target_triple != host_target;
|
||||||
let cross_compiling_args = if is_cross_compiling {
|
let cross_compiling_args = if is_cross_compiling {
|
||||||
vec![
|
vec![
|
||||||
format!("--target={}", target_triple),
|
format!("--target={}", target_triple),
|
||||||
"-fuse-ld=lld".to_string(),
|
"-fuse-ld=lld".to_string(),
|
||||||
|
"-nodefaultlibs".to_string(),
|
||||||
|
"-nostdlib".to_string(),
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
let output = Command::new("gcc")
|
trace!(
|
||||||
|
"Compiling for target {} from host {}",
|
||||||
|
target_triple.to_string(),
|
||||||
|
host_target.to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
let linker = if is_cross_compiling {
|
||||||
|
"clang-10"
|
||||||
|
} else {
|
||||||
|
"gcc"
|
||||||
|
};
|
||||||
|
|
||||||
|
let output = Command::new(linker)
|
||||||
.arg(&filepath)
|
.arg(&filepath)
|
||||||
.arg("-nostartfiles")
|
.arg("-nostartfiles")
|
||||||
.arg("-nodefaultlibs")
|
|
||||||
.arg("-nostdlib")
|
|
||||||
.arg("-o")
|
.arg("-o")
|
||||||
.arg(&shared_filepath)
|
.arg(&shared_filepath)
|
||||||
.args(&wasmer_symbols)
|
.arg("-Wl,-undefined,dynamic_lookup")
|
||||||
|
// .args(&wasmer_symbols)
|
||||||
.arg("-shared")
|
.arg("-shared")
|
||||||
.args(&cross_compiling_args)
|
.args(&cross_compiling_args)
|
||||||
.arg("-v")
|
.arg("-v")
|
||||||
|
|||||||
@@ -405,36 +405,36 @@ pub unsafe extern "C" fn wasmer_probestack() {
|
|||||||
/// This list is likely to grow over time.
|
/// This list is likely to grow over time.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum LibCall {
|
pub enum LibCall {
|
||||||
|
/// ceil.f32
|
||||||
|
CeilF32,
|
||||||
|
|
||||||
|
/// ceil.f64
|
||||||
|
CeilF64,
|
||||||
|
|
||||||
|
/// floor.f32
|
||||||
|
FloorF32,
|
||||||
|
|
||||||
|
/// floor.f64
|
||||||
|
FloorF64,
|
||||||
|
|
||||||
|
/// nearest.f32
|
||||||
|
NearestF32,
|
||||||
|
|
||||||
|
/// nearest.f64
|
||||||
|
NearestF64,
|
||||||
|
|
||||||
/// probe for stack overflow. These are emitted for functions which need
|
/// probe for stack overflow. These are emitted for functions which need
|
||||||
/// when the `enable_probestack` setting is true.
|
/// when the `enable_probestack` setting is true.
|
||||||
Probestack,
|
Probestack,
|
||||||
/// ceil.f32
|
|
||||||
CeilF32,
|
|
||||||
/// ceil.f64
|
|
||||||
CeilF64,
|
|
||||||
/// floor.f32
|
|
||||||
FloorF32,
|
|
||||||
/// floor.f64
|
|
||||||
FloorF64,
|
|
||||||
/// trunc.f32
|
|
||||||
TruncF32,
|
|
||||||
/// frunc.f64
|
|
||||||
TruncF64,
|
|
||||||
/// nearest.f32
|
|
||||||
NearestF32,
|
|
||||||
/// nearest.f64
|
|
||||||
NearestF64,
|
|
||||||
/// A custom trap
|
/// A custom trap
|
||||||
RaiseTrap,
|
RaiseTrap,
|
||||||
// /// libc.memcpy
|
|
||||||
// Memcpy,
|
|
||||||
// /// libc.memset
|
|
||||||
// Memset,
|
|
||||||
// /// libc.memmove
|
|
||||||
// Memmove,
|
|
||||||
|
|
||||||
// /// Elf __tls_get_addr
|
/// trunc.f32
|
||||||
// ElfTlsGetAddr,
|
TruncF32,
|
||||||
|
|
||||||
|
/// frunc.f64
|
||||||
|
TruncF64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LibCall {
|
impl LibCall {
|
||||||
@@ -442,16 +442,31 @@ impl LibCall {
|
|||||||
pub fn function_pointer(self) -> usize {
|
pub fn function_pointer(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::CeilF32 => wasmer_f32_ceil as usize,
|
Self::CeilF32 => wasmer_f32_ceil as usize,
|
||||||
Self::FloorF32 => wasmer_f32_floor as usize,
|
|
||||||
Self::TruncF32 => wasmer_f32_trunc as usize,
|
|
||||||
Self::NearestF32 => wasmer_f32_nearest as usize,
|
|
||||||
Self::CeilF64 => wasmer_f64_ceil as usize,
|
Self::CeilF64 => wasmer_f64_ceil as usize,
|
||||||
|
Self::FloorF32 => wasmer_f32_floor as usize,
|
||||||
Self::FloorF64 => wasmer_f64_floor as usize,
|
Self::FloorF64 => wasmer_f64_floor as usize,
|
||||||
Self::TruncF64 => wasmer_f64_trunc as usize,
|
Self::NearestF32 => wasmer_f32_nearest as usize,
|
||||||
Self::NearestF64 => wasmer_f64_nearest as usize,
|
Self::NearestF64 => wasmer_f64_nearest as usize,
|
||||||
|
Self::Probestack => wasmer_probestack as usize,
|
||||||
Self::RaiseTrap => wasmer_raise_trap as usize,
|
Self::RaiseTrap => wasmer_raise_trap as usize,
|
||||||
Self::Probestack => PROBESTACK as usize,
|
Self::TruncF32 => wasmer_f32_trunc as usize,
|
||||||
// other => panic!("unexpected libcall: {}", other),
|
Self::TruncF64 => wasmer_f64_trunc as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the function name associated to the libcall.
|
||||||
|
pub fn to_function_name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::CeilF32 => "wasmer_f32_ceil",
|
||||||
|
Self::CeilF64 => "wasmer_f64_ceil",
|
||||||
|
Self::FloorF32 => "wasmer_f32_floor",
|
||||||
|
Self::FloorF64 => "wasmer_f64_floor",
|
||||||
|
Self::NearestF32 => "wasmer_f32_nearest",
|
||||||
|
Self::NearestF64 => "wasmer_f64_nearest",
|
||||||
|
Self::Probestack => "wasmer_probestack",
|
||||||
|
Self::RaiseTrap => "wasmer_raise_trap",
|
||||||
|
Self::TruncF32 => "wasmer_f32_trunc",
|
||||||
|
Self::TruncF64 => "wasmer_f64_trunc",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub use crate::syscalls::types;
|
|||||||
pub use crate::utils::{get_wasi_version, is_wasi_module, WasiVersion};
|
pub use crate::utils::{get_wasi_version, is_wasi_module, WasiVersion};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wasmer::{imports, Function, ImportObject, Memory, Store};
|
use wasmer::{imports, Function, ImportObject, Memory, Module, Store};
|
||||||
|
|
||||||
/// This is returned in `RuntimeError`.
|
/// This is returned in `RuntimeError`.
|
||||||
/// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`.
|
/// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`.
|
||||||
@@ -34,6 +34,8 @@ use wasmer::{imports, Function, ImportObject, Memory, Store};
|
|||||||
pub enum WasiError {
|
pub enum WasiError {
|
||||||
#[error("WASI exited with code: {0}")]
|
#[error("WASI exited with code: {0}")]
|
||||||
Exit(syscalls::types::__wasi_exitcode_t),
|
Exit(syscalls::types::__wasi_exitcode_t),
|
||||||
|
#[error("The WASI version could not be determined")]
|
||||||
|
UnknownWasiVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The environment provided to the WASI imports.
|
/// The environment provided to the WASI imports.
|
||||||
@@ -51,6 +53,15 @@ impl<'a> WasiEnv<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn import_object(&mut self, module: &Module) -> Result<ImportObject, WasiError> {
|
||||||
|
let wasi_version = get_wasi_version(module, false).ok_or(WasiError::UnknownWasiVersion)?;
|
||||||
|
Ok(generate_import_object_from_env(
|
||||||
|
module.store(),
|
||||||
|
self,
|
||||||
|
wasi_version,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the state
|
/// Set the state
|
||||||
pub fn set_memory(&mut self, memory: &'a Memory) {
|
pub fn set_memory(&mut self, memory: &'a Memory) {
|
||||||
self.memory = Some(memory);
|
self.memory = Some(memory);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::state::{WasiFile, WasiFs, WasiFsError, WasiState};
|
use crate::state::{WasiFile, WasiFs, WasiFsError, WasiState};
|
||||||
use crate::syscalls::types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO};
|
use crate::syscalls::types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO};
|
||||||
|
use crate::WasiEnv;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@@ -388,6 +389,14 @@ impl WasiStateBuilder {
|
|||||||
envs: self.envs.clone(),
|
envs: self.envs.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes the [`WasiStateBuilder`] and produces a [`WasiEnv`]
|
||||||
|
///
|
||||||
|
/// Returns the error from `WasiFs::new` if there's an error
|
||||||
|
pub fn finalize(&mut self) -> Result<WasiEnv, WasiStateCreationError> {
|
||||||
|
let state = self.build()?;
|
||||||
|
Ok(WasiEnv::new(state))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder for preopened directories.
|
/// Builder for preopened directories.
|
||||||
|
|||||||
@@ -1481,8 +1481,8 @@ impl WasiState {
|
|||||||
/// Create a [`WasiStateBuilder`] to construct a validated instance of
|
/// Create a [`WasiStateBuilder`] to construct a validated instance of
|
||||||
/// [`WasiState`].
|
/// [`WasiState`].
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(program_name: &str) -> WasiStateBuilder {
|
pub fn new(program_name: impl AsRef<str>) -> WasiStateBuilder {
|
||||||
create_wasi_state(program_name)
|
create_wasi_state(program_name.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn the WasiState into bytes
|
/// Turn the WasiState into bytes
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ impl Run {
|
|||||||
#[cfg(feature = "wasi")]
|
#[cfg(feature = "wasi")]
|
||||||
{
|
{
|
||||||
let wasi_version = Wasi::get_version(&module);
|
let wasi_version = Wasi::get_version(&module);
|
||||||
if let Some(version) = wasi_version {
|
if wasi_version.is_some() {
|
||||||
let program_name = self
|
let program_name = self
|
||||||
.command_name
|
.command_name
|
||||||
.clone()
|
.clone()
|
||||||
@@ -121,7 +121,7 @@ impl Run {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
return self
|
return self
|
||||||
.wasi
|
.wasi
|
||||||
.execute(module, version, program_name, self.args.clone())
|
.execute(module, program_name, self.args.clone())
|
||||||
.with_context(|| "WASI execution failed");
|
.with_context(|| "WASI execution failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
|
use crate::utils::{parse_envvar, parse_mapdir};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use wasmer::{Instance, Module};
|
||||||
use wasmer::{Function, Instance, Memory, Module};
|
use wasmer_wasi::{get_wasi_version, WasiState, WasiVersion};
|
||||||
use wasmer_wasi::{
|
|
||||||
generate_import_object_from_env, get_wasi_version, WasiEnv, WasiState, WasiVersion,
|
|
||||||
};
|
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
@@ -12,16 +10,16 @@ use structopt::StructOpt;
|
|||||||
/// WASI Options
|
/// WASI Options
|
||||||
pub struct Wasi {
|
pub struct Wasi {
|
||||||
/// WASI pre-opened directory
|
/// WASI pre-opened directory
|
||||||
#[structopt(long = "dir", multiple = true, group = "wasi")]
|
#[structopt(long = "dir", name = "DIR", multiple = true, group = "wasi")]
|
||||||
pre_opened_directories: Vec<PathBuf>,
|
pre_opened_directories: Vec<PathBuf>,
|
||||||
|
|
||||||
/// Map a host directory to a different location for the wasm module
|
/// Map a host directory to a different location for the wasm module
|
||||||
#[structopt(long = "mapdir", multiple = true)]
|
#[structopt(long = "mapdir", name = "GUEST_DIR:HOST_DIR", multiple = true, parse(try_from_str = parse_mapdir))]
|
||||||
mapped_dirs: Vec<String>,
|
mapped_dirs: Vec<(String, PathBuf)>,
|
||||||
|
|
||||||
/// Pass custom environment variables
|
/// Pass custom environment variables
|
||||||
#[structopt(long = "env", multiple = true)]
|
#[structopt(long = "env", name = "KEY=VALUE", multiple = true, parse(try_from_str = parse_envvar))]
|
||||||
env_vars: Vec<String>,
|
env_vars: Vec<(String, String)>,
|
||||||
|
|
||||||
/// Enable experimental IO devices
|
/// Enable experimental IO devices
|
||||||
#[cfg(feature = "experimental-io-devices")]
|
#[cfg(feature = "experimental-io-devices")]
|
||||||
@@ -30,44 +28,6 @@ pub struct Wasi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Wasi {
|
impl Wasi {
|
||||||
fn get_mapped_dirs(&self) -> Result<Vec<(String, PathBuf)>> {
|
|
||||||
let mut md = vec![];
|
|
||||||
for entry in self.mapped_dirs.iter() {
|
|
||||||
if let [alias, real_dir] = entry.split(':').collect::<Vec<&str>>()[..] {
|
|
||||||
let pb = PathBuf::from(&real_dir);
|
|
||||||
if let Ok(pb_metadata) = pb.metadata() {
|
|
||||||
if !pb_metadata.is_dir() {
|
|
||||||
bail!("\"{}\" exists, but it is not a directory", &real_dir);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bail!("Directory \"{}\" does not exist", &real_dir);
|
|
||||||
}
|
|
||||||
md.push((alias.to_string(), pb));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
bail!(
|
|
||||||
"Directory mappings must consist of two paths separate by a colon. Found {}",
|
|
||||||
&entry
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_env_vars(&self) -> Result<Vec<(&str, &str)>> {
|
|
||||||
let mut env = vec![];
|
|
||||||
for entry in self.env_vars.iter() {
|
|
||||||
if let [env_var, value] = entry.split('=').collect::<Vec<&str>>()[..] {
|
|
||||||
env.push((env_var, value));
|
|
||||||
} else {
|
|
||||||
bail!(
|
|
||||||
"Env vars must be of the form <var_name>=<value>. Found {}",
|
|
||||||
&entry
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(env)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the WASI version (if any) for the provided module
|
/// Gets the WASI version (if any) for the provided module
|
||||||
pub fn get_version(module: &Module) -> Option<WasiVersion> {
|
pub fn get_version(module: &Module) -> Option<WasiVersion> {
|
||||||
// Get the wasi version on strict mode, so no other imports are
|
// Get the wasi version on strict mode, so no other imports are
|
||||||
@@ -76,25 +36,15 @@ impl Wasi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function for executing Wasi from the `Run` command.
|
/// Helper function for executing Wasi from the `Run` command.
|
||||||
pub fn execute(
|
pub fn execute(&self, module: Module, program_name: String, args: Vec<String>) -> Result<()> {
|
||||||
&self,
|
|
||||||
module: Module,
|
|
||||||
wasi_version: WasiVersion,
|
|
||||||
program_name: String,
|
|
||||||
args: Vec<String>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut wasi_state_builder = WasiState::new(&program_name);
|
|
||||||
|
|
||||||
let args = args.iter().cloned().map(|arg| arg.into_bytes());
|
let args = args.iter().cloned().map(|arg| arg.into_bytes());
|
||||||
let env_vars = self.get_env_vars()?;
|
|
||||||
let preopened_files = self.pre_opened_directories.clone();
|
|
||||||
let mapped_dirs = self.get_mapped_dirs()?;
|
|
||||||
|
|
||||||
|
let mut wasi_state_builder = WasiState::new(program_name);
|
||||||
wasi_state_builder
|
wasi_state_builder
|
||||||
.args(args)
|
.args(args)
|
||||||
.envs(env_vars)
|
.envs(self.env_vars.clone())
|
||||||
.preopen_dirs(preopened_files)?
|
.preopen_dirs(self.pre_opened_directories.clone())?
|
||||||
.map_dirs(mapped_dirs)?;
|
.map_dirs(self.mapped_dirs.clone())?;
|
||||||
|
|
||||||
#[cfg(feature = "experimental-io-devices")]
|
#[cfg(feature = "experimental-io-devices")]
|
||||||
{
|
{
|
||||||
@@ -104,18 +54,13 @@ impl Wasi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let wasi_state = wasi_state_builder.build()?;
|
let mut wasi_env = wasi_state_builder.finalize()?;
|
||||||
let mut wasi_env = WasiEnv::new(wasi_state);
|
let import_object = wasi_env.import_object(&module)?;
|
||||||
let import_object =
|
|
||||||
generate_import_object_from_env(module.store(), &mut wasi_env, wasi_version);
|
|
||||||
|
|
||||||
let instance = Instance::new(&module, &import_object)?;
|
let instance = Instance::new(&module, &import_object)?;
|
||||||
|
|
||||||
let memory: &Memory = instance.exports.get("memory")?;
|
wasi_env.set_memory(instance.exports.get_memory("memory")?);
|
||||||
wasi_env.set_memory(memory);
|
|
||||||
|
|
||||||
let start: &Function = instance.exports.get("_start")?;
|
|
||||||
|
|
||||||
|
let start = instance.exports.get_function("_start")?;
|
||||||
start
|
start
|
||||||
.call(&[])
|
.call(&[])
|
||||||
.with_context(|| "failed to run WASI `_start` function")?;
|
.with_context(|| "failed to run WASI `_start` function")?;
|
||||||
|
|||||||
33
src/utils.rs
33
src/utils.rs
@@ -1,5 +1,7 @@
|
|||||||
//! Utility functions for the WebAssembly module
|
//! Utility functions for the WebAssembly module
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// Whether or not Wasmer should print with color
|
/// Whether or not Wasmer should print with color
|
||||||
pub fn wasmer_should_print_color() -> bool {
|
pub fn wasmer_should_print_color() -> bool {
|
||||||
@@ -8,3 +10,34 @@ pub fn wasmer_should_print_color() -> bool {
|
|||||||
.and_then(|inner| inner.parse::<bool>().ok())
|
.and_then(|inner| inner.parse::<bool>().ok())
|
||||||
.unwrap_or_else(|| atty::is(atty::Stream::Stdout))
|
.unwrap_or_else(|| atty::is(atty::Stream::Stdout))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a mapdir from a string
|
||||||
|
pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> {
|
||||||
|
if let [alias, real_dir] = entry.split(':').collect::<Vec<&str>>()[..] {
|
||||||
|
let pb = PathBuf::from(&real_dir);
|
||||||
|
if let Ok(pb_metadata) = pb.metadata() {
|
||||||
|
if !pb_metadata.is_dir() {
|
||||||
|
bail!("\"{}\" exists, but it is not a directory", &real_dir);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bail!("Directory \"{}\" does not exist", &real_dir);
|
||||||
|
}
|
||||||
|
return Ok((alias.to_string(), pb));
|
||||||
|
}
|
||||||
|
bail!(
|
||||||
|
"Directory mappings must consist of two paths separate by a colon. Found {}",
|
||||||
|
&entry
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a mapdir from an env var
|
||||||
|
pub fn parse_envvar(entry: &str) -> Result<(String, String)> {
|
||||||
|
if let [env_var, value] = entry.split('=').collect::<Vec<&str>>()[..] {
|
||||||
|
return Ok((env_var.to_string(), value.to_string()));
|
||||||
|
} else {
|
||||||
|
bail!(
|
||||||
|
"Env vars must be of the form <var_name>=<value>. Found {}",
|
||||||
|
&entry
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user