Merge branch 'master' into feature/clif-cgapi

This commit is contained in:
Brandon Fish 2019-05-14 23:01:28 -05:00 committed by GitHub
commit ac3fafae9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 459 additions and 141 deletions

View File

@ -6,6 +6,10 @@ Blocks of changes will separated by version increments.
## **[Unreleased]**
- [#442](https://github.com/wasmerio/wasmer/pull/442) Misc. WASI FS fixes and implement readdir
- [#440](https://github.com/wasmerio/wasmer/pull/440) Fix type mismatch between `wasmer_instance_call` and `wasmer_export_func_*_arity` functions in the runtime C API.
- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs
- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API
- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements
- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits

View File

@ -49,11 +49,13 @@ test:
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
cargo test --manifest-path lib/spectests/Cargo.toml --features clif
cargo test --manifest-path lib/spectests/Cargo.toml --features llvm
cargo test --manifest-path lib/runtime/Cargo.toml --features llvm
cargo build -p wasmer-runtime-c-api
cargo test -p wasmer-runtime-c-api -- --nocapture
test-singlepass:
cargo test --manifest-path lib/spectests/Cargo.toml --features singlepass
cargo test --manifest-path lib/runtime/Cargo.toml --features singlepass
test-emscripten-llvm:
cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs)

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
mod cache;
mod code;

View File

@ -1,5 +1,5 @@
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
//! accesses that occur when runniing webassembly.
//! accesses that occur when running WebAssembly.
//!
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
//!

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#[macro_use]
extern crate wasmer_runtime_core;

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![cfg_attr(nightly, feature(unwind_attributes))]
mod backend;

View File

@ -1,3 +1,3 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
pub mod call_trace;

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#[cfg(not(target_os = "windows"))]
#[macro_use]

View File

@ -1,4 +1,4 @@
//! Errors.
//! Read runtime errors.
use libc::{c_char, c_int};
use std::cell::RefCell;
@ -61,20 +61,20 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length:
return -1;
}
let last_error = match take_last_error() {
Some(err) => err,
let error_message = match take_last_error() {
Some(err) => err.to_string(),
None => return 0,
};
let error_message = last_error.to_string();
let length = length as usize;
let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize);
if error_message.len() >= buffer.len() {
// buffer to small for err message
if error_message.len() >= length {
// buffer is too small to hold the error message
return -1;
}
let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length);
ptr::copy_nonoverlapping(
error_message.as_ptr(),
buffer.as_mut_ptr(),
@ -85,7 +85,7 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length:
// accidentally read into garbage.
buffer[error_message.len()] = 0;
error_message.len() as c_int
error_message.len() as c_int + 1
}
#[derive(Debug)]

View File

