Merge remote-tracking branch 'upstream/master' into poll
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "High-level API to expose the Wasmtime runtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -13,11 +13,11 @@ name = "wasmtime"
|
||||
crate-type = ["lib", "staticlib", "cdylib"]
|
||||
|
||||
[dependencies]
|
||||
wasmtime-runtime = { path = "../runtime" }
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
wasmtime-jit = { path = "../jit" }
|
||||
wasmparser = { version = "0.45.1", default-features = false }
|
||||
target-lexicon = { version = "0.9.0", default-features = false }
|
||||
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
wasmtime-jit = { path = "../jit", version = "0.9.0" }
|
||||
wasmparser = { version = "0.47.0", default-features = false }
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
anyhow = "1.0.19"
|
||||
region = "2.0.0"
|
||||
libc = "0.2"
|
||||
@@ -28,10 +28,8 @@ winapi = "0.3.7"
|
||||
|
||||
[dev-dependencies]
|
||||
# for wasmtime.rs
|
||||
wasi-common = { path = "../wasi-common" }
|
||||
wasi-common = { path = "../wasi-common", version = "0.9.0" }
|
||||
pretty_env_logger = "0.3.0"
|
||||
wasmtime-wast = { path = "../wast" }
|
||||
wasmtime-wasi = { path = "../wasi" }
|
||||
rayon = "1.2.1"
|
||||
file-per-thread-logger = "0.1.1"
|
||||
wat = "1.0"
|
||||
|
||||
Submodule crates/api/c-examples/wasm-c-api updated: 8782d5b456...d9a80099d4
@@ -50,11 +50,11 @@ fn main() -> anyhow::Result<()> {
|
||||
.0;
|
||||
|
||||
// Instantiate the module.
|
||||
let instance = Instance::new(&store, &module, &[])?;
|
||||
let instance = Instance::new(&module, &[])?;
|
||||
|
||||
// Invoke `gcd` export
|
||||
let gcd = instance.exports()[gcd_index].func().expect("gcd");
|
||||
let result = gcd.borrow().call(&[Val::from(6i32), Val::from(27i32)])?;
|
||||
let result = gcd.call(&[Val::from(6i32), Val::from(27i32)])?;
|
||||
|
||||
println!("{:?}", result);
|
||||
Ok(())
|
||||
|
||||
@@ -41,15 +41,14 @@ fn main() -> Result<()> {
|
||||
// `HelloCallback` type and its associated implementation of `Callback.
|
||||
println!("Creating callback...");
|
||||
let hello_type = FuncType::new(Box::new([]), Box::new([]));
|
||||
let hello_func = HostRef::new(Func::new(&store, hello_type, Rc::new(HelloCallback)));
|
||||
let hello_func = Func::new(&store, hello_type, Rc::new(HelloCallback));
|
||||
|
||||
// Once we've got that all set up we can then move to the instantiation
|
||||
// phase, pairing together a compiled module as well as a set of imports.
|
||||
// Note that this is where the wasm `start` function, if any, would run.
|
||||
println!("Instantiating module...");
|
||||
let imports = vec![hello_func.into()];
|
||||
let instance = Instance::new(&store, &module, imports.as_slice())
|
||||
.context("> Error instantiating module!")?;
|
||||
let instance = Instance::new(&module, &imports).context("> Error instantiating module!")?;
|
||||
|
||||
// Next we poke around a bit to extract the `run` function from the module.
|
||||
println!("Extracting export...");
|
||||
@@ -59,7 +58,7 @@ fn main() -> Result<()> {
|
||||
|
||||
// And last but not least we can call it!
|
||||
println!("Calling export...");
|
||||
run_func.borrow().call(&[])?;
|
||||
run_func.call(&[])?;
|
||||
|
||||
println!("Done.");
|
||||
Ok(())
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use anyhow::{bail, ensure, Context as _, Error};
|
||||
use wasmtime::*;
|
||||
|
||||
fn get_export_memory(exports: &[Extern], i: usize) -> Result<HostRef<Memory>, Error> {
|
||||
fn get_export_memory(exports: &[Extern], i: usize) -> Result<Memory, Error> {
|
||||
if exports.len() <= i {
|
||||
bail!("> Error accessing memory export {}!", i);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ fn get_export_memory(exports: &[Extern], i: usize) -> Result<HostRef<Memory>, Er
|
||||
.clone())
|
||||
}
|
||||
|
||||
fn get_export_func(exports: &[Extern], i: usize) -> Result<HostRef<Func>, Error> {
|
||||
fn get_export_func(exports: &[Extern], i: usize) -> Result<Func, Error> {
|
||||
if exports.len() <= i {
|
||||
bail!("> Error accessing function export {}!", i);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ macro_rules! check {
|
||||
|
||||
macro_rules! check_ok {
|
||||
($func:expr, $($p:expr),*) => {
|
||||
if let Err(_) = $func.borrow().call(&[$($p.into()),*]) {
|
||||
if let Err(_) = $func.call(&[$($p.into()),*]) {
|
||||
bail!("> Error on result, expected return");
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ macro_rules! check_ok {
|
||||
|
||||
macro_rules! check_trap {
|
||||
($func:expr, $($p:expr),*) => {
|
||||
if let Ok(_) = $func.borrow().call(&[$($p.into()),*]) {
|
||||
if let Ok(_) = $func.call(&[$($p.into()),*]) {
|
||||
bail!("> Error on result, expected trap");
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ macro_rules! check_trap {
|
||||
|
||||
macro_rules! call {
|
||||
($func:expr, $($p:expr),*) => {
|
||||
match $func.borrow().call(&[$($p.into()),*]) {
|
||||
match $func.call(&[$($p.into()),*]) {
|
||||
Ok(result) => {
|
||||
let result: i32 = result[0].unwrap_i32();
|
||||
result
|
||||
@@ -90,7 +90,7 @@ fn main() -> Result<(), Error> {
|
||||
|
||||
// Instantiate.
|
||||
println!("Instantiating module...");
|
||||
let instance = Instance::new(&store, &module, &[]).context("> Error instantiating module!")?;
|
||||
let instance = Instance::new(&module, &[]).context("> Error instantiating module!")?;
|
||||
|
||||
// Extract export.
|
||||
println!("Extracting export...");
|
||||
@@ -101,16 +101,13 @@ fn main() -> Result<(), Error> {
|
||||
let load_func = get_export_func(&exports, 2)?;
|
||||
let store_func = get_export_func(&exports, 3)?;
|
||||
|
||||
// Try cloning.
|
||||
check!(memory.clone().ptr_eq(&memory), true);
|
||||
|
||||
// Check initial memory.
|
||||
println!("Checking memory...");
|
||||
check!(memory.borrow().size(), 2u32);
|
||||
check!(memory.borrow().data_size(), 0x20000usize);
|
||||
check!(unsafe { memory.borrow().data()[0] }, 0);
|
||||
check!(unsafe { memory.borrow().data()[0x1000] }, 1);
|
||||
check!(unsafe { memory.borrow().data()[0x1003] }, 4);
|
||||
check!(memory.size(), 2u32);
|
||||
check!(memory.data_size(), 0x20000usize);
|
||||
check!(unsafe { memory.data()[0] }, 0);
|
||||
check!(unsafe { memory.data()[0x1000] }, 1);
|
||||
check!(unsafe { memory.data()[0x1003] }, 4);
|
||||
|
||||
check!(call!(size_func,), 2);
|
||||
check!(call!(load_func, 0), 0);
|
||||
@@ -122,36 +119,36 @@ fn main() -> Result<(), Error> {
|
||||
// Mutate memory.
|
||||
println!("Mutating memory...");
|
||||
unsafe {
|
||||
memory.borrow_mut().data()[0x1003] = 5;
|
||||
memory.data()[0x1003] = 5;
|
||||
}
|
||||
|
||||
check_ok!(store_func, 0x1002, 6);
|
||||
check_trap!(store_func, 0x20000, 0);
|
||||
|
||||
check!(unsafe { memory.borrow().data()[0x1002] }, 6);
|
||||
check!(unsafe { memory.borrow().data()[0x1003] }, 5);
|
||||
check!(unsafe { memory.data()[0x1002] }, 6);
|
||||
check!(unsafe { memory.data()[0x1003] }, 5);
|
||||
check!(call!(load_func, 0x1002), 6);
|
||||
check!(call!(load_func, 0x1003), 5);
|
||||
|
||||
// Grow memory.
|
||||
println!("Growing memory...");
|
||||
check!(memory.borrow_mut().grow(1), true);
|
||||
check!(memory.borrow().size(), 3u32);
|
||||
check!(memory.borrow().data_size(), 0x30000usize);
|
||||
check!(memory.grow(1), true);
|
||||
check!(memory.size(), 3u32);
|
||||
check!(memory.data_size(), 0x30000usize);
|
||||
|
||||
check!(call!(load_func, 0x20000), 0);
|
||||
check_ok!(store_func, 0x20000, 0);
|
||||
check_trap!(load_func, 0x30000);
|
||||
check_trap!(store_func, 0x30000, 0);
|
||||
|
||||
check!(memory.borrow_mut().grow(1), false);
|
||||
check!(memory.borrow_mut().grow(0), true);
|
||||
check!(memory.grow(1), false);
|
||||
check!(memory.grow(0), true);
|
||||
|
||||
// Create stand-alone memory.
|
||||
// TODO(wasm+): Once Wasm allows multiple memories, turn this into import.
|
||||
println!("Creating stand-alone memory...");
|
||||
let memorytype = MemoryType::new(Limits::new(5, Some(5)));
|
||||
let mut memory2 = Memory::new(&store, memorytype);
|
||||
let memory2 = Memory::new(&store, memorytype);
|
||||
check!(memory2.size(), 5u32);
|
||||
check!(memory2.grow(1), false);
|
||||
check!(memory2.grow(0), true);
|
||||
|
||||
@@ -62,13 +62,13 @@ fn main() -> Result<()> {
|
||||
Box::new([ValType::I32, ValType::I64]),
|
||||
Box::new([ValType::I64, ValType::I32]),
|
||||
);
|
||||
let callback_func = HostRef::new(Func::new(&store, callback_type, Rc::new(Callback)));
|
||||
let callback_func = Func::new(&store, callback_type, Rc::new(Callback));
|
||||
|
||||
// Instantiate.
|
||||
println!("Instantiating module...");
|
||||
let imports = vec![callback_func.into()];
|
||||
let instance = Instance::new(&store, &module, imports.as_slice())
|
||||
.context("Error instantiating module!")?;
|
||||
let instance =
|
||||
Instance::new(&module, imports.as_slice()).context("Error instantiating module!")?;
|
||||
|
||||
// Extract exports.
|
||||
println!("Extracting export...");
|
||||
@@ -83,7 +83,6 @@ fn main() -> Result<()> {
|
||||
println!("Calling export \"g\"...");
|
||||
let args = vec![Val::I32(1), Val::I64(3)];
|
||||
let results = g
|
||||
.borrow()
|
||||
.call(&args)
|
||||
.map_err(|e| format_err!("> Error calling g! {:?}", e))?;
|
||||
|
||||
@@ -108,7 +107,6 @@ fn main() -> Result<()> {
|
||||
Val::I64(9),
|
||||
];
|
||||
let results = round_trip_many
|
||||
.borrow()
|
||||
.call(&args)
|
||||
.map_err(|e| format_err!("> Error calling round_trip_many! {:?}", e))?;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use wasmtime_runtime::Export;
|
||||
/// WebAssembly.
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use wasmtime::{HostRef, Val};
|
||||
/// use wasmtime::Val;
|
||||
///
|
||||
/// struct TimesTwo;
|
||||
///
|
||||
@@ -54,13 +54,11 @@ use wasmtime_runtime::Export;
|
||||
/// );
|
||||
///
|
||||
/// // Build a reference to the "times_two" function that can be used.
|
||||
/// let times_two_function = HostRef::new(
|
||||
/// wasmtime::Func::new(&store, times_two_type, std::rc::Rc::new(TimesTwo))
|
||||
/// );
|
||||
/// let times_two_function =
|
||||
/// wasmtime::Func::new(&store, times_two_type, std::rc::Rc::new(TimesTwo));
|
||||
///
|
||||
/// // Create module instance that imports our function
|
||||
/// let instance = wasmtime::Instance::new(
|
||||
/// &store,
|
||||
/// &module,
|
||||
/// &[times_two_function.into()]
|
||||
/// )?;
|
||||
@@ -71,7 +69,6 @@ use wasmtime_runtime::Export;
|
||||
/// // Borrow and call "run". Returning any error message from Wasm as a string.
|
||||
/// let original = 5i32;
|
||||
/// let results = run_function
|
||||
/// .borrow()
|
||||
/// .call(&[original.into()])
|
||||
/// .map_err(|trap| trap.to_string())?;
|
||||
///
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::callable::{Callable, NativeCallable, WasmtimeFn, WrappedCallable};
|
||||
use crate::r#ref::{AnyRef, HostRef};
|
||||
use crate::r#ref::AnyRef;
|
||||
use crate::runtime::Store;
|
||||
use crate::trampoline::{generate_global_export, generate_memory_export, generate_table_export};
|
||||
use crate::trap::Trap;
|
||||
@@ -15,53 +15,53 @@ use wasmtime_runtime::InstanceHandle;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Extern {
|
||||
Func(HostRef<Func>),
|
||||
Global(HostRef<Global>),
|
||||
Table(HostRef<Table>),
|
||||
Memory(HostRef<Memory>),
|
||||
Func(Func),
|
||||
Global(Global),
|
||||
Table(Table),
|
||||
Memory(Memory),
|
||||
}
|
||||
|
||||
impl Extern {
|
||||
pub fn func(&self) -> Option<&HostRef<Func>> {
|
||||
pub fn func(&self) -> Option<&Func> {
|
||||
match self {
|
||||
Extern::Func(func) => Some(func),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn global(&self) -> Option<&HostRef<Global>> {
|
||||
pub fn global(&self) -> Option<&Global> {
|
||||
match self {
|
||||
Extern::Global(global) => Some(global),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn table(&self) -> Option<&HostRef<Table>> {
|
||||
pub fn table(&self) -> Option<&Table> {
|
||||
match self {
|
||||
Extern::Table(table) => Some(table),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn memory(&self) -> Option<&HostRef<Memory>> {
|
||||
pub fn memory(&self) -> Option<&Memory> {
|
||||
match self {
|
||||
Extern::Memory(memory) => Some(memory),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> ExternType {
|
||||
pub fn ty(&self) -> ExternType {
|
||||
match self {
|
||||
Extern::Func(ft) => ExternType::Func(ft.borrow().r#type().clone()),
|
||||
Extern::Memory(ft) => ExternType::Memory(ft.borrow().r#type().clone()),
|
||||
Extern::Table(tt) => ExternType::Table(tt.borrow().r#type().clone()),
|
||||
Extern::Global(gt) => ExternType::Global(gt.borrow().r#type().clone()),
|
||||
Extern::Func(ft) => ExternType::Func(ft.ty().clone()),
|
||||
Extern::Memory(ft) => ExternType::Memory(ft.ty().clone()),
|
||||
Extern::Table(tt) => ExternType::Table(tt.ty().clone()),
|
||||
Extern::Global(gt) => ExternType::Global(gt.ty().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_wasmtime_export(&mut self) -> wasmtime_runtime::Export {
|
||||
pub(crate) fn get_wasmtime_export(&self) -> wasmtime_runtime::Export {
|
||||
match self {
|
||||
Extern::Func(f) => f.borrow().wasmtime_export().clone(),
|
||||
Extern::Global(g) => g.borrow().wasmtime_export().clone(),
|
||||
Extern::Memory(m) => m.borrow().wasmtime_export().clone(),
|
||||
Extern::Table(t) => t.borrow().wasmtime_export().clone(),
|
||||
Extern::Func(f) => f.wasmtime_export().clone(),
|
||||
Extern::Global(g) => g.wasmtime_export().clone(),
|
||||
Extern::Memory(m) => m.wasmtime_export().clone(),
|
||||
Extern::Table(t) => t.wasmtime_export().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,50 +71,51 @@ impl Extern {
|
||||
export: wasmtime_runtime::Export,
|
||||
) -> Extern {
|
||||
match export {
|
||||
wasmtime_runtime::Export::Function { .. } => Extern::Func(HostRef::new(
|
||||
Func::from_wasmtime_function(export, store, instance_handle),
|
||||
)),
|
||||
wasmtime_runtime::Export::Memory { .. } => Extern::Memory(HostRef::new(
|
||||
Memory::from_wasmtime_memory(export, store, instance_handle),
|
||||
)),
|
||||
wasmtime_runtime::Export::Global { .. } => {
|
||||
Extern::Global(HostRef::new(Global::from_wasmtime_global(export, store)))
|
||||
wasmtime_runtime::Export::Function { .. } => {
|
||||
Extern::Func(Func::from_wasmtime_function(export, store, instance_handle))
|
||||
}
|
||||
wasmtime_runtime::Export::Memory { .. } => {
|
||||
Extern::Memory(Memory::from_wasmtime_memory(export, store, instance_handle))
|
||||
}
|
||||
wasmtime_runtime::Export::Global { .. } => {
|
||||
Extern::Global(Global::from_wasmtime_global(export, store))
|
||||
}
|
||||
wasmtime_runtime::Export::Table { .. } => {
|
||||
Extern::Table(Table::from_wasmtime_table(export, store, instance_handle))
|
||||
}
|
||||
wasmtime_runtime::Export::Table { .. } => Extern::Table(HostRef::new(
|
||||
Table::from_wasmtime_table(export, store, instance_handle),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Func>> for Extern {
|
||||
fn from(r: HostRef<Func>) -> Self {
|
||||
impl From<Func> for Extern {
|
||||
fn from(r: Func) -> Self {
|
||||
Extern::Func(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Global>> for Extern {
|
||||
fn from(r: HostRef<Global>) -> Self {
|
||||
impl From<Global> for Extern {
|
||||
fn from(r: Global) -> Self {
|
||||
Extern::Global(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Memory>> for Extern {
|
||||
fn from(r: HostRef<Memory>) -> Self {
|
||||
impl From<Memory> for Extern {
|
||||
fn from(r: Memory) -> Self {
|
||||
Extern::Memory(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Table>> for Extern {
|
||||
fn from(r: HostRef<Table>) -> Self {
|
||||
impl From<Table> for Extern {
|
||||
fn from(r: Table) -> Self {
|
||||
Extern::Table(r)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Func {
|
||||
_store: Store,
|
||||
callable: Rc<dyn WrappedCallable + 'static>,
|
||||
r#type: FuncType,
|
||||
ty: FuncType,
|
||||
}
|
||||
|
||||
impl Func {
|
||||
@@ -125,26 +126,26 @@ impl Func {
|
||||
|
||||
fn from_wrapped(
|
||||
store: &Store,
|
||||
r#type: FuncType,
|
||||
ty: FuncType,
|
||||
callable: Rc<dyn WrappedCallable + 'static>,
|
||||
) -> Func {
|
||||
Func {
|
||||
_store: store.clone(),
|
||||
callable,
|
||||
r#type,
|
||||
ty,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &FuncType {
|
||||
&self.r#type
|
||||
pub fn ty(&self) -> &FuncType {
|
||||
&self.ty
|
||||
}
|
||||
|
||||
pub fn param_arity(&self) -> usize {
|
||||
self.r#type.params().len()
|
||||
self.ty.params().len()
|
||||
}
|
||||
|
||||
pub fn result_arity(&self) -> usize {
|
||||
self.r#type.results().len()
|
||||
self.ty.results().len()
|
||||
}
|
||||
|
||||
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, Trap> {
|
||||
@@ -162,8 +163,12 @@ impl Func {
|
||||
store: &Store,
|
||||
instance_handle: InstanceHandle,
|
||||
) -> Self {
|
||||
// This is only called with `Export::Function`, and since it's coming
|
||||
// from wasmtime_runtime itself we should support all the types coming
|
||||
// out of it, so assert such here.
|
||||
let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
|
||||
FuncType::from_wasmtime_signature(signature.clone())
|
||||
.expect("core wasm signature should be supported")
|
||||
} else {
|
||||
panic!("expected function export")
|
||||
};
|
||||
@@ -178,32 +183,39 @@ impl fmt::Debug for Func {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Global {
|
||||
inner: Rc<GlobalInner>,
|
||||
}
|
||||
|
||||
struct GlobalInner {
|
||||
_store: Store,
|
||||
r#type: GlobalType,
|
||||
ty: GlobalType,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
#[allow(dead_code)]
|
||||
wasmtime_state: Option<crate::trampoline::GlobalState>,
|
||||
}
|
||||
|
||||
impl Global {
|
||||
pub fn new(store: &Store, r#type: GlobalType, val: Val) -> Global {
|
||||
pub fn new(store: &Store, ty: GlobalType, val: Val) -> Global {
|
||||
let (wasmtime_export, wasmtime_state) =
|
||||
generate_global_export(&r#type, val).expect("generated global");
|
||||
generate_global_export(&ty, val).expect("generated global");
|
||||
Global {
|
||||
_store: store.clone(),
|
||||
r#type,
|
||||
wasmtime_export,
|
||||
wasmtime_state: Some(wasmtime_state),
|
||||
inner: Rc::new(GlobalInner {
|
||||
_store: store.clone(),
|
||||
ty,
|
||||
wasmtime_export,
|
||||
wasmtime_state: Some(wasmtime_state),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &GlobalType {
|
||||
&self.r#type
|
||||
pub fn ty(&self) -> &GlobalType {
|
||||
&self.inner.ty
|
||||
}
|
||||
|
||||
fn wasmtime_global_definition(&self) -> *mut wasmtime_runtime::VMGlobalDefinition {
|
||||
match self.wasmtime_export {
|
||||
match self.inner.wasmtime_export {
|
||||
wasmtime_runtime::Export::Global { definition, .. } => definition,
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
@@ -212,22 +224,22 @@ impl Global {
|
||||
pub fn get(&self) -> Val {
|
||||
let definition = unsafe { &mut *self.wasmtime_global_definition() };
|
||||
unsafe {
|
||||
match self.r#type().content() {
|
||||
match self.ty().content() {
|
||||
ValType::I32 => Val::from(*definition.as_i32()),
|
||||
ValType::I64 => Val::from(*definition.as_i64()),
|
||||
ValType::F32 => Val::F32(*definition.as_u32()),
|
||||
ValType::F64 => Val::F64(*definition.as_u64()),
|
||||
_ => unimplemented!("Global::get for {:?}", self.r#type().content()),
|
||||
_ => unimplemented!("Global::get for {:?}", self.ty().content()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, val: Val) {
|
||||
if val.r#type() != *self.r#type().content() {
|
||||
pub fn set(&self, val: Val) {
|
||||
if val.ty() != *self.ty().content() {
|
||||
panic!(
|
||||
"global of type {:?} cannot be set to {:?}",
|
||||
self.r#type().content(),
|
||||
val.r#type()
|
||||
self.ty().content(),
|
||||
val.ty()
|
||||
);
|
||||
}
|
||||
let definition = unsafe { &mut *self.wasmtime_global_definition() };
|
||||
@@ -237,34 +249,40 @@ impl Global {
|
||||
Val::I64(i) => *definition.as_i64_mut() = i,
|
||||
Val::F32(f) => *definition.as_u32_mut() = f,
|
||||
Val::F64(f) => *definition.as_u64_mut() = f,
|
||||
_ => unimplemented!("Global::set for {:?}", val.r#type()),
|
||||
_ => unimplemented!("Global::set for {:?}", val.ty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
&self.wasmtime_export
|
||||
&self.inner.wasmtime_export
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_global(export: wasmtime_runtime::Export, store: &Store) -> Global {
|
||||
let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export {
|
||||
global
|
||||
} else {
|
||||
panic!("wasmtime export is not memory")
|
||||
panic!("wasmtime export is not global")
|
||||
};
|
||||
let ty = GlobalType::from_wasmtime_global(&global);
|
||||
// The original export is coming from wasmtime_runtime itself we should
|
||||
// support all the types coming out of it, so assert such here.
|
||||
let ty = GlobalType::from_wasmtime_global(&global)
|
||||
.expect("core wasm global type should be supported");
|
||||
Global {
|
||||
_store: store.clone(),
|
||||
r#type: ty,
|
||||
wasmtime_export: export,
|
||||
wasmtime_state: None,
|
||||
inner: Rc::new(GlobalInner {
|
||||
_store: store.clone(),
|
||||
ty: ty,
|
||||
wasmtime_export: export,
|
||||
wasmtime_state: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Table {
|
||||
store: Store,
|
||||
r#type: TableType,
|
||||
ty: TableType,
|
||||
wasmtime_handle: InstanceHandle,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
}
|
||||
@@ -299,13 +317,13 @@ fn set_table_item(
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub fn new(store: &Store, r#type: TableType, init: Val) -> Table {
|
||||
match r#type.element() {
|
||||
pub fn new(store: &Store, ty: TableType, init: Val) -> Table {
|
||||
match ty.element() {
|
||||
ValType::FuncRef => (),
|
||||
_ => panic!("table is not for funcref"),
|
||||
}
|
||||
let (mut wasmtime_handle, wasmtime_export) =
|
||||
generate_table_export(&r#type).expect("generated table");
|
||||
generate_table_export(&ty).expect("generated table");
|
||||
|
||||
// Initialize entries with the init value.
|
||||
match wasmtime_export {
|
||||
@@ -323,14 +341,14 @@ impl Table {
|
||||
|
||||
Table {
|
||||
store: store.clone(),
|
||||
r#type,
|
||||
ty,
|
||||
wasmtime_handle,
|
||||
wasmtime_export,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &TableType {
|
||||
&self.r#type
|
||||
pub fn ty(&self) -> &TableType {
|
||||
&self.ty
|
||||
}
|
||||
|
||||
fn wasmtime_table_index(&self) -> wasm::DefinedTableIndex {
|
||||
@@ -362,9 +380,9 @@ impl Table {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, delta: u32, init: Val) -> bool {
|
||||
pub fn grow(&self, delta: u32, init: Val) -> bool {
|
||||
let index = self.wasmtime_table_index();
|
||||
if let Some(len) = self.wasmtime_handle.table_grow(index, delta) {
|
||||
if let Some(len) = self.wasmtime_handle.clone().table_grow(index, delta) {
|
||||
let mut wasmtime_handle = self.wasmtime_handle.clone();
|
||||
for i in 0..delta {
|
||||
let i = len - (delta - i);
|
||||
@@ -395,34 +413,35 @@ impl Table {
|
||||
let ty = TableType::from_wasmtime_table(&table.table);
|
||||
Table {
|
||||
store: store.clone(),
|
||||
r#type: ty,
|
||||
ty: ty,
|
||||
wasmtime_handle: instance_handle,
|
||||
wasmtime_export: export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Memory {
|
||||
_store: Store,
|
||||
r#type: MemoryType,
|
||||
ty: MemoryType,
|
||||
wasmtime_handle: InstanceHandle,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new(store: &Store, r#type: MemoryType) -> Memory {
|
||||
pub fn new(store: &Store, ty: MemoryType) -> Memory {
|
||||
let (wasmtime_handle, wasmtime_export) =
|
||||
generate_memory_export(&r#type).expect("generated memory");
|
||||
generate_memory_export(&ty).expect("generated memory");
|
||||
Memory {
|
||||
_store: store.clone(),
|
||||
r#type,
|
||||
ty,
|
||||
wasmtime_handle,
|
||||
wasmtime_export,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &MemoryType {
|
||||
&self.r#type
|
||||
pub fn ty(&self) -> &MemoryType {
|
||||
&self.ty
|
||||
}
|
||||
|
||||
fn wasmtime_memory_definition(&self) -> *mut wasmtime_runtime::VMMemoryDefinition {
|
||||
@@ -453,12 +472,15 @@ impl Memory {
|
||||
(self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, delta: u32) -> bool {
|
||||
pub fn grow(&self, delta: u32) -> bool {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Memory { definition, .. } => {
|
||||
let definition = unsafe { &(*definition) };
|
||||
let index = self.wasmtime_handle.memory_index(definition);
|
||||
self.wasmtime_handle.memory_grow(index, delta).is_some()
|
||||
self.wasmtime_handle
|
||||
.clone()
|
||||
.memory_grow(index, delta)
|
||||
.is_some()
|
||||
}
|
||||
_ => panic!("memory definition not found"),
|
||||
}
|
||||
@@ -481,7 +503,7 @@ impl Memory {
|
||||
let ty = MemoryType::from_wasmtime_memory(&memory.memory);
|
||||
Memory {
|
||||
_store: store.clone(),
|
||||
r#type: ty,
|
||||
ty: ty,
|
||||
wasmtime_handle: instance_handle,
|
||||
wasmtime_export: export,
|
||||
}
|
||||
|
||||
@@ -9,45 +9,50 @@ use anyhow::{Error, Result};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
use wasmtime_jit::{instantiate, Resolver, SetupError};
|
||||
use wasmtime_jit::{CompiledModule, Resolver};
|
||||
use wasmtime_runtime::{Export, InstanceHandle, InstantiationError};
|
||||
|
||||
struct SimpleResolver {
|
||||
imports: Vec<(String, String, Extern)>,
|
||||
struct SimpleResolver<'a> {
|
||||
imports: &'a [Extern],
|
||||
}
|
||||
|
||||
impl Resolver for SimpleResolver {
|
||||
fn resolve(&mut self, name: &str, field: &str) -> Option<Export> {
|
||||
// TODO speedup lookup
|
||||
impl Resolver for SimpleResolver<'_> {
|
||||
fn resolve(&mut self, idx: u32, _name: &str, _field: &str) -> Option<Export> {
|
||||
self.imports
|
||||
.iter_mut()
|
||||
.find(|(n, f, _)| name == n && field == f)
|
||||
.map(|(_, _, e)| e.get_wasmtime_export())
|
||||
.get(idx as usize)
|
||||
.map(|i| i.get_wasmtime_export())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instantiate_in_context(
|
||||
fn instantiate_in_context(
|
||||
store: &Store,
|
||||
data: &[u8],
|
||||
imports: Vec<(String, String, Extern)>,
|
||||
module_name: Option<String>,
|
||||
imports: &[Extern],
|
||||
module_name: Option<&str>,
|
||||
context: Context,
|
||||
exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
|
||||
) -> Result<(InstanceHandle, HashSet<Context>), Error> {
|
||||
let mut contexts = HashSet::new();
|
||||
let debug_info = context.debug_info();
|
||||
let mut resolver = SimpleResolver { imports };
|
||||
let instance = instantiate(
|
||||
let mut compiled_module = CompiledModule::new(
|
||||
&mut context.compiler(),
|
||||
data,
|
||||
module_name,
|
||||
&mut resolver,
|
||||
exports,
|
||||
debug_info,
|
||||
)
|
||||
.map_err(|e| -> Error {
|
||||
)?;
|
||||
|
||||
// Register all module signatures
|
||||
for signature in compiled_module.module().signatures.values() {
|
||||
store.register_wasmtime_signature(signature);
|
||||
}
|
||||
|
||||
let instance = compiled_module.instantiate().map_err(|e| -> Error {
|
||||
if let Some(trap) = take_api_trap() {
|
||||
trap.into()
|
||||
} else if let SetupError::Instantiate(InstantiationError::StartTrap(msg)) = e {
|
||||
} else if let InstantiationError::StartTrap(msg) = e {
|
||||
Trap::new(msg).into()
|
||||
} else {
|
||||
e.into()
|
||||
@@ -70,19 +75,15 @@ pub struct Instance {
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub fn new(store: &Store, module: &Module, externs: &[Extern]) -> Result<Instance, Error> {
|
||||
pub fn new(module: &Module, externs: &[Extern]) -> Result<Instance, Error> {
|
||||
let store = module.store();
|
||||
let context = store.context().clone();
|
||||
let exports = store.global_exports().clone();
|
||||
let imports = module
|
||||
.imports()
|
||||
.iter()
|
||||
.zip(externs.iter())
|
||||
.map(|(i, e)| (i.module().to_string(), i.name().to_string(), e.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
let (mut instance_handle, contexts) = instantiate_in_context(
|
||||
module.store(),
|
||||
module.binary().expect("binary"),
|
||||
imports,
|
||||
module.name().cloned(),
|
||||
externs,
|
||||
module.name(),
|
||||
context,
|
||||
exports,
|
||||
)?;
|
||||
@@ -108,14 +109,27 @@ impl Instance {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn exports(&self) -> &[Extern] {
|
||||
&self.exports
|
||||
/// Returns the associated [`Store`] that this `Instance` is compiled into.
|
||||
///
|
||||
/// This is the [`Store`] that generally serves as a sort of global cache
|
||||
/// for various instance-related things.
|
||||
pub fn store(&self) -> &Store {
|
||||
self.module.store()
|
||||
}
|
||||
|
||||
/// Returns the associated [`Module`] that this `Instance` instantiated.
|
||||
///
|
||||
/// The corresponding [`Module`] here is a static version of this `Instance`
|
||||
/// which can be used to learn information such as naming information about
|
||||
/// various functions.
|
||||
pub fn module(&self) -> &Module {
|
||||
&self.module
|
||||
}
|
||||
|
||||
pub fn exports(&self) -> &[Extern] {
|
||||
&self.exports
|
||||
}
|
||||
|
||||
pub fn find_export_by_name(&self, name: &str) -> Option<&Extern> {
|
||||
let (i, _) = self
|
||||
.module
|
||||
@@ -140,7 +154,14 @@ impl Instance {
|
||||
// imported into this store using the from_handle() method.
|
||||
let _ = store.register_wasmtime_signature(signature);
|
||||
}
|
||||
let extern_type = ExternType::from_wasmtime_export(&export);
|
||||
|
||||
// We should support everything supported by wasmtime_runtime, or
|
||||
// otherwise we've got a bug in this crate, so panic if anything
|
||||
// fails to convert here.
|
||||
let extern_type = match ExternType::from_wasmtime_export(&export) {
|
||||
Some(ty) => ty,
|
||||
None => panic!("unsupported core wasm external type {:?}", export),
|
||||
};
|
||||
exports_types.push(ExportType::new(name, extern_type));
|
||||
exports.push(Extern::from_wasmtime_export(
|
||||
store,
|
||||
@@ -175,29 +196,29 @@ cfg_if::cfg_if! {
|
||||
impl Instance {
|
||||
/// The signal handler must be
|
||||
/// [async-signal-safe](http://man7.org/linux/man-pages/man7/signal-safety.7.html).
|
||||
pub fn set_signal_handler<H>(&mut self, handler: H)
|
||||
pub fn set_signal_handler<H>(&self, handler: H)
|
||||
where
|
||||
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool,
|
||||
{
|
||||
self.instance_handle.set_signal_handler(handler);
|
||||
self.instance_handle.clone().set_signal_handler(handler);
|
||||
}
|
||||
}
|
||||
} else if #[cfg(target_os = "windows")] {
|
||||
impl Instance {
|
||||
pub fn set_signal_handler<H>(&mut self, handler: H)
|
||||
pub fn set_signal_handler<H>(&self, handler: H)
|
||||
where
|
||||
H: 'static + Fn(winapi::um::winnt::EXCEPTION_POINTERS) -> bool,
|
||||
{
|
||||
self.instance_handle.set_signal_handler(handler);
|
||||
self.instance_handle.clone().set_signal_handler(handler);
|
||||
}
|
||||
}
|
||||
} else if #[cfg(target_os = "macos")] {
|
||||
impl Instance {
|
||||
pub fn set_signal_handler<H>(&mut self, handler: H)
|
||||
pub fn set_signal_handler<H>(&self, handler: H)
|
||||
where
|
||||
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool,
|
||||
{
|
||||
self.instance_handle.set_signal_handler(handler);
|
||||
self.instance_handle.clone().set_signal_handler(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
//! and there to implement Rust idioms. This crate also defines the actual C API
|
||||
//! itself for consumption from other languages.
|
||||
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
mod callable;
|
||||
mod context;
|
||||
mod externals;
|
||||
@@ -26,7 +24,7 @@ pub use crate::callable::Callable;
|
||||
pub use crate::externals::*;
|
||||
pub use crate::instance::Instance;
|
||||
pub use crate::module::Module;
|
||||
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
|
||||
pub use crate::r#ref::AnyRef;
|
||||
pub use crate::runtime::{Config, Engine, OptLevel, Store, Strategy};
|
||||
pub use crate::trap::{FrameInfo, Trap};
|
||||
pub use crate::types::*;
|
||||
|
||||
@@ -56,138 +56,6 @@ fn into_table_type(tt: wasmparser::TableType) -> TableType {
|
||||
TableType::new(ty, limits)
|
||||
}
|
||||
|
||||
fn read_imports_and_exports(
|
||||
binary: &[u8],
|
||||
) -> Result<(Box<[ImportType]>, Box<[ExportType]>, Option<String>)> {
|
||||
let mut reader = ModuleReader::new(binary)?;
|
||||
let mut imports = Vec::new();
|
||||
let mut exports = Vec::new();
|
||||
let mut memories = Vec::new();
|
||||
let mut tables = Vec::new();
|
||||
let mut func_sig = Vec::new();
|
||||
let mut sigs = Vec::new();
|
||||
let mut globals = Vec::new();
|
||||
let mut module_name = None;
|
||||
while !reader.eof() {
|
||||
let section = reader.read()?;
|
||||
match section.code {
|
||||
SectionCode::Memory => {
|
||||
let section = section.get_memory_section_reader()?;
|
||||
memories.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
memories.push(into_memory_type(entry?));
|
||||
}
|
||||
}
|
||||
SectionCode::Type => {
|
||||
let section = section.get_type_section_reader()?;
|
||||
sigs.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
sigs.push(into_func_type(entry?));
|
||||
}
|
||||
}
|
||||
SectionCode::Function => {
|
||||
let section = section.get_function_section_reader()?;
|
||||
func_sig.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
func_sig.push(entry?);
|
||||
}
|
||||
}
|
||||
SectionCode::Global => {
|
||||
let section = section.get_global_section_reader()?;
|
||||
globals.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
globals.push(into_global_type(entry?.ty));
|
||||
}
|
||||
}
|
||||
SectionCode::Table => {
|
||||
let section = section.get_table_section_reader()?;
|
||||
tables.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
tables.push(into_table_type(entry?))
|
||||
}
|
||||
}
|
||||
SectionCode::Import => {
|
||||
let section = section.get_import_section_reader()?;
|
||||
imports.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
let entry = entry?;
|
||||
let r#type = match entry.ty {
|
||||
ImportSectionEntryType::Function(index) => {
|
||||
func_sig.push(index);
|
||||
let sig = &sigs[index as usize];
|
||||
ExternType::Func(sig.clone())
|
||||
}
|
||||
ImportSectionEntryType::Table(tt) => {
|
||||
let table = into_table_type(tt);
|
||||
tables.push(table.clone());
|
||||
ExternType::Table(table)
|
||||
}
|
||||
ImportSectionEntryType::Memory(mt) => {
|
||||
let memory = into_memory_type(mt);
|
||||
memories.push(memory.clone());
|
||||
ExternType::Memory(memory)
|
||||
}
|
||||
ImportSectionEntryType::Global(gt) => {
|
||||
let global = into_global_type(gt);
|
||||
globals.push(global.clone());
|
||||
ExternType::Global(global)
|
||||
}
|
||||
};
|
||||
imports.push(ImportType::new(entry.module, entry.field, r#type));
|
||||
}
|
||||
}
|
||||
SectionCode::Export => {
|
||||
let section = section.get_export_section_reader()?;
|
||||
exports.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
let entry = entry?;
|
||||
let r#type = match entry.kind {
|
||||
ExternalKind::Function => {
|
||||
let sig_index = func_sig[entry.index as usize] as usize;
|
||||
let sig = &sigs[sig_index];
|
||||
ExternType::Func(sig.clone())
|
||||
}
|
||||
ExternalKind::Table => {
|
||||
ExternType::Table(tables[entry.index as usize].clone())
|
||||
}
|
||||
ExternalKind::Memory => {
|
||||
ExternType::Memory(memories[entry.index as usize].clone())
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
ExternType::Global(globals[entry.index as usize].clone())
|
||||
}
|
||||
};
|
||||
exports.push(ExportType::new(entry.field, r#type));
|
||||
}
|
||||
}
|
||||
SectionCode::Custom {
|
||||
kind: CustomSectionKind::Name,
|
||||
..
|
||||
} => {
|
||||
// Read name section. Per spec, ignore invalid custom section.
|
||||
if let Ok(mut reader) = section.get_name_section_reader() {
|
||||
while let Ok(entry) = reader.read() {
|
||||
if let Name::Module(name) = entry {
|
||||
if let Ok(name) = name.get_name() {
|
||||
module_name = Some(name.to_string());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// skip other sections
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((
|
||||
imports.into_boxed_slice(),
|
||||
exports.into_boxed_slice(),
|
||||
module_name,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum ModuleCodeSource {
|
||||
Binary(Box<[u8]>),
|
||||
@@ -259,20 +127,21 @@ impl Module {
|
||||
///
|
||||
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html
|
||||
pub fn new(store: &Store, binary: &[u8]) -> Result<Module> {
|
||||
Self::validate(store, binary)?;
|
||||
Module::validate(store, binary)?;
|
||||
// Note that the call to `unsafe` here should be ok because we
|
||||
// previously validated the binary, meaning we're guaranteed to pass a
|
||||
// valid binary for `store`.
|
||||
unsafe { Self::create(store, binary, None) }
|
||||
unsafe { Module::new_unchecked(store, binary) }
|
||||
}
|
||||
|
||||
/// Creates a new WebAssembly `Module` from the given in-memory `binary`
|
||||
/// data. The provided `name` will be used in traps/backtrace details.
|
||||
///
|
||||
/// See [`Module::new`] for other details.
|
||||
pub fn new_with_name(store: &Store, binary: &[u8], name: String) -> Result<Module> {
|
||||
Self::validate(store, binary)?;
|
||||
unsafe { Self::create(store, binary, Some(name)) }
|
||||
pub fn new_with_name(store: &Store, binary: &[u8], name: &str) -> Result<Module> {
|
||||
let mut ret = Module::new(store, binary)?;
|
||||
Rc::get_mut(&mut ret.inner).unwrap().name = Some(name.to_string());
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Creates a new WebAssembly `Module` from the given in-memory `binary`
|
||||
@@ -302,24 +171,9 @@ impl Module {
|
||||
/// be somewhat valid for decoding purposes, and the basics of decoding can
|
||||
/// still fail.
|
||||
pub unsafe fn new_unchecked(store: &Store, binary: &[u8]) -> Result<Module> {
|
||||
Self::create(store, binary, None)
|
||||
}
|
||||
|
||||
unsafe fn create(
|
||||
store: &Store,
|
||||
binary: &[u8],
|
||||
name_override: Option<String>,
|
||||
) -> Result<Module> {
|
||||
let (imports, exports, name) = read_imports_and_exports(binary)?;
|
||||
Ok(Module {
|
||||
inner: Rc::new(ModuleInner {
|
||||
store: store.clone(),
|
||||
source: ModuleCodeSource::Binary(binary.into()),
|
||||
imports,
|
||||
exports,
|
||||
name: name_override.or(name),
|
||||
}),
|
||||
})
|
||||
let mut ret = Module::empty(store);
|
||||
ret.read_imports_and_exports(binary)?;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Validates `binary` input data as a WebAssembly binary given the
|
||||
@@ -356,12 +210,18 @@ impl Module {
|
||||
}
|
||||
|
||||
pub fn from_exports(store: &Store, exports: Box<[ExportType]>) -> Self {
|
||||
let mut ret = Module::empty(store);
|
||||
Rc::get_mut(&mut ret.inner).unwrap().exports = exports;
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn empty(store: &Store) -> Self {
|
||||
Module {
|
||||
inner: Rc::new(ModuleInner {
|
||||
store: store.clone(),
|
||||
source: ModuleCodeSource::Unknown,
|
||||
imports: Box::new([]),
|
||||
exports,
|
||||
exports: Box::new([]),
|
||||
name: None,
|
||||
}),
|
||||
}
|
||||
@@ -376,8 +236,8 @@ impl Module {
|
||||
|
||||
/// Returns identifier/name that this [`Module`] has. This name
|
||||
/// is used in traps/backtrace details.
|
||||
pub fn name(&self) -> Option<&String> {
|
||||
self.inner.name.as_ref()
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.inner.name.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the list of imports that this [`Module`] has and must be
|
||||
@@ -396,4 +256,134 @@ impl Module {
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.inner.store
|
||||
}
|
||||
|
||||
fn read_imports_and_exports(&mut self, binary: &[u8]) -> Result<()> {
|
||||
let inner = Rc::get_mut(&mut self.inner).unwrap();
|
||||
inner.source = ModuleCodeSource::Binary(binary.into());
|
||||
let mut reader = ModuleReader::new(binary)?;
|
||||
let mut imports = Vec::new();
|
||||
let mut exports = Vec::new();
|
||||
let mut memories = Vec::new();
|
||||
let mut tables = Vec::new();
|
||||
let mut func_sig = Vec::new();
|
||||
let mut sigs = Vec::new();
|
||||
let mut globals = Vec::new();
|
||||
while !reader.eof() {
|
||||
let section = reader.read()?;
|
||||
match section.code {
|
||||
SectionCode::Memory => {
|
||||
let section = section.get_memory_section_reader()?;
|
||||
memories.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
memories.push(into_memory_type(entry?));
|
||||
}
|
||||
}
|
||||
SectionCode::Type => {
|
||||
let section = section.get_type_section_reader()?;
|
||||
sigs.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
sigs.push(into_func_type(entry?));
|
||||
}
|
||||
}
|
||||
SectionCode::Function => {
|
||||
let section = section.get_function_section_reader()?;
|
||||
func_sig.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
func_sig.push(entry?);
|
||||
}
|
||||
}
|
||||
SectionCode::Global => {
|
||||
let section = section.get_global_section_reader()?;
|
||||
globals.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
globals.push(into_global_type(entry?.ty));
|
||||
}
|
||||
}
|
||||
SectionCode::Table => {
|
||||
let section = section.get_table_section_reader()?;
|
||||
tables.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
tables.push(into_table_type(entry?))
|
||||
}
|
||||
}
|
||||
SectionCode::Import => {
|
||||
let section = section.get_import_section_reader()?;
|
||||
imports.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
let entry = entry?;
|
||||
let r#type = match entry.ty {
|
||||
ImportSectionEntryType::Function(index) => {
|
||||
func_sig.push(index);
|
||||
let sig = &sigs[index as usize];
|
||||
ExternType::Func(sig.clone())
|
||||
}
|
||||
ImportSectionEntryType::Table(tt) => {
|
||||
let table = into_table_type(tt);
|
||||
tables.push(table.clone());
|
||||
ExternType::Table(table)
|
||||
}
|
||||
ImportSectionEntryType::Memory(mt) => {
|
||||
let memory = into_memory_type(mt);
|
||||
memories.push(memory.clone());
|
||||
ExternType::Memory(memory)
|
||||
}
|
||||
ImportSectionEntryType::Global(gt) => {
|
||||
let global = into_global_type(gt);
|
||||
globals.push(global.clone());
|
||||
ExternType::Global(global)
|
||||
}
|
||||
};
|
||||
imports.push(ImportType::new(entry.module, entry.field, r#type));
|
||||
}
|
||||
}
|
||||
SectionCode::Export => {
|
||||
let section = section.get_export_section_reader()?;
|
||||
exports.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
let entry = entry?;
|
||||
let r#type = match entry.kind {
|
||||
ExternalKind::Function => {
|
||||
let sig_index = func_sig[entry.index as usize] as usize;
|
||||
let sig = &sigs[sig_index];
|
||||
ExternType::Func(sig.clone())
|
||||
}
|
||||
ExternalKind::Table => {
|
||||
ExternType::Table(tables[entry.index as usize].clone())
|
||||
}
|
||||
ExternalKind::Memory => {
|
||||
ExternType::Memory(memories[entry.index as usize].clone())
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
ExternType::Global(globals[entry.index as usize].clone())
|
||||
}
|
||||
};
|
||||
exports.push(ExportType::new(entry.field, r#type));
|
||||
}
|
||||
}
|
||||
SectionCode::Custom {
|
||||
kind: CustomSectionKind::Name,
|
||||
..
|
||||
} => {
|
||||
// Read name section. Per spec, ignore invalid custom section.
|
||||
if let Ok(mut reader) = section.get_name_section_reader() {
|
||||
while let Ok(entry) = reader.read() {
|
||||
if let Name::Module(name) = entry {
|
||||
if let Ok(name) = name.get_name() {
|
||||
inner.name = Some(name.to_string());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// skip other sections
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner.imports = imports.into();
|
||||
inner.exports = exports.into();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,6 +397,10 @@ impl Store {
|
||||
.get(&type_index)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn ptr_eq(a: &Store, b: &Store) -> bool {
|
||||
Rc::ptr_eq(&a.inner, &b.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Store {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use super::create_handle::create_handle;
|
||||
use super::trap::{record_api_trap, TrapSink, API_TRAP_CODE};
|
||||
use crate::{Callable, FuncType, Store, Val};
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Result};
|
||||
use std::cmp;
|
||||
use std::convert::TryFrom;
|
||||
use std::rc::Rc;
|
||||
@@ -234,7 +234,10 @@ pub fn create_handle_with_function(
|
||||
func: &Rc<dyn Callable + 'static>,
|
||||
store: &Store,
|
||||
) -> Result<InstanceHandle> {
|
||||
let sig = ft.get_wasmtime_signature().clone();
|
||||
let sig = match ft.get_wasmtime_signature() {
|
||||
Some(sig) => sig.clone(),
|
||||
None => bail!("not a supported core wasm signature {:?}", ft),
|
||||
};
|
||||
|
||||
let isa = {
|
||||
let isa_builder = native::builder();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::create_handle::create_handle;
|
||||
use crate::{GlobalType, Mutability, Val};
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Result};
|
||||
use wasmtime_environ::entity::PrimaryMap;
|
||||
use wasmtime_environ::{wasm, Module};
|
||||
use wasmtime_runtime::{InstanceHandle, VMGlobalDefinition};
|
||||
@@ -24,7 +24,10 @@ pub fn create_global(gt: &GlobalType, val: Val) -> Result<(wasmtime_runtime::Exp
|
||||
}
|
||||
|
||||
let global = wasm::Global {
|
||||
ty: gt.content().get_wasmtime_type(),
|
||||
ty: match gt.content().get_wasmtime_type() {
|
||||
Some(t) => t,
|
||||
None => bail!("cannot support {:?} as a wasm global type", gt.content()),
|
||||
},
|
||||
mutability: match gt.mutability() {
|
||||
Mutability::Const => false,
|
||||
Mutability::Var => true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::create_handle::create_handle;
|
||||
use crate::{TableType, ValType};
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Result};
|
||||
use wasmtime_environ::entity::PrimaryMap;
|
||||
use wasmtime_environ::{wasm, Module};
|
||||
use wasmtime_runtime::InstanceHandle;
|
||||
@@ -13,7 +13,10 @@ pub fn create_handle_with_table(table: &TableType) -> Result<InstanceHandle> {
|
||||
maximum: table.limits().max(),
|
||||
ty: match table.element() {
|
||||
ValType::FuncRef => wasm::TableElementType::Func,
|
||||
_ => wasm::TableElementType::Val(table.element().get_wasmtime_type()),
|
||||
_ => match table.element().get_wasmtime_type() {
|
||||
Some(t) => wasm::TableElementType::Val(t),
|
||||
None => bail!("cannot support {:?} as a table element", table.element()),
|
||||
},
|
||||
},
|
||||
};
|
||||
let tunable = Default::default();
|
||||
|
||||
@@ -84,25 +84,25 @@ impl ValType {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_wasmtime_type(&self) -> ir::Type {
|
||||
pub(crate) fn get_wasmtime_type(&self) -> Option<ir::Type> {
|
||||
match self {
|
||||
ValType::I32 => ir::types::I32,
|
||||
ValType::I64 => ir::types::I64,
|
||||
ValType::F32 => ir::types::F32,
|
||||
ValType::F64 => ir::types::F64,
|
||||
ValType::V128 => ir::types::I8X16,
|
||||
_ => unimplemented!("get_wasmtime_type other"),
|
||||
ValType::I32 => Some(ir::types::I32),
|
||||
ValType::I64 => Some(ir::types::I64),
|
||||
ValType::F32 => Some(ir::types::F32),
|
||||
ValType::F64 => Some(ir::types::F64),
|
||||
ValType::V128 => Some(ir::types::I8X16),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_type(ty: ir::Type) -> ValType {
|
||||
pub(crate) fn from_wasmtime_type(ty: ir::Type) -> Option<ValType> {
|
||||
match ty {
|
||||
ir::types::I32 => ValType::I32,
|
||||
ir::types::I64 => ValType::I64,
|
||||
ir::types::F32 => ValType::F32,
|
||||
ir::types::F64 => ValType::F64,
|
||||
ir::types::I8X16 => ValType::V128,
|
||||
_ => unimplemented!("from_wasmtime_type other"),
|
||||
ir::types::I32 => Some(ValType::I32),
|
||||
ir::types::I64 => Some(ValType::I64),
|
||||
ir::types::F32 => Some(ValType::F32),
|
||||
ir::types::F64 => Some(ValType::F64),
|
||||
ir::types::I8X16 => Some(ValType::V128),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,26 +153,29 @@ impl ExternType {
|
||||
(Table(TableType) table unwrap_table)
|
||||
(Memory(MemoryType) memory unwrap_memory)
|
||||
}
|
||||
pub(crate) fn from_wasmtime_export(export: &wasmtime_runtime::Export) -> Self {
|
||||
match export {
|
||||
|
||||
/// Returns `None` if the sub-type fails to get converted, see documentation
|
||||
/// for sub-types about what may fail.
|
||||
pub(crate) fn from_wasmtime_export(export: &wasmtime_runtime::Export) -> Option<Self> {
|
||||
Some(match export {
|
||||
wasmtime_runtime::Export::Function { signature, .. } => {
|
||||
ExternType::Func(FuncType::from_wasmtime_signature(signature.clone()))
|
||||
ExternType::Func(FuncType::from_wasmtime_signature(signature.clone())?)
|
||||
}
|
||||
wasmtime_runtime::Export::Memory { memory, .. } => {
|
||||
ExternType::Memory(MemoryType::from_wasmtime_memory(&memory.memory))
|
||||
}
|
||||
wasmtime_runtime::Export::Global { global, .. } => {
|
||||
ExternType::Global(GlobalType::from_wasmtime_global(&global))
|
||||
ExternType::Global(GlobalType::from_wasmtime_global(&global)?)
|
||||
}
|
||||
wasmtime_runtime::Export::Table { table, .. } => {
|
||||
ExternType::Table(TableType::from_wasmtime_table(&table.table))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Function Types
|
||||
fn from_wasmtime_abiparam(param: &ir::AbiParam) -> ValType {
|
||||
fn from_wasmtime_abiparam(param: &ir::AbiParam) -> Option<ValType> {
|
||||
assert_eq!(param.purpose, ir::ArgumentPurpose::Normal);
|
||||
ValType::from_wasmtime_type(param.value_type)
|
||||
}
|
||||
@@ -184,7 +187,12 @@ fn from_wasmtime_abiparam(param: &ir::AbiParam) -> ValType {
|
||||
pub struct FuncType {
|
||||
params: Box<[ValType]>,
|
||||
results: Box<[ValType]>,
|
||||
signature: ir::Signature,
|
||||
// `None` if params/results aren't wasm-compatible (e.g. use wasm interface
|
||||
// types), or if they're not implemented (like anyref at the time of this
|
||||
// writing)
|
||||
//
|
||||
// `Some` if they're all wasm-compatible.
|
||||
signature: Option<ir::Signature>,
|
||||
}
|
||||
|
||||
impl FuncType {
|
||||
@@ -196,23 +204,26 @@ impl FuncType {
|
||||
use wasmtime_environ::ir::{types, AbiParam, ArgumentPurpose, Signature};
|
||||
use wasmtime_jit::native;
|
||||
let call_conv = native::call_conv();
|
||||
let signature: Signature = {
|
||||
let mut params = params
|
||||
.iter()
|
||||
.map(|p| AbiParam::new(p.get_wasmtime_type()))
|
||||
.collect::<Vec<_>>();
|
||||
let returns = results
|
||||
.iter()
|
||||
.map(|p| AbiParam::new(p.get_wasmtime_type()))
|
||||
.collect::<Vec<_>>();
|
||||
params.insert(0, AbiParam::special(types::I64, ArgumentPurpose::VMContext));
|
||||
let signature = params
|
||||
.iter()
|
||||
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.and_then(|params| {
|
||||
results
|
||||
.iter()
|
||||
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.map(|results| (params, results))
|
||||
})
|
||||
.map(|(mut params, returns)| {
|
||||
params.insert(0, AbiParam::special(types::I64, ArgumentPurpose::VMContext));
|
||||
|
||||
Signature {
|
||||
params,
|
||||
returns,
|
||||
call_conv,
|
||||
}
|
||||
};
|
||||
Signature {
|
||||
params,
|
||||
returns,
|
||||
call_conv,
|
||||
}
|
||||
});
|
||||
FuncType {
|
||||
params,
|
||||
results,
|
||||
@@ -230,27 +241,33 @@ impl FuncType {
|
||||
&self.results
|
||||
}
|
||||
|
||||
pub(crate) fn get_wasmtime_signature(&self) -> &ir::Signature {
|
||||
&self.signature
|
||||
/// Returns `Some` if this function signature was compatible with cranelift,
|
||||
/// or `None` if one of the types/results wasn't supported or compatible
|
||||
/// with cranelift.
|
||||
pub(crate) fn get_wasmtime_signature(&self) -> Option<&ir::Signature> {
|
||||
self.signature.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_signature(signature: ir::Signature) -> FuncType {
|
||||
/// Returns `None` if any types in the signature can't be converted to the
|
||||
/// types in this crate, but that should very rarely happen and largely only
|
||||
/// indicate a bug in our cranelift integration.
|
||||
pub(crate) fn from_wasmtime_signature(signature: ir::Signature) -> Option<FuncType> {
|
||||
let params = signature
|
||||
.params
|
||||
.iter()
|
||||
.filter(|p| p.purpose == ir::ArgumentPurpose::Normal)
|
||||
.map(|p| from_wasmtime_abiparam(p))
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
let results = signature
|
||||
.returns
|
||||
.iter()
|
||||
.map(|p| from_wasmtime_abiparam(p))
|
||||
.collect::<Vec<_>>();
|
||||
FuncType {
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
Some(FuncType {
|
||||
params: params.into_boxed_slice(),
|
||||
results: results.into_boxed_slice(),
|
||||
signature,
|
||||
}
|
||||
signature: Some(signature),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,14 +304,16 @@ impl GlobalType {
|
||||
self.mutability
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_global(global: &wasm::Global) -> GlobalType {
|
||||
let ty = ValType::from_wasmtime_type(global.ty);
|
||||
/// Returns `None` if the wasmtime global has a type that we can't
|
||||
/// represent, but that should only very rarely happen and indicate a bug.
|
||||
pub(crate) fn from_wasmtime_global(global: &wasm::Global) -> Option<GlobalType> {
|
||||
let ty = ValType::from_wasmtime_type(global.ty)?;
|
||||
let mutability = if global.mutability {
|
||||
Mutability::Var
|
||||
} else {
|
||||
Mutability::Const
|
||||
};
|
||||
GlobalType::new(ty, mutability)
|
||||
Some(GlobalType::new(ty, mutability))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::externals::Func;
|
||||
use crate::r#ref::{AnyRef, HostRef};
|
||||
use crate::r#ref::AnyRef;
|
||||
use crate::runtime::Store;
|
||||
use crate::types::ValType;
|
||||
use std::ptr;
|
||||
@@ -34,7 +34,7 @@ pub enum Val {
|
||||
AnyRef(AnyRef),
|
||||
|
||||
/// A first-class reference to a WebAssembly function.
|
||||
FuncRef(HostRef<Func>),
|
||||
FuncRef(Func),
|
||||
|
||||
/// A 128-bit number
|
||||
V128(u128),
|
||||
@@ -71,7 +71,7 @@ impl Val {
|
||||
}
|
||||
|
||||
/// Returns the corresponding [`ValType`] for this `Val`.
|
||||
pub fn r#type(&self) -> ValType {
|
||||
pub fn ty(&self) -> ValType {
|
||||
match self {
|
||||
Val::I32(_) => ValType::I32,
|
||||
Val::I64(_) => ValType::I64,
|
||||
@@ -111,7 +111,7 @@ impl Val {
|
||||
(I64(i64) i64 unwrap_i64 *e)
|
||||
(F32(f32) f32 unwrap_f32 f32::from_bits(*e))
|
||||
(F64(f64) f64 unwrap_f64 f64::from_bits(*e))
|
||||
(FuncRef(&HostRef<Func>) funcref unwrap_funcref e)
|
||||
(FuncRef(&Func) funcref unwrap_funcref e)
|
||||
(V128(u128) v128 unwrap_v128 *e)
|
||||
}
|
||||
|
||||
@@ -122,7 +122,6 @@ impl Val {
|
||||
pub fn anyref(&self) -> Option<AnyRef> {
|
||||
match self {
|
||||
Val::AnyRef(e) => Some(e.clone()),
|
||||
Val::FuncRef(e) => Some(e.anyref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -164,21 +163,12 @@ impl From<f64> for Val {
|
||||
|
||||
impl From<AnyRef> for Val {
|
||||
fn from(val: AnyRef) -> Val {
|
||||
match &val {
|
||||
AnyRef::Ref(r) => {
|
||||
if r.is_ref::<Func>() {
|
||||
Val::FuncRef(r.get_ref())
|
||||
} else {
|
||||
Val::AnyRef(val)
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("AnyRef::Other"),
|
||||
}
|
||||
Val::AnyRef(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Func>> for Val {
|
||||
fn from(val: HostRef<Func>) -> Val {
|
||||
impl From<Func> for Val {
|
||||
fn from(val: Func) -> Val {
|
||||
Val::FuncRef(val)
|
||||
}
|
||||
}
|
||||
@@ -206,7 +196,6 @@ pub(crate) fn into_checked_anyfunc(
|
||||
vmctx: ptr::null_mut(),
|
||||
},
|
||||
Val::FuncRef(f) => {
|
||||
let f = f.borrow();
|
||||
let (vmctx, func_ptr, signature) = match f.wasmtime_export() {
|
||||
wasmtime_runtime::Export::Function {
|
||||
vmctx,
|
||||
@@ -243,5 +232,5 @@ pub(crate) fn from_checked_anyfunc(
|
||||
vmctx: item.vmctx,
|
||||
};
|
||||
let f = Func::from_wasmtime_function(export, store, instance_handle);
|
||||
Val::FuncRef(HostRef::new(f))
|
||||
Val::FuncRef(f)
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
|
||||
use super::{
|
||||
AnyRef, Callable, Engine, ExportType, Extern, ExternType, Func, FuncType, Global, GlobalType,
|
||||
HostInfo, HostRef, ImportType, Instance, Limits, Memory, MemoryType, Module, Store, Table,
|
||||
TableType, Trap, Val, ValType,
|
||||
ImportType, Instance, Limits, Memory, MemoryType, Module, Store, Table, TableType, Trap, Val,
|
||||
ValType,
|
||||
};
|
||||
use crate::r#ref::{HostInfo, HostRef};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
@@ -243,6 +245,12 @@ enum wasm_externtype_t_type_cache {
|
||||
declare_vec!(wasm_externtype_vec_t, *mut wasm_externtype_t);
|
||||
|
||||
pub type wasm_externkind_t = u8;
|
||||
|
||||
const WASM_EXTERN_FUNC: wasm_externkind_t = 0;
|
||||
const WASM_EXTERN_GLOBAL: wasm_externkind_t = 1;
|
||||
const WASM_EXTERN_TABLE: wasm_externkind_t = 2;
|
||||
const WASM_EXTERN_MEMORY: wasm_externkind_t = 3;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_importtype_t {
|
||||
@@ -312,6 +320,7 @@ declare_vec!(wasm_frame_vec_t, *mut wasm_frame_t);
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_instance_t {
|
||||
instance: HostRef<Instance>,
|
||||
exports_cache: RefCell<Option<Vec<ExternHost>>>,
|
||||
}
|
||||
pub type wasm_message_t = wasm_name_t;
|
||||
#[repr(C)]
|
||||
@@ -336,12 +345,22 @@ pub struct wasm_module_t {
|
||||
pub struct wasm_shared_module_t {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
#[repr(C)]
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct wasm_func_t {
|
||||
func: HostRef<Func>,
|
||||
ext: Option<Box<wasm_extern_t>>,
|
||||
ext: wasm_extern_t,
|
||||
}
|
||||
|
||||
impl wasm_func_t {
|
||||
fn func(&self) -> &HostRef<Func> {
|
||||
match &self.ext.which {
|
||||
ExternHost::Func(f) => f,
|
||||
_ => unsafe { std::hint::unreachable_unchecked() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type wasm_func_callback_t = std::option::Option<
|
||||
unsafe extern "C" fn(args: *const wasm_val_t, results: *mut wasm_val_t) -> *mut wasm_trap_t,
|
||||
>;
|
||||
@@ -352,40 +371,67 @@ pub type wasm_func_callback_with_env_t = std::option::Option<
|
||||
results: *mut wasm_val_t,
|
||||
) -> *mut wasm_trap_t,
|
||||
>;
|
||||
#[repr(C)]
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct wasm_global_t {
|
||||
global: HostRef<Global>,
|
||||
ext: Option<Box<wasm_extern_t>>,
|
||||
ext: wasm_extern_t,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_table_t {
|
||||
table: HostRef<Table>,
|
||||
ext: Option<Box<wasm_extern_t>>,
|
||||
}
|
||||
pub type wasm_table_size_t = u32;
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_memory_t {
|
||||
memory: HostRef<Memory>,
|
||||
ext: Option<Box<wasm_extern_t>>,
|
||||
}
|
||||
pub type wasm_memory_pages_t = u32;
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_extern_t {
|
||||
ext: Extern,
|
||||
cache: wasm_extern_t_type_cache,
|
||||
|
||||
impl wasm_global_t {
|
||||
fn global(&self) -> &HostRef<Global> {
|
||||
match &self.ext.which {
|
||||
ExternHost::Global(g) => g,
|
||||
_ => unsafe { std::hint::unreachable_unchecked() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum wasm_extern_t_type_cache {
|
||||
Empty,
|
||||
Func(wasm_func_t),
|
||||
Global(wasm_global_t),
|
||||
Memory(wasm_memory_t),
|
||||
Table(wasm_table_t),
|
||||
#[repr(transparent)]
|
||||
pub struct wasm_table_t {
|
||||
ext: wasm_extern_t,
|
||||
}
|
||||
|
||||
impl wasm_table_t {
|
||||
fn table(&self) -> &HostRef<Table> {
|
||||
match &self.ext.which {
|
||||
ExternHost::Table(t) => t,
|
||||
_ => unsafe { std::hint::unreachable_unchecked() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type wasm_table_size_t = u32;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct wasm_memory_t {
|
||||
ext: wasm_extern_t,
|
||||
}
|
||||
|
||||
impl wasm_memory_t {
|
||||
fn memory(&self) -> &HostRef<Memory> {
|
||||
match &self.ext.which {
|
||||
ExternHost::Memory(m) => m,
|
||||
_ => unsafe { std::hint::unreachable_unchecked() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type wasm_memory_pages_t = u32;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_extern_t {
|
||||
which: ExternHost,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ExternHost {
|
||||
Func(HostRef<Func>),
|
||||
Global(HostRef<Global>),
|
||||
Memory(HostRef<Memory>),
|
||||
Table(HostRef<Table>),
|
||||
}
|
||||
|
||||
declare_vec!(wasm_extern_vec_t, *mut wasm_extern_t);
|
||||
@@ -415,16 +461,9 @@ pub unsafe extern "C" fn wasm_engine_new() -> *mut wasm_engine_t {
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_extern_as_func(e: *mut wasm_extern_t) -> *mut wasm_func_t {
|
||||
if let wasm_extern_t_type_cache::Empty = (*e).cache {
|
||||
(*e).cache = wasm_extern_t_type_cache::Func(wasm_func_t {
|
||||
func: (*e).ext.func().unwrap().clone(),
|
||||
ext: None,
|
||||
});
|
||||
}
|
||||
|
||||
match &mut (*e).cache {
|
||||
wasm_extern_t_type_cache::Func(f) => f,
|
||||
_ => panic!("wasm_extern_as_func"),
|
||||
match &(*e).which {
|
||||
ExternHost::Func(_) => e.cast(),
|
||||
_ => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,17 +474,7 @@ pub unsafe extern "C" fn wasm_extern_vec_delete(v: *mut wasm_extern_vec_t) {
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_func_as_extern(f: *mut wasm_func_t) -> *mut wasm_extern_t {
|
||||
if (*f).ext.is_none() {
|
||||
(*f).ext = Some(Box::new(wasm_extern_t {
|
||||
ext: Extern::Func((*f).func.clone()),
|
||||
cache: wasm_extern_t_type_cache::Empty,
|
||||
}));
|
||||
}
|
||||
|
||||
match &mut (*f).ext {
|
||||
Some(e) => e.as_mut(),
|
||||
_ => panic!("wasm_func_as_extern"),
|
||||
}
|
||||
&mut (*f).ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -454,7 +483,7 @@ pub unsafe extern "C" fn wasm_func_call(
|
||||
args: *const wasm_val_t,
|
||||
results: *mut wasm_val_t,
|
||||
) -> *mut wasm_trap_t {
|
||||
let func = (*func).func.borrow();
|
||||
let func = (*func).func().borrow();
|
||||
let mut params = Vec::with_capacity(func.param_arity());
|
||||
for i in 0..func.param_arity() {
|
||||
let val = &(*args.add(i));
|
||||
@@ -606,8 +635,9 @@ pub unsafe extern "C" fn wasm_func_new(
|
||||
let ty = (*ty).functype.clone();
|
||||
let callback = Rc::new(callback);
|
||||
let func = Box::new(wasm_func_t {
|
||||
func: HostRef::new(Func::new(store, ty, callback)),
|
||||
ext: None,
|
||||
ext: wasm_extern_t {
|
||||
which: ExternHost::Func(HostRef::new(Func::new(store, ty, callback))),
|
||||
},
|
||||
});
|
||||
Box::into_raw(func)
|
||||
}
|
||||
@@ -656,17 +686,35 @@ pub unsafe extern "C" fn wasm_instance_new(
|
||||
imports: *const *const wasm_extern_t,
|
||||
result: *mut *mut wasm_trap_t,
|
||||
) -> *mut wasm_instance_t {
|
||||
let store = &(*store).store.borrow();
|
||||
let mut externs: Vec<Extern> = Vec::with_capacity((*module).imports.len());
|
||||
for i in 0..(*module).imports.len() {
|
||||
let import = *imports.add(i);
|
||||
externs.push((*import).ext.clone());
|
||||
externs.push(match &(*import).which {
|
||||
ExternHost::Func(e) => Extern::Func(e.borrow().clone()),
|
||||
ExternHost::Table(e) => Extern::Table(e.borrow().clone()),
|
||||
ExternHost::Global(e) => Extern::Global(e.borrow().clone()),
|
||||
ExternHost::Memory(e) => Extern::Memory(e.borrow().clone()),
|
||||
});
|
||||
}
|
||||
let store = &(*store).store.borrow();
|
||||
let module = &(*module).module.borrow();
|
||||
match Instance::new(store, module, &externs) {
|
||||
// FIXME(WebAssembly/wasm-c-api#126) what else can we do with the `store`
|
||||
// argument?
|
||||
if !Store::ptr_eq(&store, module.store()) {
|
||||
if !result.is_null() {
|
||||
let trap = Trap::new("wasm_store_t must match store in wasm_module_t");
|
||||
let trap = Box::new(wasm_trap_t {
|
||||
trap: HostRef::new(trap),
|
||||
});
|
||||
(*result) = Box::into_raw(trap);
|
||||
}
|
||||
return ptr::null_mut();
|
||||
}
|
||||
match Instance::new(module, &externs) {
|
||||
Ok(instance) => {
|
||||
let instance = Box::new(wasm_instance_t {
|
||||
instance: HostRef::new(instance),
|
||||
exports_cache: RefCell::new(None),
|
||||
});
|
||||
if !result.is_null() {
|
||||
(*result) = ptr::null_mut();
|
||||
@@ -694,14 +742,23 @@ pub unsafe extern "C" fn wasm_instance_exports(
|
||||
instance: *const wasm_instance_t,
|
||||
out: *mut wasm_extern_vec_t,
|
||||
) {
|
||||
let instance = &(*instance).instance.borrow();
|
||||
let exports = instance.exports();
|
||||
let mut cache = (*instance).exports_cache.borrow_mut();
|
||||
let exports = cache.get_or_insert_with(|| {
|
||||
let instance = &(*instance).instance.borrow();
|
||||
instance
|
||||
.exports()
|
||||
.iter()
|
||||
.map(|e| match e {
|
||||
Extern::Func(f) => ExternHost::Func(HostRef::new(f.clone())),
|
||||
Extern::Global(f) => ExternHost::Global(HostRef::new(f.clone())),
|
||||
Extern::Memory(f) => ExternHost::Memory(HostRef::new(f.clone())),
|
||||
Extern::Table(f) => ExternHost::Table(HostRef::new(f.clone())),
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
let mut buffer = Vec::with_capacity(exports.len());
|
||||
for e in exports.iter() {
|
||||
let ext = Box::new(wasm_extern_t {
|
||||
ext: e.clone(),
|
||||
cache: wasm_extern_t_type_cache::Empty,
|
||||
});
|
||||
for e in exports {
|
||||
let ext = Box::new(wasm_extern_t { which: e.clone() });
|
||||
buffer.push(Box::into_raw(ext));
|
||||
}
|
||||
(*out).set_buffer(buffer);
|
||||
@@ -817,8 +874,9 @@ pub unsafe extern "C" fn wasm_func_new_with_env(
|
||||
finalizer,
|
||||
});
|
||||
let func = Box::new(wasm_func_t {
|
||||
func: HostRef::new(Func::new(store, ty, callback)),
|
||||
ext: None,
|
||||
ext: wasm_extern_t {
|
||||
which: ExternHost::Func(HostRef::new(Func::new(store, ty, callback))),
|
||||
},
|
||||
});
|
||||
Box::into_raw(func)
|
||||
}
|
||||
@@ -1012,24 +1070,25 @@ pub unsafe extern "C" fn wasm_exporttype_vec_delete(et: *mut wasm_exporttype_vec
|
||||
(*et).uninitialize();
|
||||
}
|
||||
|
||||
fn from_externtype(ty: &ExternType) -> wasm_externkind_t {
|
||||
match ty {
|
||||
ExternType::Func(_) => 0,
|
||||
ExternType::Global(_) => 1,
|
||||
ExternType::Table(_) => 2,
|
||||
ExternType::Memory(_) => 3,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_extern_kind(e: *const wasm_extern_t) -> wasm_externkind_t {
|
||||
from_externtype(&(*e).ext.r#type())
|
||||
match (*e).which {
|
||||
ExternHost::Func(_) => WASM_EXTERN_FUNC,
|
||||
ExternHost::Global(_) => WASM_EXTERN_GLOBAL,
|
||||
ExternHost::Table(_) => WASM_EXTERN_TABLE,
|
||||
ExternHost::Memory(_) => WASM_EXTERN_MEMORY,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_extern_type(e: *const wasm_extern_t) -> *mut wasm_externtype_t {
|
||||
let et = Box::new(wasm_externtype_t {
|
||||
ty: (*e).ext.r#type(),
|
||||
ty: match &(*e).which {
|
||||
ExternHost::Func(f) => ExternType::Func(f.borrow().ty().clone()),
|
||||
ExternHost::Global(f) => ExternType::Global(f.borrow().ty().clone()),
|
||||
ExternHost::Table(f) => ExternType::Table(f.borrow().ty().clone()),
|
||||
ExternHost::Memory(f) => ExternType::Memory(f.borrow().ty().clone()),
|
||||
},
|
||||
cache: wasm_externtype_t_type_cache::Empty,
|
||||
});
|
||||
Box::into_raw(et)
|
||||
@@ -1120,17 +1179,22 @@ pub unsafe extern "C" fn wasm_externtype_delete(et: *mut wasm_externtype_t) {
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_externtype_kind(et: *const wasm_externtype_t) -> wasm_externkind_t {
|
||||
from_externtype(&(*et).ty)
|
||||
match &(*et).ty {
|
||||
ExternType::Func(_) => WASM_EXTERN_FUNC,
|
||||
ExternType::Table(_) => WASM_EXTERN_TABLE,
|
||||
ExternType::Global(_) => WASM_EXTERN_GLOBAL,
|
||||
ExternType::Memory(_) => WASM_EXTERN_MEMORY,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_func_param_arity(f: *const wasm_func_t) -> usize {
|
||||
(*f).func.borrow().param_arity()
|
||||
(*f).func().borrow().param_arity()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_func_result_arity(f: *const wasm_func_t) -> usize {
|
||||
(*f).func.borrow().result_arity()
|
||||
(*f).func().borrow().result_arity()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1279,32 +1343,15 @@ pub unsafe extern "C" fn wasm_valtype_kind(vt: *const wasm_valtype_t) -> wasm_va
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_extern_as_global(e: *mut wasm_extern_t) -> *mut wasm_global_t {
|
||||
if let wasm_extern_t_type_cache::Empty = (*e).cache {
|
||||
(*e).cache = wasm_extern_t_type_cache::Global(wasm_global_t {
|
||||
global: (*e).ext.global().unwrap().clone(),
|
||||
ext: None,
|
||||
});
|
||||
}
|
||||
|
||||
match &mut (*e).cache {
|
||||
wasm_extern_t_type_cache::Global(g) => g,
|
||||
_ => panic!("wasm_extern_as_global"),
|
||||
match &(*e).which {
|
||||
ExternHost::Global(_) => e.cast(),
|
||||
_ => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_global_as_extern(g: *mut wasm_global_t) -> *mut wasm_extern_t {
|
||||
if (*g).ext.is_none() {
|
||||
(*g).ext = Some(Box::new(wasm_extern_t {
|
||||
ext: Extern::Global((*g).global.clone()),
|
||||
cache: wasm_extern_t_type_cache::Empty,
|
||||
}));
|
||||
}
|
||||
|
||||
match &mut (*g).ext {
|
||||
Some(e) => e.as_mut(),
|
||||
_ => panic!("wasm_global_as_extern"),
|
||||
}
|
||||
&mut (*g).ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1322,7 +1369,7 @@ pub unsafe extern "C" fn wasm_global_same(
|
||||
g1: *const wasm_global_t,
|
||||
g2: *const wasm_global_t,
|
||||
) -> bool {
|
||||
(*g1).global.ptr_eq(&(*g2).global)
|
||||
(*g1).global().ptr_eq(&(*g2).global())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1336,18 +1383,22 @@ pub unsafe extern "C" fn wasm_global_new(
|
||||
(*gt).globaltype.clone(),
|
||||
(*val).val(),
|
||||
));
|
||||
let g = Box::new(wasm_global_t { global, ext: None });
|
||||
let g = Box::new(wasm_global_t {
|
||||
ext: wasm_extern_t {
|
||||
which: ExternHost::Global(global),
|
||||
},
|
||||
});
|
||||
Box::into_raw(g)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_global_get(g: *const wasm_global_t, out: *mut wasm_val_t) {
|
||||
(*out).set((*g).global.borrow_mut().get());
|
||||
(*out).set((*g).global().borrow().get());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_global_set(g: *mut wasm_global_t, val: *const wasm_val_t) {
|
||||
(*g).global.borrow_mut().set((*val).val())
|
||||
(*g).global().borrow().set((*val).val())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1377,32 +1428,15 @@ pub unsafe extern "C" fn wasm_globaltype_new(
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_extern_as_memory(e: *mut wasm_extern_t) -> *mut wasm_memory_t {
|
||||
if let wasm_extern_t_type_cache::Empty = (*e).cache {
|
||||
(*e).cache = wasm_extern_t_type_cache::Memory(wasm_memory_t {
|
||||
memory: (*e).ext.memory().unwrap().clone(),
|
||||
ext: None,
|
||||
});
|
||||
}
|
||||
|
||||
match &mut (*e).cache {
|
||||
wasm_extern_t_type_cache::Memory(m) => m,
|
||||
_ => panic!("wasm_extern_as_memory"),
|
||||
match &(*e).which {
|
||||
ExternHost::Memory(_) => e.cast(),
|
||||
_ => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_memory_as_extern(m: *mut wasm_memory_t) -> *mut wasm_extern_t {
|
||||
if (*m).ext.is_none() {
|
||||
(*m).ext = Some(Box::new(wasm_extern_t {
|
||||
ext: Extern::Memory((*m).memory.clone()),
|
||||
cache: wasm_extern_t_type_cache::Empty,
|
||||
}));
|
||||
}
|
||||
|
||||
match &mut (*m).ext {
|
||||
Some(e) => e.as_mut(),
|
||||
_ => panic!("wasm_global_as_extern"),
|
||||
}
|
||||
&mut (*m).ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1420,22 +1454,22 @@ pub unsafe extern "C" fn wasm_memory_same(
|
||||
m1: *const wasm_memory_t,
|
||||
m2: *const wasm_memory_t,
|
||||
) -> bool {
|
||||
(*m1).memory.ptr_eq(&(*m2).memory)
|
||||
(*m1).memory().ptr_eq(&(*m2).memory())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_memory_data(m: *mut wasm_memory_t) -> *mut u8 {
|
||||
(*m).memory.borrow().data_ptr()
|
||||
(*m).memory().borrow().data_ptr()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_memory_data_size(m: *const wasm_memory_t) -> usize {
|
||||
(*m).memory.borrow().data_size()
|
||||
(*m).memory().borrow().data_size()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_memory_size(m: *const wasm_memory_t) -> wasm_memory_pages_t {
|
||||
(*m).memory.borrow().size()
|
||||
(*m).memory().borrow().size()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1443,7 +1477,7 @@ pub unsafe extern "C" fn wasm_memory_grow(
|
||||
m: *mut wasm_memory_t,
|
||||
delta: wasm_memory_pages_t,
|
||||
) -> bool {
|
||||
(*m).memory.borrow_mut().grow(delta)
|
||||
(*m).memory().borrow().grow(delta)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1455,7 +1489,11 @@ pub unsafe extern "C" fn wasm_memory_new(
|
||||
&(*store).store.borrow(),
|
||||
(*mt).memorytype.clone(),
|
||||
));
|
||||
let m = Box::new(wasm_memory_t { memory, ext: None });
|
||||
let m = Box::new(wasm_memory_t {
|
||||
ext: wasm_extern_t {
|
||||
which: ExternHost::Memory(memory),
|
||||
},
|
||||
});
|
||||
Box::into_raw(m)
|
||||
}
|
||||
|
||||
@@ -1483,37 +1521,20 @@ pub unsafe extern "C" fn wasm_memorytype_new(
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_extern_as_table(e: *mut wasm_extern_t) -> *mut wasm_table_t {
|
||||
if let wasm_extern_t_type_cache::Empty = (*e).cache {
|
||||
(*e).cache = wasm_extern_t_type_cache::Table(wasm_table_t {
|
||||
table: (*e).ext.table().unwrap().clone(),
|
||||
ext: None,
|
||||
});
|
||||
}
|
||||
|
||||
match &mut (*e).cache {
|
||||
wasm_extern_t_type_cache::Table(t) => t,
|
||||
_ => panic!("wasm_extern_as_table"),
|
||||
match &(*e).which {
|
||||
ExternHost::Table(_) => e.cast(),
|
||||
_ => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_table_as_extern(t: *mut wasm_table_t) -> *mut wasm_extern_t {
|
||||
if (*t).ext.is_none() {
|
||||
(*t).ext = Some(Box::new(wasm_extern_t {
|
||||
ext: Extern::Table((*t).table.clone()),
|
||||
cache: wasm_extern_t_type_cache::Empty,
|
||||
}));
|
||||
}
|
||||
|
||||
match &mut (*t).ext {
|
||||
Some(e) => e.as_mut(),
|
||||
_ => panic!("wasm_table_as_extern"),
|
||||
}
|
||||
&mut (*t).ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_func_as_ref(f: *mut wasm_func_t) -> *mut wasm_ref_t {
|
||||
let r = (*f).func.anyref();
|
||||
let r = (*f).func().anyref();
|
||||
let f = Box::new(wasm_ref_t { r });
|
||||
Box::into_raw(f)
|
||||
}
|
||||
@@ -1547,12 +1568,13 @@ pub unsafe extern "C" fn wasm_table_new(
|
||||
Val::AnyRef(AnyRef::Null)
|
||||
};
|
||||
let t = Box::new(wasm_table_t {
|
||||
table: HostRef::new(Table::new(
|
||||
&(*store).store.borrow(),
|
||||
(*tt).tabletype.clone(),
|
||||
init,
|
||||
)),
|
||||
ext: None,
|
||||
ext: wasm_extern_t {
|
||||
which: ExternHost::Table(HostRef::new(Table::new(
|
||||
&(*store).store.borrow(),
|
||||
(*tt).tabletype.clone(),
|
||||
init,
|
||||
))),
|
||||
},
|
||||
});
|
||||
Box::into_raw(t)
|
||||
}
|
||||
@@ -1582,7 +1604,7 @@ pub unsafe extern "C" fn wasm_table_get(
|
||||
t: *const wasm_table_t,
|
||||
index: wasm_table_size_t,
|
||||
) -> *mut wasm_ref_t {
|
||||
let val = (*t).table.borrow().get(index);
|
||||
let val = (*t).table().borrow().get(index);
|
||||
into_funcref(val)
|
||||
}
|
||||
|
||||
@@ -1593,12 +1615,12 @@ pub unsafe extern "C" fn wasm_table_set(
|
||||
r: *mut wasm_ref_t,
|
||||
) -> bool {
|
||||
let val = from_funcref(r);
|
||||
(*t).table.borrow().set(index, val)
|
||||
(*t).table().borrow().set(index, val)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_table_size(t: *const wasm_table_t) -> wasm_table_size_t {
|
||||
(*t).table.borrow().size()
|
||||
(*t).table().borrow().size()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1608,12 +1630,12 @@ pub unsafe extern "C" fn wasm_table_grow(
|
||||
init: *mut wasm_ref_t,
|
||||
) -> bool {
|
||||
let init = from_funcref(init);
|
||||
(*t).table.borrow_mut().grow(delta, init)
|
||||
(*t).table().borrow().grow(delta, init)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_table_same(t1: *const wasm_table_t, t2: *const wasm_table_t) -> bool {
|
||||
(*t1).table.ptr_eq(&(*t2).table)
|
||||
(*t1).table().ptr_eq((*t2).table())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
68
crates/api/tests/import-indexes.rs
Normal file
68
crates/api/tests/import-indexes.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::rc::Rc;
|
||||
use wasmtime::*;
|
||||
|
||||
#[test]
|
||||
fn same_import_names_still_distinct() -> anyhow::Result<()> {
|
||||
const WAT: &str = r#"
|
||||
(module
|
||||
(import "" "" (func $a (result i32)))
|
||||
(import "" "" (func $b (result f32)))
|
||||
(func (export "foo") (result i32)
|
||||
call $a
|
||||
call $b
|
||||
i32.trunc_f32_u
|
||||
i32.add)
|
||||
)
|
||||
"#;
|
||||
|
||||
struct Ret1;
|
||||
|
||||
impl Callable for Ret1 {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
|
||||
assert!(params.is_empty());
|
||||
assert_eq!(results.len(), 1);
|
||||
results[0] = 1i32.into();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Ret2;
|
||||
|
||||
impl Callable for Ret2 {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
|
||||
assert!(params.is_empty());
|
||||
assert_eq!(results.len(), 1);
|
||||
results[0] = 2.0f32.into();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let store = Store::default();
|
||||
let wasm = wat::parse_str(WAT)?;
|
||||
let module = Module::new(&store, &wasm)?;
|
||||
|
||||
let imports = [
|
||||
Func::new(
|
||||
&store,
|
||||
FuncType::new(Box::new([]), Box::new([ValType::I32])),
|
||||
Rc::new(Ret1),
|
||||
)
|
||||
.into(),
|
||||
Func::new(
|
||||
&store,
|
||||
FuncType::new(Box::new([]), Box::new([ValType::F32])),
|
||||
Rc::new(Ret2),
|
||||
)
|
||||
.into(),
|
||||
];
|
||||
let instance = Instance::new(&module, &imports)?;
|
||||
|
||||
let func = instance.find_export_by_name("foo").unwrap().func().unwrap();
|
||||
let results = func.call(&[])?;
|
||||
assert_eq!(results.len(), 1);
|
||||
match results[0] {
|
||||
Val::I32(n) => assert_eq!(n, 3),
|
||||
_ => panic!("unexpected type of return"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use wasmtime::*;
|
||||
|
||||
@@ -16,7 +16,7 @@ fn test_import_calling_export() {
|
||||
"#;
|
||||
|
||||
struct Callback {
|
||||
pub other: RefCell<Option<HostRef<Func>>>,
|
||||
pub other: RefCell<Option<Func>>,
|
||||
}
|
||||
|
||||
impl Callable for Callback {
|
||||
@@ -25,7 +25,6 @@ fn test_import_calling_export() {
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.expect("expected a function ref")
|
||||
.borrow()
|
||||
.call(&[])
|
||||
.expect("expected function not to trap");
|
||||
Ok(())
|
||||
@@ -40,18 +39,17 @@ fn test_import_calling_export() {
|
||||
other: RefCell::new(None),
|
||||
});
|
||||
|
||||
let callback_func = HostRef::new(Func::new(
|
||||
let callback_func = Func::new(
|
||||
&store,
|
||||
FuncType::new(Box::new([]), Box::new([])),
|
||||
callback.clone(),
|
||||
));
|
||||
|
||||
let imports = vec![callback_func.into()];
|
||||
let instance = HostRef::new(
|
||||
Instance::new(&store, &module, imports.as_slice()).expect("failed to instantiate module"),
|
||||
);
|
||||
|
||||
let exports = Ref::map(instance.borrow(), |instance| instance.exports());
|
||||
let imports = vec![callback_func.into()];
|
||||
let instance =
|
||||
Instance::new(&module, imports.as_slice()).expect("failed to instantiate module");
|
||||
|
||||
let exports = instance.exports();
|
||||
assert!(!exports.is_empty());
|
||||
|
||||
let run_func = exports[0]
|
||||
@@ -65,8 +63,5 @@ fn test_import_calling_export() {
|
||||
.clone(),
|
||||
);
|
||||
|
||||
run_func
|
||||
.borrow()
|
||||
.call(&[])
|
||||
.expect("expected function not to trap");
|
||||
run_func.call(&[]).expect("expected function not to trap");
|
||||
}
|
||||
|
||||
33
crates/api/tests/invoke_func_via_table.rs
Normal file
33
crates/api/tests/invoke_func_via_table.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use anyhow::{Context as _, Result};
|
||||
use wasmtime::*;
|
||||
|
||||
#[test]
|
||||
fn test_invoke_func_via_table() -> Result<()> {
|
||||
let store = Store::default();
|
||||
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module
|
||||
(func $f (result i64) (i64.const 42))
|
||||
|
||||
(table (export "table") 1 1 anyfunc)
|
||||
(elem (i32.const 0) $f)
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
let module = Module::new(&store, &binary).context("> Error compiling module!")?;
|
||||
let instance = Instance::new(&store, &module, &[]).context("> Error instantiating module!")?;
|
||||
|
||||
let f = instance
|
||||
.find_export_by_name("table")
|
||||
.unwrap()
|
||||
.table()
|
||||
.unwrap()
|
||||
.get(0)
|
||||
.funcref()
|
||||
.unwrap()
|
||||
.clone();
|
||||
let result = f.call(&[]).unwrap();
|
||||
assert_eq!(result[0].unwrap_i64(), 42);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,54 +1,38 @@
|
||||
use wasmtime::*;
|
||||
use wat::parse_str;
|
||||
|
||||
#[test]
|
||||
fn test_module_no_name() -> Result<(), String> {
|
||||
fn test_module_no_name() -> anyhow::Result<()> {
|
||||
let store = Store::default();
|
||||
let binary = parse_str(
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module
|
||||
(func (export "run") (nop))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
||||
(module
|
||||
(func (export "run") (nop))
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let module = HostRef::new(
|
||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?,
|
||||
);
|
||||
assert_eq!(module.borrow().name().cloned(), None);
|
||||
let module = Module::new(&store, &binary)?;
|
||||
assert_eq!(module.name(), None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_module_name() -> Result<(), String> {
|
||||
fn test_module_name() -> anyhow::Result<()> {
|
||||
let store = Store::default();
|
||||
let binary = parse_str(
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module $from_name_section
|
||||
(func (export "run") (nop))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
||||
(module $from_name_section
|
||||
(func (export "run") (nop))
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let module = HostRef::new(
|
||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?,
|
||||
);
|
||||
assert_eq!(
|
||||
module.borrow().name().cloned(),
|
||||
Some("from_name_section".to_string())
|
||||
);
|
||||
let module = Module::new(&store, &binary)?;
|
||||
assert_eq!(module.name(), Some("from_name_section"));
|
||||
|
||||
let module = HostRef::new(
|
||||
Module::new_with_name(&store, &binary, "override".to_string())
|
||||
.map_err(|e| format!("failed to compile module: {}", e))?,
|
||||
);
|
||||
assert_eq!(
|
||||
module.borrow().name().cloned(),
|
||||
Some("override".to_string())
|
||||
);
|
||||
let module = Module::new_with_name(&store, &binary, "override")?;
|
||||
assert_eq!(module.name(), Some("override"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -26,20 +26,16 @@ fn test_trap_return() -> Result<(), String> {
|
||||
let module =
|
||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
||||
let hello_type = FuncType::new(Box::new([]), Box::new([]));
|
||||
let hello_func = HostRef::new(Func::new(&store, hello_type, Rc::new(HelloCallback)));
|
||||
let hello_func = Func::new(&store, hello_type, Rc::new(HelloCallback));
|
||||
|
||||
let imports = vec![hello_func.into()];
|
||||
let instance = Instance::new(&store, &module, imports.as_slice())
|
||||
let instance = Instance::new(&module, &imports)
|
||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
||||
let run_func = instance.exports()[0]
|
||||
.func()
|
||||
.expect("expected function export");
|
||||
|
||||
let e = run_func
|
||||
.borrow()
|
||||
.call(&[])
|
||||
.err()
|
||||
.expect("error calling function");
|
||||
let e = run_func.call(&[]).err().expect("error calling function");
|
||||
|
||||
assert_eq!(e.message(), "test 123");
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-debug"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "Debug utils for WebAsssembly code in Cranelift"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -13,10 +13,10 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
gimli = "0.19.0"
|
||||
wasmparser = "0.45.1"
|
||||
faerie = "0.13.0"
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
target-lexicon = { version = "0.9.0", default-features = false }
|
||||
wasmparser = "0.47.0"
|
||||
faerie = "0.14.0"
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0.4"
|
||||
more-asserts = "0.2.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-environ"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "Standalone environment support for WebAsssembly code in Cranelift"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -13,11 +13,11 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
cranelift-codegen = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-entity = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.52.0", features = ["enable-serde"] }
|
||||
wasmparser = "0.45.1"
|
||||
lightbeam = { path = "../lightbeam", optional = true }
|
||||
cranelift-codegen = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-entity = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.54", features = ["enable-serde"] }
|
||||
wasmparser = "0.47.0"
|
||||
lightbeam = { path = "../lightbeam", optional = true, version = "0.9.0" }
|
||||
indexmap = "1.0.2"
|
||||
rayon = "1.2.1"
|
||||
thiserror = "1.0.4"
|
||||
@@ -43,10 +43,10 @@ errno = "0.2.4"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
target-lexicon = { version = "0.9.0", default-features = false }
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
pretty_env_logger = "0.3.0"
|
||||
rand = { version = "0.7.0", default-features = false, features = ["small_rng"] }
|
||||
cranelift-codegen = { version = "0.52.0", features = ["enable-serde", "all-arch"] }
|
||||
cranelift-codegen = { version = "0.54", features = ["enable-serde", "all-arch"] }
|
||||
filetime = "0.2.7"
|
||||
|
||||
[badges]
|
||||
|
||||
@@ -362,6 +362,10 @@ impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environm
|
||||
}
|
||||
|
||||
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
|
||||
fn is_wasm_parameter(&self, func: &ir::Function, index: usize) -> bool {
|
||||
func.signature.params[index].purpose == ir::ArgumentPurpose::Normal
|
||||
}
|
||||
|
||||
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult<ir::Table> {
|
||||
let pointer_type = self.pointer_type();
|
||||
|
||||
|
||||
@@ -136,17 +136,18 @@ pub struct Module {
|
||||
/// Unprocessed signatures exactly as provided by `declare_signature()`.
|
||||
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
|
||||
|
||||
/// Names of imported functions.
|
||||
pub imported_funcs: PrimaryMap<FuncIndex, (String, String)>,
|
||||
/// Names of imported functions, as well as the index of the import that
|
||||
/// performed this import.
|
||||
pub imported_funcs: PrimaryMap<FuncIndex, (String, String, u32)>,
|
||||
|
||||
/// Names of imported tables.
|
||||
pub imported_tables: PrimaryMap<TableIndex, (String, String)>,
|
||||
pub imported_tables: PrimaryMap<TableIndex, (String, String, u32)>,
|
||||
|
||||
/// Names of imported memories.
|
||||
pub imported_memories: PrimaryMap<MemoryIndex, (String, String)>,
|
||||
pub imported_memories: PrimaryMap<MemoryIndex, (String, String, u32)>,
|
||||
|
||||
/// Names of imported globals.
|
||||
pub imported_globals: PrimaryMap<GlobalIndex, (String, String)>,
|
||||
pub imported_globals: PrimaryMap<GlobalIndex, (String, String, u32)>,
|
||||
|
||||
/// Types of functions, imported and local.
|
||||
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
||||
|
||||
@@ -55,6 +55,7 @@ impl<'data> ModuleTranslation<'data> {
|
||||
pub struct ModuleEnvironment<'data> {
|
||||
/// The result to be filled in.
|
||||
result: ModuleTranslation<'data>,
|
||||
imports: u32,
|
||||
}
|
||||
|
||||
impl<'data> ModuleEnvironment<'data> {
|
||||
@@ -69,6 +70,7 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
tunables,
|
||||
module_translation: None,
|
||||
},
|
||||
imports: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,10 +125,12 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
);
|
||||
self.result.module.functions.push(sig_index);
|
||||
|
||||
self.result
|
||||
.module
|
||||
.imported_funcs
|
||||
.push((String::from(module), String::from(field)));
|
||||
self.result.module.imported_funcs.push((
|
||||
String::from(module),
|
||||
String::from(field),
|
||||
self.imports,
|
||||
));
|
||||
self.imports += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -139,10 +143,12 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
let plan = TablePlan::for_table(table, &self.result.tunables);
|
||||
self.result.module.table_plans.push(plan);
|
||||
|
||||
self.result
|
||||
.module
|
||||
.imported_tables
|
||||
.push((String::from(module), String::from(field)));
|
||||
self.result.module.imported_tables.push((
|
||||
String::from(module),
|
||||
String::from(field),
|
||||
self.imports,
|
||||
));
|
||||
self.imports += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -160,10 +166,12 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
|
||||
self.result.module.memory_plans.push(plan);
|
||||
|
||||
self.result
|
||||
.module
|
||||
.imported_memories
|
||||
.push((String::from(module), String::from(field)));
|
||||
self.result.module.imported_memories.push((
|
||||
String::from(module),
|
||||
String::from(field),
|
||||
self.imports,
|
||||
));
|
||||
self.imports += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -180,10 +188,12 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
||||
);
|
||||
self.result.module.globals.push(global);
|
||||
|
||||
self.result
|
||||
.module
|
||||
.imported_globals
|
||||
.push((String::from(module), String::from(field)));
|
||||
self.result.module.imported_globals.push((
|
||||
String::from(module),
|
||||
String::from(field),
|
||||
self.imports,
|
||||
));
|
||||
self.imports += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Fuzzing infrastructure for Wasmtime"
|
||||
edition = "2018"
|
||||
name = "wasmtime-fuzzing"
|
||||
publish = false
|
||||
version = "0.1.0"
|
||||
version = "0.9.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -14,11 +14,11 @@ arbitrary = "0.2.0"
|
||||
binaryen = "0.8.2"
|
||||
env_logger = { version = "0.7.1", optional = true }
|
||||
log = "0.4.8"
|
||||
wasmparser = "0.45.1"
|
||||
wasmparser = "0.47.0"
|
||||
wasmprinter = "0.2.0"
|
||||
wasmtime = { path = "../api" }
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
wasmtime-jit = { path = "../jit" }
|
||||
wasmtime = { path = "../api", version = "0.9.0" }
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
wasmtime-jit = { path = "../jit", version = "0.9.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
wat = "1.0"
|
||||
|
||||
@@ -60,7 +60,7 @@ pub fn instantiate(wasm: &[u8], strategy: Strategy) {
|
||||
// aren't caught during validation or compilation. For example, an imported
|
||||
// table might not have room for an element segment that we want to
|
||||
// initialize into it.
|
||||
let _result = Instance::new(&store, &module, &imports);
|
||||
let _result = Instance::new(&module, &imports);
|
||||
}
|
||||
|
||||
/// Compile the Wasm buffer, and implicitly fail if we have an unexpected
|
||||
@@ -96,7 +96,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
|
||||
let mut engine: Option<Engine> = None;
|
||||
let mut store: Option<Store> = None;
|
||||
let mut modules: HashMap<usize, Module> = Default::default();
|
||||
let mut instances: HashMap<usize, HostRef<Instance>> = Default::default();
|
||||
let mut instances: HashMap<usize, Instance> = Default::default();
|
||||
|
||||
for call in api.calls {
|
||||
match call {
|
||||
@@ -152,8 +152,8 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
|
||||
// aren't caught during validation or compilation. For example, an imported
|
||||
// table might not have room for an element segment that we want to
|
||||
// initialize into it.
|
||||
if let Ok(instance) = Instance::new(store.as_ref().unwrap(), &module, &imports) {
|
||||
instances.insert(id, HostRef::new(instance));
|
||||
if let Ok(instance) = Instance::new(&module, &imports) {
|
||||
instances.insert(id, instance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,25 +175,22 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
|
||||
}
|
||||
};
|
||||
|
||||
let funcs = {
|
||||
let instance = instance.borrow();
|
||||
instance
|
||||
.exports()
|
||||
.iter()
|
||||
.filter_map(|e| match e {
|
||||
Extern::Func(f) => Some(f.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let funcs = instance
|
||||
.exports()
|
||||
.iter()
|
||||
.filter_map(|e| match e {
|
||||
Extern::Func(f) => Some(f.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if funcs.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let nth = nth % funcs.len();
|
||||
let f = funcs[nth].borrow();
|
||||
let ty = f.r#type();
|
||||
let f = &funcs[nth];
|
||||
let ty = f.ty();
|
||||
let params = match ty
|
||||
.params()
|
||||
.iter()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::rc::Rc;
|
||||
use wasmtime::{
|
||||
Callable, Extern, ExternType, Func, FuncType, Global, GlobalType, HostRef, ImportType, Memory,
|
||||
Callable, Extern, ExternType, Func, FuncType, Global, GlobalType, ImportType, Memory,
|
||||
MemoryType, Store, Table, TableType, Trap, Val, ValType,
|
||||
};
|
||||
|
||||
@@ -11,18 +11,12 @@ pub fn dummy_imports(store: &Store, import_tys: &[ImportType]) -> Result<Vec<Ext
|
||||
let mut imports = Vec::with_capacity(import_tys.len());
|
||||
for imp in import_tys {
|
||||
imports.push(match imp.ty() {
|
||||
ExternType::Func(func_ty) => {
|
||||
Extern::Func(HostRef::new(DummyFunc::new(&store, func_ty.clone())))
|
||||
}
|
||||
ExternType::Func(func_ty) => Extern::Func(DummyFunc::new(&store, func_ty.clone())),
|
||||
ExternType::Global(global_ty) => {
|
||||
Extern::Global(HostRef::new(dummy_global(&store, global_ty.clone())?))
|
||||
}
|
||||
ExternType::Table(table_ty) => {
|
||||
Extern::Table(HostRef::new(dummy_table(&store, table_ty.clone())?))
|
||||
}
|
||||
ExternType::Memory(mem_ty) => {
|
||||
Extern::Memory(HostRef::new(dummy_memory(&store, mem_ty.clone())))
|
||||
Extern::Global(dummy_global(&store, global_ty.clone())?)
|
||||
}
|
||||
ExternType::Table(table_ty) => Extern::Table(dummy_table(&store, table_ty.clone())?),
|
||||
ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(&store, mem_ty.clone())),
|
||||
});
|
||||
}
|
||||
Ok(imports)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-interface-types"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "Support for wasm interface types with wasmtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -13,13 +13,13 @@ edition = "2018"
|
||||
[dependencies]
|
||||
anyhow = "1.0.19"
|
||||
walrus = "0.13"
|
||||
wasmparser = { version = "0.45.1", default-features = false }
|
||||
wasmparser = { version = "0.47.0", default-features = false }
|
||||
wasm-webidl-bindings = "0.6"
|
||||
wasmtime = { path = '../api' }
|
||||
wasmtime-jit = { path = '../jit' }
|
||||
wasmtime-environ = { path = '../environ' }
|
||||
wasmtime-runtime = { path = '../runtime' }
|
||||
wasmtime-wasi = { path = '../wasi' }
|
||||
wasmtime = { path = "../api", version = "0.9.0" }
|
||||
wasmtime-jit = { path = "../jit", version = "0.9.0" }
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
|
||||
wasmtime-wasi = { path = "../wasi", version = "0.9.0" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -125,18 +125,17 @@ impl ModuleData {
|
||||
/// wasm interface types.
|
||||
pub fn invoke_export(
|
||||
&self,
|
||||
instance: &wasmtime::HostRef<wasmtime::Instance>,
|
||||
instance: &wasmtime::Instance,
|
||||
export: &str,
|
||||
args: &[Value],
|
||||
) -> Result<Vec<Value>> {
|
||||
let mut handle = instance.borrow().handle().clone();
|
||||
let mut handle = instance.handle().clone();
|
||||
|
||||
let binding = self.binding_for_export(&mut handle, export)?;
|
||||
let incoming = binding.param_bindings()?;
|
||||
let outgoing = binding.result_bindings()?;
|
||||
|
||||
let f = instance
|
||||
.borrow()
|
||||
.find_export_by_name(export)
|
||||
.ok_or_else(|| format_err!("failed to find export `{}`", export))?
|
||||
.func()
|
||||
@@ -148,7 +147,7 @@ impl ModuleData {
|
||||
.into_iter()
|
||||
.map(|rv| rv.into())
|
||||
.collect::<Vec<_>>();
|
||||
let wasm_results = match f.borrow().call(&wasm_args) {
|
||||
let wasm_results = match f.call(&wasm_args) {
|
||||
Ok(values) => values
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
@@ -322,20 +321,19 @@ trait TranslateContext {
|
||||
unsafe fn get_memory(&mut self) -> Result<&mut [u8]>;
|
||||
}
|
||||
|
||||
struct InstanceTranslateContext(pub wasmtime::HostRef<wasmtime::Instance>);
|
||||
struct InstanceTranslateContext(pub wasmtime::Instance);
|
||||
|
||||
impl TranslateContext for InstanceTranslateContext {
|
||||
fn invoke_alloc(&mut self, alloc_func_name: &str, len: i32) -> Result<i32> {
|
||||
let alloc = self
|
||||
.0
|
||||
.borrow()
|
||||
.find_export_by_name(alloc_func_name)
|
||||
.ok_or_else(|| format_err!("failed to find alloc function `{}`", alloc_func_name))?
|
||||
.func()
|
||||
.ok_or_else(|| format_err!("`{}` is not a (alloc) function", alloc_func_name))?
|
||||
.clone();
|
||||
let alloc_args = vec![wasmtime::Val::I32(len)];
|
||||
let results = match alloc.borrow().call(&alloc_args) {
|
||||
let results = match alloc.call(&alloc_args) {
|
||||
Ok(values) => values,
|
||||
Err(trap) => bail!("trapped: {:?}", trap),
|
||||
};
|
||||
@@ -350,14 +348,13 @@ impl TranslateContext for InstanceTranslateContext {
|
||||
unsafe fn get_memory(&mut self) -> Result<&mut [u8]> {
|
||||
let memory = self
|
||||
.0
|
||||
.borrow()
|
||||
.find_export_by_name("memory")
|
||||
.ok_or_else(|| format_err!("failed to find `memory` export"))?
|
||||
.memory()
|
||||
.ok_or_else(|| format_err!("`memory` is not a memory"))?
|
||||
.clone();
|
||||
let ptr = memory.borrow().data_ptr();
|
||||
let len = memory.borrow().data_size();
|
||||
let ptr = memory.data_ptr();
|
||||
let len = memory.data_size();
|
||||
Ok(std::slice::from_raw_parts_mut(ptr, len))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-jit"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "JIT-style execution for WebAsssembly code in Cranelift"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -11,18 +11,18 @@ readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cranelift-codegen = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-entity = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-native = "0.52.0"
|
||||
cranelift-frontend = "0.52.0"
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
wasmtime-runtime = { path = "../runtime" }
|
||||
wasmtime-debug = { path = "../debug" }
|
||||
cranelift-codegen = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-entity = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-native = "0.54"
|
||||
cranelift-frontend = "0.54"
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
|
||||
wasmtime-debug = { path = "../debug", version = "0.9.0" }
|
||||
region = "2.0.0"
|
||||
thiserror = "1.0.4"
|
||||
target-lexicon = { version = "0.9.0", default-features = false }
|
||||
wasmparser = { version = "0.45.1", default-features = false }
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
wasmparser = { version = "0.47.0", default-features = false }
|
||||
more-asserts = "0.2.1"
|
||||
anyhow = "1.0"
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@ pub struct CodeMemory {
|
||||
published: usize,
|
||||
}
|
||||
|
||||
fn _assert() {
|
||||
fn _assert_send_sync<T: Send + Sync>() {}
|
||||
_assert_send_sync::<CodeMemory>();
|
||||
}
|
||||
|
||||
impl CodeMemory {
|
||||
/// Create a new `CodeMemory` instance.
|
||||
pub fn new() -> Self {
|
||||
|
||||
@@ -60,7 +60,7 @@ impl<'data> RawCompiledModule<'data> {
|
||||
fn new(
|
||||
compiler: &mut Compiler,
|
||||
data: &'data [u8],
|
||||
module_name: Option<String>,
|
||||
module_name: Option<&str>,
|
||||
resolver: &mut dyn Resolver,
|
||||
debug_info: bool,
|
||||
) -> Result<Self, SetupError> {
|
||||
@@ -76,7 +76,7 @@ impl<'data> RawCompiledModule<'data> {
|
||||
None
|
||||
};
|
||||
|
||||
translation.module.name = module_name;
|
||||
translation.module.name = module_name.map(|s| s.to_string());
|
||||
|
||||
let (allocated_functions, jt_offsets, relocations, dbg_image) = compiler.compile(
|
||||
&translation.module,
|
||||
@@ -155,7 +155,7 @@ impl CompiledModule {
|
||||
pub fn new<'data>(
|
||||
compiler: &mut Compiler,
|
||||
data: &'data [u8],
|
||||
module_name: Option<String>,
|
||||
module_name: Option<&str>,
|
||||
resolver: &mut dyn Resolver,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>,
|
||||
debug_info: bool,
|
||||
@@ -263,7 +263,7 @@ impl OwnedDataInitializer {
|
||||
pub fn instantiate(
|
||||
compiler: &mut Compiler,
|
||||
data: &[u8],
|
||||
module_name: Option<String>,
|
||||
module_name: Option<&str>,
|
||||
resolver: &mut dyn Resolver,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>,
|
||||
debug_info: bool,
|
||||
|
||||
@@ -30,8 +30,8 @@ pub fn link_module(
|
||||
let mut dependencies = HashSet::new();
|
||||
|
||||
let mut function_imports = PrimaryMap::with_capacity(module.imported_funcs.len());
|
||||
for (index, (ref module_name, ref field)) in module.imported_funcs.iter() {
|
||||
match resolver.resolve(module_name, field) {
|
||||
for (index, (module_name, field, import_idx)) in module.imported_funcs.iter() {
|
||||
match resolver.resolve(*import_idx, module_name, field) {
|
||||
Some(export_value) => match export_value {
|
||||
Export::Function {
|
||||
address,
|
||||
@@ -71,8 +71,8 @@ pub fn link_module(
|
||||
}
|
||||
|
||||
let mut table_imports = PrimaryMap::with_capacity(module.imported_tables.len());
|
||||
for (index, (ref module_name, ref field)) in module.imported_tables.iter() {
|
||||
match resolver.resolve(module_name, field) {
|
||||
for (index, (module_name, field, import_idx)) in module.imported_tables.iter() {
|
||||
match resolver.resolve(*import_idx, module_name, field) {
|
||||
Some(export_value) => match export_value {
|
||||
Export::Table {
|
||||
definition,
|
||||
@@ -110,8 +110,8 @@ pub fn link_module(
|
||||
}
|
||||
|
||||
let mut memory_imports = PrimaryMap::with_capacity(module.imported_memories.len());
|
||||
for (index, (ref module_name, ref field)) in module.imported_memories.iter() {
|
||||
match resolver.resolve(module_name, field) {
|
||||
for (index, (module_name, field, import_idx)) in module.imported_memories.iter() {
|
||||
match resolver.resolve(*import_idx, module_name, field) {
|
||||
Some(export_value) => match export_value {
|
||||
Export::Memory {
|
||||
definition,
|
||||
@@ -163,8 +163,8 @@ pub fn link_module(
|
||||
}
|
||||
|
||||
let mut global_imports = PrimaryMap::with_capacity(module.imported_globals.len());
|
||||
for (index, (ref module_name, ref field)) in module.imported_globals.iter() {
|
||||
match resolver.resolve(module_name, field) {
|
||||
for (index, (module_name, field, import_idx)) in module.imported_globals.iter() {
|
||||
match resolver.resolve(*import_idx, module_name, field) {
|
||||
Some(export_value) => match export_value {
|
||||
Export::Table { .. } | Export::Memory { .. } | Export::Function { .. } => {
|
||||
return Err(LinkError(format!(
|
||||
|
||||
@@ -36,7 +36,7 @@ impl Namespace {
|
||||
}
|
||||
|
||||
impl Resolver for Namespace {
|
||||
fn resolve(&mut self, name: &str, field: &str) -> Option<Export> {
|
||||
fn resolve(&mut self, _idx: u32, name: &str, field: &str) -> Option<Export> {
|
||||
if let Some(instance) = self.names.get_mut(name) {
|
||||
instance.lookup(field)
|
||||
} else {
|
||||
|
||||
@@ -5,15 +5,22 @@ use wasmtime_runtime::Export;
|
||||
|
||||
/// Import resolver connects imports with available exported values.
|
||||
pub trait Resolver {
|
||||
/// Resolve the given module/field combo.
|
||||
fn resolve(&mut self, module: &str, field: &str) -> Option<Export>;
|
||||
/// Resolves an import a WebAssembly module to an export it's hooked up to.
|
||||
///
|
||||
/// The `index` provided is the index of the import in the wasm module
|
||||
/// that's being resolved. For example 1 means that it's the second import
|
||||
/// listed in the wasm module.
|
||||
///
|
||||
/// The `module` and `field` arguments provided are the module/field names
|
||||
/// listed on the import itself.
|
||||
fn resolve(&mut self, index: u32, module: &str, field: &str) -> Option<Export>;
|
||||
}
|
||||
|
||||
/// `Resolver` implementation that always resolves to `None`.
|
||||
pub struct NullResolver {}
|
||||
|
||||
impl Resolver for NullResolver {
|
||||
fn resolve(&mut self, _module: &str, _field: &str) -> Option<Export> {
|
||||
fn resolve(&mut self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lightbeam"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Lightbeam Project Developers"]
|
||||
description = "An optimising one-pass streaming compiler for WebAssembly"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -14,12 +14,12 @@ edition = "2018"
|
||||
smallvec = "1.0.0"
|
||||
dynasm = "0.5.2"
|
||||
dynasmrt = "0.5.2"
|
||||
wasmparser = "0.45.1"
|
||||
wasmparser = "0.47.0"
|
||||
memoffset = "0.5.3"
|
||||
itertools = "0.8.2"
|
||||
capstone = "0.6.0"
|
||||
thiserror = "1.0.9"
|
||||
cranelift-codegen = "0.52.0"
|
||||
cranelift-codegen = "0.54"
|
||||
multi_mut = "0.1"
|
||||
either = "1.5"
|
||||
typemap = "0.3"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-py"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "Python extension for Wasmtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -17,15 +17,15 @@ test = false
|
||||
doc = false
|
||||
|
||||
[dependencies]
|
||||
wasmtime = { path = "../../api" }
|
||||
wasmtime-environ = { path = "../../environ" }
|
||||
wasmtime-interface-types = { path = "../../interface-types" }
|
||||
wasmtime-runtime = { path = "../../runtime" }
|
||||
wasmtime-wasi = { path = "../../wasi" }
|
||||
target-lexicon = { version = "0.9.0", default-features = false }
|
||||
wasmtime = { path = "../../api", version = "0.9.0" }
|
||||
wasmtime-environ = { path = "../../environ", version = "0.9.0" }
|
||||
wasmtime-interface-types = { path = "../../interface-types", version = "0.9.0" }
|
||||
wasmtime-runtime = { path = "../../runtime", version = "0.9.0" }
|
||||
wasmtime-wasi = { path = "../../wasi", version = "0.9.0" }
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
anyhow = "1.0.19"
|
||||
region = "2.0.0"
|
||||
wasmparser = "0.45.1"
|
||||
wasmparser = "0.47.0"
|
||||
pyo3 = { version = "0.8.0", features = ["extension-module"] }
|
||||
|
||||
[badges]
|
||||
|
||||
@@ -10,17 +10,16 @@ use wasmtime_interface_types::ModuleData;
|
||||
// TODO support non-export functions
|
||||
#[pyclass]
|
||||
pub struct Function {
|
||||
pub instance: wasmtime::HostRef<wasmtime::Instance>,
|
||||
pub instance: wasmtime::Instance,
|
||||
pub export_name: String,
|
||||
pub args_types: Vec<wasmtime::ValType>,
|
||||
pub data: Rc<ModuleData>,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn func(&self) -> wasmtime::HostRef<wasmtime::Func> {
|
||||
pub fn func(&self) -> wasmtime::Func {
|
||||
let e = self
|
||||
.instance
|
||||
.borrow()
|
||||
.find_export_by_name(&self.export_name)
|
||||
.expect("named export")
|
||||
.clone();
|
||||
@@ -125,10 +124,7 @@ impl wasmtime::Callable for WrappedFn {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrap_into_pyfunction(
|
||||
store: &wasmtime::Store,
|
||||
callable: &PyAny,
|
||||
) -> PyResult<wasmtime::HostRef<wasmtime::Func>> {
|
||||
pub fn wrap_into_pyfunction(store: &wasmtime::Store, callable: &PyAny) -> PyResult<wasmtime::Func> {
|
||||
if !callable.hasattr("__annotations__")? {
|
||||
// TODO support calls without annotations?
|
||||
return Err(PyErr::new::<Exception, _>(
|
||||
@@ -154,6 +150,5 @@ pub fn wrap_into_pyfunction(
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let wrapped = WrappedFn::new(callable.to_object(gil.python()), returns);
|
||||
let f = wasmtime::Func::new(store, ft, Rc::new(wrapped));
|
||||
Ok(wasmtime::HostRef::new(f))
|
||||
Ok(wasmtime::Func::new(store, ft, Rc::new(wrapped)))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use wasmtime_interface_types::ModuleData;
|
||||
|
||||
#[pyclass]
|
||||
pub struct Instance {
|
||||
pub instance: wasmtime::HostRef<wasmtime::Instance>,
|
||||
pub instance: wasmtime::Instance,
|
||||
pub data: Rc<ModuleData>,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ impl Instance {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let exports = PyDict::new(py);
|
||||
let module = self.instance.borrow().module().clone();
|
||||
let module = self.instance.module().clone();
|
||||
for (i, e) in module.exports().iter().enumerate() {
|
||||
match e.ty() {
|
||||
wasmtime::ExternType::Func(ft) => {
|
||||
@@ -43,10 +43,7 @@ impl Instance {
|
||||
let f = Py::new(
|
||||
py,
|
||||
Memory {
|
||||
memory: self.instance.borrow().exports()[i]
|
||||
.memory()
|
||||
.unwrap()
|
||||
.clone(),
|
||||
memory: self.instance.exports()[i].memory().unwrap().clone(),
|
||||
},
|
||||
)?;
|
||||
exports.set_item(e.name().to_string(), f)?;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
use crate::function::{wrap_into_pyfunction, Function};
|
||||
use crate::instance::Instance;
|
||||
use crate::memory::Memory;
|
||||
@@ -122,10 +120,8 @@ pub fn instantiate(
|
||||
}
|
||||
}
|
||||
|
||||
let instance = wasmtime::HostRef::new(
|
||||
wasmtime::Instance::new(&store, &module, &imports)
|
||||
.map_err(|t| PyErr::new::<Exception, _>(format!("instantiated with trap {:?}", t)))?,
|
||||
);
|
||||
let instance = wasmtime::Instance::new(&module, &imports)
|
||||
.map_err(|t| PyErr::new::<Exception, _>(format!("instantiated with trap {:?}", t)))?;
|
||||
|
||||
let module = Py::new(py, Module { module })?;
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ use std::ptr;
|
||||
|
||||
#[pyclass]
|
||||
pub struct Memory {
|
||||
pub memory: wasmtime::HostRef<wasmtime::Memory>,
|
||||
pub memory: wasmtime::Memory,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl Memory {
|
||||
#[getter(current)]
|
||||
pub fn current(&self) -> u32 {
|
||||
self.memory.borrow().size()
|
||||
self.memory.size()
|
||||
}
|
||||
|
||||
pub fn grow(&self, _number: u32) -> u32 {
|
||||
@@ -48,8 +48,8 @@ impl PyBufferProtocol for Memory {
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let base = self.memory.borrow().data_ptr();
|
||||
let current_length = self.memory.borrow().data_size();
|
||||
let base = self.memory.data_ptr();
|
||||
let current_length = self.memory.data_size();
|
||||
|
||||
(*view).buf = base as *mut c_void;
|
||||
(*view).len = current_length as isize;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-rust"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
description = "Rust extension for Wasmtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -15,10 +15,10 @@ test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
wasmtime-interface-types = { path = "../../interface-types" }
|
||||
wasmtime-rust-macro = { path = "./macro" }
|
||||
wasmtime-wasi = { path = "../../wasi" }
|
||||
wasmtime = { path = "../../api" }
|
||||
wasmtime-interface-types = { path = "../../interface-types", version = "0.9.0" }
|
||||
wasmtime-rust-macro = { path = "./macro", version = "0.9.0" }
|
||||
wasmtime-wasi = { path = "../../wasi", version = "0.9.0" }
|
||||
wasmtime = { path = "../../api", version = "0.9.0" }
|
||||
anyhow = "1.0.19"
|
||||
|
||||
[badges]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-rust-macro"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
description = "Macro support crate for wasmtime-rust"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
@@ -34,7 +34,7 @@ fn generate_struct(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
|
||||
let root = root();
|
||||
Ok(quote! {
|
||||
#vis struct #name {
|
||||
instance: #root::wasmtime::HostRef<#root::wasmtime::Instance>,
|
||||
instance: #root::wasmtime::Instance,
|
||||
data: #root::wasmtime_interface_types::ModuleData,
|
||||
}
|
||||
})
|
||||
@@ -48,7 +48,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
|
||||
#vis fn load_file(path: impl AsRef<std::path::Path>) -> #root::anyhow::Result<#name> {
|
||||
let bytes = std::fs::read(path)?;
|
||||
|
||||
use #root::wasmtime::{HostRef, Config, Extern, Engine, Store, Instance, Module};
|
||||
use #root::wasmtime::{Config, Extern, Engine, Store, Instance, Module};
|
||||
use #root::anyhow::{bail, format_err};
|
||||
|
||||
let engine = Engine::new(Config::new().wasm_multi_value(true));
|
||||
@@ -74,9 +74,8 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
|
||||
}
|
||||
}
|
||||
}
|
||||
let instance = HostRef::new(
|
||||
Instance::new(&store, &module, &imports).map_err(|t| format_err!("instantiation trap: {:?}", t))?
|
||||
);
|
||||
let instance =
|
||||
Instance::new(&module, &imports).map_err(|t| format_err!("instantiation trap: {:?}", t))?;
|
||||
|
||||
Ok(#name { instance, data })
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-obj"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "Native object file output for WebAsssembly code in Wasmtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -12,8 +12,8 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
faerie = "0.13.0"
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
faerie = "0.14.0"
|
||||
more-asserts = "0.2.1"
|
||||
|
||||
[badges]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-runtime"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "Runtime library support for Wasmtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -11,7 +11,7 @@ readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
region = "2.0.0"
|
||||
lazy_static = "1.2.0"
|
||||
libc = { version = "0.2.60", default-features = false }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Runtime library support for Wasmtime.
|
||||
|
||||
#![allow(improper_ctypes)]
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
//! Low-level abstraction for allocating and managing zero-filled pages
|
||||
//! of memory.
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use libc;
|
||||
use more_asserts::assert_le;
|
||||
use more_asserts::assert_lt;
|
||||
use region;
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
@@ -19,7 +16,11 @@ fn round_up_to_page_size(size: usize, page_size: usize) -> usize {
|
||||
/// and initially-zeroed memory and a length.
|
||||
#[derive(Debug)]
|
||||
pub struct Mmap {
|
||||
ptr: *mut u8,
|
||||
// Note that this is stored as a `usize` instead of a `*const` or `*mut`
|
||||
// pointer to allow this structure to be natively `Send` and `Sync` without
|
||||
// `unsafe impl`. This type is sendable across threads and shareable since
|
||||
// the coordination all happens at the OS layer.
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
@@ -29,8 +30,9 @@ impl Mmap {
|
||||
// Rust's slices require non-null pointers, even when empty. `Vec`
|
||||
// contains code to create a non-null dangling pointer value when
|
||||
// constructed empty, so we reuse that here.
|
||||
let empty = Vec::<u8>::new();
|
||||
Self {
|
||||
ptr: Vec::new().as_mut_ptr(),
|
||||
ptr: empty.as_ptr() as usize,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
@@ -78,7 +80,7 @@ impl Mmap {
|
||||
}
|
||||
|
||||
Self {
|
||||
ptr: ptr as *mut u8,
|
||||
ptr: ptr as usize,
|
||||
len: mapping_size,
|
||||
}
|
||||
} else {
|
||||
@@ -98,7 +100,7 @@ impl Mmap {
|
||||
}
|
||||
|
||||
let mut result = Self {
|
||||
ptr: ptr as *mut u8,
|
||||
ptr: ptr as usize,
|
||||
len: mapping_size,
|
||||
};
|
||||
|
||||
@@ -142,7 +144,7 @@ impl Mmap {
|
||||
}
|
||||
|
||||
Self {
|
||||
ptr: ptr as *mut u8,
|
||||
ptr: ptr as usize,
|
||||
len: mapping_size,
|
||||
}
|
||||
} else {
|
||||
@@ -154,7 +156,7 @@ impl Mmap {
|
||||
}
|
||||
|
||||
let mut result = Self {
|
||||
ptr: ptr as *mut u8,
|
||||
ptr: ptr as usize,
|
||||
len: mapping_size,
|
||||
};
|
||||
|
||||
@@ -179,7 +181,8 @@ impl Mmap {
|
||||
assert_lt!(start, self.len - len);
|
||||
|
||||
// Commit the accessible size.
|
||||
unsafe { region::protect(self.ptr.add(start), len, region::Protection::ReadWrite) }
|
||||
let ptr = self.ptr as *const u8;
|
||||
unsafe { region::protect(ptr.add(start), len, region::Protection::ReadWrite) }
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@@ -198,9 +201,10 @@ impl Mmap {
|
||||
assert_lt!(start, self.len - len);
|
||||
|
||||
// Commit the accessible size.
|
||||
let ptr = self.ptr as *const u8;
|
||||
if unsafe {
|
||||
VirtualAlloc(
|
||||
self.ptr.add(start) as *mut c_void,
|
||||
ptr.add(start) as *mut c_void,
|
||||
len,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE,
|
||||
@@ -216,22 +220,22 @@ impl Mmap {
|
||||
|
||||
/// Return the allocated memory as a slice of u8.
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.ptr, self.len) }
|
||||
unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) }
|
||||
}
|
||||
|
||||
/// Return the allocated memory as a mutable slice of u8.
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.ptr, self.len) }
|
||||
unsafe { slice::from_raw_parts_mut(self.ptr as *mut u8, self.len) }
|
||||
}
|
||||
|
||||
/// Return the allocated memory as a pointer to u8.
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.ptr
|
||||
self.ptr as *const u8
|
||||
}
|
||||
|
||||
/// Return the allocated memory as a mutable pointer to u8.
|
||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.ptr
|
||||
self.ptr as *mut u8
|
||||
}
|
||||
|
||||
/// Return the length of the allocated memory.
|
||||
@@ -266,6 +270,11 @@ impl Drop for Mmap {
|
||||
}
|
||||
}
|
||||
|
||||
fn _assert() {
|
||||
fn _assert_send_sync<T: Send + Sync>() {}
|
||||
_assert_send_sync::<Mmap>();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "test-programs"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
@@ -10,13 +10,13 @@ publish = false
|
||||
cfg-if = "0.1.9"
|
||||
|
||||
[dev-dependencies]
|
||||
wasi-common = { path = "../wasi-common" }
|
||||
wasmtime-runtime = { path = "../runtime" }
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
wasmtime-jit = { path = "../jit" }
|
||||
wasmtime-wasi = { path = "../wasi" }
|
||||
wasmtime = { path = "../api" }
|
||||
target-lexicon = "0.9.0"
|
||||
wasi-common = { path = "../wasi-common", version = "0.9.0" }
|
||||
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
wasmtime-jit = { path = "../jit", version = "0.9.0" }
|
||||
wasmtime-wasi = { path = "../wasi", version = "0.9.0" }
|
||||
wasmtime = { path = "../api", version = "0.9.0" }
|
||||
target-lexicon = "0.10.0"
|
||||
pretty_env_logger = "0.3.0"
|
||||
tempfile = "3.1.0"
|
||||
os_pipe = "0.9"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{bail, Context};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use wasmtime::{HostRef, Instance, Module, Store};
|
||||
use wasmtime::{Instance, Module, Store};
|
||||
|
||||
pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
|
||||
let store = Store::default();
|
||||
@@ -61,13 +61,12 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let instance = HostRef::new(Instance::new(&store, &module, &imports).context(format!(
|
||||
let instance = Instance::new(&module, &imports).context(format!(
|
||||
"error while instantiating Wasm module '{}'",
|
||||
bin_name,
|
||||
))?);
|
||||
))?;
|
||||
|
||||
let export = instance
|
||||
.borrow()
|
||||
.find_export_by_name("_start")
|
||||
.context("expected a _start export")?
|
||||
.clone();
|
||||
@@ -75,7 +74,6 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
|
||||
if let Err(trap) = export
|
||||
.func()
|
||||
.context("expected export to be a func")?
|
||||
.borrow()
|
||||
.call(&[])
|
||||
{
|
||||
bail!("trapped: {:?}", trap);
|
||||
|
||||
30
crates/test-programs/wasi-tests/Cargo.lock
generated
Normal file
30
crates/test-programs/wasi-tests/Cargo.lock
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "more-asserts"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasi-tests"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
"checksum more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238"
|
||||
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasi-tests"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
@@ -9,7 +9,11 @@ unsafe fn test_fd_advise(dir_fd: wasi::Fd) {
|
||||
0,
|
||||
"file",
|
||||
wasi::OFLAGS_CREAT,
|
||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE,
|
||||
wasi::RIGHTS_FD_READ
|
||||
| wasi::RIGHTS_FD_WRITE
|
||||
| wasi::RIGHTS_FD_ADVISE
|
||||
| wasi::RIGHTS_FD_FILESTAT_GET
|
||||
| wasi::RIGHTS_FD_ALLOCATE,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -9,7 +9,11 @@ unsafe fn test_fd_filestat_set(dir_fd: wasi::Fd) {
|
||||
0,
|
||||
"file",
|
||||
wasi::OFLAGS_CREAT,
|
||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE,
|
||||
wasi::RIGHTS_FD_READ
|
||||
| wasi::RIGHTS_FD_WRITE
|
||||
| wasi::RIGHTS_FD_FILESTAT_GET
|
||||
| wasi::RIGHTS_FD_FILESTAT_SET_SIZE
|
||||
| wasi::RIGHTS_FD_FILESTAT_SET_TIMES,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
165
crates/test-programs/wasi-tests/src/bin/fd_flags_set.rs
Normal file
165
crates/test-programs/wasi-tests/src/bin/fd_flags_set.rs
Normal file
@@ -0,0 +1,165 @@
|
||||
use std::{env, process};
|
||||
use wasi;
|
||||
use wasi_tests::open_scratch_directory;
|
||||
|
||||
unsafe fn test_fd_fdstat_set_flags(dir_fd: wasi::Fd) {
|
||||
const FILE_NAME: &str = "file";
|
||||
let data = &[0u8; 100];
|
||||
|
||||
let file_fd = wasi::path_open(
|
||||
dir_fd,
|
||||
0,
|
||||
FILE_NAME,
|
||||
wasi::OFLAGS_CREAT,
|
||||
wasi::RIGHTS_FD_READ
|
||||
| wasi::RIGHTS_FD_WRITE
|
||||
| wasi::RIGHTS_FD_SEEK
|
||||
| wasi::RIGHTS_FD_TELL
|
||||
| wasi::RIGHTS_FD_FDSTAT_SET_FLAGS,
|
||||
0,
|
||||
wasi::FDFLAGS_APPEND,
|
||||
)
|
||||
.expect("opening a file");
|
||||
|
||||
// Write some data and then verify the written data
|
||||
assert_eq!(
|
||||
wasi::fd_write(
|
||||
file_fd,
|
||||
&[wasi::Ciovec {
|
||||
buf: data.as_ptr(),
|
||||
buf_len: data.len(),
|
||||
}],
|
||||
)
|
||||
.expect("writing to a file"),
|
||||
data.len(),
|
||||
"should write {} bytes",
|
||||
data.len(),
|
||||
);
|
||||
|
||||
wasi::fd_seek(file_fd, 0, wasi::WHENCE_SET).expect("seeking file");
|
||||
|
||||
let buffer = &mut [0u8; 100];
|
||||
|
||||
assert_eq!(
|
||||
wasi::fd_read(
|
||||
file_fd,
|
||||
&[wasi::Iovec {
|
||||
buf: buffer.as_mut_ptr(),
|
||||
buf_len: buffer.len(),
|
||||
}]
|
||||
)
|
||||
.expect("reading file"),
|
||||
buffer.len(),
|
||||
"shoudl read {} bytes",
|
||||
buffer.len()
|
||||
);
|
||||
|
||||
assert_eq!(&data[..], &buffer[..]);
|
||||
|
||||
let data = &[1u8; 100];
|
||||
|
||||
// Seek back to the start to ensure we're in append-only mode
|
||||
wasi::fd_seek(file_fd, 0, wasi::WHENCE_SET).expect("seeking file");
|
||||
|
||||
assert_eq!(
|
||||
wasi::fd_write(
|
||||
file_fd,
|
||||
&[wasi::Ciovec {
|
||||
buf: data.as_ptr(),
|
||||
buf_len: data.len(),
|
||||
}],
|
||||
)
|
||||
.expect("writing to a file"),
|
||||
data.len(),
|
||||
"should write {} bytes",
|
||||
data.len(),
|
||||
);
|
||||
|
||||
wasi::fd_seek(file_fd, 100, wasi::WHENCE_SET).expect("seeking file");
|
||||
|
||||
assert_eq!(
|
||||
wasi::fd_read(
|
||||
file_fd,
|
||||
&[wasi::Iovec {
|
||||
buf: buffer.as_mut_ptr(),
|
||||
buf_len: buffer.len(),
|
||||
}]
|
||||
)
|
||||
.expect("reading file"),
|
||||
buffer.len(),
|
||||
"shoudl read {} bytes",
|
||||
buffer.len()
|
||||
);
|
||||
|
||||
assert_eq!(&data[..], &buffer[..]);
|
||||
|
||||
wasi::fd_fdstat_set_flags(file_fd, 0).expect("disabling flags");
|
||||
|
||||
// Overwrite some existing data to ensure the append mode is now off
|
||||
wasi::fd_seek(file_fd, 0, wasi::WHENCE_SET).expect("seeking file");
|
||||
|
||||
let data = &[2u8; 100];
|
||||
|
||||
assert_eq!(
|
||||
wasi::fd_write(
|
||||
file_fd,
|
||||
&[wasi::Ciovec {
|
||||
buf: data.as_ptr(),
|
||||
buf_len: data.len(),
|
||||
}],
|
||||
)
|
||||
.expect("writing to a file"),
|
||||
data.len(),
|
||||
"should write {} bytes",
|
||||
data.len(),
|
||||
);
|
||||
|
||||
wasi::fd_seek(file_fd, 0, wasi::WHENCE_SET).expect("seeking file");
|
||||
|
||||
assert_eq!(
|
||||
wasi::fd_read(
|
||||
file_fd,
|
||||
&[wasi::Iovec {
|
||||
buf: buffer.as_mut_ptr(),
|
||||
buf_len: buffer.len(),
|
||||
}]
|
||||
)
|
||||
.expect("reading file"),
|
||||
buffer.len(),
|
||||
"shoudl read {} bytes",
|
||||
buffer.len()
|
||||
);
|
||||
|
||||
assert_eq!(&data[..], &buffer[..]);
|
||||
|
||||
wasi::fd_close(file_fd).expect("close file");
|
||||
|
||||
let stat = wasi::path_filestat_get(dir_fd, 0, FILE_NAME).expect("stat path");
|
||||
|
||||
assert_eq!(stat.size, 200, "expected a file size of 200");
|
||||
|
||||
wasi::path_unlink_file(dir_fd, FILE_NAME).expect("unlinking file");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
let prog = args.next().unwrap();
|
||||
let arg = if let Some(arg) = args.next() {
|
||||
arg
|
||||
} else {
|
||||
eprintln!("usage: {} <scratch directory>", prog);
|
||||
process::exit(1);
|
||||
};
|
||||
|
||||
let dir_fd = match open_scratch_directory(&arg) {
|
||||
Ok(dir_fd) => dir_fd,
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
process::exit(1)
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
test_fd_fdstat_set_flags(dir_fd);
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,10 @@ unsafe fn test_fd_readdir(dir_fd: wasi::Fd) {
|
||||
0,
|
||||
"file",
|
||||
wasi::OFLAGS_CREAT,
|
||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE,
|
||||
wasi::RIGHTS_FD_READ
|
||||
| wasi::RIGHTS_FD_WRITE
|
||||
| wasi::RIGHTS_FD_READDIR
|
||||
| wasi::RIGHTS_FD_FILESTAT_GET,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -9,7 +9,10 @@ unsafe fn test_file_allocate(dir_fd: wasi::Fd) {
|
||||
0,
|
||||
"file",
|
||||
wasi::OFLAGS_CREAT,
|
||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE,
|
||||
wasi::RIGHTS_FD_READ
|
||||
| wasi::RIGHTS_FD_WRITE
|
||||
| wasi::RIGHTS_FD_ALLOCATE
|
||||
| wasi::RIGHTS_FD_FILESTAT_GET,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ unsafe fn test_file_seek_tell(dir_fd: wasi::Fd) {
|
||||
0,
|
||||
"file",
|
||||
wasi::OFLAGS_CREAT,
|
||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE,
|
||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE | wasi::RIGHTS_FD_SEEK | wasi::RIGHTS_FD_TELL,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -2,8 +2,15 @@ use more_asserts::assert_gt;
|
||||
use std::{env, process};
|
||||
use wasi_tests::{create_file, open_scratch_directory};
|
||||
|
||||
const TEST_RIGHTS: wasi::Rights = wasi::RIGHTS_FD_READ
|
||||
| wasi::RIGHTS_PATH_LINK_SOURCE
|
||||
| wasi::RIGHTS_PATH_LINK_TARGET
|
||||
| wasi::RIGHTS_FD_FILESTAT_GET
|
||||
| wasi::RIGHTS_PATH_OPEN
|
||||
| wasi::RIGHTS_PATH_UNLINK_FILE;
|
||||
|
||||
unsafe fn create_or_open(dir_fd: wasi::Fd, name: &str, flags: wasi::Oflags) -> wasi::Fd {
|
||||
let file_fd = wasi::path_open(dir_fd, 0, name, flags, 0, 0, 0)
|
||||
let file_fd = wasi::path_open(dir_fd, 0, name, flags, TEST_RIGHTS, TEST_RIGHTS, 0)
|
||||
.unwrap_or_else(|_| panic!("opening '{}'", name));
|
||||
assert_gt!(
|
||||
file_fd,
|
||||
@@ -14,7 +21,7 @@ unsafe fn create_or_open(dir_fd: wasi::Fd, name: &str, flags: wasi::Oflags) -> w
|
||||
}
|
||||
|
||||
unsafe fn open_link(dir_fd: wasi::Fd, name: &str) -> wasi::Fd {
|
||||
let file_fd = wasi::path_open(dir_fd, 0, name, 0, 0, 0, 0)
|
||||
let file_fd = wasi::path_open(dir_fd, 0, name, 0, TEST_RIGHTS, TEST_RIGHTS, 0)
|
||||
.unwrap_or_else(|_| panic!("opening a link '{}'", name));
|
||||
assert_gt!(
|
||||
file_fd,
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
use std::{env, process};
|
||||
use wasi_tests::open_scratch_directory;
|
||||
use wasi_tests::{create_file, drop_rights, fd_get_rights};
|
||||
|
||||
const TEST_FILENAME: &'static str = "file";
|
||||
|
||||
unsafe fn try_read_file(dir_fd: wasi::Fd) {
|
||||
let fd = wasi::path_open(dir_fd, 0, TEST_FILENAME, 0, 0, 0, 0).expect("opening the file");
|
||||
|
||||
// Check that we don't have the right to exeucute fd_read
|
||||
let (rbase, rinher) = fd_get_rights(fd);
|
||||
assert_eq!(
|
||||
rbase & wasi::RIGHTS_FD_READ,
|
||||
0,
|
||||
"should not have base RIGHTS_FD_READ"
|
||||
);
|
||||
assert_eq!(
|
||||
rinher & wasi::RIGHTS_FD_READ,
|
||||
0,
|
||||
"should not have inheriting RIGHTS_FD_READ"
|
||||
);
|
||||
|
||||
let contents = &mut [0u8; 1];
|
||||
let iovec = wasi::Iovec {
|
||||
buf: contents.as_mut_ptr() as *mut _,
|
||||
buf_len: contents.len(),
|
||||
};
|
||||
// Since we no longer have the right to fd_read, trying to read a file
|
||||
// should be an error.
|
||||
assert_eq!(
|
||||
wasi::fd_read(fd, &[iovec])
|
||||
.expect_err("reading bytes from file should fail")
|
||||
.raw_error(),
|
||||
wasi::ERRNO_NOTCAPABLE,
|
||||
"the errno should be ENOTCAPABLE"
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn test_read_rights(dir_fd: wasi::Fd) {
|
||||
create_file(dir_fd, TEST_FILENAME);
|
||||
drop_rights(dir_fd, wasi::RIGHTS_FD_READ, wasi::RIGHTS_FD_READ);
|
||||
|
||||
let (rbase, rinher) = fd_get_rights(dir_fd);
|
||||
assert_eq!(
|
||||
rbase & wasi::RIGHTS_FD_READ,
|
||||
0,
|
||||
"dir should not have base RIGHTS_FD_READ"
|
||||
);
|
||||
assert_eq!(
|
||||
rinher & wasi::RIGHTS_FD_READ,
|
||||
0,
|
||||
"dir should not have inheriting RIGHTS_FD_READ"
|
||||
);
|
||||
|
||||
try_read_file(dir_fd);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
let prog = args.next().unwrap();
|
||||
let arg = if let Some(arg) = args.next() {
|
||||
arg
|
||||
} else {
|
||||
eprintln!("usage: {} <scratch directory>", prog);
|
||||
process::exit(1);
|
||||
};
|
||||
|
||||
// Open scratch directory
|
||||
let dir_fd = match open_scratch_directory(&arg) {
|
||||
Ok(dir_fd) => dir_fd,
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
process::exit(1)
|
||||
}
|
||||
};
|
||||
|
||||
// Run the tests.
|
||||
unsafe { test_read_rights(dir_fd) }
|
||||
}
|
||||
@@ -198,7 +198,7 @@ unsafe fn test_fd_readwrite_valid_fd(dir_fd: wasi::Fd) {
|
||||
0,
|
||||
"file",
|
||||
wasi::OFLAGS_CREAT,
|
||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE,
|
||||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE | wasi::RIGHTS_POLL_FD_READWRITE,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -24,8 +24,11 @@ pub fn open_scratch_directory(path: &str) -> Result<wasi::Fd, String> {
|
||||
}
|
||||
dst.set_len(stat.u.dir.pr_name_len);
|
||||
if dst == path.as_bytes() {
|
||||
return Ok(wasi::path_open(i, 0, ".", wasi::OFLAGS_DIRECTORY, 0, 0, 0)
|
||||
.expect("failed to open dir"));
|
||||
let (base, inherit) = fd_get_rights(i);
|
||||
return Ok(
|
||||
wasi::path_open(i, 0, ".", wasi::OFLAGS_DIRECTORY, base, inherit, 0)
|
||||
.expect("failed to open dir"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,3 +46,18 @@ pub unsafe fn create_file(dir_fd: wasi::Fd, filename: &str) {
|
||||
);
|
||||
wasi::fd_close(file_fd).expect("closing a file");
|
||||
}
|
||||
|
||||
// Returns: (rights_base, rights_inheriting)
|
||||
pub unsafe fn fd_get_rights(fd: wasi::Fd) -> (wasi::Rights, wasi::Rights) {
|
||||
let fdstat = wasi::fd_fdstat_get(fd).expect("fd_fdstat_get failed");
|
||||
(fdstat.fs_rights_base, fdstat.fs_rights_inheriting)
|
||||
}
|
||||
|
||||
pub unsafe fn drop_rights(fd: wasi::Fd, drop_base: wasi::Rights, drop_inheriting: wasi::Rights) {
|
||||
let (current_base, current_inheriting) = fd_get_rights(fd);
|
||||
|
||||
let new_base = current_base & !drop_base;
|
||||
let new_inheriting = current_inheriting & !drop_inheriting;
|
||||
|
||||
wasi::fd_fdstat_set_rights(fd, new_base, new_inheriting).expect("dropping fd rights");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-wasi-c"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
description = "WASI API support for Wasmtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -11,20 +11,20 @@ readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmtime-runtime = { path = "../runtime" }
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
wasmtime-jit = { path = "../jit" }
|
||||
cranelift-codegen = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-entity = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.52.0", features = ["enable-serde"] }
|
||||
target-lexicon = "0.9.0"
|
||||
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
wasmtime-jit = { path = "../jit", version = "0.9.0" }
|
||||
cranelift-codegen = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-entity = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.54", features = ["enable-serde"] }
|
||||
target-lexicon = "0.10.0"
|
||||
log = { version = "0.4.8", default-features = false }
|
||||
libc = "0.2.60"
|
||||
more-asserts = "0.2.1"
|
||||
|
||||
[build-dependencies]
|
||||
cmake = "0.1.35"
|
||||
bindgen = "0.51.0"
|
||||
bindgen = "0.52.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasi-common"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "WASI implementation in Rust"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -11,7 +11,7 @@ readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasi-common-cbindgen = { path = "wasi-common-cbindgen" }
|
||||
wasi-common-cbindgen = { path = "wasi-common-cbindgen", version = "0.9.0" }
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
libc = "0.2"
|
||||
@@ -21,14 +21,14 @@ log = "0.4"
|
||||
filetime = "0.2.7"
|
||||
lazy_static = "1.4.0"
|
||||
num = { version = "0.2.0", default-features = false }
|
||||
wig = { path = "wig" }
|
||||
crossbeam = "0.7.3"
|
||||
wig = { path = "wig", version = "0.9.2" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
yanix = { path = "yanix" }
|
||||
yanix = { path = "yanix", version = "0.9.0" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winx = { path = "winx" }
|
||||
winx = { path = "winx", version = "0.9.0" }
|
||||
winapi = "0.3"
|
||||
cpu-time = "1.0"
|
||||
|
||||
|
||||
Submodule crates/wasi-common/WASI deleted from 9150e66a34
@@ -155,8 +155,21 @@ impl FdEntry {
|
||||
rights_base: wasi::__wasi_rights_t,
|
||||
rights_inheriting: wasi::__wasi_rights_t,
|
||||
) -> Result<()> {
|
||||
if !self.rights_base & rights_base != 0 || !self.rights_inheriting & rights_inheriting != 0
|
||||
{
|
||||
let missing_base = !self.rights_base & rights_base;
|
||||
let missing_inheriting = !self.rights_inheriting & rights_inheriting;
|
||||
if missing_base != 0 || missing_inheriting != 0 {
|
||||
log::trace!(
|
||||
" | validate_rights failed: required: \
|
||||
rights_base = {:#x}, rights_inheriting = {:#x}; \
|
||||
actual: rights_base = {:#x}, rights_inheriting = {:#x}; \
|
||||
missing_base = {:#x}, missing_inheriting = {:#x}",
|
||||
rights_base,
|
||||
rights_inheriting,
|
||||
self.rights_base,
|
||||
self.rights_inheriting,
|
||||
missing_base,
|
||||
missing_inheriting
|
||||
);
|
||||
Err(Error::ENOTCAPABLE)
|
||||
} else {
|
||||
Ok(())
|
||||
|
||||
@@ -67,7 +67,7 @@ hostcalls! {
|
||||
) -> wasi::__wasi_errno_t;
|
||||
|
||||
pub unsafe fn fd_fdstat_set_flags(
|
||||
wasi_ctx: &WasiCtx,
|
||||
wasi_ctx: &mut WasiCtx,
|
||||
memory: &mut [u8],
|
||||
fd: wasi::__wasi_fd_t,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::fdentry::{Descriptor, FdEntry};
|
||||
use crate::helpers::*;
|
||||
use crate::memory::*;
|
||||
use crate::sandboxed_tty_writer::SandboxedTTYWriter;
|
||||
use crate::sys::fdentry_impl::determine_type_rights;
|
||||
use crate::sys::hostcalls_impl::fs_helpers::path_open_rights;
|
||||
use crate::sys::{host_impl, hostcalls_impl};
|
||||
use crate::{helpers, host, wasi, wasi32, Error, Result};
|
||||
@@ -299,19 +298,24 @@ pub(crate) unsafe fn fd_fdstat_get(
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn fd_fdstat_set_flags(
|
||||
wasi_ctx: &WasiCtx,
|
||||
wasi_ctx: &mut WasiCtx,
|
||||
_memory: &mut [u8],
|
||||
fd: wasi::__wasi_fd_t,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
) -> Result<()> {
|
||||
trace!("fd_fdstat_set_flags(fd={:?}, fdflags={:#x?})", fd, fdflags);
|
||||
|
||||
let fd = wasi_ctx
|
||||
.get_fd_entry(fd)?
|
||||
.as_descriptor(0, 0)?
|
||||
.as_os_handle();
|
||||
let descriptor = wasi_ctx
|
||||
.get_fd_entry_mut(fd)?
|
||||
.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_FDSTAT_SET_FLAGS, 0)?;
|
||||
|
||||
hostcalls_impl::fd_fdstat_set_flags(&fd, fdflags)
|
||||
if let Some(new_handle) =
|
||||
hostcalls_impl::fd_fdstat_set_flags(&descriptor.as_os_handle(), fdflags)?
|
||||
{
|
||||
*descriptor = Descriptor::OsHandle(new_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn fd_fdstat_set_rights(
|
||||
@@ -574,6 +578,11 @@ pub(crate) unsafe fn path_open(
|
||||
|
||||
let (needed_base, needed_inheriting) =
|
||||
path_open_rights(fs_rights_base, fs_rights_inheriting, oflags, fs_flags);
|
||||
trace!(
|
||||
" | needed_base = {}, needed_inheriting = {}",
|
||||
needed_base,
|
||||
needed_inheriting
|
||||
);
|
||||
let fe = wasi_ctx.get_fd_entry(dirfd)?;
|
||||
let resolved = path_get(
|
||||
fe,
|
||||
@@ -593,13 +602,20 @@ pub(crate) unsafe fn path_open(
|
||||
| wasi::__WASI_RIGHTS_FD_FILESTAT_SET_SIZE)
|
||||
!= 0;
|
||||
|
||||
trace!(
|
||||
" | calling path_open impl: read={}, write={}",
|
||||
read,
|
||||
write
|
||||
);
|
||||
let fd = hostcalls_impl::path_open(resolved, read, write, oflags, fs_flags)?;
|
||||
|
||||
// Determine the type of the new file descriptor and which rights contradict with this type
|
||||
let (_ty, max_base, max_inheriting) = determine_type_rights(&fd)?;
|
||||
let mut fe = FdEntry::from(fd)?;
|
||||
fe.rights_base &= max_base;
|
||||
fe.rights_inheriting &= max_inheriting;
|
||||
// We need to manually deny the rights which are not explicitly requested.
|
||||
// This should not be needed, but currently determine_type_and_access_rights,
|
||||
// which is used by FdEntry::from, may grant extra rights while inferring it
|
||||
// from the open mode.
|
||||
fe.rights_base &= fs_rights_base;
|
||||
fe.rights_inheriting &= fs_rights_inheriting;
|
||||
let guest_fd = wasi_ctx.insert_fd_entry(fe)?;
|
||||
|
||||
trace!(" | *fd={:?}", guest_fd);
|
||||
@@ -709,7 +725,10 @@ pub(crate) unsafe fn fd_filestat_get(
|
||||
filestat_ptr
|
||||
);
|
||||
|
||||
let fd = wasi_ctx.get_fd_entry(fd)?.as_descriptor(0, 0)?.as_file()?;
|
||||
let fd = wasi_ctx
|
||||
.get_fd_entry(fd)?
|
||||
.as_descriptor(wasi::__WASI_RIGHTS_FD_FILESTAT_GET, 0)?
|
||||
.as_file()?;
|
||||
let host_filestat = hostcalls_impl::fd_filestat_get(fd)?;
|
||||
|
||||
trace!(" | *filestat_ptr={:?}", host_filestat);
|
||||
|
||||
@@ -242,9 +242,9 @@ pub(crate) fn poll_oneoff(
|
||||
{
|
||||
let wasi_fd = unsafe { subscription.u.fd_readwrite.file_descriptor };
|
||||
let rights = if r#type == wasi::__WASI_EVENTTYPE_FD_READ {
|
||||
wasi::__WASI_RIGHTS_FD_READ
|
||||
wasi::__WASI_RIGHTS_FD_READ | wasi::__WASI_RIGHTS_POLL_FD_READWRITE
|
||||
} else {
|
||||
wasi::__WASI_RIGHTS_FD_WRITE
|
||||
wasi::__WASI_RIGHTS_FD_WRITE | wasi::__WASI_RIGHTS_POLL_FD_READWRITE
|
||||
};
|
||||
|
||||
match unsafe {
|
||||
|
||||
@@ -29,9 +29,14 @@ pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> {
|
||||
pub(crate) fn fd_fdstat_set_flags(
|
||||
fd: &File,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
) -> Result<Option<OsHandle>> {
|
||||
let nix_flags = host_impl::nix_from_fdflags(fdflags);
|
||||
unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), nix_flags) }.map_err(Into::into)
|
||||
unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), nix_flags) }
|
||||
.map(|_| None)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_advise(
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::ctx::WasiCtx;
|
||||
use crate::fdentry::FdEntry;
|
||||
use crate::host::{Dirent, FileType};
|
||||
use crate::hostcalls_impl::{fd_filestat_set_times_impl, PathGet};
|
||||
use crate::sys::fdentry_impl::determine_type_rights;
|
||||
use crate::sys::fdentry_impl::{determine_type_rights, OsHandle};
|
||||
use crate::sys::host_impl::{self, path_from_host};
|
||||
use crate::sys::hostcalls_impl::fs_helpers::PathGetExt;
|
||||
use crate::{wasi, Error, Result};
|
||||
@@ -78,8 +78,26 @@ pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
|
||||
Ok(fdflags)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> {
|
||||
unimplemented!("fd_fdstat_set_flags")
|
||||
pub(crate) fn fd_fdstat_set_flags(
|
||||
fd: &File,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
) -> Result<Option<OsHandle>> {
|
||||
let handle = unsafe { fd.as_raw_handle() };
|
||||
|
||||
let access_mode = winx::file::query_access_information(handle)?;
|
||||
|
||||
let new_access_mode = file_access_mode_from_fdflags(
|
||||
fdflags,
|
||||
access_mode.contains(AccessMode::FILE_READ_DATA),
|
||||
access_mode.contains(AccessMode::FILE_WRITE_DATA)
|
||||
| access_mode.contains(AccessMode::FILE_APPEND_DATA),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
Ok(Some(OsHandle::from(File::from_raw_handle(
|
||||
winx::file::reopen_file(handle, new_access_mode, file_flags_from_fdflags(fdflags))?,
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fd_advise(
|
||||
@@ -119,9 +137,20 @@ pub(crate) fn path_open(
|
||||
) -> Result<File> {
|
||||
use winx::file::{AccessMode, CreationDisposition, Flags};
|
||||
|
||||
let is_trunc = oflags & wasi::__WASI_OFLAGS_TRUNC != 0;
|
||||
|
||||
if is_trunc {
|
||||
// Windows does not support append mode when opening for truncation
|
||||
// This is because truncation requires `GENERIC_WRITE` access, which will override the removal
|
||||
// of the `FILE_WRITE_DATA` permission.
|
||||
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
|
||||
return Err(Error::ENOTSUP);
|
||||
}
|
||||
}
|
||||
|
||||
// convert open flags
|
||||
// note: the calls to `write(true)` are to bypass an internal OpenOption check
|
||||
// the write flag will ultimately be ignored when `access_mode` is called below.
|
||||
// the write flag will ultimately be ignored when `access_mode` is calculated below.
|
||||
let mut opts = OpenOptions::new();
|
||||
match creation_disposition_from_oflags(oflags) {
|
||||
CreationDisposition::CREATE_ALWAYS => {
|
||||
@@ -168,7 +197,14 @@ pub(crate) fn path_open(
|
||||
},
|
||||
}
|
||||
|
||||
opts.access_mode(file_access_mode_from_fdflags(fdflags, read, write).bits())
|
||||
let mut access_mode = file_access_mode_from_fdflags(fdflags, read, write);
|
||||
|
||||
// Truncation requires the special `GENERIC_WRITE` bit set (this is why it doesn't work with append-only mode)
|
||||
if is_trunc {
|
||||
access_mode |= AccessMode::GENERIC_WRITE;
|
||||
}
|
||||
|
||||
opts.access_mode(access_mode.bits())
|
||||
.custom_flags(file_flags_from_fdflags(fdflags).bits())
|
||||
.open(&path)
|
||||
.map_err(Into::into)
|
||||
@@ -195,12 +231,15 @@ fn file_access_mode_from_fdflags(
|
||||
) -> AccessMode {
|
||||
let mut access_mode = AccessMode::READ_CONTROL;
|
||||
|
||||
// Note that `GENERIC_READ` and `GENERIC_WRITE` cannot be used to properly support append-only mode
|
||||
// The file-specific flags `FILE_GENERIC_READ` and `FILE_GENERIC_WRITE` are used here instead
|
||||
// These flags have the same semantic meaning for file objects, but allow removal of specific permissions (see below)
|
||||
if read {
|
||||
access_mode.insert(AccessMode::GENERIC_READ);
|
||||
access_mode.insert(AccessMode::FILE_GENERIC_READ);
|
||||
}
|
||||
|
||||
if write {
|
||||
access_mode.insert(AccessMode::GENERIC_WRITE);
|
||||
access_mode.insert(AccessMode::FILE_GENERIC_WRITE);
|
||||
}
|
||||
|
||||
// For append, grant the handle FILE_APPEND_DATA access but *not* FILE_WRITE_DATA.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasi-common-cbindgen"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["Jakub Konka <kubkon@jakubkonka.com>"]
|
||||
description = "Interface generator utilities used by wasi-common"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wig"
|
||||
version = "0.7.0"
|
||||
version = "0.9.2"
|
||||
authors = ["Dan Gohman <sunfish@mozilla.com>"]
|
||||
description = "WebAssembly Interface Generator"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -8,6 +8,7 @@ categories = ["wasm"]
|
||||
keywords = ["webassembly", "wasm"]
|
||||
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
edition = "2018"
|
||||
include = ["src/**/*", "LICENSE", "WASI"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
@@ -19,7 +20,7 @@ heck = "0.3.1"
|
||||
# We include the WASI repo primarily for the witx files, but it's also useful
|
||||
# to use the witx parser it contains, rather than the witx crate from
|
||||
# crates.io, so that it always matches the version of the witx files.
|
||||
witx = { path = "../WASI/tools/witx" }
|
||||
witx = "0.6.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
1
crates/wasi-common/wig/WASI
Submodule
1
crates/wasi-common/wig/WASI
Submodule
Submodule crates/wasi-common/wig/WASI added at 04d4eba571
@@ -28,7 +28,7 @@ pub(crate) fn witx_path_from_args(args: TokenStream) -> (String, String) {
|
||||
|
||||
fn witx_path(phase: &str, id: &str) -> String {
|
||||
let root = env!("CARGO_MANIFEST_DIR");
|
||||
format!("{}/../WASI/phases/{}/witx/{}.witx", root, phase, id)
|
||||
format!("{}/WASI/phases/{}/witx/{}.witx", root, phase, id)
|
||||
}
|
||||
|
||||
// Convert a `Literal` holding a string literal into the `String`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winx"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["Jakub Konka <kubkon@jakubkonka.com>"]
|
||||
description = "Windows API helper library"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
@@ -434,3 +434,22 @@ pub fn query_mode_information(handle: RawHandle) -> Result<FileModeInformation>
|
||||
|
||||
Ok(FileModeInformation::from_bits_truncate(info.Mode))
|
||||
}
|
||||
|
||||
pub fn reopen_file(handle: RawHandle, access_mode: AccessMode, flags: Flags) -> Result<RawHandle> {
|
||||
// Files on Windows are opened with DELETE, READ, and WRITE share mode by default (see OpenOptions in stdlib)
|
||||
// This keeps the same share mode when reopening the file handle
|
||||
let new_handle = unsafe {
|
||||
winbase::ReOpenFile(
|
||||
handle,
|
||||
access_mode.bits(),
|
||||
winnt::FILE_SHARE_DELETE | winnt::FILE_SHARE_READ | winnt::FILE_SHARE_WRITE,
|
||||
flags.bits(),
|
||||
)
|
||||
};
|
||||
|
||||
if new_handle == winapi::um::handleapi::INVALID_HANDLE_VALUE {
|
||||
return Err(winerror::WinError::last());
|
||||
}
|
||||
|
||||
Ok(new_handle)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "yanix"
|
||||
version = "0.1.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "Yet Another Nix crate: a Unix API helper library"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-wasi"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
description = "WASI API support for Wasmtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -11,17 +11,17 @@ readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmtime = { path = "../api" }
|
||||
wasmtime-runtime = { path = "../runtime" }
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
wasmtime-jit = { path = "../jit" }
|
||||
wasi-common = { path = "../wasi-common" }
|
||||
cranelift-codegen = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-entity = { version = "0.52.0", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.52.0", features = ["enable-serde"] }
|
||||
target-lexicon = "0.9.0"
|
||||
wasmtime = { path = "../api", version = "0.9.0" }
|
||||
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
|
||||
wasmtime-environ = { path = "../environ", version = "0.9.0" }
|
||||
wasmtime-jit = { path = "../jit", version = "0.9.0" }
|
||||
wasi-common = { path = "../wasi-common", version = "0.9.0" }
|
||||
cranelift-codegen = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-entity = { version = "0.54", features = ["enable-serde"] }
|
||||
cranelift-wasm = { version = "0.54", features = ["enable-serde"] }
|
||||
target-lexicon = "0.10.0"
|
||||
log = { version = "0.4.8", default-features = false }
|
||||
wig = { path = "../wasi-common/wig" }
|
||||
wig = { path = "../wasi-common/wig", version = "0.9.2" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
mod instantiate;
|
||||
pub mod old;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod instantiate;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasmtime-wast"
|
||||
version = "0.7.0"
|
||||
version = "0.9.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
description = "wast testing support for wasmtime"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -12,8 +12,8 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.19"
|
||||
wasmtime = { path = "../api" }
|
||||
wast = "5.0.1"
|
||||
wasmtime = { path = "../api", version = "0.9.0" }
|
||||
wast = "6.0.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
@@ -31,35 +29,35 @@ pub fn instantiate_spectest(store: &Store) -> HashMap<&'static str, Extern> {
|
||||
|
||||
let ty = FuncType::new(Box::new([]), Box::new([]));
|
||||
let func = wrap(store, ty, |_params, _results| Ok(()));
|
||||
ret.insert("print", Extern::Func(HostRef::new(func)));
|
||||
ret.insert("print", Extern::Func(func));
|
||||
|
||||
let ty = FuncType::new(Box::new([ValType::I32]), Box::new([]));
|
||||
let func = wrap(store, ty, |params, _results| {
|
||||
println!("{}: i32", params[0].unwrap_i32());
|
||||
Ok(())
|
||||
});
|
||||
ret.insert("print_i32", Extern::Func(HostRef::new(func)));
|
||||
ret.insert("print_i32", Extern::Func(func));
|
||||
|
||||
let ty = FuncType::new(Box::new([ValType::I64]), Box::new([]));
|
||||
let func = wrap(store, ty, |params, _results| {
|
||||
println!("{}: i64", params[0].unwrap_i64());
|
||||
Ok(())
|
||||
});
|
||||
ret.insert("print_i64", Extern::Func(HostRef::new(func)));
|
||||
ret.insert("print_i64", Extern::Func(func));
|
||||
|
||||
let ty = FuncType::new(Box::new([ValType::F32]), Box::new([]));
|
||||
let func = wrap(store, ty, |params, _results| {
|
||||
println!("{}: f32", params[0].unwrap_f32());
|
||||
Ok(())
|
||||
});
|
||||
ret.insert("print_f32", Extern::Func(HostRef::new(func)));
|
||||
ret.insert("print_f32", Extern::Func(func));
|
||||
|
||||
let ty = FuncType::new(Box::new([ValType::F64]), Box::new([]));
|
||||
let func = wrap(store, ty, |params, _results| {
|
||||
println!("{}: f64", params[0].unwrap_f64());
|
||||
Ok(())
|
||||
});
|
||||
ret.insert("print_f64", Extern::Func(HostRef::new(func)));
|
||||
ret.insert("print_f64", Extern::Func(func));
|
||||
|
||||
let ty = FuncType::new(Box::new([ValType::I32, ValType::F32]), Box::new([]));
|
||||
let func = wrap(store, ty, |params, _results| {
|
||||
@@ -67,7 +65,7 @@ pub fn instantiate_spectest(store: &Store) -> HashMap<&'static str, Extern> {
|
||||
println!("{}: f32", params[1].unwrap_f32());
|
||||
Ok(())
|
||||
});
|
||||
ret.insert("print_i32_f32", Extern::Func(HostRef::new(func)));
|
||||
ret.insert("print_i32_f32", Extern::Func(func));
|
||||
|
||||
let ty = FuncType::new(Box::new([ValType::F64, ValType::F64]), Box::new([]));
|
||||
let func = wrap(store, ty, |params, _results| {
|
||||
@@ -75,31 +73,31 @@ pub fn instantiate_spectest(store: &Store) -> HashMap<&'static str, Extern> {
|
||||
println!("{}: f64", params[1].unwrap_f64());
|
||||
Ok(())
|
||||
});
|
||||
ret.insert("print_f64_f64", Extern::Func(HostRef::new(func)));
|
||||
ret.insert("print_f64_f64", Extern::Func(func));
|
||||
|
||||
let ty = GlobalType::new(ValType::I32, Mutability::Const);
|
||||
let g = Global::new(store, ty, Val::I32(666));
|
||||
ret.insert("global_i32", Extern::Global(HostRef::new(g)));
|
||||
ret.insert("global_i32", Extern::Global(g));
|
||||
|
||||
let ty = GlobalType::new(ValType::I64, Mutability::Const);
|
||||
let g = Global::new(store, ty, Val::I64(666));
|
||||
ret.insert("global_i64", Extern::Global(HostRef::new(g)));
|
||||
ret.insert("global_i64", Extern::Global(g));
|
||||
|
||||
let ty = GlobalType::new(ValType::F32, Mutability::Const);
|
||||
let g = Global::new(store, ty, Val::F32(0x4426_8000));
|
||||
ret.insert("global_f32", Extern::Global(HostRef::new(g)));
|
||||
ret.insert("global_f32", Extern::Global(g));
|
||||
|
||||
let ty = GlobalType::new(ValType::F64, Mutability::Const);
|
||||
let g = Global::new(store, ty, Val::F64(0x4084_d000_0000_0000));
|
||||
ret.insert("global_f64", Extern::Global(HostRef::new(g)));
|
||||
ret.insert("global_f64", Extern::Global(g));
|
||||
|
||||
let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20)));
|
||||
let table = Table::new(store, ty, Val::AnyRef(AnyRef::Null));
|
||||
ret.insert("table", Extern::Table(HostRef::new(table)));
|
||||
ret.insert("table", Extern::Table(table));
|
||||
|
||||
let ty = MemoryType::new(Limits::new(1, Some(2)));
|
||||
let memory = Memory::new(store, ty);
|
||||
ret.insert("memory", Extern::Memory(HostRef::new(memory)));
|
||||
ret.insert("memory", Extern::Memory(memory));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ fn runtime_value(v: &wast::Expression<'_>) -> Result<Val> {
|
||||
pub struct WastContext {
|
||||
/// Wast files have a concept of a "current" module, which is the most
|
||||
/// recently defined.
|
||||
current: Option<HostRef<Instance>>,
|
||||
current: Option<Instance>,
|
||||
|
||||
instances: HashMap<String, HostRef<Instance>>,
|
||||
instances: HashMap<String, Instance>,
|
||||
store: Store,
|
||||
spectest: Option<HashMap<&'static str, Extern>>,
|
||||
}
|
||||
@@ -39,6 +39,15 @@ enum Outcome<T = Vec<Val>> {
|
||||
Trap(Trap),
|
||||
}
|
||||
|
||||
impl<T> Outcome<T> {
|
||||
fn into_result(self) -> Result<T, Trap> {
|
||||
match self {
|
||||
Outcome::Ok(t) => Ok(t),
|
||||
Outcome::Trap(t) => Err(t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WastContext {
|
||||
/// Construct a new instance of `WastContext`.
|
||||
pub fn new(store: Store) -> Self {
|
||||
@@ -50,7 +59,7 @@ impl WastContext {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_instance(&self, instance_name: Option<&str>) -> Result<HostRef<Instance>> {
|
||||
fn get_instance(&self, instance_name: Option<&str>) -> Result<Instance> {
|
||||
match instance_name {
|
||||
Some(name) => self
|
||||
.instances
|
||||
@@ -64,7 +73,7 @@ impl WastContext {
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate(&self, module: &[u8]) -> Result<Outcome<HostRef<Instance>>> {
|
||||
fn instantiate(&self, module: &[u8]) -> Result<Outcome<Instance>> {
|
||||
let module = Module::new(&self.store, module)?;
|
||||
let mut imports = Vec::new();
|
||||
for import in module.imports() {
|
||||
@@ -85,23 +94,16 @@ impl WastContext {
|
||||
.get(import.module())
|
||||
.ok_or_else(|| anyhow!("no module named `{}`", import.module()))?;
|
||||
let export = instance
|
||||
.borrow()
|
||||
.find_export_by_name(import.name())
|
||||
.ok_or_else(|| anyhow!("unknown import `{}::{}`", import.name(), import.module()))?
|
||||
.clone();
|
||||
imports.push(export);
|
||||
}
|
||||
let instance = match Instance::new(&self.store, &module, &imports) {
|
||||
let instance = match Instance::new(&module, &imports) {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
let err = e.chain().filter_map(|e| e.downcast_ref::<Trap>()).next();
|
||||
if let Some(trap) = err {
|
||||
return Ok(Outcome::Trap(trap.clone()));
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
Err(e) => return e.downcast::<Trap>().map(Outcome::Trap),
|
||||
};
|
||||
Ok(Outcome::Ok(HostRef::new(instance)))
|
||||
Ok(Outcome::Ok(instance))
|
||||
}
|
||||
|
||||
/// Register "spectest" which is used by the spec testsuite.
|
||||
@@ -127,7 +129,12 @@ impl WastContext {
|
||||
}
|
||||
|
||||
fn perform_invoke(&mut self, exec: wast::WastInvoke<'_>) -> Result<Outcome> {
|
||||
self.invoke(exec.module.map(|i| i.name()), exec.name, &exec.args)
|
||||
let values = exec
|
||||
.args
|
||||
.iter()
|
||||
.map(runtime_value)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
self.invoke(exec.module.map(|i| i.name()), exec.name, &values)
|
||||
}
|
||||
|
||||
/// Define a module and register it.
|
||||
@@ -155,19 +162,17 @@ impl WastContext {
|
||||
&mut self,
|
||||
instance_name: Option<&str>,
|
||||
field: &str,
|
||||
args: &[wast::Expression],
|
||||
args: &[Val],
|
||||
) -> Result<Outcome> {
|
||||
let values = args.iter().map(runtime_value).collect::<Result<Vec<_>>>()?;
|
||||
let instance = self.get_instance(instance_name.as_ref().map(|x| &**x))?;
|
||||
let instance = instance.borrow();
|
||||
let export = instance
|
||||
.find_export_by_name(field)
|
||||
.ok_or_else(|| anyhow!("no global named `{}`", field))?;
|
||||
let func = match export {
|
||||
Extern::Func(f) => f.borrow(),
|
||||
Extern::Func(f) => f,
|
||||
_ => bail!("export of `{}` wasn't a global", field),
|
||||
};
|
||||
Ok(match func.call(&values) {
|
||||
Ok(match func.call(args) {
|
||||
Ok(result) => Outcome::Ok(result.into()),
|
||||
Err(e) => Outcome::Trap(e),
|
||||
})
|
||||
@@ -176,21 +181,44 @@ impl WastContext {
|
||||
/// Get the value of an exported global from an instance.
|
||||
fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<Outcome> {
|
||||
let instance = self.get_instance(instance_name.as_ref().map(|x| &**x))?;
|
||||
let instance = instance.borrow();
|
||||
let export = instance
|
||||
.find_export_by_name(field)
|
||||
.ok_or_else(|| anyhow!("no global named `{}`", field))?;
|
||||
let global = match export {
|
||||
Extern::Global(g) => g.borrow(),
|
||||
Extern::Global(g) => g,
|
||||
_ => bail!("export of `{}` wasn't a global", field),
|
||||
};
|
||||
Ok(Outcome::Ok(vec![global.get()]))
|
||||
}
|
||||
|
||||
fn assert_return(&self, result: Outcome, results: &[wast::AssertExpression]) -> Result<()> {
|
||||
let values = result.into_result()?;
|
||||
for (v, e) in values.iter().zip(results) {
|
||||
if val_matches(v, e)? {
|
||||
continue;
|
||||
}
|
||||
bail!("expected {:?}, got {:?}", e, v)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assert_trap(&self, result: Outcome, message: &str) -> Result<()> {
|
||||
let trap = match result {
|
||||
Outcome::Ok(values) => bail!("expected trap, got {:?}", values),
|
||||
Outcome::Trap(t) => t,
|
||||
};
|
||||
if trap.message().contains(message) {
|
||||
return Ok(());
|
||||
}
|
||||
if cfg!(feature = "lightbeam") {
|
||||
println!("TODO: Check the assert_trap message: {}", message);
|
||||
return Ok(());
|
||||
}
|
||||
bail!("expected {}, got {}", message, trap.message())
|
||||
}
|
||||
|
||||
/// Run a wast script from a byte buffer.
|
||||
pub fn run_buffer(&mut self, filename: &str, wast: &[u8]) -> Result<()> {
|
||||
use wast::WastDirective::*;
|
||||
|
||||
let wast = str::from_utf8(wast)?;
|
||||
|
||||
let adjust_wast = |mut err: wast::Error| {
|
||||
@@ -198,311 +226,126 @@ impl WastContext {
|
||||
err.set_text(wast);
|
||||
err
|
||||
};
|
||||
let context = |sp: wast::Span| {
|
||||
let (line, col) = sp.linecol_in(wast);
|
||||
format!("for directive on {}:{}:{}", filename, line + 1, col)
|
||||
};
|
||||
|
||||
let buf = wast::parser::ParseBuffer::new(wast).map_err(adjust_wast)?;
|
||||
let wast = wast::parser::parse::<wast::Wast>(&buf).map_err(adjust_wast)?;
|
||||
let ast = wast::parser::parse::<wast::Wast>(&buf).map_err(adjust_wast)?;
|
||||
|
||||
for directive in wast.directives {
|
||||
match directive {
|
||||
Module(mut module) => {
|
||||
let binary = module.encode().map_err(adjust_wast)?;
|
||||
self.module(module.name.map(|s| s.name()), &binary)
|
||||
.with_context(|| context(module.span))?;
|
||||
}
|
||||
Register { span, name, module } => {
|
||||
self.register(module.map(|s| s.name()), name)
|
||||
.with_context(|| context(span))?;
|
||||
}
|
||||
Invoke(i) => {
|
||||
let span = i.span;
|
||||
self.perform_invoke(i).with_context(|| context(span))?;
|
||||
}
|
||||
AssertReturn {
|
||||
span,
|
||||
exec,
|
||||
results,
|
||||
} => match self.perform_execute(exec).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
for (v, e) in values.iter().zip(results.iter().map(runtime_value)) {
|
||||
let e = e?;
|
||||
if values_equal(v, &e)? {
|
||||
continue;
|
||||
}
|
||||
bail!("{}\nexpected {:?}, got {:?}", context(span), e, v)
|
||||
}
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), t.message())
|
||||
}
|
||||
},
|
||||
AssertTrap {
|
||||
span,
|
||||
exec,
|
||||
message,
|
||||
} => match self.perform_execute(exec).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
bail!("{}\nexpected trap, got {:?}", context(span), values)
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
if t.message().contains(message) {
|
||||
continue;
|
||||
}
|
||||
if cfg!(feature = "lightbeam") {
|
||||
println!(
|
||||
"{}\nTODO: Check the assert_trap message: {}",
|
||||
context(span),
|
||||
message
|
||||
);
|
||||
continue;
|
||||
}
|
||||
bail!(
|
||||
"{}\nexpected {}, got {}",
|
||||
context(span),
|
||||
message,
|
||||
t.message(),
|
||||
)
|
||||
}
|
||||
},
|
||||
AssertExhaustion {
|
||||
span,
|
||||
call,
|
||||
message,
|
||||
} => match self.perform_invoke(call).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
bail!("{}\nexpected trap, got {:?}", context(span), values)
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
if t.message().contains(message) {
|
||||
continue;
|
||||
}
|
||||
bail!(
|
||||
"{}\nexpected exhaustion with {}, got {}",
|
||||
context(span),
|
||||
message,
|
||||
t.message(),
|
||||
)
|
||||
}
|
||||
},
|
||||
AssertReturnCanonicalNan { span, invoke } => {
|
||||
match self.perform_invoke(invoke).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
for v in values.iter() {
|
||||
match v {
|
||||
Val::F32(x) => {
|
||||
if !is_canonical_f32_nan(*x) {
|
||||
bail!("{}\nexpected canonical NaN", context(span))
|
||||
}
|
||||
}
|
||||
Val::F64(x) => {
|
||||
if !is_canonical_f64_nan(*x) {
|
||||
bail!("{}\nexpected canonical NaN", context(span))
|
||||
}
|
||||
}
|
||||
other => bail!("expected float, got {:?}", other),
|
||||
};
|
||||
}
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), t.message())
|
||||
}
|
||||
}
|
||||
}
|
||||
AssertReturnCanonicalNanF32x4 { span, invoke } => {
|
||||
match self.perform_invoke(invoke).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
for v in values.iter() {
|
||||
let val = match v {
|
||||
Val::V128(x) => x,
|
||||
other => bail!("expected v128, got {:?}", other),
|
||||
};
|
||||
for l in 0..4 {
|
||||
if !is_canonical_f32_nan(extract_lane_as_u32(val, l)?) {
|
||||
bail!(
|
||||
"{}\nexpected f32x4 canonical NaN in lane {}",
|
||||
context(span),
|
||||
l
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), t.message())
|
||||
}
|
||||
}
|
||||
}
|
||||
AssertReturnCanonicalNanF64x2 { span, invoke } => {
|
||||
match self.perform_invoke(invoke).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
for v in values.iter() {
|
||||
let val = match v {
|
||||
Val::V128(x) => x,
|
||||
other => bail!("expected v128, got {:?}", other),
|
||||
};
|
||||
for l in 0..2 {
|
||||
if !is_canonical_f64_nan(extract_lane_as_u64(val, l)?) {
|
||||
bail!(
|
||||
"{}\nexpected f64x2 canonical NaN in lane {}",
|
||||
context(span),
|
||||
l
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), t.message())
|
||||
}
|
||||
}
|
||||
}
|
||||
AssertReturnArithmeticNan { span, invoke } => {
|
||||
match self.perform_invoke(invoke).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
for v in values.iter() {
|
||||
match v {
|
||||
Val::F32(x) => {
|
||||
if !is_arithmetic_f32_nan(*x) {
|
||||
bail!("{}\nexpected arithmetic NaN", context(span))
|
||||
}
|
||||
}
|
||||
Val::F64(x) => {
|
||||
if !is_arithmetic_f64_nan(*x) {
|
||||
bail!("{}\nexpected arithmetic NaN", context(span))
|
||||
}
|
||||
}
|
||||
other => bail!("expected float, got {:?}", other),
|
||||
};
|
||||
}
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), t.message())
|
||||
}
|
||||
}
|
||||
}
|
||||
AssertReturnArithmeticNanF32x4 { span, invoke } => {
|
||||
match self.perform_invoke(invoke).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
for v in values.iter() {
|
||||
let val = match v {
|
||||
Val::V128(x) => x,
|
||||
other => bail!("expected v128, got {:?}", other),
|
||||
};
|
||||
for l in 0..4 {
|
||||
if !is_arithmetic_f32_nan(extract_lane_as_u32(val, l)?) {
|
||||
bail!(
|
||||
"{}\nexpected f32x4 arithmetic NaN in lane {}",
|
||||
context(span),
|
||||
l
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), t.message())
|
||||
}
|
||||
}
|
||||
}
|
||||
AssertReturnArithmeticNanF64x2 { span, invoke } => {
|
||||
match self.perform_invoke(invoke).with_context(|| context(span))? {
|
||||
Outcome::Ok(values) => {
|
||||
for v in values.iter() {
|
||||
let val = match v {
|
||||
Val::V128(x) => x,
|
||||
other => bail!("expected v128, got {:?}", other),
|
||||
};
|
||||
for l in 0..2 {
|
||||
if !is_arithmetic_f64_nan(extract_lane_as_u64(val, l)?) {
|
||||
bail!(
|
||||
"{}\nexpected f64x2 arithmetic NaN in lane {}",
|
||||
context(span),
|
||||
l
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Outcome::Trap(t) => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), t.message())
|
||||
}
|
||||
}
|
||||
}
|
||||
AssertInvalid {
|
||||
span,
|
||||
mut module,
|
||||
message,
|
||||
} => {
|
||||
let bytes = module.encode().map_err(adjust_wast)?;
|
||||
let err = match self.module(None, &bytes) {
|
||||
Ok(()) => bail!("{}\nexpected module to fail to build", context(span)),
|
||||
Err(e) => e,
|
||||
};
|
||||
let error_message = format!("{:?}", err);
|
||||
if !error_message.contains(&message) {
|
||||
// TODO: change to bail!
|
||||
println!(
|
||||
"{}\nassert_invalid: expected {}, got {}",
|
||||
context(span),
|
||||
message,
|
||||
error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
AssertMalformed {
|
||||
span,
|
||||
module,
|
||||
message,
|
||||
} => {
|
||||
let mut module = match module {
|
||||
wast::QuoteModule::Module(m) => m,
|
||||
// this is a `*.wat` parser test which we're not
|
||||
// interested in
|
||||
wast::QuoteModule::Quote(_) => return Ok(()),
|
||||
};
|
||||
let bytes = module.encode().map_err(adjust_wast)?;
|
||||
let err = match self.module(None, &bytes) {
|
||||
Ok(()) => {
|
||||
bail!("{}\nexpected module to fail to instantiate", context(span))
|
||||
}
|
||||
Err(e) => e,
|
||||
};
|
||||
let error_message = format!("{:?}", err);
|
||||
if !error_message.contains(&message) {
|
||||
// TODO: change to bail!
|
||||
println!(
|
||||
"{}\nassert_malformed: expected {}, got {}",
|
||||
context(span),
|
||||
message,
|
||||
error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
AssertUnlinkable {
|
||||
span,
|
||||
mut module,
|
||||
message,
|
||||
} => {
|
||||
let bytes = module.encode().map_err(adjust_wast)?;
|
||||
let err = match self.module(None, &bytes) {
|
||||
Ok(()) => bail!("{}\nexpected module to fail to link", context(span)),
|
||||
Err(e) => e,
|
||||
};
|
||||
let error_message = format!("{:?}", err);
|
||||
if !error_message.contains(&message) {
|
||||
bail!(
|
||||
"{}\nassert_unlinkable: expected {}, got {}",
|
||||
context(span),
|
||||
message,
|
||||
error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
AssertReturnFunc { .. } => bail!("need to implement assert_return_func"),
|
||||
for directive in ast.directives {
|
||||
let sp = directive.span();
|
||||
self.run_directive(directive).with_context(|| {
|
||||
let (line, col) = sp.linecol_in(wast);
|
||||
format!("failed directive on {}:{}:{}", filename, line + 1, col)
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_directive(&mut self, directive: wast::WastDirective) -> Result<()> {
|
||||
use wast::WastDirective::*;
|
||||
|
||||
match directive {
|
||||
Module(mut module) => {
|
||||
let binary = module.encode()?;
|
||||
self.module(module.name.map(|s| s.name()), &binary)?;
|
||||
}
|
||||
Register {
|
||||
span: _,
|
||||
name,
|
||||
module,
|
||||
} => {
|
||||
self.register(module.map(|s| s.name()), name)?;
|
||||
}
|
||||
Invoke(i) => {
|
||||
self.perform_invoke(i)?;
|
||||
}
|
||||
AssertReturn {
|
||||
span: _,
|
||||
exec,
|
||||
results,
|
||||
} => {
|
||||
let result = self.perform_execute(exec)?;
|
||||
self.assert_return(result, &results)?;
|
||||
}
|
||||
AssertTrap {
|
||||
span: _,
|
||||
exec,
|
||||
message,
|
||||
} => {
|
||||
let result = self.perform_execute(exec)?;
|
||||
self.assert_trap(result, message)?;
|
||||
}
|
||||
AssertExhaustion {
|
||||
span: _,
|
||||
call,
|
||||
message,
|
||||
} => {
|
||||
let result = self.perform_invoke(call)?;
|
||||
self.assert_trap(result, message)?;
|
||||
}
|
||||
AssertInvalid {
|
||||
span: _,
|
||||
mut module,
|
||||
message,
|
||||
} => {
|
||||
let bytes = module.encode()?;
|
||||
let err = match self.module(None, &bytes) {
|
||||
Ok(()) => bail!("expected module to fail to build"),
|
||||
Err(e) => e,
|
||||
};
|
||||
let error_message = format!("{:?}", err);
|
||||
if !error_message.contains(&message) {
|
||||
// TODO: change to bail!
|
||||
println!(
|
||||
"assert_invalid: expected {}, got {}",
|
||||
message, error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
AssertMalformed {
|
||||
span: _,
|
||||
module,
|
||||
message,
|
||||
} => {
|
||||
let mut module = match module {
|
||||
wast::QuoteModule::Module(m) => m,
|
||||
// this is a `*.wat` parser test which we're not
|
||||
// interested in
|
||||
wast::QuoteModule::Quote(_) => return Ok(()),
|
||||
};
|
||||
let bytes = module.encode()?;
|
||||
let err = match self.module(None, &bytes) {
|
||||
Ok(()) => bail!("expected module to fail to instantiate"),
|
||||
Err(e) => e,
|
||||
};
|
||||
let error_message = format!("{:?}", err);
|
||||
if !error_message.contains(&message) {
|
||||
// TODO: change to bail!
|
||||
println!(
|
||||
"assert_malformed: expected {}, got {}",
|
||||
message, error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
AssertUnlinkable {
|
||||
span: _,
|
||||
mut module,
|
||||
message,
|
||||
} => {
|
||||
let bytes = module.encode()?;
|
||||
let err = match self.module(None, &bytes) {
|
||||
Ok(()) => bail!("expected module to fail to link"),
|
||||
Err(e) => e,
|
||||
};
|
||||
let error_message = format!("{:?}", err);
|
||||
if !error_message.contains(&message) {
|
||||
bail!(
|
||||
"assert_unlinkable: expected {}, got {}",
|
||||
message,
|
||||
error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
AssertReturnFunc { .. } => bail!("need to implement assert_return_func"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -516,12 +359,20 @@ impl WastContext {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_lane_as_u32(bytes: &u128, lane: usize) -> Result<u32> {
|
||||
Ok((*bytes >> (lane * 32)) as u32)
|
||||
fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 {
|
||||
(bytes >> (lane * 8)) as i8
|
||||
}
|
||||
|
||||
fn extract_lane_as_u64(bytes: &u128, lane: usize) -> Result<u64> {
|
||||
Ok((*bytes >> (lane * 64)) as u64)
|
||||
fn extract_lane_as_i16(bytes: u128, lane: usize) -> i16 {
|
||||
(bytes >> (lane * 16)) as i16
|
||||
}
|
||||
|
||||
fn extract_lane_as_i32(bytes: u128, lane: usize) -> i32 {
|
||||
(bytes >> (lane * 32)) as i32
|
||||
}
|
||||
|
||||
fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 {
|
||||
(bytes >> (lane * 64)) as i64
|
||||
}
|
||||
|
||||
fn is_canonical_f32_nan(bits: u32) -> bool {
|
||||
@@ -542,15 +393,64 @@ fn is_arithmetic_f64_nan(bits: u64) -> bool {
|
||||
(bits & AF64_NAN) == AF64_NAN
|
||||
}
|
||||
|
||||
fn values_equal(v1: &Val, v2: &Val) -> Result<bool> {
|
||||
Ok(match (v1, v2) {
|
||||
(Val::I32(a), Val::I32(b)) => a == b,
|
||||
(Val::I64(a), Val::I64(b)) => a == b,
|
||||
fn val_matches(actual: &Val, expected: &wast::AssertExpression) -> Result<bool> {
|
||||
Ok(match (actual, expected) {
|
||||
(Val::I32(a), wast::AssertExpression::I32(b)) => a == b,
|
||||
(Val::I64(a), wast::AssertExpression::I64(b)) => a == b,
|
||||
// Note that these float comparisons are comparing bits, not float
|
||||
// values, so we're testing for bit-for-bit equivalence
|
||||
(Val::F32(a), Val::F32(b)) => a == b,
|
||||
(Val::F64(a), Val::F64(b)) => a == b,
|
||||
(Val::V128(a), Val::V128(b)) => a == b,
|
||||
_ => bail!("don't know how to compare {:?} and {:?} yet", v1, v2),
|
||||
(Val::F32(a), wast::AssertExpression::F32(b)) => f32_matches(*a, b),
|
||||
(Val::F64(a), wast::AssertExpression::F64(b)) => f64_matches(*a, b),
|
||||
(Val::V128(a), wast::AssertExpression::V128(b)) => v128_matches(*a, b),
|
||||
_ => bail!(
|
||||
"don't know how to compare {:?} and {:?} yet",
|
||||
actual,
|
||||
expected
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn f32_matches(actual: u32, expected: &wast::NanPattern<wast::Float32>) -> bool {
|
||||
match expected {
|
||||
wast::NanPattern::CanonicalNan => is_canonical_f32_nan(actual),
|
||||
wast::NanPattern::ArithmeticNan => is_arithmetic_f32_nan(actual),
|
||||
wast::NanPattern::Value(expected_value) => actual == expected_value.bits,
|
||||
}
|
||||
}
|
||||
|
||||
fn f64_matches(actual: u64, expected: &wast::NanPattern<wast::Float64>) -> bool {
|
||||
match expected {
|
||||
wast::NanPattern::CanonicalNan => is_canonical_f64_nan(actual),
|
||||
wast::NanPattern::ArithmeticNan => is_arithmetic_f64_nan(actual),
|
||||
wast::NanPattern::Value(expected_value) => actual == expected_value.bits,
|
||||
}
|
||||
}
|
||||
|
||||
fn v128_matches(actual: u128, expected: &wast::V128Pattern) -> bool {
|
||||
match expected {
|
||||
wast::V128Pattern::I8x16(b) => b
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, b)| *b == extract_lane_as_i8(actual, i)),
|
||||
wast::V128Pattern::I16x8(b) => b
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, b)| *b == extract_lane_as_i16(actual, i)),
|
||||
wast::V128Pattern::I32x4(b) => b
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, b)| *b == extract_lane_as_i32(actual, i)),
|
||||
wast::V128Pattern::I64x2(b) => b
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, b)| *b == extract_lane_as_i64(actual, i)),
|
||||
wast::V128Pattern::F32x4(b) => b.iter().enumerate().all(|(i, b)| {
|
||||
let a = extract_lane_as_i32(actual, i) as u32;
|
||||
f32_matches(a, b)
|
||||
}),
|
||||
wast::V128Pattern::F64x2(b) => b.iter().enumerate().all(|(i, b)| {
|
||||
let a = extract_lane_as_i64(actual, i) as u64;
|
||||
f64_matches(a, b)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user