mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-14 06:35:40 +00:00
Merge branch 'master' into feature/more-wasi-syscalls
This commit is contained in:
commit
09acf3e581
@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file.
|
||||
Blocks of changes will separated by version increments.
|
||||
|
||||
## **[Unreleased]**
|
||||
- [#609](https://github.com/wasmerio/wasmer/issues/609) Update dependencies
|
||||
|
||||
## 0.6.0 - 2019-07-31
|
||||
- [#603](https://github.com/wasmerio/wasmer/pull/603) Update Wapm-cli, bump version numbers
|
||||
|
578
Cargo.lock
generated
578
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -19,11 +19,10 @@ include = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.3.1"
|
||||
byteorder = "1.3.2"
|
||||
errno = "0.2.4"
|
||||
structopt = "0.2.11"
|
||||
structopt = "0.2.18"
|
||||
wabt = "0.9.0"
|
||||
hashbrown = "0.1.8"
|
||||
wasmer-clif-backend = { path = "lib/clif-backend" }
|
||||
wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true }
|
||||
wasmer-middleware-common = { path = "lib/middleware-common" }
|
||||
@ -62,7 +61,7 @@ members = [
|
||||
|
||||
[build-dependencies]
|
||||
wabt = "0.9.0"
|
||||
glob = "0.2.11"
|
||||
glob = "0.3.0"
|
||||
rustc_version = "0.2.3"
|
||||
|
||||
[features]
|
||||
|
2
Makefile
2
Makefile
@ -84,7 +84,7 @@ singlepass: spectests-singlepass emtests-singlepass middleware-singlepass wasite
|
||||
cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests-cranelift
|
||||
cargo test -p wasmer-clif-backend --release
|
||||
|
||||
llvm: spectests-llvm emtests-llvm middleware-llvm wasitests-llvm
|
||||
llvm: spectests-llvm emtests-llvm wasitests-llvm
|
||||
cargo test -p wasmer-llvm-backend --release
|
||||
|
||||
|
||||
|
@ -15,26 +15,26 @@ cranelift-codegen = { version = "0.31" }
|
||||
cranelift-entity = { version = "0.31" }
|
||||
cranelift-frontend = { package = "wasmer-clif-fork-frontend", version = "0.33" }
|
||||
cranelift-wasm = { package = "wasmer-clif-fork-wasm", version = "0.33" }
|
||||
hashbrown = "0.1"
|
||||
target-lexicon = "0.4.0"
|
||||
wasmparser = "0.35.1"
|
||||
byteorder = "1"
|
||||
nix = "0.14.0"
|
||||
libc = "0.2.49"
|
||||
rayon = "1.0"
|
||||
byteorder = "1.3.2"
|
||||
nix = "0.14.1"
|
||||
libc = "0.2.60"
|
||||
rayon = "1.1.0"
|
||||
|
||||
# Dependencies for caching.
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
version = "1.0.98"
|
||||
features = ["rc"]
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
version = "1.0.98"
|
||||
[dependencies.serde_bytes]
|
||||
version = "0.10"
|
||||
version = "0.11.1"
|
||||
[dependencies.serde-bench]
|
||||
version = "0.0.7"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
winapi = { version = "0.3.7", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.6.0" }
|
||||
|
||||
[features]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::relocation::{ExternalRelocation, TrapSink};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use wasmer_runtime_core::{
|
||||
backend::{sys::Memory, CacheGen},
|
||||
|
@ -89,10 +89,12 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
|
||||
func,
|
||||
func_translator,
|
||||
next_local: 0,
|
||||
clif_signatures: self.clif_signatures.clone(),
|
||||
module_info: Arc::clone(&module_info),
|
||||
target_config: self.isa.frontend_config().clone(),
|
||||
position: Position::default(),
|
||||
func_env: FunctionEnvironment {
|
||||
module_info: Arc::clone(&module_info),
|
||||
target_config: self.isa.frontend_config().clone(),
|
||||
clif_signatures: self.clif_signatures.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
debug_assert_eq!(func_env.func.dfg.num_ebbs(), 0, "Function must be empty");
|
||||
@ -391,10 +393,8 @@ pub struct CraneliftFunctionCodeGenerator {
|
||||
func: Function,
|
||||
func_translator: FuncTranslator,
|
||||
next_local: usize,
|
||||
pub clif_signatures: Map<SigIndex, ir::Signature>,
|
||||
module_info: Arc<RwLock<ModuleInfo>>,
|
||||
target_config: isa::TargetFrontendConfig,
|
||||
position: Position,
|
||||
func_env: FunctionEnvironment,
|
||||
}
|
||||
|
||||
pub struct FunctionEnvironment {
|
||||
@ -1138,11 +1138,6 @@ impl FunctionCodeGenerator<CodegenError> for CraneliftFunctionCodeGenerator {
|
||||
//let builder = self.builder.as_mut().unwrap();
|
||||
//let func_environment = FuncEnv::new();
|
||||
//let state = TranslationState::new();
|
||||
let mut function_environment = FunctionEnvironment {
|
||||
module_info: Arc::clone(&self.module_info),
|
||||
target_config: self.target_config.clone(),
|
||||
clif_signatures: self.clif_signatures.clone(),
|
||||
};
|
||||
|
||||
if self.func_translator.state.control_stack.is_empty() {
|
||||
return Ok(());
|
||||
@ -1154,7 +1149,7 @@ impl FunctionCodeGenerator<CodegenError> for CraneliftFunctionCodeGenerator {
|
||||
&mut self.position,
|
||||
);
|
||||
let state = &mut self.func_translator.state;
|
||||
translate_operator(op, &mut builder, state, &mut function_environment)?;
|
||||
translate_operator(op, &mut builder, state, &mut self.func_env)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
mod cache;
|
||||
mod code;
|
||||
mod libcalls;
|
||||
|
@ -5,7 +5,7 @@ use cranelift_codegen::{
|
||||
ir::{self, InstBuilder},
|
||||
isa, Context,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
use std::{iter, mem, ptr::NonNull};
|
||||
use wasmer_runtime_core::{
|
||||
backend::sys::{Memory, Protect},
|
||||
|
@ -8,4 +8,4 @@ edition = "2018"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.49"
|
||||
libc = "0.2.60"
|
||||
|
@ -20,7 +20,7 @@ wabt = "0.9.0"
|
||||
wasmer-dev-utils = { path = "../dev-utils", version = "0.6.0"}
|
||||
|
||||
[build-dependencies]
|
||||
glob = "0.2.11"
|
||||
glob = "0.3.0"
|
||||
|
||||
[features]
|
||||
clif = []
|
||||
|
@ -8,15 +8,14 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1"
|
||||
hashbrown = "0.1"
|
||||
lazy_static = "1.2.0"
|
||||
libc = "0.2.49"
|
||||
time = "0.1.41"
|
||||
byteorder = "1.3.2"
|
||||
lazy_static = "1.3.0"
|
||||
libc = "0.2.60"
|
||||
time = "0.1.42"
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
rand = "0.6"
|
||||
rand = "0.7.0"
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
||||
|
1
lib/emscripten/src/env/mod.rs
vendored
1
lib/emscripten/src/env/mod.rs
vendored
@ -30,6 +30,7 @@ pub fn call_malloc(ctx: &mut Ctx, size: u32) -> u32 {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[warn(dead_code)]
|
||||
pub fn call_malloc_with_cast<T: Copy, Ty>(ctx: &mut Ctx, size: u32) -> WasmPtr<T, Ty> {
|
||||
WasmPtr::new(call_malloc(ctx, size))
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#[macro_use]
|
||||
extern crate wasmer_runtime_core;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::{f64, ffi::c_void};
|
||||
use wasmer_runtime_core::{
|
||||
@ -25,11 +30,11 @@ use wasmer_runtime_core::{
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
use ::libc::DIR as libcDIR;
|
||||
use ::libc::DIR as LibcDir;
|
||||
|
||||
// We use a placeholder for windows
|
||||
#[cfg(not(unix))]
|
||||
type libcDIR = u8;
|
||||
type LibcDir = u8;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
@ -93,7 +98,7 @@ pub struct EmscriptenData<'a> {
|
||||
pub memset: Option<Func<'a, (u32, u32, u32), u32>>,
|
||||
pub stack_alloc: Option<Func<'a, u32, u32>>,
|
||||
pub jumps: Vec<UnsafeCell<[u32; 27]>>,
|
||||
pub opened_dirs: HashMap<i32, Box<*mut libcDIR>>,
|
||||
pub opened_dirs: HashMap<i32, Box<*mut LibcDir>>,
|
||||
|
||||
pub dyn_call_i: Option<Func<'a, i32, i32>>,
|
||||
pub dyn_call_ii: Option<Func<'a, (i32, i32), i32>>,
|
||||
|
@ -5,5 +5,5 @@ authors = ["Heyang Zhou <zhy20000919@hotmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.49"
|
||||
libc = "0.2.60"
|
||||
wasmer-runtime-core = { path = "../runtime-core" }
|
||||
|
@ -8,12 +8,11 @@ readme = "README.md"
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
wasmparser = "0.35.1"
|
||||
hashbrown = "0.1.8"
|
||||
smallvec = "0.6.8"
|
||||
goblin = "0.0.20"
|
||||
libc = "0.2.49"
|
||||
nix = "0.14.0"
|
||||
capstone = { version = "0.5.0", optional = true }
|
||||
smallvec = "0.6.10"
|
||||
goblin = "0.0.24"
|
||||
libc = "0.2.60"
|
||||
nix = "0.14.1"
|
||||
capstone = { version = "0.6.0", optional = true }
|
||||
|
||||
[dependencies.inkwell]
|
||||
git = "https://github.com/wasmerio/inkwell"
|
||||
@ -22,12 +21,12 @@ default-features = false
|
||||
features = ["llvm8-0", "target-x86"]
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["memoryapi"] }
|
||||
winapi = { version = "0.3.7", features = ["memoryapi"] }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
lazy_static = "1.2.0"
|
||||
regex = "1.1.0"
|
||||
lazy_static = "1.3.0"
|
||||
regex = "1.2.0"
|
||||
semver = "0.9"
|
||||
rustc_version = "0.2.3"
|
||||
|
||||
|
@ -7,201 +7,214 @@ extern "C" void __deregister_frame(uint8_t *);
|
||||
|
||||
struct MemoryManager : llvm::RuntimeDyld::MemoryManager {
|
||||
public:
|
||||
MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {}
|
||||
MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {}
|
||||
|
||||
virtual ~MemoryManager() override {
|
||||
deregisterEHFrames();
|
||||
// Deallocate all of the allocated memory.
|
||||
callbacks.dealloc_memory(code_section.base, code_section.size);
|
||||
callbacks.dealloc_memory(read_section.base, read_section.size);
|
||||
callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size);
|
||||
virtual ~MemoryManager() override {
|
||||
deregisterEHFrames();
|
||||
// Deallocate all of the allocated memory.
|
||||
callbacks.dealloc_memory(code_section.base, code_section.size);
|
||||
callbacks.dealloc_memory(read_section.base, read_section.size);
|
||||
callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size);
|
||||
}
|
||||
|
||||
virtual uint8_t *allocateCodeSection(uintptr_t size, unsigned alignment,
|
||||
unsigned section_id,
|
||||
llvm::StringRef section_name) override {
|
||||
return allocate_bump(code_section, code_bump_ptr, size, alignment);
|
||||
}
|
||||
|
||||
virtual uint8_t *allocateDataSection(uintptr_t size, unsigned alignment,
|
||||
unsigned section_id,
|
||||
llvm::StringRef section_name,
|
||||
bool read_only) override {
|
||||
// Allocate from the read-only section or the read-write section, depending
|
||||
// on if this allocation should be read-only or not.
|
||||
if (read_only) {
|
||||
return allocate_bump(read_section, read_bump_ptr, size, alignment);
|
||||
} else {
|
||||
return allocate_bump(readwrite_section, readwrite_bump_ptr, size,
|
||||
alignment);
|
||||
}
|
||||
}
|
||||
|
||||
virtual uint8_t* allocateCodeSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name) override {
|
||||
return allocate_bump(code_section, code_bump_ptr, size, alignment);
|
||||
}
|
||||
|
||||
virtual uint8_t* allocateDataSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name, bool read_only) override {
|
||||
// Allocate from the read-only section or the read-write section, depending on if this allocation
|
||||
// should be read-only or not.
|
||||
if (read_only) {
|
||||
return allocate_bump(read_section, read_bump_ptr, size, alignment);
|
||||
} else {
|
||||
return allocate_bump(readwrite_section, readwrite_bump_ptr, size, alignment);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void reserveAllocationSpace(
|
||||
uintptr_t code_size,
|
||||
uint32_t code_align,
|
||||
uintptr_t read_data_size,
|
||||
uint32_t read_data_align,
|
||||
uintptr_t read_write_data_size,
|
||||
uint32_t read_write_data_align
|
||||
) override {
|
||||
auto aligner = [](uintptr_t ptr, size_t align) {
|
||||
if (ptr == 0) {
|
||||
return align;
|
||||
}
|
||||
return (ptr + align - 1) & ~(align - 1);
|
||||
};
|
||||
|
||||
|
||||
uint8_t *code_ptr_out = nullptr;
|
||||
size_t code_size_out = 0;
|
||||
auto code_result = callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE, &code_ptr_out, &code_size_out);
|
||||
assert(code_result == RESULT_OK);
|
||||
code_section = Section { code_ptr_out, code_size_out };
|
||||
code_bump_ptr = (uintptr_t)code_ptr_out;
|
||||
|
||||
uint8_t *read_ptr_out = nullptr;
|
||||
size_t read_size_out = 0;
|
||||
auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096), PROTECT_READ_WRITE, &read_ptr_out, &read_size_out);
|
||||
assert(read_result == RESULT_OK);
|
||||
read_section = Section { read_ptr_out, read_size_out };
|
||||
read_bump_ptr = (uintptr_t)read_ptr_out;
|
||||
|
||||
uint8_t *readwrite_ptr_out = nullptr;
|
||||
size_t readwrite_size_out = 0;
|
||||
auto readwrite_result = callbacks.alloc_memory(aligner(read_write_data_size, 4096), PROTECT_READ_WRITE, &readwrite_ptr_out, &readwrite_size_out);
|
||||
assert(readwrite_result == RESULT_OK);
|
||||
readwrite_section = Section { readwrite_ptr_out, readwrite_size_out };
|
||||
readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out;
|
||||
}
|
||||
|
||||
/* Turn on the `reserveAllocationSpace` callback. */
|
||||
virtual bool needsToReserveAllocationSpace() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void registerEHFrames(uint8_t* addr, uint64_t LoadAddr, size_t size) override {
|
||||
// We don't know yet how to do this on Windows, so we hide this on compilation
|
||||
// so we can compile and pass spectests on unix systems
|
||||
#ifndef _WIN32
|
||||
eh_frame_ptr = addr;
|
||||
eh_frame_size = size;
|
||||
eh_frames_registered = true;
|
||||
callbacks.visit_fde(addr, size, __register_frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void deregisterEHFrames() override {
|
||||
// We don't know yet how to do this on Windows, so we hide this on compilation
|
||||
// so we can compile and pass spectests on unix systems
|
||||
#ifndef _WIN32
|
||||
if (eh_frames_registered) {
|
||||
callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override {
|
||||
auto code_result = callbacks.protect_memory(code_section.base, code_section.size, mem_protect_t::PROTECT_READ_EXECUTE);
|
||||
if (code_result != RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto read_result = callbacks.protect_memory(read_section.base, read_section.size, mem_protect_t::PROTECT_READ);
|
||||
if (read_result != RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The readwrite section is already mapped as read-write.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld, const llvm::object::ObjectFile &Obj) override {}
|
||||
private:
|
||||
struct Section {
|
||||
uint8_t* base;
|
||||
size_t size;
|
||||
virtual void reserveAllocationSpace(uintptr_t code_size, uint32_t code_align,
|
||||
uintptr_t read_data_size,
|
||||
uint32_t read_data_align,
|
||||
uintptr_t read_write_data_size,
|
||||
uint32_t read_write_data_align) override {
|
||||
auto aligner = [](uintptr_t ptr, size_t align) {
|
||||
if (ptr == 0) {
|
||||
return align;
|
||||
}
|
||||
return (ptr + align - 1) & ~(align - 1);
|
||||
};
|
||||
|
||||
uint8_t* allocate_bump(Section& section, uintptr_t& bump_ptr, size_t size, size_t align) {
|
||||
auto aligner = [](uintptr_t& ptr, size_t align) {
|
||||
ptr = (ptr + align - 1) & ~(align - 1);
|
||||
};
|
||||
uint8_t *code_ptr_out = nullptr;
|
||||
size_t code_size_out = 0;
|
||||
auto code_result =
|
||||
callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE,
|
||||
&code_ptr_out, &code_size_out);
|
||||
assert(code_result == RESULT_OK);
|
||||
code_section = Section{code_ptr_out, code_size_out};
|
||||
code_bump_ptr = (uintptr_t)code_ptr_out;
|
||||
|
||||
// Align the bump pointer to the requires alignment.
|
||||
aligner(bump_ptr, align);
|
||||
uint8_t *read_ptr_out = nullptr;
|
||||
size_t read_size_out = 0;
|
||||
auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096),
|
||||
PROTECT_READ_WRITE, &read_ptr_out,
|
||||
&read_size_out);
|
||||
assert(read_result == RESULT_OK);
|
||||
read_section = Section{read_ptr_out, read_size_out};
|
||||
read_bump_ptr = (uintptr_t)read_ptr_out;
|
||||
|
||||
auto ret_ptr = bump_ptr;
|
||||
bump_ptr += size;
|
||||
uint8_t *readwrite_ptr_out = nullptr;
|
||||
size_t readwrite_size_out = 0;
|
||||
auto readwrite_result = callbacks.alloc_memory(
|
||||
aligner(read_write_data_size, 4096), PROTECT_READ_WRITE,
|
||||
&readwrite_ptr_out, &readwrite_size_out);
|
||||
assert(readwrite_result == RESULT_OK);
|
||||
readwrite_section = Section{readwrite_ptr_out, readwrite_size_out};
|
||||
readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out;
|
||||
}
|
||||
|
||||
assert(bump_ptr <= (uintptr_t)section.base + section.size);
|
||||
/* Turn on the `reserveAllocationSpace` callback. */
|
||||
virtual bool needsToReserveAllocationSpace() override { return true; }
|
||||
|
||||
return (uint8_t*)ret_ptr;
|
||||
virtual void registerEHFrames(uint8_t *addr, uint64_t LoadAddr,
|
||||
size_t size) override {
|
||||
// We don't know yet how to do this on Windows, so we hide this on compilation
|
||||
// so we can compile and pass spectests on unix systems
|
||||
#ifndef _WIN32
|
||||
eh_frame_ptr = addr;
|
||||
eh_frame_size = size;
|
||||
eh_frames_registered = true;
|
||||
callbacks.visit_fde(addr, size, __register_frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void deregisterEHFrames() override {
|
||||
// We don't know yet how to do this on Windows, so we hide this on compilation
|
||||
// so we can compile and pass spectests on unix systems
|
||||
#ifndef _WIN32
|
||||
if (eh_frames_registered) {
|
||||
callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override {
|
||||
auto code_result =
|
||||
callbacks.protect_memory(code_section.base, code_section.size,
|
||||
mem_protect_t::PROTECT_READ_EXECUTE);
|
||||
if (code_result != RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Section code_section, read_section, readwrite_section;
|
||||
uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr;
|
||||
uint8_t* eh_frame_ptr;
|
||||
size_t eh_frame_size;
|
||||
bool eh_frames_registered = false;
|
||||
auto read_result = callbacks.protect_memory(
|
||||
read_section.base, read_section.size, mem_protect_t::PROTECT_READ);
|
||||
if (read_result != RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
callbacks_t callbacks;
|
||||
// The readwrite section is already mapped as read-write.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void
|
||||
notifyObjectLoaded(llvm::RuntimeDyld &RTDyld,
|
||||
const llvm::object::ObjectFile &Obj) override {}
|
||||
|
||||
private:
|
||||
struct Section {
|
||||
uint8_t *base;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
uint8_t *allocate_bump(Section §ion, uintptr_t &bump_ptr, size_t size,
|
||||
size_t align) {
|
||||
auto aligner = [](uintptr_t &ptr, size_t align) {
|
||||
ptr = (ptr + align - 1) & ~(align - 1);
|
||||
};
|
||||
|
||||
// Align the bump pointer to the requires alignment.
|
||||
aligner(bump_ptr, align);
|
||||
|
||||
auto ret_ptr = bump_ptr;
|
||||
bump_ptr += size;
|
||||
|
||||
assert(bump_ptr <= (uintptr_t)section.base + section.size);
|
||||
|
||||
return (uint8_t *)ret_ptr;
|
||||
}
|
||||
|
||||
Section code_section, read_section, readwrite_section;
|
||||
uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr;
|
||||
uint8_t *eh_frame_ptr;
|
||||
size_t eh_frame_size;
|
||||
bool eh_frames_registered = false;
|
||||
|
||||
callbacks_t callbacks;
|
||||
};
|
||||
|
||||
struct SymbolLookup : llvm::JITSymbolResolver {
|
||||
public:
|
||||
SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {}
|
||||
SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {}
|
||||
|
||||
void lookup(const LookupSet& symbols, OnResolvedFunction OnResolved) {
|
||||
LookupResult result;
|
||||
void lookup(const LookupSet &symbols, OnResolvedFunction OnResolved) {
|
||||
LookupResult result;
|
||||
|
||||
for (auto symbol : symbols) {
|
||||
result.emplace(symbol, symbol_lookup(symbol));
|
||||
}
|
||||
|
||||
OnResolved(result);
|
||||
for (auto symbol : symbols) {
|
||||
result.emplace(symbol, symbol_lookup(symbol));
|
||||
}
|
||||
|
||||
llvm::Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) {
|
||||
const std::set<llvm::StringRef> empty;
|
||||
return empty;
|
||||
}
|
||||
OnResolved(result);
|
||||
}
|
||||
|
||||
llvm::Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) {
|
||||
const std::set<llvm::StringRef> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) {
|
||||
uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size());
|
||||
llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) {
|
||||
uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size());
|
||||
|
||||
return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None);
|
||||
}
|
||||
return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None);
|
||||
}
|
||||
|
||||
callbacks_t callbacks;
|
||||
callbacks_t callbacks;
|
||||
};
|
||||
|
||||
WasmModule::WasmModule(
|
||||
const uint8_t *object_start,
|
||||
size_t object_size,
|
||||
callbacks_t callbacks
|
||||
) : memory_manager(std::unique_ptr<MemoryManager>(new MemoryManager(callbacks)))
|
||||
{
|
||||
WasmModule::WasmModule(const uint8_t *object_start, size_t object_size,
|
||||
callbacks_t callbacks)
|
||||
: memory_manager(
|
||||
std::unique_ptr<MemoryManager>(new MemoryManager(callbacks))) {
|
||||
|
||||
if (auto created_object_file =
|
||||
llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef(
|
||||
llvm::StringRef((const char *)object_start, object_size),
|
||||
"object"))) {
|
||||
object_file = cantFail(std::move(created_object_file));
|
||||
SymbolLookup symbol_resolver(callbacks);
|
||||
runtime_dyld = std::unique_ptr<llvm::RuntimeDyld>(
|
||||
new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
|
||||
|
||||
if (auto created_object_file = llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef(
|
||||
llvm::StringRef((const char *)object_start, object_size), "object"
|
||||
))) {
|
||||
object_file = cantFail(std::move(created_object_file));
|
||||
SymbolLookup symbol_resolver(callbacks);
|
||||
runtime_dyld = std::unique_ptr<llvm::RuntimeDyld>(new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
|
||||
runtime_dyld->setProcessAllSections(true);
|
||||
|
||||
runtime_dyld->setProcessAllSections(true);
|
||||
runtime_dyld->loadObject(*object_file);
|
||||
runtime_dyld->finalizeWithMemoryManagerLocking();
|
||||
|
||||
runtime_dyld->loadObject(*object_file);
|
||||
runtime_dyld->finalizeWithMemoryManagerLocking();
|
||||
|
||||
if (runtime_dyld->hasError()) {
|
||||
_init_failed = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_init_failed = true;
|
||||
if (runtime_dyld->hasError()) {
|
||||
_init_failed = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_init_failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void* WasmModule::get_func(llvm::StringRef name) const {
|
||||
auto symbol = runtime_dyld->getSymbol(name);
|
||||
return (void*)symbol.getAddress();
|
||||
void *WasmModule::get_func(llvm::StringRef name) const {
|
||||
auto symbol = runtime_dyld->getSymbol(name);
|
||||
return (void *)symbol.getAddress();
|
||||
}
|
||||
|
@ -1,236 +1,220 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <llvm/ExecutionEngine/RuntimeDyld.h>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PROTECT_NONE,
|
||||
PROTECT_READ,
|
||||
PROTECT_READ_WRITE,
|
||||
PROTECT_READ_EXECUTE,
|
||||
#include <llvm/ExecutionEngine/RuntimeDyld.h>
|
||||
|
||||
typedef enum {
|
||||
PROTECT_NONE,
|
||||
PROTECT_READ,
|
||||
PROTECT_READ_WRITE,
|
||||
PROTECT_READ_EXECUTE,
|
||||
} mem_protect_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RESULT_OK,
|
||||
RESULT_ALLOCATE_FAILURE,
|
||||
RESULT_PROTECT_FAILURE,
|
||||
RESULT_DEALLOC_FAILURE,
|
||||
RESULT_OBJECT_LOAD_FAILURE,
|
||||
typedef enum {
|
||||
RESULT_OK,
|
||||
RESULT_ALLOCATE_FAILURE,
|
||||
RESULT_PROTECT_FAILURE,
|
||||
RESULT_DEALLOC_FAILURE,
|
||||
RESULT_OBJECT_LOAD_FAILURE,
|
||||
} result_t;
|
||||
|
||||
typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect, uint8_t **ptr_out, size_t *size_out);
|
||||
typedef result_t (*protect_memory_t)(uint8_t *ptr, size_t size, mem_protect_t protect);
|
||||
typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect,
|
||||
uint8_t **ptr_out, size_t *size_out);
|
||||
typedef result_t (*protect_memory_t)(uint8_t *ptr, size_t size,
|
||||
mem_protect_t protect);
|
||||
typedef result_t (*dealloc_memory_t)(uint8_t *ptr, size_t size);
|
||||
typedef uintptr_t (*lookup_vm_symbol_t)(const char *name_ptr, size_t length);
|
||||
typedef void (*fde_visitor_t)(uint8_t *fde);
|
||||
typedef result_t (*visit_fde_t)(uint8_t *fde, size_t size, fde_visitor_t visitor);
|
||||
typedef result_t (*visit_fde_t)(uint8_t *fde, size_t size,
|
||||
fde_visitor_t visitor);
|
||||
|
||||
typedef void (*trampoline_t)(void *, void *, void *, void *);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Memory management. */
|
||||
alloc_memory_t alloc_memory;
|
||||
protect_memory_t protect_memory;
|
||||
dealloc_memory_t dealloc_memory;
|
||||
typedef struct {
|
||||
/* Memory management. */
|
||||
alloc_memory_t alloc_memory;
|
||||
protect_memory_t protect_memory;
|
||||
dealloc_memory_t dealloc_memory;
|
||||
|
||||
lookup_vm_symbol_t lookup_vm_symbol;
|
||||
lookup_vm_symbol_t lookup_vm_symbol;
|
||||
|
||||
visit_fde_t visit_fde;
|
||||
visit_fde_t visit_fde;
|
||||
} callbacks_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t data, vtable;
|
||||
typedef struct {
|
||||
size_t data, vtable;
|
||||
} box_any_t;
|
||||
|
||||
struct WasmException
|
||||
{
|
||||
public:
|
||||
virtual std::string description() const noexcept = 0;
|
||||
struct WasmException {
|
||||
public:
|
||||
virtual std::string description() const noexcept = 0;
|
||||
};
|
||||
|
||||
struct UncatchableException : WasmException
|
||||
{
|
||||
public:
|
||||
virtual std::string description() const noexcept override
|
||||
{
|
||||
return "Uncatchable exception";
|
||||
}
|
||||
struct UncatchableException : WasmException {
|
||||
public:
|
||||
virtual std::string description() const noexcept override {
|
||||
return "Uncatchable exception";
|
||||
}
|
||||
};
|
||||
|
||||
struct UserException : UncatchableException
|
||||
{
|
||||
public:
|
||||
UserException(size_t data, size_t vtable) : error_data({ data, vtable }) {}
|
||||
struct UserException : UncatchableException {
|
||||
public:
|
||||
UserException(size_t data, size_t vtable) : error_data({data, vtable}) {}
|
||||
|
||||
virtual std::string description() const noexcept override
|
||||
{
|
||||
return "user exception";
|
||||
}
|
||||
virtual std::string description() const noexcept override {
|
||||
return "user exception";
|
||||
}
|
||||
|
||||
// The parts of a `Box<dyn Any>`.
|
||||
box_any_t error_data;
|
||||
// The parts of a `Box<dyn Any>`.
|
||||
box_any_t error_data;
|
||||
};
|
||||
|
||||
struct WasmTrap : UncatchableException
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Unreachable = 0,
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
MemoryOutOfBounds = 2,
|
||||
CallIndirectOOB = 3,
|
||||
IllegalArithmetic = 4,
|
||||
Unknown,
|
||||
};
|
||||
struct BreakpointException : UncatchableException {
|
||||
public:
|
||||
BreakpointException(uintptr_t callback) : callback(callback) {}
|
||||
|
||||
WasmTrap(Type type) : type(type) {}
|
||||
virtual std::string description() const noexcept override {
|
||||
return "breakpoint exception";
|
||||
}
|
||||
|
||||
virtual std::string description() const noexcept override
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss
|
||||
<< "WebAssembly trap:" << '\n'
|
||||
<< " - type: " << type << '\n';
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Type type;
|
||||
|
||||
private:
|
||||
friend std::ostream &operator<<(std::ostream &out, const Type &ty)
|
||||
{
|
||||
switch (ty)
|
||||
{
|
||||
case Type::Unreachable:
|
||||
out << "unreachable";
|
||||
break;
|
||||
case Type::IncorrectCallIndirectSignature:
|
||||
out << "incorrect call_indirect signature";
|
||||
break;
|
||||
case Type::MemoryOutOfBounds:
|
||||
out << "memory access out-of-bounds";
|
||||
break;
|
||||
case Type::CallIndirectOOB:
|
||||
out << "call_indirect out-of-bounds";
|
||||
break;
|
||||
case Type::IllegalArithmetic:
|
||||
out << "illegal arithmetic operation";
|
||||
break;
|
||||
case Type::Unknown:
|
||||
default:
|
||||
out << "unknown";
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
uintptr_t callback;
|
||||
};
|
||||
|
||||
struct CatchableException : WasmException
|
||||
{
|
||||
public:
|
||||
CatchableException(uint32_t type_id, uint32_t value_num) : type_id(type_id), value_num(value_num) {}
|
||||
struct WasmTrap : UncatchableException {
|
||||
public:
|
||||
enum Type {
|
||||
Unreachable = 0,
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
MemoryOutOfBounds = 2,
|
||||
CallIndirectOOB = 3,
|
||||
IllegalArithmetic = 4,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
virtual std::string description() const noexcept override
|
||||
{
|
||||
return "catchable exception";
|
||||
WasmTrap(Type type) : type(type) {}
|
||||
|
||||
virtual std::string description() const noexcept override {
|
||||
std::ostringstream ss;
|
||||
ss << "WebAssembly trap:" << '\n' << " - type: " << type << '\n';
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Type type;
|
||||
|
||||
private:
|
||||
friend std::ostream &operator<<(std::ostream &out, const Type &ty) {
|
||||
switch (ty) {
|
||||
case Type::Unreachable:
|
||||
out << "unreachable";
|
||||
break;
|
||||
case Type::IncorrectCallIndirectSignature:
|
||||
out << "incorrect call_indirect signature";
|
||||
break;
|
||||
case Type::MemoryOutOfBounds:
|
||||
out << "memory access out-of-bounds";
|
||||
break;
|
||||
case Type::CallIndirectOOB:
|
||||
out << "call_indirect out-of-bounds";
|
||||
break;
|
||||
case Type::IllegalArithmetic:
|
||||
out << "illegal arithmetic operation";
|
||||
break;
|
||||
case Type::Unknown:
|
||||
default:
|
||||
out << "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t type_id, value_num;
|
||||
uint64_t values[1];
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
struct WasmModule
|
||||
{
|
||||
public:
|
||||
WasmModule(
|
||||
const uint8_t *object_start,
|
||||
size_t object_size,
|
||||
callbacks_t callbacks);
|
||||
struct CatchableException : WasmException {
|
||||
public:
|
||||
CatchableException(uint32_t type_id, uint32_t value_num)
|
||||
: type_id(type_id), value_num(value_num) {}
|
||||
|
||||
void *get_func(llvm::StringRef name) const;
|
||||
virtual std::string description() const noexcept override {
|
||||
return "catchable exception";
|
||||
}
|
||||
|
||||
bool _init_failed = false;
|
||||
private:
|
||||
std::unique_ptr<llvm::RuntimeDyld::MemoryManager> memory_manager;
|
||||
std::unique_ptr<llvm::object::ObjectFile> object_file;
|
||||
std::unique_ptr<llvm::RuntimeDyld> runtime_dyld;
|
||||
uint32_t type_id, value_num;
|
||||
uint64_t values[1];
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
result_t module_load(const uint8_t *mem_ptr, size_t mem_size, callbacks_t callbacks, WasmModule **module_out)
|
||||
{
|
||||
*module_out = new WasmModule(mem_ptr, mem_size, callbacks);
|
||||
struct WasmModule {
|
||||
public:
|
||||
WasmModule(const uint8_t *object_start, size_t object_size,
|
||||
callbacks_t callbacks);
|
||||
|
||||
if ((*module_out)->_init_failed) {
|
||||
return RESULT_OBJECT_LOAD_FAILURE;
|
||||
}
|
||||
void *get_func(llvm::StringRef name) const;
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
bool _init_failed = false;
|
||||
|
||||
[[noreturn]] void throw_trap(WasmTrap::Type ty) {
|
||||
throw WasmTrap(ty);
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<llvm::RuntimeDyld::MemoryManager> memory_manager;
|
||||
std::unique_ptr<llvm::object::ObjectFile> object_file;
|
||||
std::unique_ptr<llvm::RuntimeDyld> runtime_dyld;
|
||||
};
|
||||
|
||||
void module_delete(WasmModule *module)
|
||||
{
|
||||
delete module;
|
||||
}
|
||||
extern "C" {
|
||||
void callback_trampoline(void *, void *);
|
||||
|
||||
// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust
|
||||
// side.
|
||||
[[noreturn]] void throw_any(size_t data, size_t vtable) {
|
||||
throw UserException(data, vtable);
|
||||
}
|
||||
result_t module_load(const uint8_t *mem_ptr, size_t mem_size,
|
||||
callbacks_t callbacks, WasmModule **module_out) {
|
||||
*module_out = new WasmModule(mem_ptr, mem_size, callbacks);
|
||||
|
||||
bool invoke_trampoline(
|
||||
trampoline_t trampoline,
|
||||
void *ctx,
|
||||
void *func,
|
||||
void *params,
|
||||
void *results,
|
||||
WasmTrap::Type *trap_out,
|
||||
box_any_t *user_error,
|
||||
void *invoke_env) throw()
|
||||
{
|
||||
try
|
||||
{
|
||||
trampoline(ctx, func, params, results);
|
||||
return true;
|
||||
}
|
||||
catch (const WasmTrap &e)
|
||||
{
|
||||
*trap_out = e.type;
|
||||
return false;
|
||||
}
|
||||
catch (const UserException &e)
|
||||
{
|
||||
*user_error = e.error_data;
|
||||
return false;
|
||||
}
|
||||
catch (const WasmException &e)
|
||||
{
|
||||
*trap_out = WasmTrap::Type::Unknown;
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
*trap_out = WasmTrap::Type::Unknown;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((*module_out)->_init_failed) {
|
||||
return RESULT_OBJECT_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
void *get_func_symbol(WasmModule *module, const char *name)
|
||||
{
|
||||
return module->get_func(llvm::StringRef(name));
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
[[noreturn]] void throw_trap(WasmTrap::Type ty) { throw WasmTrap(ty); }
|
||||
|
||||
void module_delete(WasmModule *module) { delete module; }
|
||||
|
||||
// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust
|
||||
// side.
|
||||
[[noreturn]] void throw_any(size_t data, size_t vtable) {
|
||||
throw UserException(data, vtable);
|
||||
}
|
||||
|
||||
// Throw a pointer that's assumed to be codegen::BreakpointHandler on the
|
||||
// rust side.
|
||||
[[noreturn]] void throw_breakpoint(uintptr_t callback) {
|
||||
throw BreakpointException(callback);
|
||||
}
|
||||
|
||||
bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
|
||||
void *params, void *results, WasmTrap::Type *trap_out,
|
||||
box_any_t *user_error, void *invoke_env) noexcept {
|
||||
try {
|
||||
trampoline(ctx, func, params, results);
|
||||
return true;
|
||||
} catch (const WasmTrap &e) {
|
||||
*trap_out = e.type;
|
||||
return false;
|
||||
} catch (const UserException &e) {
|
||||
*user_error = e.error_data;
|
||||
return false;
|
||||
} catch (const BreakpointException &e) {
|
||||
callback_trampoline(user_error, (void *)e.callback);
|
||||
return false;
|
||||
} catch (const WasmException &e) {
|
||||
*trap_out = WasmTrap::Type::Unknown;
|
||||
return false;
|
||||
} catch (...) {
|
||||
*trap_out = WasmTrap::Type::Unknown;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void *get_func_symbol(WasmModule *module, const char *name) {
|
||||
return module->get_func(llvm::StringRef(name));
|
||||
}
|
||||
}
|
@ -39,7 +39,8 @@ extern "C" {
|
||||
fn module_delete(module: *mut LLVMModule);
|
||||
fn get_func_symbol(module: *mut LLVMModule, name: *const c_char) -> *const vm::Func;
|
||||
|
||||
fn throw_trap(ty: i32);
|
||||
fn throw_trap(ty: i32) -> !;
|
||||
fn throw_breakpoint(ty: i64) -> !;
|
||||
|
||||
/// This should be the same as spliting up the fat pointer into two arguments,
|
||||
/// but this is cleaner, I think?
|
||||
@ -103,6 +104,7 @@ fn get_callbacks() -> Callbacks {
|
||||
fn_name!("vm.memory.size.static.local") => vmcalls::local_static_memory_size as _,
|
||||
|
||||
fn_name!("vm.exception.trap") => throw_trap as _,
|
||||
fn_name!("vm.breakpoint") => throw_breakpoint as _,
|
||||
|
||||
_ => ptr::null(),
|
||||
}
|
||||
|
@ -496,6 +496,21 @@ pub struct CodegenError {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
// This is only called by C++ code, the 'pub' + '#[no_mangle]' combination
|
||||
// prevents unused function elimination.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn callback_trampoline(
|
||||
b: *mut Option<Box<dyn std::any::Any>>,
|
||||
callback: *mut BreakpointHandler,
|
||||
) {
|
||||
let callback = Box::from_raw(callback);
|
||||
let result: Result<(), Box<dyn std::any::Any>> = callback(BreakpointInfo { fault: None });
|
||||
match result {
|
||||
Ok(()) => *b = None,
|
||||
Err(e) => *b = Some(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LLVMModuleCodeGenerator {
|
||||
context: Option<Context>,
|
||||
builder: Option<Builder>,
|
||||
@ -612,7 +627,14 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
|
||||
InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {
|
||||
return Ok(());
|
||||
}
|
||||
InternalEvent::Breakpoint(_callback) => {
|
||||
InternalEvent::Breakpoint(callback) => {
|
||||
let raw = Box::into_raw(Box::new(callback)) as u64;
|
||||
let callback = intrinsics.i64_ty.const_int(raw, false);
|
||||
builder.build_call(
|
||||
intrinsics.throw_breakpoint,
|
||||
&[callback.as_basic_value_enum()],
|
||||
"",
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
InternalEvent::GetInternal(idx) => {
|
||||
|
@ -1,4 +1,3 @@
|
||||
use hashbrown::HashMap;
|
||||
use inkwell::{
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
@ -9,6 +8,7 @@ use inkwell::{
|
||||
values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use wasmer_runtime_core::{
|
||||
memory::MemoryType,
|
||||
@ -147,6 +147,7 @@ pub struct Intrinsics {
|
||||
pub memory_size_shared_import: FunctionValue,
|
||||
|
||||
pub throw_trap: FunctionValue,
|
||||
pub throw_breakpoint: FunctionValue,
|
||||
|
||||
pub ctx_ptr_ty: PointerType,
|
||||
}
|
||||
@ -309,7 +310,6 @@ impl Intrinsics {
|
||||
i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false);
|
||||
|
||||
let ret_i1_take_i1_i1 = i1_ty.fn_type(&[i1_ty_basic, i1_ty_basic], false);
|
||||
|
||||
Self {
|
||||
ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None),
|
||||
ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None),
|
||||
@ -525,6 +525,11 @@ impl Intrinsics {
|
||||
void_ty.fn_type(&[i32_ty_basic], false),
|
||||
None,
|
||||
),
|
||||
throw_breakpoint: module.add_function(
|
||||
"vm.breakpoint",
|
||||
void_ty.fn_type(&[i64_ty_basic], false),
|
||||
None,
|
||||
),
|
||||
ctx_ptr_ty,
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#![cfg_attr(nightly, feature(unwind_attributes))]
|
||||
|
||||
mod backend;
|
||||
|
@ -1,4 +1,9 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
pub mod call_trace;
|
||||
pub mod metering;
|
||||
|
@ -129,7 +129,7 @@ pub fn set_points_used_ctx(ctx: &mut Ctx, value: u64) {
|
||||
ctx.set_internal(&INTERNAL_FIELD, value);
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "singlepass"))]
|
||||
#[cfg(all(test, any(feature = "singlepass", feature = "llvm")))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use wabt::wat2wasm;
|
||||
@ -247,7 +247,7 @@ mod tests {
|
||||
// verify it returns the correct value
|
||||
assert_eq!(value, 7);
|
||||
|
||||
// verify is uses the correct number of points
|
||||
// verify it used the correct number of points
|
||||
assert_eq!(get_points_used(&instance), 74);
|
||||
}
|
||||
|
||||
@ -276,7 +276,7 @@ mod tests {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// verify is uses the correct number of points
|
||||
// verify it used the correct number of points
|
||||
assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking.
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,11 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.50"
|
||||
libc = "0.2.60"
|
||||
wasmer-runtime-core = { path = "../runtime-core" }
|
||||
hashbrown = "0.1"
|
||||
failure = "0.1"
|
||||
tar = "0.4"
|
||||
wasmparser = "0.34.0"
|
||||
wasmparser = "0.35.1"
|
||||
zstd = "0.4"
|
||||
|
||||
# [target.'cfg(unix)'.dependencies.zbox]
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(dead_code, unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::vfs::file_like::FileLike;
|
||||
use crate::vfs::vfs_header::{header_from_bytes, ArchiveType, CompressionType};
|
||||
use crate::vfs::virtual_file::VirtualFile;
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
|
@ -12,7 +12,7 @@ readme = "README.md"
|
||||
crate-type = ["cdylib", "rlib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
libc = "0.2.60"
|
||||
|
||||
[dependencies.wasmer-runtime]
|
||||
path = "../runtime"
|
||||
@ -27,4 +27,4 @@ debug = ["wasmer-runtime/debug"]
|
||||
llvm = ["wasmer-runtime/llvm"]
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.8"
|
||||
cbindgen = "0.9.0"
|
||||
|
@ -10,9 +10,10 @@ use crate::{
|
||||
};
|
||||
use libc::c_uint;
|
||||
use std::{ffi::c_void, ptr, slice, sync::Arc};
|
||||
use wasmer_runtime::Module;
|
||||
use wasmer_runtime::{Global, Memory, Module, Table};
|
||||
use wasmer_runtime_core::{
|
||||
export::{Context, Export, FuncPointer},
|
||||
import::ImportObject,
|
||||
module::ImportName,
|
||||
types::{FuncSig, Type},
|
||||
};
|
||||
@ -25,6 +26,9 @@ pub struct wasmer_import_t {
|
||||
pub value: wasmer_import_export_value,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmer_import_object_t;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_func_t;
|
||||
@ -37,6 +41,83 @@ pub struct wasmer_import_descriptor_t;
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_descriptors_t;
|
||||
|
||||
/// Creates a new empty import object.
|
||||
/// See also `wasmer_import_object_append`
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_new() -> *mut wasmer_import_object_t {
|
||||
let import_object = Box::new(ImportObject::new());
|
||||
|
||||
Box::into_raw(import_object) as *mut wasmer_import_object_t
|
||||
}
|
||||
|
||||
/// Extends an existing import object with new imports
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_import_object_extend(
|
||||
import_object: *mut wasmer_import_object_t,
|
||||
imports: *mut wasmer_import_t,
|
||||
imports_len: c_uint,
|
||||
) -> wasmer_result_t {
|
||||
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
|
||||
|
||||
let mut extensions: Vec<(String, String, Export)> = Vec::new();
|
||||
|
||||
let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
|
||||
for import in imports {
|
||||
let module_name = slice::from_raw_parts(
|
||||
import.module_name.bytes,
|
||||
import.module_name.bytes_len as usize,
|
||||
);
|
||||
let module_name = if let Ok(s) = std::str::from_utf8(module_name) {
|
||||
s
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: "error converting module name to string".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
let import_name = slice::from_raw_parts(
|
||||
import.import_name.bytes,
|
||||
import.import_name.bytes_len as usize,
|
||||
);
|
||||
let import_name = if let Ok(s) = std::str::from_utf8(import_name) {
|
||||
s
|
||||
} else {
|
||||
update_last_error(CApiError {
|
||||
msg: "error converting import_name to string".to_string(),
|
||||
});
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
};
|
||||
|
||||
let export = match import.tag {
|
||||
wasmer_import_export_kind::WASM_MEMORY => {
|
||||
let mem = import.value.memory as *mut Memory;
|
||||
Export::Memory((&*mem).clone())
|
||||
}
|
||||
wasmer_import_export_kind::WASM_FUNCTION => {
|
||||
let func_export = import.value.func as *mut Export;
|
||||
(&*func_export).clone()
|
||||
}
|
||||
wasmer_import_export_kind::WASM_GLOBAL => {
|
||||
let global = import.value.global as *mut Global;
|
||||
Export::Global((&*global).clone())
|
||||
}
|
||||
wasmer_import_export_kind::WASM_TABLE => {
|
||||
let table = import.value.table as *mut Table;
|
||||
Export::Table((&*table).clone())
|
||||
}
|
||||
};
|
||||
|
||||
let extension = (module_name.to_string(), import_name.to_string(), export);
|
||||
extensions.push(extension)
|
||||
}
|
||||
|
||||
import_object.extend(extensions);
|
||||
|
||||
return wasmer_result_t::WASMER_OK;
|
||||
}
|
||||
|
||||
/// Gets import descriptors for the given module
|
||||
///
|
||||
/// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it.
|
||||
@ -352,6 +433,14 @@ pub extern "C" fn wasmer_import_func_destroy(func: *mut wasmer_import_func_t) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees memory of the given ImportObject
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmer_import_object_destroy(import_object: *mut wasmer_import_object_t) {
|
||||
if !import_object.is_null() {
|
||||
unsafe { Box::from_raw(import_object as *mut ImportObject) };
|
||||
}
|
||||
}
|
||||
|
||||
struct NamedImportDescriptor {
|
||||
module: String,
|
||||
name: String,
|
||||
|
@ -3,15 +3,19 @@
|
||||
use crate::{
|
||||
error::{update_last_error, CApiError},
|
||||
export::{wasmer_exports_t, wasmer_import_export_kind, NamedExport, NamedExports},
|
||||
import::wasmer_import_t,
|
||||
import::{wasmer_import_object_t, wasmer_import_t},
|
||||
memory::wasmer_memory_t,
|
||||
module::wasmer_module_t,
|
||||
value::{wasmer_value, wasmer_value_t, wasmer_value_tag},
|
||||
wasmer_result_t,
|
||||
};
|
||||
use libc::{c_char, c_int, c_void};
|
||||
use std::{collections::HashMap, ffi::CStr, slice};
|
||||
use wasmer_runtime::{Ctx, Global, ImportObject, Instance, Memory, Table, Value};
|
||||
use wasmer_runtime_core::{export::Export, import::Namespace};
|
||||
use wasmer_runtime::{Ctx, Global, Instance, Memory, Module, Table, Value};
|
||||
use wasmer_runtime_core::{
|
||||
export::Export,
|
||||
import::{ImportObject, Namespace},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmer_instance_t;
|
||||
@ -108,6 +112,45 @@ pub unsafe extern "C" fn wasmer_instantiate(
|
||||
wasmer_result_t::WASMER_OK
|
||||
}
|
||||
|
||||
/// Given:
|
||||
/// * A prepared `wasmer` import-object
|
||||
/// * A compiled wasmer module
|
||||
///
|
||||
/// Instantiates a wasmer instance
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_module_import_instantiate(
|
||||
instance: *mut *mut wasmer_instance_t,
|
||||
module: *const wasmer_module_t,
|
||||
import_object: *const wasmer_import_object_t,
|
||||
) -> wasmer_result_t {
|
||||
let import_object: &ImportObject = &*(import_object as *const ImportObject);
|
||||
let module: &Module = &*(module as *const Module);
|
||||
|
||||
let new_instance: Instance = match module.instantiate(import_object) {
|
||||
Ok(instance) => instance,
|
||||
Err(error) => {
|
||||
update_last_error(error);
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
};
|
||||
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
|
||||
|
||||
return wasmer_result_t::WASMER_OK;
|
||||
}
|
||||
|
||||
/// Extracts the instance's context and returns it.
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmer_instance_context_get(
|
||||
instance: *mut wasmer_instance_t,
|
||||
) -> *const wasmer_instance_context_t {
|
||||
let instance_ref = &*(instance as *const Instance);
|
||||
|
||||
let ctx: *const Ctx = instance_ref.context() as *const _;
|
||||
|
||||
ctx as *const wasmer_instance_context_t
|
||||
}
|
||||
|
||||
/// Calls an instances exported function by `name` with the provided parameters.
|
||||
/// Results are set using the provided `results` pointer.
|
||||
///
|
||||
|
@ -80,8 +80,13 @@
|
||||
//!
|
||||
//! [wasmer_h]: ./wasmer.h
|
||||
//! [wasmer_hh]: ./wasmer.hh
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
extern crate wasmer_runtime;
|
||||
extern crate wasmer_runtime_core;
|
||||
|
||||
|
2
lib/runtime-c-api/tests/.gitignore
vendored
2
lib/runtime-c-api/tests/.gitignore
vendored
@ -23,3 +23,5 @@ test-module-imports
|
||||
test-module-serialize
|
||||
test-tables
|
||||
test-validate
|
||||
test-context
|
||||
test-module-import-instantiate
|
||||
|
@ -14,6 +14,8 @@ add_executable(test-module-imports test-module-imports.c)
|
||||
add_executable(test-module-serialize test-module-serialize.c)
|
||||
add_executable(test-tables test-tables.c)
|
||||
add_executable(test-validate test-validate.c)
|
||||
add_executable(test-context test-context.c)
|
||||
add_executable(test-module-import-instantiate test-module-import-instantiate.c)
|
||||
|
||||
find_library(
|
||||
WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll
|
||||
@ -87,3 +89,11 @@ add_test(test-tables test-tables)
|
||||
target_link_libraries(test-validate general ${WASMER_LIB})
|
||||
target_compile_options(test-validate PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-validate test-validate)
|
||||
|
||||
target_link_libraries(test-context general ${WASMER_LIB})
|
||||
target_compile_options(test-context PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-context test-context)
|
||||
|
||||
target_link_libraries(test-module-import-instantiate general ${WASMER_LIB})
|
||||
target_compile_options(test-module-import-instantiate PRIVATE ${COMPILER_OPTIONS})
|
||||
add_test(test-module-import-instantiate test-module-import-instantiate)
|
||||
|
BIN
lib/runtime-c-api/tests/assets/inc.wasm
Normal file
BIN
lib/runtime-c-api/tests/assets/inc.wasm
Normal file
Binary file not shown.
12
lib/runtime-c-api/tests/assets/inc.wast
Normal file
12
lib/runtime-c-api/tests/assets/inc.wast
Normal file
@ -0,0 +1,12 @@
|
||||
(module
|
||||
(func $inc (import "env" "inc"))
|
||||
(func $mul (import "env" "mul"))
|
||||
(func $get (import "env" "get") (result i32))
|
||||
|
||||
(func (export "inc_and_get") (result i32)
|
||||
call $inc
|
||||
call $get)
|
||||
|
||||
(func (export "mul_and_get") (result i32)
|
||||
call $mul
|
||||
call $get))
|
@ -4,19 +4,17 @@ use std::process::Command;
|
||||
fn test_c_api() {
|
||||
let project_tests_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests");
|
||||
|
||||
run_command("cmake", project_tests_dir, Some("."));
|
||||
run_command("make", project_tests_dir, Some("-Wdev -Werror=dev"));
|
||||
run_command("make", project_tests_dir, Some("test"));
|
||||
run_command("cmake", project_tests_dir, vec!["."]);
|
||||
run_command("make", project_tests_dir, vec!["-Wdev", "-Werror=dev"]);
|
||||
run_command("make", project_tests_dir, vec!["test", "ARGS=\"-V\""]);
|
||||
}
|
||||
|
||||
fn run_command(command_str: &str, dir: &str, arg: Option<&str>) {
|
||||
println!("Running command: `{}` arg: {:?}", command_str, arg);
|
||||
fn run_command(command_str: &str, dir: &str, args: Vec<&str>) {
|
||||
println!("Running command: `{}` args: {:?}", command_str, args);
|
||||
|
||||
let mut command = Command::new(command_str);
|
||||
|
||||
if let Some(a) = arg {
|
||||
command.arg(a);
|
||||
}
|
||||
command.args(&args);
|
||||
|
||||
command.current_dir(dir);
|
||||
|
||||
|
137
lib/runtime-c-api/tests/test-context.c
Normal file
137
lib/runtime-c-api/tests/test-context.c
Normal file
@ -0,0 +1,137 @@
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
int32_t amount;
|
||||
int32_t value;
|
||||
} counter_data;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* bytes;
|
||||
long bytes_len;
|
||||
} wasm_file_t;
|
||||
|
||||
wasm_file_t read_wasm_file(const char* file_name) {
|
||||
wasm_file_t wasm_file;
|
||||
|
||||
FILE *file = fopen(file_name, "r");
|
||||
fseek(file, 0, SEEK_END);
|
||||
wasm_file.bytes_len = ftell(file);
|
||||
|
||||
wasm_file.bytes = malloc(wasm_file.bytes_len);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
|
||||
fclose(file);
|
||||
|
||||
return wasm_file;
|
||||
}
|
||||
|
||||
void inc_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
data->value = data->value + data->amount;
|
||||
}
|
||||
|
||||
void mul_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
data->value = data->value * data->amount;
|
||||
}
|
||||
|
||||
int32_t get_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
return data->value;
|
||||
}
|
||||
|
||||
counter_data *init_counter(int32_t value, int32_t amount) {
|
||||
counter_data* counter = malloc(sizeof(counter_data));
|
||||
counter->value = value;
|
||||
counter->amount = amount;
|
||||
return counter;
|
||||
}
|
||||
|
||||
void assert_counter(wasmer_instance_t *instance, int32_t expected) {
|
||||
wasmer_value_t result_one;
|
||||
wasmer_value_t params[] = {};
|
||||
wasmer_value_t results[] = {result_one};
|
||||
|
||||
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call1_result);
|
||||
printf("Result: %d\n", results[0].value.I32);
|
||||
assert(results[0].value.I32 == expected);
|
||||
assert(call1_result == WASMER_OK);
|
||||
|
||||
const wasmer_instance_context_t *ctx = wasmer_instance_context_get(instance);
|
||||
counter_data *cd = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
assert(cd->value == expected);
|
||||
}
|
||||
|
||||
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
|
||||
wasmer_import_t import;
|
||||
wasmer_byte_array module_name_bytes;
|
||||
wasmer_byte_array import_name_bytes;
|
||||
|
||||
module_name_bytes.bytes = (const uint8_t *) module_name;
|
||||
module_name_bytes.bytes_len = strlen(module_name);
|
||||
|
||||
import_name_bytes.bytes = (const uint8_t *) import_name;
|
||||
import_name_bytes.bytes_len = strlen(import_name);
|
||||
|
||||
import.module_name = module_name_bytes;
|
||||
import.import_name = import_name_bytes;
|
||||
|
||||
import.tag = WASM_FUNCTION;
|
||||
import.value.func = func;
|
||||
|
||||
return import;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Prepare Imports
|
||||
wasmer_value_tag inc_params_sig[] = {};
|
||||
wasmer_value_tag inc_returns_sig[] = {};
|
||||
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
|
||||
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
|
||||
|
||||
wasmer_value_tag mul_params_sig[] = {};
|
||||
wasmer_value_tag mul_returns_sig[] = {};
|
||||
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
|
||||
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
|
||||
|
||||
wasmer_value_tag get_params_sig[] = {};
|
||||
wasmer_value_tag get_returns_sig[] = {WASM_I32};
|
||||
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
|
||||
wasmer_import_t get_import = create_import("env", "get", get_func);
|
||||
|
||||
wasmer_import_t imports[] = {inc_import, mul_import, get_import};
|
||||
|
||||
// Read the wasm file
|
||||
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
|
||||
|
||||
// Instantiate instance
|
||||
printf("Instantiating\n");
|
||||
wasmer_instance_t *instance = NULL;
|
||||
wasmer_result_t instantiate_res = wasmer_instantiate(&instance, wasm_file.bytes, wasm_file.bytes_len, imports, 3);
|
||||
printf("Compile result: %d\n", instantiate_res);
|
||||
assert(instantiate_res == WASMER_OK);
|
||||
|
||||
// Init counter
|
||||
counter_data *counter = init_counter(2, 5);
|
||||
wasmer_instance_context_data_set(instance, counter);
|
||||
|
||||
// Run `instance.inc_and_get` and assert
|
||||
assert_counter(instance, 7);
|
||||
assert_counter(instance, 12);
|
||||
assert_counter(instance, 17);
|
||||
|
||||
// Clear resources
|
||||
wasmer_import_func_destroy(inc_func);
|
||||
wasmer_import_func_destroy(get_func);
|
||||
wasmer_instance_destroy(instance);
|
||||
free(counter);
|
||||
free(wasm_file.bytes);
|
||||
|
||||
return 0;
|
||||
}
|
@ -42,8 +42,9 @@ int main()
|
||||
assert(export_length == 5);
|
||||
|
||||
// Get the `memory` export.
|
||||
wasmer_export_t *export = wasmer_exports_get(exports, 1);
|
||||
wasmer_export_t *export = wasmer_exports_get(exports, 0);
|
||||
wasmer_import_export_kind kind = wasmer_export_kind(export);
|
||||
printf("Wasmer import export kind: %d (Memory is %d)\n", kind, WASM_MEMORY);
|
||||
assert(kind == WASM_MEMORY);
|
||||
|
||||
wasmer_byte_array export_name = wasmer_export_name(export);
|
||||
|
149
lib/runtime-c-api/tests/test-module-import-instantiate.c
Normal file
149
lib/runtime-c-api/tests/test-module-import-instantiate.c
Normal file
@ -0,0 +1,149 @@
|
||||
#include <stdio.h>
|
||||
#include "../wasmer.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
int32_t amount;
|
||||
int32_t value;
|
||||
} counter_data;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* bytes;
|
||||
long bytes_len;
|
||||
} wasm_file_t;
|
||||
|
||||
wasm_file_t read_wasm_file(const char* file_name) {
|
||||
wasm_file_t wasm_file;
|
||||
|
||||
FILE *file = fopen(file_name, "r");
|
||||
fseek(file, 0, SEEK_END);
|
||||
wasm_file.bytes_len = ftell(file);
|
||||
|
||||
wasm_file.bytes = malloc(wasm_file.bytes_len);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
|
||||
fclose(file);
|
||||
|
||||
return wasm_file;
|
||||
}
|
||||
|
||||
void inc_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
data->value = data->value + data->amount;
|
||||
}
|
||||
|
||||
void mul_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
data->value = data->value * data->amount;
|
||||
}
|
||||
|
||||
int32_t get_counter(wasmer_instance_context_t *ctx) {
|
||||
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
|
||||
return data->value;
|
||||
}
|
||||
|
||||
counter_data *init_counter(int32_t value, int32_t amount) {
|
||||
counter_data* counter = malloc(sizeof(counter_data));
|
||||
counter->value = value;
|
||||
counter->amount = amount;
|
||||
return counter;
|
||||
}
|
||||
|
||||
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
|
||||
wasmer_import_t import;
|
||||
wasmer_byte_array module_name_bytes;
|
||||
wasmer_byte_array import_name_bytes;
|
||||
|
||||
module_name_bytes.bytes = (const uint8_t *) module_name;
|
||||
module_name_bytes.bytes_len = strlen(module_name);
|
||||
|
||||
import_name_bytes.bytes = (const uint8_t *) import_name;
|
||||
import_name_bytes.bytes_len = strlen(import_name);
|
||||
|
||||
import.module_name = module_name_bytes;
|
||||
import.import_name = import_name_bytes;
|
||||
|
||||
import.tag = WASM_FUNCTION;
|
||||
import.value.func = func;
|
||||
|
||||
return import;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Prepare Imports
|
||||
wasmer_value_tag inc_params_sig[] = {};
|
||||
wasmer_value_tag inc_returns_sig[] = {};
|
||||
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
|
||||
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
|
||||
|
||||
wasmer_value_tag mul_params_sig[] = {};
|
||||
wasmer_value_tag mul_returns_sig[] = {};
|
||||
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
|
||||
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
|
||||
|
||||
wasmer_value_tag get_params_sig[] = {};
|
||||
wasmer_value_tag get_returns_sig[] = {WASM_I32};
|
||||
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
|
||||
wasmer_import_t get_import = create_import("env", "get", get_func);
|
||||
|
||||
// Read the wasm file
|
||||
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
|
||||
|
||||
// Compile module
|
||||
wasmer_module_t *module = NULL;
|
||||
wasmer_result_t compile_res = wasmer_compile(&module, wasm_file.bytes, wasm_file.bytes_len);
|
||||
assert(compile_res == WASMER_OK);
|
||||
|
||||
// Prepare Import Object
|
||||
wasmer_import_object_t *import_object = wasmer_import_object_new();
|
||||
|
||||
// First, we import `inc_counter` and `mul_counter`
|
||||
wasmer_import_t imports[] = {inc_import, mul_import};
|
||||
wasmer_result_t extend_res = wasmer_import_object_extend(import_object, imports, 2);
|
||||
assert(extend_res == WASMER_OK);
|
||||
|
||||
// Now, we'll import `inc_counter` and `mul_counter`
|
||||
wasmer_import_t more_imports[] = {get_import};
|
||||
wasmer_result_t extend_res2 = wasmer_import_object_extend(import_object, more_imports, 1);
|
||||
assert(extend_res2 == WASMER_OK);
|
||||
|
||||
// Same `wasmer_import_object_extend` as the first, doesn't affect anything
|
||||
wasmer_result_t extend_res3 = wasmer_import_object_extend(import_object, imports, 2);
|
||||
assert(extend_res3 == WASMER_OK);
|
||||
|
||||
// Instantiate instance
|
||||
printf("Instantiating\n");
|
||||
wasmer_instance_t *instance = NULL;
|
||||
wasmer_result_t instantiate_res = wasmer_module_import_instantiate(&instance, module, import_object);
|
||||
printf("Compile result: %d\n", instantiate_res);
|
||||
assert(instantiate_res == WASMER_OK);
|
||||
|
||||
// Init counter
|
||||
counter_data *counter = init_counter(2, 5);
|
||||
wasmer_instance_context_data_set(instance, counter);
|
||||
|
||||
wasmer_value_t result_one;
|
||||
wasmer_value_t params[] = {};
|
||||
wasmer_value_t results[] = {result_one};
|
||||
|
||||
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call1_result);
|
||||
printf("Result: %d\n", results[0].value.I32);
|
||||
|
||||
wasmer_result_t call2_result = wasmer_instance_call(instance, "mul_and_get", params, 0, results, 1);
|
||||
printf("Call result: %d\n", call2_result);
|
||||
printf("Result: %d\n", results[0].value.I32);
|
||||
|
||||
// Clear resources
|
||||
wasmer_import_func_destroy(inc_func);
|
||||
wasmer_import_func_destroy(mul_func);
|
||||
wasmer_import_func_destroy(get_func);
|
||||
wasmer_instance_destroy(instance);
|
||||
free(counter);
|
||||
free(wasm_file.bytes);
|
||||
|
||||
return 0;
|
||||
}
|
@ -95,11 +95,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_context_t;
|
||||
} wasmer_import_object_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -119,6 +115,14 @@ typedef struct {
|
||||
wasmer_import_export_value value;
|
||||
} wasmer_import_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
} wasmer_instance_context_t;
|
||||
|
||||
typedef struct {
|
||||
bool has_some;
|
||||
uint32_t some;
|
||||
@ -392,6 +396,24 @@ wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
|
||||
wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func,
|
||||
uint32_t *result);
|
||||
|
||||
/**
|
||||
* Frees memory of the given ImportObject
|
||||
*/
|
||||
void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
|
||||
|
||||
/**
|
||||
* Extends an existing import object with new imports
|
||||
*/
|
||||
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
|
||||
wasmer_import_t *imports,
|
||||
unsigned int imports_len);
|
||||
|
||||
/**
|
||||
* Creates a new empty import object.
|
||||
* See also `wasmer_import_object_append`
|
||||
*/
|
||||
wasmer_import_object_t *wasmer_import_object_new(void);
|
||||
|
||||
/**
|
||||
* Calls an instances exported function by `name` with the provided parameters.
|
||||
* Results are set using the provided `results` pointer.
|
||||
@ -417,6 +439,11 @@ void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx);
|
||||
*/
|
||||
void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr);
|
||||
|
||||
/**
|
||||
* Extracts the instance's context and returns it.
|
||||
*/
|
||||
const wasmer_instance_context_t *wasmer_instance_context_get(wasmer_instance_t *instance);
|
||||
|
||||
/**
|
||||
* Gets the memory within the context at the index `memory_idx`.
|
||||
* The index is always 0 until multiple memories are supported.
|
||||
@ -526,6 +553,16 @@ wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module,
|
||||
*/
|
||||
void wasmer_module_destroy(wasmer_module_t *module);
|
||||
|
||||
/**
|
||||
* Given:
|
||||
* A prepared `wasmer svm` import-object
|
||||
* A compiled wasmer module
|
||||
* Instantiates a wasmer instance
|
||||
*/
|
||||
wasmer_result_t wasmer_module_import_instantiate(wasmer_instance_t **instance,
|
||||
const wasmer_module_t *module,
|
||||
const wasmer_import_object_t *import_object);
|
||||
|
||||
/**
|
||||
* Creates a new Instance from the given module and imports.
|
||||
* Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
|
@ -91,11 +91,7 @@ struct wasmer_import_func_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_instance_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_instance_context_t {
|
||||
struct wasmer_import_object_t {
|
||||
|
||||
};
|
||||
|
||||
@ -117,6 +113,14 @@ struct wasmer_import_t {
|
||||
wasmer_import_export_value value;
|
||||
};
|
||||
|
||||
struct wasmer_instance_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_instance_context_t {
|
||||
|
||||
};
|
||||
|
||||
struct wasmer_limit_option_t {
|
||||
bool has_some;
|
||||
uint32_t some;
|
||||
@ -318,6 +322,18 @@ wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
|
||||
wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func,
|
||||
uint32_t *result);
|
||||
|
||||
/// Frees memory of the given ImportObject
|
||||
void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
|
||||
|
||||
/// Extends an existing import object with new imports
|
||||
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
|
||||
wasmer_import_t *imports,
|
||||
unsigned int imports_len);
|
||||
|
||||
/// Creates a new empty import object.
|
||||
/// See also `wasmer_import_object_append`
|
||||
wasmer_import_object_t *wasmer_import_object_new();
|
||||
|
||||
/// Calls an instances exported function by `name` with the provided parameters.
|
||||
/// Results are set using the provided `results` pointer.
|
||||
/// Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
@ -337,6 +353,9 @@ void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx);
|
||||
/// passed to all imported function for instance.
|
||||
void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr);
|
||||
|
||||
/// Extracts the instance's context and returns it.
|
||||
const wasmer_instance_context_t *wasmer_instance_context_get(wasmer_instance_t *instance);
|
||||
|
||||
/// Gets the memory within the context at the index `memory_idx`.
|
||||
/// The index is always 0 until multiple memories are supported.
|
||||
const wasmer_memory_t *wasmer_instance_context_memory(const wasmer_instance_context_t *ctx,
|
||||
@ -418,6 +437,14 @@ wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module,
|
||||
/// Frees memory for the given Module
|
||||
void wasmer_module_destroy(wasmer_module_t *module);
|
||||
|
||||
/// Given:
|
||||
/// A prepared `wasmer svm` import-object
|
||||
/// A compiled wasmer module
|
||||
/// Instantiates a wasmer instance
|
||||
wasmer_result_t wasmer_module_import_instantiate(wasmer_instance_t **instance,
|
||||
const wasmer_module_t *module,
|
||||
const wasmer_import_object_t *import_object);
|
||||
|
||||
/// Creates a new Instance from the given module and imports.
|
||||
/// Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
|
||||
|
@ -8,46 +8,46 @@ repository = "https://github.com/wasmerio/wasmer"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
nix = "0.14.0"
|
||||
nix = "0.14.1"
|
||||
page_size = "0.4.1"
|
||||
wasmparser = "0.35.1"
|
||||
parking_lot = "0.7.1"
|
||||
lazy_static = "1.2.0"
|
||||
indexmap = "1.0.2"
|
||||
parking_lot = "0.9.0"
|
||||
lazy_static = "1.3.0"
|
||||
errno = "0.2.4"
|
||||
libc = "0.2.49"
|
||||
libc = "0.2.60"
|
||||
hex = "0.3.2"
|
||||
smallvec = "0.6.9"
|
||||
smallvec = "0.6.10"
|
||||
bincode = "1.1"
|
||||
colored = "1.8"
|
||||
|
||||
[dependencies.indexmap]
|
||||
version = "1.0.2"
|
||||
features = ["serde-1"]
|
||||
|
||||
# Dependencies for caching.
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
version = "1.0.98"
|
||||
# This feature is required for serde to support serializing/deserializing reference counted pointers (e.g. Rc and Arc).
|
||||
features = ["rc"]
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
version = "1.0.98"
|
||||
[dependencies.serde_bytes]
|
||||
version = "0.10"
|
||||
version = "0.11.1"
|
||||
[dependencies.serde-bench]
|
||||
version = "0.0.7"
|
||||
[dependencies.blake2b_simd]
|
||||
version = "0.4.1"
|
||||
version = "0.5.5"
|
||||
[dependencies.digest]
|
||||
version = "0.8.0"
|
||||
[dependencies.hashbrown]
|
||||
version = "0.1"
|
||||
features = ["serde"]
|
||||
version = "0.8.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["memoryapi"] }
|
||||
winapi = { version = "0.3.7", features = ["memoryapi"] }
|
||||
|
||||
[dev-dependencies]
|
||||
field-offset = "0.1.1"
|
||||
|
||||
[build-dependencies]
|
||||
blake2b_simd = "0.4.1"
|
||||
blake2b_simd = "0.5.5"
|
||||
rustc_version = "0.2.3"
|
||||
cc = "1.0"
|
||||
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
};
|
||||
use std::{any::Any, ptr::NonNull};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub mod sys {
|
||||
pub use crate::sys::*;
|
||||
|
@ -146,7 +146,7 @@ pub fn validating_parser_config(features: &Features) -> wasmparser::ValidatingPa
|
||||
enable_bulk_memory: false,
|
||||
enable_multi_value: false,
|
||||
},
|
||||
mutable_global_imports: false,
|
||||
mutable_global_imports: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ impl std::fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
RuntimeError::Trap { ref msg } => {
|
||||
write!(f, "WebAssembly trap occured during runtime: {}", msg)
|
||||
write!(f, "WebAssembly trap occurred during runtime: {}", msg)
|
||||
}
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(s) = data.downcast_ref::<String>() {
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
global::Global, instance::InstanceInner, memory::Memory, module::ExportIndex,
|
||||
module::ModuleInner, table::Table, types::FuncSig, vm,
|
||||
};
|
||||
use hashbrown::hash_map;
|
||||
use indexmap::map::Iter as IndexMapIter;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@ -41,7 +41,7 @@ impl FuncPointer {
|
||||
|
||||
pub struct ExportIter<'a> {
|
||||
inner: &'a InstanceInner,
|
||||
iter: hash_map::Iter<'a, String, ExportIndex>,
|
||||
iter: IndexMapIter<'a, String, ExportIndex>,
|
||||
module: &'a ModuleInner,
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::export::Export;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
ffi::c_void,
|
||||
|
@ -1,4 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#![cfg_attr(nightly, feature(unwind_attributes))]
|
||||
|
||||
#[cfg(test)]
|
||||
@ -134,7 +140,7 @@ pub fn validate_and_report_errors_with_features(
|
||||
enable_reference_types: false,
|
||||
enable_threads: false,
|
||||
},
|
||||
mutable_global_imports: false,
|
||||
mutable_global_imports: true,
|
||||
};
|
||||
let mut parser = wasmparser::ValidatingParser::new(wasm, Some(config));
|
||||
loop {
|
||||
|
@ -14,8 +14,8 @@ use crate::{
|
||||
};
|
||||
|
||||
use crate::backend::CacheGen;
|
||||
use hashbrown::HashMap;
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This is used to instantiate a new WebAssembly module.
|
||||
@ -40,7 +40,7 @@ pub struct ModuleInfo {
|
||||
pub imported_tables: Map<ImportedTableIndex, (ImportName, TableDescriptor)>,
|
||||
pub imported_globals: Map<ImportedGlobalIndex, (ImportName, GlobalDescriptor)>,
|
||||
|
||||
pub exports: HashMap<String, ExportIndex>,
|
||||
pub exports: IndexMap<String, ExportIndex>,
|
||||
|
||||
pub data_initializers: Vec<DataInitializer>,
|
||||
pub elem_initializers: Vec<TableInitializer>,
|
||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||
},
|
||||
units::Pages,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use wasmparser::{
|
||||
|
@ -2,9 +2,9 @@ use crate::{
|
||||
structures::Map,
|
||||
types::{FuncSig, SigIndex},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
lazy_static! {
|
||||
|
@ -100,6 +100,7 @@ pub struct InstanceImage {
|
||||
}
|
||||
|
||||
impl ModuleStateMap {
|
||||
#[warn(dead_code)]
|
||||
fn lookup_call_ip(&self, ip: usize, base: usize) -> Option<(&FunctionStateMap, MachineState)> {
|
||||
if ip < base || ip - base >= self.total_size {
|
||||
None
|
||||
@ -123,6 +124,7 @@ impl ModuleStateMap {
|
||||
}
|
||||
}
|
||||
|
||||
#[warn(dead_code)]
|
||||
fn lookup_trappable_ip(
|
||||
&self,
|
||||
ip: usize,
|
||||
@ -150,6 +152,7 @@ impl ModuleStateMap {
|
||||
}
|
||||
}
|
||||
|
||||
#[warn(dead_code)]
|
||||
fn lookup_loop_ip(&self, ip: usize, base: usize) -> Option<(&FunctionStateMap, MachineState)> {
|
||||
if ip < base || ip - base >= self.total_size {
|
||||
None
|
||||
|
@ -16,7 +16,7 @@ use serde::{
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
|
||||
use serde_bytes::Bytes;
|
||||
use serde_bytes::{ByteBuf, Bytes};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
@ -56,7 +56,7 @@ impl<'de> Deserialize<'de> for Memory {
|
||||
.next_element()?
|
||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
||||
|
||||
let bytes: Bytes = seq
|
||||
let bytes: ByteBuf = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
|
||||
|
||||
|
@ -14,7 +14,7 @@ use std::{
|
||||
sync::Once,
|
||||
};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// The context of the currently running WebAssembly instance.
|
||||
///
|
||||
@ -850,8 +850,9 @@ mod vm_ctx_tests {
|
||||
use crate::cache::Error as CacheError;
|
||||
use crate::typed_func::Wasm;
|
||||
use crate::types::{LocalFuncIndex, SigIndex};
|
||||
use hashbrown::HashMap;
|
||||
use indexmap::IndexMap;
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::ptr::NonNull;
|
||||
struct Placeholder;
|
||||
impl RunnableModule for Placeholder {
|
||||
@ -890,7 +891,7 @@ mod vm_ctx_tests {
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
|
||||
exports: HashMap::new(),
|
||||
exports: IndexMap::new(),
|
||||
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
|
@ -10,7 +10,7 @@ readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true }
|
||||
lazy_static = "1.2.0"
|
||||
lazy_static = "1.3.0"
|
||||
memmap = "0.7.0"
|
||||
|
||||
[dependencies.wasmer-runtime-core]
|
||||
@ -23,7 +23,7 @@ version = "0.6.0"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0.7"
|
||||
tempfile = "3.1.0"
|
||||
criterion = "0.2"
|
||||
wabt = "0.9.0"
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
//! Wasmer-runtime is a library that makes embedding WebAssembly
|
||||
//! in your application easy, efficient, and safe.
|
||||
//!
|
||||
|
@ -13,10 +13,9 @@ wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
wasmparser = "0.35.1"
|
||||
dynasm = "0.3.2"
|
||||
dynasmrt = "0.3.1"
|
||||
lazy_static = "1.2.0"
|
||||
byteorder = "1"
|
||||
nix = "0.14.0"
|
||||
libc = "0.2.49"
|
||||
smallvec = "0.6.9"
|
||||
hashbrown = "0.1"
|
||||
lazy_static = "1.3.0"
|
||||
byteorder = "1.3.2"
|
||||
nix = "0.14.1"
|
||||
libc = "0.2.60"
|
||||
smallvec = "0.6.10"
|
||||
colored = "1.8"
|
||||
|
@ -1,4 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#![feature(proc_macro_hygiene)]
|
||||
|
||||
#[cfg(not(any(
|
||||
|
@ -132,7 +132,7 @@ fn get_compiler() -> impl Compiler {
|
||||
CraneliftCompiler::new()
|
||||
}
|
||||
|
||||
pub fn generate_imports() -> ImportObject {
|
||||
pub fn generate_imports(extra_imports: Vec<(String, Instance)>) -> ImportObject {
|
||||
let mut features = wabt::Features::new();
|
||||
features.enable_simd();
|
||||
let wasm_binary = wat2wasm_with_features(IMPORT_MODULE.as_bytes(), features).expect("WAST not valid or malformed");
|
||||
@ -143,6 +143,9 @@ pub fn generate_imports() -> ImportObject {
|
||||
.expect("WASM can't be instantiated");
|
||||
let mut imports = ImportObject::new();
|
||||
imports.register("spectest", instance);
|
||||
for (name, instance) in extra_imports {
|
||||
imports.register(name, instance);
|
||||
}
|
||||
imports
|
||||
}
|
||||
|
||||
@ -303,6 +306,8 @@ struct WastTestGenerator {
|
||||
script_parser: ScriptParser,
|
||||
module_calls: HashMap<i32, Vec<String>>,
|
||||
buffer: String,
|
||||
modules_by_name: HashMap<String, i32>,
|
||||
registered_modules: Vec<(i32, String)>,
|
||||
}
|
||||
|
||||
impl WastTestGenerator {
|
||||
@ -322,6 +327,8 @@ impl WastTestGenerator {
|
||||
script_parser: script,
|
||||
buffer: buffer,
|
||||
module_calls: HashMap::new(),
|
||||
modules_by_name: HashMap::new(),
|
||||
registered_modules: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,7 +399,7 @@ fn test_module_{}() {{
|
||||
self.module_calls.remove(&module);
|
||||
}
|
||||
|
||||
fn visit_module(&mut self, module: &ModuleBinary, _name: &Option<String>) {
|
||||
fn visit_module(&mut self, module: &ModuleBinary, name: &Option<String>) {
|
||||
let wasm_binary: Vec<u8> = module.clone().into_vec();
|
||||
let mut features = Features::new();
|
||||
features.enable_simd();
|
||||
@ -409,7 +416,9 @@ fn test_module_{}() {{
|
||||
println!(\"{{}}\", module_str);
|
||||
let wasm_binary = wat2wasm(module_str.as_bytes()).expect(\"WAST not valid or malformed\");
|
||||
let module = wasmer_runtime_core::compile_with_config(&wasm_binary[..], &get_compiler(), CompilerConfig {{ features: Features {{ simd: true }}, ..Default::default() }}).expect(\"WASM can't be compiled\");
|
||||
module.instantiate(&generate_imports()).expect(\"WASM can't be instantiated\")
|
||||
let mut extra_imports = Vec::new();
|
||||
{}
|
||||
module.instantiate(&generate_imports(extra_imports)).expect(\"WASM can't be instantiated\")
|
||||
}}\n",
|
||||
self.last_module,
|
||||
// We do this to ident four spaces, so it looks aligned to the function body
|
||||
@ -417,9 +426,22 @@ fn test_module_{}() {{
|
||||
.replace("\n", "\n ")
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\""),
|
||||
self.registered_modules.iter().map(|(number, name)| format!("extra_imports.push((String::from(\"{}\"), create_module_{}()));", name, number)).fold(String::from(""), |acc, x| format!("{}{}\n", acc, x)),
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
if let Some(name) = name {
|
||||
self.record_named_module(name);
|
||||
}
|
||||
}
|
||||
|
||||
fn record_named_module(&mut self, name: &String) {
|
||||
self.modules_by_name.insert(name.clone(), self.last_module);
|
||||
}
|
||||
|
||||
fn visit_register_module(&mut self, name: &String, as_name: &String) {
|
||||
self.registered_modules
|
||||
.push((*self.modules_by_name.get(name).unwrap(), as_name.clone()));
|
||||
}
|
||||
|
||||
fn visit_assert_invalid(&mut self, module: &ModuleBinary) {
|
||||
@ -763,11 +785,8 @@ fn {}() {{
|
||||
} => {
|
||||
// Do nothing for now
|
||||
}
|
||||
CommandKind::Register {
|
||||
name: _,
|
||||
as_name: _,
|
||||
} => {
|
||||
// Do nothing for now
|
||||
CommandKind::Register { name, as_name } => {
|
||||
self.visit_register_module(name.as_ref().unwrap(), as_name);
|
||||
}
|
||||
CommandKind::PerformAction(action) => {
|
||||
self.visit_perform_action(action);
|
||||
|
5
lib/spectests/spectests/README.md
vendored
5
lib/spectests/spectests/README.md
vendored
@ -25,7 +25,7 @@ Currently supported command assertions:
|
||||
- [x] `assert_malformed` _fully implemented_
|
||||
- [ ] `assert_uninstantiable` _not implemented yet_
|
||||
- [ ] `assert_exhaustion` _not implemented yet_
|
||||
- [ ] `register` _not implemented yet_
|
||||
- [x] `register` _fully implemented_
|
||||
- [x] `perform_action` _partially implemented, only function invocations for now_
|
||||
|
||||
### Covered spec tests
|
||||
@ -86,6 +86,7 @@ The following spec tests are currently covered:
|
||||
- [x] return.wast
|
||||
- [x] select.wast
|
||||
- [x] set_local.wast
|
||||
- [x] simd.wast
|
||||
- [ ] skip-stack-guard-page.wast
|
||||
- [x] stack.wast
|
||||
- [x] start.wast
|
||||
@ -108,8 +109,6 @@ The following spec tests are currently covered:
|
||||
|
||||
There are some cases that we decided to skip for now to accelerate the release schedule:
|
||||
|
||||
- `SKIP_MUTABLE_GLOBALS`: Right now the Wasm parser can't validate a module with imported/exported `mut` globals. We decided to skip the tests until Cranelift and wasmparser can handle this (see [original spec proposal](https://github.com/WebAssembly/mutable-global)). Spec tests affected:
|
||||
- `globals.wast`
|
||||
- `SKIP_CALL_INDIRECT_TYPE_MISMATCH`: we implemented traps in a fast way. We haven't yet covered the type mismatch on `call_indirect`. Specs affected:
|
||||
|
||||
- `call_indirect.wast`
|
||||
|
66
lib/spectests/spectests/data.wast
vendored
66
lib/spectests/spectests/data.wast
vendored
@ -47,31 +47,27 @@
|
||||
(data (i32.const 1) "h")
|
||||
)
|
||||
|
||||
;; SKIP_MUTABLE_GLOBALS
|
||||
;; (module
|
||||
;; (global (import "spectest" "global_i32") i32)
|
||||
;; (memory 1)
|
||||
;; (data (get_global 0) "a")
|
||||
;; )
|
||||
;; SKIP_MUTABLE_GLOBALS
|
||||
;; (module
|
||||
;; (global (import "spectest" "global_i32") i32)
|
||||
;; (import "spectest" "memory" (memory 1))
|
||||
;; (data (get_global 0) "a")
|
||||
;; )
|
||||
(module
|
||||
(global (import "spectest" "global_i32") i32)
|
||||
(memory 1)
|
||||
(data (get_global 0) "a")
|
||||
)
|
||||
(module
|
||||
(global (import "spectest" "global_i32") i32)
|
||||
(import "spectest" "memory" (memory 1))
|
||||
(data (get_global 0) "a")
|
||||
)
|
||||
|
||||
;; SKIP_MUTABLE_GLOBALS
|
||||
;; (module
|
||||
;; (global $g (import "spectest" "global_i32") i32)
|
||||
;; (memory 1)
|
||||
;; (data (get_global $g) "a")
|
||||
;; )
|
||||
;; SKIP_MUTABLE_GLOBALS
|
||||
;; (module
|
||||
;; (global $g (import "spectest" "global_i32") i32)
|
||||
;; (import "spectest" "memory" (memory 1))
|
||||
;; (data (get_global $g) "a")
|
||||
;; )
|
||||
(module
|
||||
(global $g (import "spectest" "global_i32") i32)
|
||||
(memory 1)
|
||||
(data (get_global $g) "a")
|
||||
)
|
||||
(module
|
||||
(global $g (import "spectest" "global_i32") i32)
|
||||
(import "spectest" "memory" (memory 1))
|
||||
(data (get_global $g) "a")
|
||||
)
|
||||
|
||||
;; Use of internal globals in constant expressions is not allowed in MVP.
|
||||
;; (module (memory 1) (data (get_global 0) "a") (global i32 (i32.const 0)))
|
||||
@ -138,19 +134,17 @@
|
||||
(data (i32.const 0) "a")
|
||||
)
|
||||
|
||||
;; SKIP_MUTABLE_GLOBALS
|
||||
;; (module
|
||||
;; (global (import "spectest" "global_i32") i32)
|
||||
;; (import "spectest" "memory" (memory 0))
|
||||
;; (data (get_global 0) "a")
|
||||
;; )
|
||||
(module
|
||||
(global (import "spectest" "global_i32") i32)
|
||||
(import "spectest" "memory" (memory 0))
|
||||
(data (get_global 0) "a")
|
||||
)
|
||||
|
||||
;; SKIP_MUTABLE_GLOBALS
|
||||
;; (module
|
||||
;; (global (import "spectest" "global_i32") i32)
|
||||
;; (import "spectest" "memory" (memory 0 3))
|
||||
;; (data (get_global 0) "a")
|
||||
;; )
|
||||
(module
|
||||
(global (import "spectest" "global_i32") i32)
|
||||
(import "spectest" "memory" (memory 0 3))
|
||||
(data (get_global 0) "a")
|
||||
)
|
||||
|
||||
(module
|
||||
(import "spectest" "memory" (memory 0))
|
||||
|
6
lib/spectests/spectests/globals.wast
vendored
6
lib/spectests/spectests/globals.wast
vendored
@ -246,11 +246,9 @@
|
||||
)
|
||||
|
||||
;; mutable globals can be exported
|
||||
;; SKIP_MUTABLE_GLOBALS
|
||||
;; (module (global (mut f32) (f32.const 0)) (export "a" (global 0)))
|
||||
(module (global (mut f32) (f32.const 0)) (export "a" (global 0)))
|
||||
|
||||
;; SKIP_MUTABLE_GLOBALS
|
||||
;; (module (global (export "a") (mut f32) (f32.const 0)))
|
||||
(module (global (export "a") (mut f32) (f32.const 0)))
|
||||
|
||||
(assert_invalid
|
||||
(module (global f32 (f32.neg (f32.const 0))))
|
||||
|
63
lib/spectests/spectests/simd.wast
vendored
63
lib/spectests/spectests/simd.wast
vendored
@ -9,49 +9,48 @@
|
||||
|
||||
;; v128 globals
|
||||
|
||||
;; wasmer silently doesn't implement (register) yet
|
||||
;;(module $M
|
||||
;; (global (export "a") v128 (v128.const f32x4 0.0 1.0 2.0 3.0))
|
||||
;; (global (export "b") (mut v128) (v128.const f32x4 4.0 5.0 6.0 7.0))
|
||||
;;)
|
||||
;;(register "M" $M)
|
||||
(module $M
|
||||
(global (export "a") v128 (v128.const f32x4 0.0 1.0 2.0 3.0))
|
||||
(global (export "b") (mut v128) (v128.const f32x4 4.0 5.0 6.0 7.0))
|
||||
)
|
||||
(register "M" $M)
|
||||
|
||||
(module
|
||||
;; (global $a (import "M" "a") v128)
|
||||
;; (global $b (import "M" "b") (mut v128))
|
||||
(global $a (import "M" "a") v128)
|
||||
(global $b (import "M" "b") (mut v128))
|
||||
|
||||
;; (global $c v128 (global.get $a))
|
||||
(global $c v128 (global.get $a))
|
||||
(global $d v128 (v128.const i32x4 8 9 10 11))
|
||||
;; (global $e (mut v128) (global.get $a))
|
||||
;; (global $f (mut v128) (v128.const i32x4 12 13 14 15))
|
||||
(global $e (mut v128) (global.get $a))
|
||||
(global $f (mut v128) (v128.const i32x4 12 13 14 15))
|
||||
|
||||
;; (func (export "get-a") (result v128) (global.get $a))
|
||||
;; (func (export "get-b") (result v128) (global.get $b))
|
||||
;; (func (export "get-c") (result v128) (global.get $c))
|
||||
(func (export "get-a") (result v128) (global.get $a))
|
||||
(func (export "get-b") (result v128) (global.get $b))
|
||||
(func (export "get-c") (result v128) (global.get $c))
|
||||
(func (export "get-d") (result v128) (global.get $d))
|
||||
;; (func (export "get-e") (result v128) (global.get $e))
|
||||
;; (func (export "get-f") (result v128) (global.get $f))
|
||||
(func (export "get-e") (result v128) (global.get $e))
|
||||
(func (export "get-f") (result v128) (global.get $f))
|
||||
|
||||
;; (func (export "set-b") (param $value v128) (global.set $b (local.get $value)))
|
||||
;; (func (export "set-e") (param $value v128) (global.set $e (local.get $value)))
|
||||
;; (func (export "set-f") (param $value v128) (global.set $f (local.get $value)))
|
||||
(func (export "set-b") (param $value v128) (global.set $b (local.get $value)))
|
||||
(func (export "set-e") (param $value v128) (global.set $e (local.get $value)))
|
||||
(func (export "set-f") (param $value v128) (global.set $f (local.get $value)))
|
||||
)
|
||||
|
||||
;;(assert_return (invoke "get-a") (v128.const f32x4 0.0 1.0 2.0 3.0))
|
||||
;;(assert_return (invoke "get-b") (v128.const f32x4 4.0 5.0 6.0 7.0))
|
||||
;;(assert_return (invoke "get-c") (v128.const f32x4 0.0 1.0 2.0 3.0))
|
||||
(assert_return (invoke "get-a") (v128.const f32x4 0.0 1.0 2.0 3.0))
|
||||
(assert_return (invoke "get-b") (v128.const f32x4 4.0 5.0 6.0 7.0))
|
||||
(assert_return (invoke "get-c") (v128.const f32x4 0.0 1.0 2.0 3.0))
|
||||
(assert_return (invoke "get-d") (v128.const i32x4 8 9 10 11))
|
||||
;;(assert_return (invoke "get-e") (v128.const f32x4 0.0 1.0 2.0 3.0))
|
||||
;;(assert_return (invoke "get-f") (v128.const i32x4 12 13 14 15))
|
||||
(assert_return (invoke "get-e") (v128.const f32x4 0.0 1.0 2.0 3.0))
|
||||
(assert_return (invoke "get-f") (v128.const i32x4 12 13 14 15))
|
||||
|
||||
;;(invoke "set-b" (v128.const f64x2 nan:0x1 nan:0x2))
|
||||
;;(assert_return (invoke "get-b") (v128.const f64x2 nan:0x1 nan:0x2))
|
||||
;;
|
||||
;;(invoke "set-e" (v128.const f64x2 -nan:0x3 +inf))
|
||||
;;(assert_return (invoke "get-e") (v128.const f64x2 -nan:0x3 +inf))
|
||||
;;
|
||||
;;(invoke "set-f" (v128.const f32x4 -inf +3.14 10.0e30 +nan:0x42))
|
||||
;;(assert_return (invoke "get-f") (v128.const f32x4 -inf +3.14 10.0e30 +nan:0x42))
|
||||
(invoke "set-b" (v128.const f64x2 nan:0x1 nan:0x2))
|
||||
(assert_return (invoke "get-b") (v128.const f64x2 nan:0x1 nan:0x2))
|
||||
|
||||
(invoke "set-e" (v128.const f64x2 -nan:0x3 +inf))
|
||||
(assert_return (invoke "get-e") (v128.const f64x2 -nan:0x3 +inf))
|
||||
|
||||
(invoke "set-f" (v128.const f32x4 -inf +3.14 10.0e30 +nan:0x42))
|
||||
(assert_return (invoke "get-f") (v128.const f32x4 -inf +3.14 10.0e30 +nan:0x42))
|
||||
|
||||
(assert_invalid (module (global v128 (i32.const 0))) "invalid initializer expression")
|
||||
(assert_invalid (module (global v128 (i64.const 0))) "invalid initializer expression")
|
||||
|
@ -17,7 +17,7 @@ wasmer-llvm-backend = { path = "../llvm-backend", version = "0.6.0", optional =
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
glob = "0.2.11"
|
||||
glob = "0.3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" }
|
||||
|
@ -9,13 +9,12 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
libc = "0.2.50"
|
||||
rand = "0.6.5"
|
||||
libc = "0.2.60"
|
||||
rand = "0.7.0"
|
||||
# wasmer-runtime-abi = { path = "../runtime-abi" }
|
||||
hashbrown = "0.1.8"
|
||||
generational-arena = "0.2.2"
|
||||
log = "0.4.6"
|
||||
byteorder = "1.3.1"
|
||||
log = "0.4.8"
|
||||
byteorder = "1.3.2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = "0.3"
|
||||
winapi = "0.3.7"
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#[cfg(target = "windows")]
|
||||
extern crate winapi;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
use crate::syscalls::types::*;
|
||||
use generational_arena::Arena;
|
||||
pub use generational_arena::Index as Inode;
|
||||
use hashbrown::hash_map::HashMap;
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cell::Cell,
|
||||
|
@ -9,10 +9,10 @@ edition = "2018"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
winapi = { version = "0.3", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
libc = "0.2.49"
|
||||
winapi = { version = "0.3.7", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] }
|
||||
libc = "0.2.60"
|
||||
|
||||
[build-dependencies]
|
||||
cmake = "0.1.35"
|
||||
bindgen = "0.46.0"
|
||||
regex = "1.0.6"
|
||||
cmake = "0.1.40"
|
||||
bindgen = "0.51.0"
|
||||
regex = "1.2.0"
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#[cfg(windows)]
|
||||
mod exception_handling;
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
extern crate byteorder;
|
||||
extern crate structopt;
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
extern crate structopt;
|
||||
|
||||
use std::env;
|
||||
@ -10,7 +15,7 @@ use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use std::str::FromStr;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::HashMap;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use wasmer::*;
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#[macro_use]
|
||||
extern crate wasmer_runtime_core;
|
||||
// extern crate wasmer_emscripten;
|
||||
|
Loading…
Reference in New Issue
Block a user