@ -1,4 +1,5 @@
//! Wasm exports.
//! Create, read, destroy export definitions (function, global, memory
//! and table) on an instance.
use crate::{
error::{update_last_error, CApiError},
@ -221,7 +222,7 @@ pub unsafe extern "C" fn wasmer_export_func_params_arity(
pub unsafe extern "C" fn wasmer_export_func_params(
func: *const wasmer_export_func_t,
params: *mut wasmer_value_tag,
params_len: c_int,
params_len: uint32_t,
) -> wasmer_result_t {
let named_export = &*(func as *const NamedExport);
let export = &named_export.export;
@ -251,7 +252,7 @@ pub unsafe extern "C" fn wasmer_export_func_params(
pub unsafe extern "C" fn wasmer_export_func_returns(
func: *const wasmer_export_func_t,
returns: *mut wasmer_value_tag,
returns_len: c_int,
returns_len: uint32_t,
) -> wasmer_result_t {
let named_export = &*(func as *const NamedExport);
let export = &named_export.export;

View File

@ -1,4 +1,4 @@
//! Wasm global.
//! Create, set, get and destroy global variables of an instance.
use crate::value::{wasmer_value_t, wasmer_value_tag};
use wasmer_runtime::Global;

View File

@ -1,4 +1,5 @@
//! Wasm imports.
//! Create, read, destroy import definitions (function, global, memory
//! and table) on an instance.
use crate::{
error::{update_last_error, CApiError},

View File

@ -1,4 +1,4 @@
//! Wasm instance.
//! Instantiate a module, call functions, and read exports.
use crate::{
error::{update_last_error, CApiError},
@ -125,9 +125,9 @@ pub unsafe extern "C" fn wasmer_instance_call(
instance: *mut wasmer_instance_t,
name: *const c_char,
params: *const wasmer_value_t,
params_len: c_int,
params_len: uint32_t,
results: *mut wasmer_value_t,
results_len: c_int,
results_len: uint32_t,
) -> wasmer_result_t {
if instance.is_null() {
update_last_error(CApiError {

View File

@ -1,4 +1,86 @@
#![deny(unused_imports, unused_variables)]
//! # Wasmer Runtime C API
//!
//! Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
//! compatible with Emscripten, Rust and Go. [Learn
//! more](https://github.com/wasmerio/wasmer).
//!
//! This crate exposes a C and C++ API for the Wasmer runtime.
//!
//! # Usage
//!
//! The C and C++ header files can be found in the source tree of this
//! crate, respectively [`wasmer.h`][wasmer_h] and
//! [`wasmer.hh`][wasmer_hh]. They are automatically generated, and always
//! up-to-date in this repository.
//!
//! Here is a simple example to use the C API:
//!
//! ```c
//! #include <stdio.h>
//! #include "wasmer.h"
//! #include <assert.h>
//! #include <stdint.h>
//!
//! int main()
//! {
//! // Read the Wasm file bytes.
//! FILE *file = fopen("sum.wasm", "r");
//! fseek(file, 0, SEEK_END);
//! long len = ftell(file);
//! uint8_t *bytes = malloc(len);
//! fseek(file, 0, SEEK_SET);
//! fread(bytes, 1, len, file);
//! fclose(file);
//!
//! // Prepare the imports.
//! wasmer_import_t imports[] = {};
//!
//! // Instantiate!
//! wasmer_instance_t *instance = NULL;
//! wasmer_result_t instantiation_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
//!
//! assert(instantiation_result == WASMER_OK);
//!
//! // Let's call a function.
//! // Start by preparing the arguments.
//!
//! // Value of argument #1 is `7i32`.
//! wasmer_value_t argument_one;
//! argument_one.tag = WASM_I32;
//! argument_one.value.I32 = 7;
//!
//! // Value of argument #2 is `8i32`.
//! wasmer_value_t argument_two;
//! argument_two.tag = WASM_I32;
//! argument_two.value.I32 = 8;
//!
//! // Prepare the arguments.
//! wasmer_value_t arguments[] = {argument_one, argument_two};
//!
//! // Prepare the return value.
//! wasmer_value_t result_one;
//! wasmer_value_t results[] = {result_one};
//!
//! // Call the `sum` function with the prepared arguments and the return value.
//! wasmer_result_t call_result = wasmer_instance_call(instance, "sum", arguments, 2, results, 1);
//!
//! // Let's display the result.
//! printf("Call result: %d\n", call_result);
//! printf("Result: %d\n", results[0].value.I32);
//!
//! // `sum(7, 8) == 15`.
//! assert(results[0].value.I32 == 15);
//! assert(call_result == WASMER_OK);
//!
//! wasmer_instance_destroy(instance);
//!
//! return 0;
//! }
//! ```
//!
//! [wasmer_h]: ./wasmer.h
//! [wasmer_hh]: ./wasmer.hh
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
extern crate wasmer_runtime;
extern crate wasmer_runtime_core;

View File

@ -1,4 +1,4 @@
//! Wasm memory.o
//! Create, read, write, grow, destroy memory of an instance.
use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t};
use libc::{uint32_t, uint8_t};

View File

@ -1,4 +1,4 @@
//! Wasm module.
//! Compile, validate, instantiate, serialize, and destroy modules.
use crate::{
error::{update_last_error, CApiError},
@ -247,7 +247,7 @@ pub unsafe extern "C" fn wasmer_module_deserialize(
let serialized_module: &[u8] = &*(serialized_module as *const &[u8]);
match Artifact::deserialize(serialized_module) {
Ok(artifact) => match load_cache_with(artifact, default_compiler()) {
Ok(artifact) => match load_cache_with(artifact, &default_compiler()) {
Ok(deserialized_module) => {
*module = Box::into_raw(Box::new(deserialized_module)) as _;
wasmer_result_t::WASMER_OK

View File

@ -1,4 +1,4 @@
//! Wasm tables.
//! Create, grow, destroy tables of an instance.
use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t};
use libc::uint32_t;

View File

@ -1,4 +1,4 @@
//! Wasm values.
//! Create and map Rust to WebAssembly values.
use libc::{int32_t, int64_t};
use wasmer_runtime::Value;

View File

@ -60,7 +60,6 @@ int main()
assert(returns_sig[0] == WASM_I32);
free(returns_sig);
wasmer_value_t param_one;
param_one.tag = WASM_I32;
param_one.value.I32 = 7;
@ -71,7 +70,7 @@ int main()
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
wasmer_result_t call_result = wasmer_export_func_call(func, params, 2, results, 1);
wasmer_result_t call_result = wasmer_export_func_call(func, params, params_arity, results, returns_arity);
printf("Call result: %d\n", call_result);
printf("Result: %d\n", results[0].value.I32);
assert(results[0].value.I32 == 15);

View File

@ -46,7 +46,8 @@ int main()
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
int error_result = wasmer_last_error_message(error_str, error_len);
assert(error_len == error_result);
printf("Error str: `%s`\n", error_str);
assert(0 == strcmp(error_str, "Call error: Parameters of type [I32] did not match signature [I32, I32] -> [I32]"));
free(error_str);

View File

@ -197,7 +197,7 @@ wasmer_result_t wasmer_export_func_call(const wasmer_export_func_t *func,
*/
wasmer_result_t wasmer_export_func_params(const wasmer_export_func_t *func,
wasmer_value_tag *params,
int params_len);
uint32_t params_len);
/**
* Sets the result parameter to the arity of the params of the wasmer_export_func_t
@ -215,7 +215,7 @@ wasmer_result_t wasmer_export_func_params_arity(const wasmer_export_func_t *func
*/
wasmer_result_t wasmer_export_func_returns(const wasmer_export_func_t *func,
wasmer_value_tag *returns,
int returns_len);
uint32_t returns_len);
/**
* Sets the result parameter to the arity of the returns of the wasmer_export_func_t
@ -390,9 +390,9 @@ wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *fun
wasmer_result_t wasmer_instance_call(wasmer_instance_t *instance,
const char *name,
const wasmer_value_t *params,
int params_len,
uint32_t params_len,
wasmer_value_t *results,
int results_len);
uint32_t results_len);
/**
* Gets the `data` field within the context.

View File

@ -178,7 +178,7 @@ wasmer_result_t wasmer_export_func_call(const wasmer_export_func_t *func,
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_export_func_params(const wasmer_export_func_t *func,
wasmer_value_tag *params,
int params_len);
uint32_t params_len);
/// Sets the result parameter to the arity of the params of the wasmer_export_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success.
@ -192,7 +192,7 @@ wasmer_result_t wasmer_export_func_params_arity(const wasmer_export_func_t *func
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_export_func_returns(const wasmer_export_func_t *func,
wasmer_value_tag *returns,
int returns_len);
uint32_t returns_len);
/// Sets the result parameter to the arity of the returns of the wasmer_export_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success.
@ -313,9 +313,9 @@ wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *fun
wasmer_result_t wasmer_instance_call(wasmer_instance_t *instance,
const char *name,
const wasmer_value_t *params,
int params_len,
uint32_t params_len,
wasmer_value_t *results,
int results_len);
uint32_t results_len);
/// Gets the `data` field within the context.
void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx);

View File

@ -17,30 +17,31 @@ use crate::{
};
use std::slice;
/// The `LocalBacking` "owns" the memory used by all the local resources of an Instance.
/// That is, local memories, tables, and globals (as well as some additional
/// data for the virtual call machinery).
#[derive(Debug)]
pub struct LocalBacking {
/// This is a map from the local resource index to actual memory,
/// table, and globals.
pub(crate) memories: BoxedMap<LocalMemoryIndex, Memory>,
pub(crate) tables: BoxedMap<LocalTableIndex, Table>,
pub(crate) globals: BoxedMap<LocalGlobalIndex, Global>,
/// This own the memory containing the pointers to the local memories.
/// While simplifying implementation, this adds indirection and may hurt
/// performance, especially on cache-starved systems.
pub(crate) vm_memories: BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory>,
pub(crate) vm_tables: BoxedMap<LocalTableIndex, *mut vm::LocalTable>,
pub(crate) vm_globals: BoxedMap<LocalGlobalIndex, *mut vm::LocalGlobal>,
/// The dynamic sigindices are used to efficiently support caching and
/// the `call_indirect` wasm instruction. This field (and local_functions
/// as well) are subject to change.
pub(crate) dynamic_sigindices: BoxedMap<SigIndex, vm::SigId>,
pub(crate) local_functions: BoxedMap<LocalFuncIndex, *const vm::Func>,
}
// impl LocalBacking {
// pub fn memory(&mut self, local_memory_index: LocalMemoryIndex) -> &mut Memory {
// &mut self.memories[local_memory_index]
// }
// pub fn table(&mut self, local_table_index: LocalTableIndex) -> &mut TableBacking {
// &mut self.tables[local_table_index]
// }
// }
impl LocalBacking {
pub(crate) fn new(module: &ModuleInner, imports: &ImportBacking, vmctx: *mut vm::Ctx) -> Self {
let mut memories = Self::generate_memories(module);
@ -102,6 +103,9 @@ impl LocalBacking {
memories.into_boxed_map()
}
/// Initialize each locally-defined memory in the Module.
///
/// This involves copying in the data initializers.
fn finalize_memories(
module: &ModuleInner,
imports: &ImportBacking,
@ -174,6 +178,9 @@ impl LocalBacking {
tables.into_boxed_map()
}
/// This initializes all of the locally-defined tables in the Module, e.g.
/// putting all the table elements (function pointers)
/// in the right places.
#[allow(clippy::cast_ptr_alignment)]
fn finalize_tables(
module: &ModuleInner,

View File

@ -35,7 +35,6 @@ impl fmt::Debug for InternalEvent {
InternalEvent::Breakpoint(_) => write!(f, "Breakpoint"),
InternalEvent::SetInternal(_) => write!(f, "SetInternal"),
InternalEvent::GetInternal(_) => write!(f, "GetInternal"),
_ => panic!("unknown event"),
}
}
}

View File

@ -11,7 +11,7 @@ pub type ResolveResult<T> = std::result::Result<T, ResolveError>;
pub type ParseResult<T> = std::result::Result<T, ParseError>;
/// This is returned when the chosen compiler is unable to
/// successfully compile the provided webassembly module into
/// successfully compile the provided WebAssembly module into
/// a `Module`.
///
/// Comparing two `CompileError`s always evaluates to false.
@ -114,7 +114,7 @@ impl std::fmt::Display for LinkError {
impl std::error::Error for LinkError {}
/// This is the error type returned when calling
/// a webassembly function.
/// a WebAssembly function.
///
/// The main way to do this is `Instance.call`.
///
@ -270,7 +270,7 @@ impl std::error::Error for CreationError {}
/// The amalgamation of all errors that can occur
/// during the compilation, instantiation, or execution
/// of a webassembly module.
/// of a WebAssembly module.
///
/// Comparing two `Error`s always evaluates to false.
#[derive(Debug)]

View File

@ -262,7 +262,7 @@ impl Instance {
}
}
/// Call an exported webassembly function given the export name.
/// Call an exported WebAssembly function given the export name.
/// Pass arguments by wrapping each one in the [`Value`] enum.
/// The returned values are also each wrapped in a [`Value`].
///
@ -270,7 +270,7 @@ impl Instance {
///
/// # Note:
/// This returns `CallResult<Vec<Value>>` in order to support
/// the future multi-value returns webassembly feature.
/// the future multi-value returns WebAssembly feature.
///
/// # Usage:
/// ```
@ -601,7 +601,7 @@ pub struct DynFunc<'a> {
}
impl<'a> DynFunc<'a> {
/// Call an exported webassembly function safely.
/// Call an exported WebAssembly function safely.
///
/// Pass arguments by wrapping each one in the [`Value`] enum.
/// The returned values are also each wrapped in a [`Value`].
@ -610,7 +610,7 @@ impl<'a> DynFunc<'a> {
///
/// # Note:
/// This returns `CallResult<Vec<Value>>` in order to support
/// the future multi-value returns webassembly feature.
/// the future multi-value returns WebAssembly feature.
///
/// # Usage:
/// ```

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![cfg_attr(nightly, feature(unwind_attributes))]
#[cfg(test)]

View File

@ -11,7 +11,7 @@ use crate::{
/// This is an internal-only api.
///
/// A static memory allocates 6GB of *virtual* memory when created
/// in order to allow the webassembly module to contain no bounds-checks.
/// in order to allow the WebAssembly module to contain no bounds-checks.
///
/// Additionally, static memories stay at a single virtual address, so there is no need
/// to reload its address on each use.

View File

@ -23,10 +23,18 @@ struct GlobalSigRegistry {
sig_assoc: Map<SigIndex, Arc<FuncSig>>,
}
/// The `SigRegistry` represents a process-global map of function signatures
/// to signature indexes and vice versa (the map goes both ways).
///
/// This exists for two reasons:
/// 1. The `call_indirect` wasm instruction can compare two signature indices
/// to do signature validation very quickly.
/// 2. To intern function signatures, which may be expensive to create.
#[derive(Debug)]
pub struct SigRegistry;
impl SigRegistry {
/// Map a `FuncSig` to a global `SigIndex`.
pub fn lookup_sig_index<Sig>(&self, func_sig: Sig) -> SigIndex
where
Sig: Into<Arc<FuncSig>>,
@ -45,11 +53,15 @@ impl SigRegistry {
sig_index
}
/// Map a global `SigIndex` to an interned `FuncSig`.
pub fn lookup_signature(&self, sig_index: SigIndex) -> Arc<FuncSig> {
let global = (*GLOBAL_SIG_REGISTRY).read();
Arc::clone(&global.sig_assoc[sig_index])
}
/// Register a function signature with the global signature registry.
///
/// This will return an interned `FuncSig`.
pub fn lookup_signature_ref(&self, func_sig: &FuncSig) -> Arc<FuncSig> {
let mut global = (*GLOBAL_SIG_REGISTRY).write();
let global = &mut *global;

View File

@ -247,7 +247,7 @@ impl<A: WasmExternType> WasmTypeList for (A,) {
type CStruct = S1<A>;
type RetArray = [u64; 1];
fn from_ret_array(array: Self::RetArray) -> Self {
(WasmExternType::from_native(NativeWasmType::from_bits(
(WasmExternType::from_native(NativeWasmType::from_binary(
array[0],
)),)
}
@ -274,7 +274,7 @@ impl<A: WasmExternType> WasmTypeList for (A,) {
ctx: *mut Ctx,
) -> Result<Rets, RuntimeError> {
let (a,) = self;
let args = [a.to_native().to_bits()];
let args = [a.to_native().to_binary()];
let mut rets = Rets::empty_ret_array();
let mut trap = WasmTrapInfo::Unknown;
let mut user_error = None;
@ -322,7 +322,7 @@ macro_rules! impl_traits {
fn from_ret_array(array: Self::RetArray) -> Self {
#[allow(non_snake_case)]
let [ $( $x ),* ] = array;
( $( WasmExternType::from_native(NativeWasmType::from_bits($x)) ),* )
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
}
fn empty_ret_array() -> Self::RetArray {
[0; count_idents!( $( $x ),* )]
@ -344,7 +344,7 @@ macro_rules! impl_traits {
unsafe fn call<Rets: WasmTypeList>(self, f: NonNull<vm::Func>, wasm: Wasm, ctx: *mut Ctx) -> Result<Rets, RuntimeError> {
#[allow(unused_parens)]
let ( $( $x ),* ) = self;
let args = [ $( $x.to_native().to_bits() ),* ];
let args = [ $( $x.to_native().to_binary()),* ];
let mut rets = Rets::empty_ret_array();
let mut trap = WasmTrapInfo::Unknown;
let mut user_error = None;

View File

@ -76,44 +76,44 @@ where
Self: Sized,
{
const TYPE: Type;
fn from_bits(bits: u64) -> Self;
fn to_bits(self) -> u64;
fn from_binary(bits: u64) -> Self;
fn to_binary(self) -> u64;
}
unsafe impl NativeWasmType for i32 {
const TYPE: Type = Type::I32;
fn from_bits(bits: u64) -> Self {
fn from_binary(bits: u64) -> Self {
bits as _
}
fn to_bits(self) -> u64 {
fn to_binary(self) -> u64 {
self as _
}
}
unsafe impl NativeWasmType for i64 {
const TYPE: Type = Type::I64;
fn from_bits(bits: u64) -> Self {
fn from_binary(bits: u64) -> Self {
bits as _
}
fn to_bits(self) -> u64 {
fn to_binary(self) -> u64 {
self as _
}
}
unsafe impl NativeWasmType for f32 {
const TYPE: Type = Type::F32;
fn from_bits(bits: u64) -> Self {
bits as _
fn from_binary(bits: u64) -> Self {
f32::from_bits(bits as u32)
}
fn to_bits(self) -> u64 {
self as _
fn to_binary(self) -> u64 {
self.to_bits() as _
}
}
unsafe impl NativeWasmType for f64 {
const TYPE: Type = Type::F64;
fn from_bits(bits: u64) -> Self {
bits as _
fn from_binary(bits: u64) -> Self {
f64::from_bits(bits)
}
fn to_bits(self) -> u64 {
self as _
fn to_binary(self) -> u64 {
self.to_bits()
}
}
@ -516,3 +516,58 @@ where
}
}
}
#[cfg(test)]
mod tests {
use crate::types::NativeWasmType;
use crate::types::WasmExternType;
#[test]
fn test_native_types_round_trip() {
assert_eq!(
42i32,
i32::from_native(i32::from_binary((42i32).to_native().to_binary()))
);
assert_eq!(
-42i32,
i32::from_native(i32::from_binary((-42i32).to_native().to_binary()))
);
use std::i64;
let xi64 = i64::MAX;
assert_eq!(
xi64,
i64::from_native(i64::from_binary((xi64).to_native().to_binary()))
);
let yi64 = i64::MIN;
assert_eq!(
yi64,
i64::from_native(i64::from_binary((yi64).to_native().to_binary()))
);
assert_eq!(
16.5f32,
f32::from_native(f32::from_binary((16.5f32).to_native().to_binary()))
);
assert_eq!(
-16.5f32,
f32::from_native(f32::from_binary((-16.5f32).to_native().to_binary()))
);
use std::f64;
let xf64: f64 = f64::MAX;
assert_eq!(
xf64,
f64::from_native(f64::from_binary((xf64).to_native().to_binary()))
);
let yf64: f64 = f64::MIN;
assert_eq!(
yf64,
f64::from_native(f64::from_binary((yf64).to_native().to_binary()))
);
}
}

View File

@ -11,7 +11,16 @@ use hashbrown::HashMap;
/// The context of the currently running WebAssembly instance.
///
/// This is implicitly passed to every WebAssembly function.
/// Since this is per-instance, each field has a statically
/// (as in after compiling the wasm) known size, so no
/// runtime checks are necessary.
///
/// While the runtime currently just passes this around
/// as the first, implicit parameter of every function,
/// it may someday be pinned to a register (especially
/// on arm, which has a ton of registers) to reduce
/// register shuffling.
#[derive(Debug)]
#[repr(C)]
pub struct Ctx {
@ -20,11 +29,25 @@ pub struct Ctx {
pub(crate) local_functions: *const *const Func,
/// These are pointers to things that are known to be owned
/// by the owning `Instance`.
local_backing: *mut LocalBacking,
import_backing: *mut ImportBacking,
pub module: *const ModuleInner,
//// This is intended to be user-supplied, per-instance
/// contextual data. There are currently some issue with it,
/// notably that it cannot be set before running the `start`
/// function in a WebAssembly module.
///
/// [#219](https://github.com/wasmerio/wasmer/pull/219) fixes that
/// issue, as well as allowing the user to have *per-function*
/// context, instead of just per-instance.
pub data: *mut c_void,
/// If there's a function set in this field, it gets called
/// when the context is destructed, e.g. when an `Instance`
/// is dropped.
pub data_finalizer: Option<fn(data: *mut c_void)>,
}

View File

@ -94,7 +94,9 @@ impl Cache for FileSystemCache {
let mmap = unsafe { Mmap::map(&file)? };
let serialized_cache = Artifact::deserialize(&mmap[..])?;
unsafe { wasmer_runtime_core::load_cache_with(serialized_cache, super::default_compiler()) }
unsafe {
wasmer_runtime_core::load_cache_with(serialized_cache, &super::default_compiler())
}
}
fn store(&mut self, key: WasmHash, module: Module) -> Result<(), CacheError> {

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
//! Wasmer-runtime is a library that makes embedding WebAssembly
//! in your application easy, efficient, and safe.
@ -131,7 +131,7 @@ use wasmer_runtime_core::backend::{Compiler, CompilerConfig};
/// # Errors:
/// If the operation fails, the function returns `Err(error::CompileError::...)`.
pub fn compile(wasm: &[u8]) -> error::CompileResult<Module> {
wasmer_runtime_core::compile_with(&wasm[..], default_compiler())
wasmer_runtime_core::compile_with(&wasm[..], &default_compiler())
}
/// The same as `compile` but takes a `CompilerConfig` for the purpose of
@ -140,7 +140,7 @@ pub fn compile_with_config(
wasm: &[u8],
compiler_config: CompilerConfig,
) -> error::CompileResult<Module> {
wasmer_runtime_core::compile_with_config(&wasm[..], default_compiler(), compiler_config)
wasmer_runtime_core::compile_with_config(&wasm[..], &default_compiler(), compiler_config)
}
/// The same as `compile_with_config` but takes a `Compiler` for the purpose of
@ -177,9 +177,7 @@ pub fn instantiate(wasm: &[u8], import_object: &ImportObject) -> error::Result<I
}
/// Get a single instance of the default compiler to use.
pub fn default_compiler() -> &'static dyn Compiler {
use lazy_static::lazy_static;
pub fn default_compiler() -> impl Compiler {
#[cfg(feature = "llvm")]
use wasmer_llvm_backend::LLVMCompiler as DefaultCompiler;
@ -189,11 +187,7 @@ pub fn default_compiler() -> &'static dyn Compiler {
#[cfg(not(any(feature = "llvm", feature = "singlepass")))]
use wasmer_clif_backend::CraneliftCompiler as DefaultCompiler;
lazy_static! {
static ref DEFAULT_COMPILER: DefaultCompiler = { DefaultCompiler::new() };
}
&*DEFAULT_COMPILER as &dyn Compiler
DefaultCompiler::new()
}
/// The current version of this crate

View File

@ -212,10 +212,10 @@ impl RunnableModule for X64ExecutionContext {
user_error: *mut Option<Box<dyn Any>>,
num_params_plus_one: Option<NonNull<c_void>>,
) -> bool {
let rm: &Box<dyn RunnableModule> = &unsafe { &*(*ctx).module }.runnable_module;
let execution_context = unsafe {
::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm)
};
let rm: &Box<dyn RunnableModule> = &(&*(*ctx).module).runnable_module;
let execution_context =
::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm);
let args = ::std::slice::from_raw_parts(
args,
num_params_plus_one.unwrap().as_ptr() as usize - 1,

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![feature(proc_macro_hygiene)]
#[cfg(not(any(

View File

@ -1,5 +1,5 @@
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
//! accesses that occur when runniing webassembly.
//! accesses that occur when runniing WebAssembly.
//!
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
//!

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#[macro_use]
extern crate log;

View File

@ -139,8 +139,10 @@ pub enum Kind {
handle: WasiFile,
},
Dir {
// TODO: wrap it like WasiFile
/// Parent directory
parent: Option<Inode>,
/// The path on the host system where the directory is located
// TODO: wrap it like WasiFile
path: PathBuf,
/// The entries of a directory are lazily filled.
entries: HashMap<String, Inode>,
@ -196,6 +198,7 @@ impl WasiFs {
let cur_dir_metadata = cur_dir.metadata().expect("Could not find directory");
let kind = if cur_dir_metadata.is_dir() {
Kind::Dir {
parent: None,
path: cur_dir.clone(),
entries: Default::default(),
}
@ -414,6 +417,32 @@ impl WasiFs {
);
Ok(idx)
}
pub fn get_base_path_for_directory(&self, directory: Inode) -> Option<String> {
let mut path_segments = vec![];
let mut cur_inode = directory;
loop {
path_segments.push(self.inodes[cur_inode].name.clone());
if let Kind::Dir { parent, .. } = &self.inodes[cur_inode].kind {
if let Some(p_inode) = parent {
cur_inode = *p_inode;
} else {
break;
}
} else {
return None;
}
}
path_segments.reverse();
Some(
path_segments
.iter()
.skip(1)
.fold(path_segments.first()?.clone(), |a, b| a + "/" + b),
)
}
}
#[derive(Debug)]

View File

@ -767,14 +767,74 @@ pub fn fd_readdir(
) -> __wasi_errno_t {
debug!("wasi::fd_readdir");
let memory = ctx.memory(0);
let state = get_wasi_state(ctx);
// TODO: figure out how this is supposed to work;
// is it supposed to pack the buffer full every time until it can't? or do one at a time?
if let (Ok(buf_arr_cell), Ok(bufused_cell)) =
(buf.deref(memory, 0, buf_len), bufused.deref(memory))
{
unimplemented!("wasi::fd_readdir")
let buf_arr_cell = wasi_try!(buf.deref(memory, 0, buf_len));
let bufused_cell = wasi_try!(bufused.deref(memory));
let working_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF));
let mut cur_cookie = cookie;
let mut buf_idx = 0;
if let Kind::Dir { path, .. } = &state.fs.inodes[working_dir.inode].kind {
// we need to support multiple calls,
// simple and obviously correct implementation for now:
// maintain consistent order via lexacographic sorting
let mut entries = wasi_try!(wasi_try!(std::fs::read_dir(path).map_err(|_| __WASI_EIO))
.collect::<Result<Vec<std::fs::DirEntry>, _>>()
.map_err(|_| __WASI_EIO));
entries.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
for entry in entries.iter().skip(cookie as usize) {
cur_cookie += 1;
let entry_path = entry.path();
let entry_path_str = entry_path.to_string_lossy();
let namlen = entry_path_str.len();
let dirent = __wasi_dirent_t {
d_next: cur_cookie,
d_ino: 0, // TODO: inode
d_namlen: namlen as u32,
d_type: {
let file_type = wasi_try!(entry.file_type().map_err(|_| __WASI_EIO));
// TODO: handle other file types
if file_type.is_dir() {
__WASI_FILETYPE_DIRECTORY
} else if file_type.is_file() {
__WASI_FILETYPE_REGULAR_FILE
} else if file_type.is_symlink() {
__WASI_FILETYPE_SYMBOLIC_LINK
} else {
__WASI_FILETYPE_UNKNOWN
}
},
};
let dirent_bytes = dirent_to_le_bytes(&dirent);
let upper_limit = std::cmp::min(
buf_len as usize - buf_idx,
std::mem::size_of::<__wasi_dirent_t>(),
);
for i in 0..upper_limit {
buf_arr_cell[i + buf_idx].set(dirent_bytes[i]);
}
buf_idx += upper_limit;
if upper_limit != std::mem::size_of::<__wasi_dirent_t>() {
break;
}
let upper_limit = std::cmp::min(buf_len as usize - buf_idx, namlen);
for (i, b) in entry_path_str.bytes().take(upper_limit).enumerate() {
buf_arr_cell[i + buf_idx].set(b);
}
buf_idx += upper_limit;
if upper_limit != namlen {
break;
}
}
} else {
__WASI_EFAULT
return __WASI_ENOTDIR;
}
bufused_cell.set(buf_idx as u32);
__WASI_ESUCCESS
}
/// ### `fd_renumber()`
@ -788,15 +848,14 @@ pub fn fd_renumber(ctx: &mut Ctx, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_
debug!("wasi::fd_renumber: from={}, to={}", from, to);
let state = get_wasi_state(ctx);
let fd_entry = wasi_try!(state.fs.fd_map.get(&from).ok_or(__WASI_EBADF));
let new_fd_entry = Fd {
// TODO: verify this is correct
rights: fd_entry.rights_inheriting,
..*fd_entry
};
state.fs.fd_map.insert(
to,
Fd {
// TODO: verify this is correct
rights: fd_entry.rights_inheriting,
..*fd_entry
},
);
state.fs.fd_map.insert(to, new_fd_entry);
state.fs.fd_map.remove(&from);
__WASI_ESUCCESS
}
@ -1025,7 +1084,7 @@ pub fn path_create_directory(
wasi_try!(std::fs::create_dir(&path).map_err(|_| __WASI_EIO));
let kind = Kind::Dir {
//parent: Some(working_dir.inode),
parent: Some(working_dir.inode),
path: path.clone(),
entries: Default::default(),
};
@ -1118,6 +1177,7 @@ pub fn path_filestat_get(
return __WASI_ENOTDIR;
}
let kind = Kind::Dir {
parent: Some(inode),
path: std::path::PathBuf::from(&segment),
entries: Default::default(),
};
@ -1177,6 +1237,7 @@ pub fn path_filestat_get(
is_preopened: false, // is this correct?
name: last_segment.clone(),
kind: Kind::Dir {
parent: Some(inode),
path: std::path::PathBuf::from(&last_segment),
entries: Default::default(),
},
@ -1361,7 +1422,10 @@ pub fn path_open(
}
let mut cur_dir_inode = working_dir.inode;
let mut cumulative_path = std::path::PathBuf::from(".");
let mut cumulative_path = std::path::PathBuf::from(wasi_try!(state
.fs
.get_base_path_for_directory(working_dir.inode)
.ok_or(__WASI_EIO)));
// traverse path
if path_vec.len() > 1 {
@ -1391,11 +1455,13 @@ pub fn path_open(
};
// TODO: handle __WASI_O_TRUNC on directories
dbg!(&cumulative_path);
// TODO: refactor and reuse
let cur_file_metadata =
wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EINVAL));
let kind = if cur_file_metadata.is_dir() {
Kind::Dir {
parent: Some(cur_dir_inode),
path: cumulative_path.clone(),
entries: Default::default(),
}
@ -1457,41 +1523,57 @@ pub fn path_open(
.fs
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
} else {
let file_metadata = wasi_try!(file_path.metadata().map_err(|_| __WASI_ENOENT));
// if entry does not exist in parent directory, try to lazily
// load it; possibly creating or truncating it if flags set
let real_opened_file = {
let mut open_options = std::fs::OpenOptions::new();
let open_options = open_options.read(true).write(true);
let open_options = if o_flags & __WASI_O_CREAT != 0 {
debug!(
"File {} may be created when opened if it does not exist",
&path_string
);
open_options.create(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
debug!("File {} will be truncated when opened", &path_string);
open_options.truncate(true)
} else {
open_options
};
let real_open_file =
wasi_try!(open_options.open(&file_path).map_err(|_| __WASI_EIO));
debug!("Opening host file {}", &path_string);
let kind = if file_metadata.is_dir() {
// special dir logic
Kind::Dir {
parent: Some(cur_dir_inode),
path: file_path.clone(),
entries: Default::default(),
}
} else {
// file is not a dir
let real_opened_file = {
let mut open_options = std::fs::OpenOptions::new();
let open_options = open_options.read(true).write(true);
let open_options = if o_flags & __WASI_O_CREAT != 0 {
debug!(
"File {:?} may be created when opened if it does not exist",
&file_path
);
open_options.create(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
debug!("File {:?} will be truncated when opened", &file_path);
open_options.truncate(true)
} else {
open_options
};
debug!("Opening host file {:?}", &file_path);
let real_open_file = wasi_try!(open_options.open(&file_path).map_err(|e| {
dbg!(e);
__WASI_EIO
}));
real_open_file
real_open_file
};
Kind::File {
handle: WasiFile::HostFile(real_opened_file),
}
};
// record lazily loaded or newly created fd
let new_inode = state.fs.inodes.insert(InodeVal {
stat: __wasi_filestat_t::default(),
is_preopened: false,
name: file_name.clone(),
kind: Kind::File {
handle: WasiFile::HostFile(real_opened_file),
},
kind,
});
// reborrow to insert entry
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind {
entries.insert(file_name.clone(), new_inode);

View File

@ -43,6 +43,31 @@ pub struct __wasi_dirent_t {
pub d_type: __wasi_filetype_t,
}
unsafe impl ValueType for __wasi_dirent_t {}
pub fn dirent_to_le_bytes(ent: &__wasi_dirent_t) -> Vec<u8> {
use std::mem::transmute;
let mut out = Vec::with_capacity(std::mem::size_of::<__wasi_dirent_t>());
let bytes: [u8; 8] = unsafe { transmute(ent.d_next.to_le()) };
for &b in &bytes {
out.push(b);
}
let bytes: [u8; 8] = unsafe { transmute(ent.d_ino.to_le()) };
for &b in &bytes {
out.push(b);
}
let bytes: [u8; 4] = unsafe { transmute(ent.d_namlen.to_le()) };
for &b in &bytes {
out.push(b);
}
out.push(ent.d_type);
out.push(0);
out.push(0);
out.push(0);
assert_eq!(out.len(), std::mem::size_of::<__wasi_dirent_t>());
out
}
pub type __wasi_errno_t = u16;
pub const __WASI_ESUCCESS: u16 = 0;
pub const __WASI_E2BIG: u16 = 1;

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#[cfg(windows)]
mod exception_handling;

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
extern crate structopt;

View File

@ -1,4 +1,4 @@
#![deny(unused_imports, unused_variables)]
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#[macro_use]
extern crate wasmer_runtime_core;