Remove the action and context modules from wasmtime_jit (#924)

* Remove the `action` and `context` modules from `wasmtime_jit`

These modules are now no longer necessary with the `wasmtime` crate
fleshed out, and they're entirely subsumed by the `wasmtime` API as
well.

* Remove some more modules
This commit is contained in:
Alex Crichton
2020-02-07 13:22:07 -06:00
committed by GitHub
parent 151075d553
commit 9802005061
8 changed files with 55 additions and 642 deletions

View File

@@ -9,7 +9,7 @@ use std::path::Path;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use wasmparser::{ use wasmparser::{
validate, CustomSectionKind, ExternalKind, ImportSectionEntryType, ModuleReader, Name, validate, CustomSectionKind, ExternalKind, ImportSectionEntryType, ModuleReader, Name,
OperatorValidatorConfig, SectionCode, ValidatingParserConfig, SectionCode,
}; };
use wasmtime_jit::CompiledModule; use wasmtime_jit::CompiledModule;
@@ -244,16 +244,7 @@ impl Module {
/// ///
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html /// [binary]: https://webassembly.github.io/spec/core/binary/index.html
pub fn validate(store: &Store, binary: &[u8]) -> Result<()> { pub fn validate(store: &Store, binary: &[u8]) -> Result<()> {
let features = store.engine().config().features.clone(); let config = store.engine().config().validating_config.clone();
let config = ValidatingParserConfig {
operator_config: OperatorValidatorConfig {
enable_threads: features.threads,
enable_reference_types: features.reference_types,
enable_bulk_memory: features.bulk_memory,
enable_simd: features.simd,
enable_multi_value: features.multi_value,
},
};
validate(binary, Some(config)).map_err(Error::new) validate(binary, Some(config)).map_err(Error::new)
} }

View File

@@ -4,9 +4,10 @@ use std::fmt;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use wasmparser::{OperatorValidatorConfig, ValidatingParserConfig};
use wasmtime_environ::settings::{self, Configurable}; use wasmtime_environ::settings::{self, Configurable};
use wasmtime_environ::CacheConfig; use wasmtime_environ::CacheConfig;
use wasmtime_jit::{native, CompilationStrategy, Compiler, Features}; use wasmtime_jit::{native, CompilationStrategy, Compiler};
// Runtime Environment // Runtime Environment
@@ -20,7 +21,7 @@ use wasmtime_jit::{native, CompilationStrategy, Compiler, Features};
#[derive(Clone)] #[derive(Clone)]
pub struct Config { pub struct Config {
pub(crate) flags: settings::Builder, pub(crate) flags: settings::Builder,
pub(crate) features: Features, pub(crate) validating_config: ValidatingParserConfig,
pub(crate) debug_info: bool, pub(crate) debug_info: bool,
pub(crate) strategy: CompilationStrategy, pub(crate) strategy: CompilationStrategy,
pub(crate) cache_config: CacheConfig, pub(crate) cache_config: CacheConfig,
@@ -45,7 +46,15 @@ impl Config {
Config { Config {
debug_info: false, debug_info: false,
features: Default::default(), validating_config: ValidatingParserConfig {
operator_config: OperatorValidatorConfig {
enable_threads: false,
enable_reference_types: false,
enable_bulk_memory: false,
enable_simd: false,
enable_multi_value: false,
},
},
flags, flags,
strategy: CompilationStrategy::Auto, strategy: CompilationStrategy::Auto,
cache_config: CacheConfig::new_cache_disabled(), cache_config: CacheConfig::new_cache_disabled(),
@@ -77,10 +86,10 @@ impl Config {
/// ///
/// [threads]: https://github.com/webassembly/threads /// [threads]: https://github.com/webassembly/threads
pub fn wasm_threads(&mut self, enable: bool) -> &mut Self { pub fn wasm_threads(&mut self, enable: bool) -> &mut Self {
self.features.threads = enable; self.validating_config.operator_config.enable_threads = enable;
// The threads proposal depends on the bulk memory proposal // The threads proposal depends on the bulk memory proposal
if enable { if enable {
self.features.bulk_memory = true; self.wasm_bulk_memory(true);
} }
self self
} }
@@ -102,10 +111,12 @@ impl Config {
/// ///
/// [proposal]: https://github.com/webassembly/reference-types /// [proposal]: https://github.com/webassembly/reference-types
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self { pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
self.features.reference_types = enable; self.validating_config
.operator_config
.enable_reference_types = enable;
// The reference types proposal depends on the bulk memory proposal // The reference types proposal depends on the bulk memory proposal
if enable { if enable {
self.features.bulk_memory = true; self.wasm_bulk_memory(true);
} }
self self
} }
@@ -126,7 +137,7 @@ impl Config {
/// ///
/// [proposal]: https://github.com/webassembly/simd /// [proposal]: https://github.com/webassembly/simd
pub fn wasm_simd(&mut self, enable: bool) -> &mut Self { pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
self.features.simd = enable; self.validating_config.operator_config.enable_simd = enable;
let val = if enable { "true" } else { "false" }; let val = if enable { "true" } else { "false" };
self.flags self.flags
.set("enable_simd", val) .set("enable_simd", val)
@@ -150,7 +161,7 @@ impl Config {
/// ///
/// [proposal]: https://github.com/webassembly/bulk-memory-operations /// [proposal]: https://github.com/webassembly/bulk-memory-operations
pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self { pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
self.features.bulk_memory = enable; self.validating_config.operator_config.enable_bulk_memory = enable;
self self
} }
@@ -170,7 +181,7 @@ impl Config {
/// ///
/// [proposal]: https://github.com/webassembly/multi-value /// [proposal]: https://github.com/webassembly/multi-value
pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self { pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self {
self.features.multi_value = enable; self.validating_config.operator_config.enable_multi_value = enable;
self self
} }
@@ -288,10 +299,15 @@ impl Default for Config {
impl fmt::Debug for Config { impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let features = &self.validating_config.operator_config;
f.debug_struct("Config") f.debug_struct("Config")
.field("debug_info", &self.debug_info) .field("debug_info", &self.debug_info)
.field("strategy", &self.strategy) .field("strategy", &self.strategy)
.field("features", &self.features) .field("wasm_threads", &features.enable_threads)
.field("wasm_reference_types", &features.enable_reference_types)
.field("wasm_bulk_memory", &features.enable_bulk_memory)
.field("wasm_simd", &features.enable_simd)
.field("wasm_multi_value", &features.enable_multi_value)
.field( .field(
"flags", "flags",
&settings::Flags::new(self.flags.clone()).to_string(), &settings::Flags::new(self.flags.clone()).to_string(),

View File

@@ -3,7 +3,6 @@ use crate::{Func, Store, ValType};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::ptr; use std::ptr;
use wasmtime_environ::ir; use wasmtime_environ::ir;
use wasmtime_jit::RuntimeValue;
/// Possible runtime values that a WebAssembly module can either consume or /// Possible runtime values that a WebAssembly module can either consume or
/// produce. /// produce.
@@ -172,18 +171,6 @@ impl From<Func> for Val {
} }
} }
impl From<RuntimeValue> for Val {
fn from(rv: RuntimeValue) -> Self {
match rv {
RuntimeValue::I32(i) => Val::I32(i),
RuntimeValue::I64(i) => Val::I64(i),
RuntimeValue::F32(u) => Val::F32(u),
RuntimeValue::F64(u) => Val::F64(u),
RuntimeValue::V128(u) => Val::V128(u128::from_le_bytes(u)),
}
}
}
pub(crate) fn into_checked_anyfunc( pub(crate) fn into_checked_anyfunc(
val: Val, val: Val,
store: &Store, store: &Store,

View File

@@ -11,8 +11,8 @@ use anyhow::{bail, format_err, Result};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::str; use std::str;
use wasm_webidl_bindings::ast; use wasm_webidl_bindings::ast;
use wasmtime::Val;
use wasmtime_environ::ir; use wasmtime_environ::ir;
use wasmtime_jit::RuntimeValue;
use wasmtime_runtime::{Export, InstanceHandle}; use wasmtime_runtime::{Export, InstanceHandle};
mod value; mod value;
@@ -147,19 +147,7 @@ impl ModuleData {
.into_iter() .into_iter()
.map(|rv| rv.into()) .map(|rv| rv.into())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let wasm_results = f let wasm_results = f.call(&wasm_args)?;
.call(&wasm_args)?
.to_vec()
.into_iter()
.map(|v: wasmtime::Val| match v {
wasmtime::Val::I32(i) => RuntimeValue::I32(i),
wasmtime::Val::I64(i) => RuntimeValue::I64(i),
wasmtime::Val::F32(i) => RuntimeValue::F32(i),
wasmtime::Val::F64(i) => RuntimeValue::F64(i),
wasmtime::Val::V128(i) => RuntimeValue::V128(i.to_le_bytes()),
_ => panic!("unsupported value {:?}", v),
})
.collect::<Vec<RuntimeValue>>();
translate_outgoing(&mut cx, &outgoing, &wasm_results) translate_outgoing(&mut cx, &outgoing, &wasm_results)
} }
@@ -358,7 +346,7 @@ fn translate_incoming(
cx: &mut dyn TranslateContext, cx: &mut dyn TranslateContext,
bindings: &[ast::IncomingBindingExpression], bindings: &[ast::IncomingBindingExpression],
args: &[Value], args: &[Value],
) -> Result<Vec<RuntimeValue>> { ) -> Result<Vec<Val>> {
let get = |expr: &ast::IncomingBindingExpression| match expr { let get = |expr: &ast::IncomingBindingExpression| match expr {
ast::IncomingBindingExpression::Get(g) => args ast::IncomingBindingExpression::Get(g) => args
.get(g.idx as usize) .get(g.idx as usize)
@@ -387,31 +375,31 @@ fn translate_incoming(
_ => bail!("expected a string"), _ => bail!("expected a string"),
}; };
let (ptr, len) = copy(&g.alloc_func_name, val.as_bytes())?; let (ptr, len) = copy(&g.alloc_func_name, val.as_bytes())?;
wasm.push(RuntimeValue::I32(ptr)); wasm.push(Val::I32(ptr));
wasm.push(RuntimeValue::I32(len)); wasm.push(Val::I32(len));
} }
ast::IncomingBindingExpression::As(g) => { ast::IncomingBindingExpression::As(g) => {
let val = get(&g.expr)?; let val = get(&g.expr)?;
match g.ty { match g.ty {
walrus::ValType::I32 => match val { walrus::ValType::I32 => match val {
Value::I32(i) => wasm.push(RuntimeValue::I32(*i)), Value::I32(i) => wasm.push(Val::I32(*i)),
Value::U32(i) => wasm.push(RuntimeValue::I32(*i as i32)), Value::U32(i) => wasm.push(Val::I32(*i as i32)),
_ => bail!("cannot convert {:?} to `i32`", val), _ => bail!("cannot convert {:?} to `i32`", val),
}, },
walrus::ValType::I64 => match val { walrus::ValType::I64 => match val {
Value::I32(i) => wasm.push(RuntimeValue::I64((*i).into())), Value::I32(i) => wasm.push(Val::I64((*i).into())),
Value::U32(i) => wasm.push(RuntimeValue::I64((*i).into())), Value::U32(i) => wasm.push(Val::I64((*i).into())),
Value::I64(i) => wasm.push(RuntimeValue::I64(*i)), Value::I64(i) => wasm.push(Val::I64(*i)),
Value::U64(i) => wasm.push(RuntimeValue::I64(*i as i64)), Value::U64(i) => wasm.push(Val::I64(*i as i64)),
_ => bail!("cannot convert {:?} to `i64`", val), _ => bail!("cannot convert {:?} to `i64`", val),
}, },
walrus::ValType::F32 => match val { walrus::ValType::F32 => match val {
Value::F32(i) => wasm.push(RuntimeValue::F32(i.to_bits())), Value::F32(i) => wasm.push(Val::F32(i.to_bits())),
_ => bail!("cannot convert {:?} to `f32`", val), _ => bail!("cannot convert {:?} to `f32`", val),
}, },
walrus::ValType::F64 => match val { walrus::ValType::F64 => match val {
Value::F32(i) => wasm.push(RuntimeValue::F64((*i as f64).to_bits())), Value::F32(i) => wasm.push(Val::F64((*i as f64).to_bits())),
Value::F64(i) => wasm.push(RuntimeValue::F64(i.to_bits())), Value::F64(i) => wasm.push(Val::F64(i.to_bits())),
_ => bail!("cannot convert {:?} to `f64`", val), _ => bail!("cannot convert {:?} to `f64`", val),
}, },
walrus::ValType::V128 | walrus::ValType::Anyref => { walrus::ValType::V128 | walrus::ValType::Anyref => {
@@ -429,7 +417,7 @@ fn translate_incoming(
fn translate_outgoing( fn translate_outgoing(
cx: &mut dyn TranslateContext, cx: &mut dyn TranslateContext,
bindings: &[ast::OutgoingBindingExpression], bindings: &[ast::OutgoingBindingExpression],
args: &[RuntimeValue], args: &[Val],
) -> Result<Vec<Value>> { ) -> Result<Vec<Value>> {
let mut values = Vec::new(); let mut values = Vec::new();
@@ -445,32 +433,32 @@ fn translate_outgoing(
let arg = get(a.idx)?; let arg = get(a.idx)?;
match a.ty { match a.ty {
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::UnsignedLong) => match arg { ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::UnsignedLong) => match arg {
RuntimeValue::I32(a) => values.push(Value::U32(a as u32)), Val::I32(a) => values.push(Value::U32(a as u32)),
_ => bail!("can't convert {:?} to unsigned long", arg), _ => bail!("can't convert {:?} to unsigned long", arg),
}, },
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Long) => match arg { ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Long) => match arg {
RuntimeValue::I32(a) => values.push(Value::I32(a)), Val::I32(a) => values.push(Value::I32(a)),
_ => bail!("can't convert {:?} to long", arg), _ => bail!("can't convert {:?} to long", arg),
}, },
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::LongLong) => match arg { ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::LongLong) => match arg {
RuntimeValue::I32(a) => values.push(Value::I64(a as i64)), Val::I32(a) => values.push(Value::I64(a as i64)),
RuntimeValue::I64(a) => values.push(Value::I64(a)), Val::I64(a) => values.push(Value::I64(a)),
_ => bail!("can't convert {:?} to long long", arg), _ => bail!("can't convert {:?} to long long", arg),
}, },
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::UnsignedLongLong) => { ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::UnsignedLongLong) => {
match arg { match arg {
RuntimeValue::I32(a) => values.push(Value::U64(a as u64)), Val::I32(a) => values.push(Value::U64(a as u64)),
RuntimeValue::I64(a) => values.push(Value::U64(a as u64)), Val::I64(a) => values.push(Value::U64(a as u64)),
_ => bail!("can't convert {:?} to unsigned long long", arg), _ => bail!("can't convert {:?} to unsigned long long", arg),
} }
} }
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Float) => match arg { ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Float) => match arg {
RuntimeValue::F32(a) => values.push(Value::F32(f32::from_bits(a))), Val::F32(a) => values.push(Value::F32(f32::from_bits(a))),
_ => bail!("can't convert {:?} to float", arg), _ => bail!("can't convert {:?} to float", arg),
}, },
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Double) => match arg { ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Double) => match arg {
RuntimeValue::F32(a) => values.push(Value::F64(f32::from_bits(a) as f64)), Val::F32(a) => values.push(Value::F64(f32::from_bits(a) as f64)),
RuntimeValue::F64(a) => values.push(Value::F64(f64::from_bits(a))), Val::F64(a) => values.push(Value::F64(f64::from_bits(a))),
_ => bail!("can't convert {:?} to double", arg), _ => bail!("can't convert {:?} to double", arg),
}, },
_ => bail!("unsupported outgoing binding expr {:?}", expr), _ => bail!("unsupported outgoing binding expr {:?}", expr),
@@ -481,11 +469,11 @@ fn translate_outgoing(
bail!("utf-8 strings must go into dom-string") bail!("utf-8 strings must go into dom-string")
} }
let offset = match get(e.offset)? { let offset = match get(e.offset)? {
RuntimeValue::I32(a) => a, Val::I32(a) => a,
_ => bail!("offset must be an i32"), _ => bail!("offset must be an i32"),
}; };
let length = match get(e.length)? { let length = match get(e.length)? {
RuntimeValue::I32(a) => a, Val::I32(a) => a,
_ => bail!("length must be an i32"), _ => bail!("length must be an i32"),
}; };
let bytes = unsafe { &cx.get_memory()?[offset as usize..][..length as usize] }; let bytes = unsafe { &cx.get_memory()?[offset as usize..][..length as usize] };

View File

@@ -1,295 +0,0 @@
//! Support for performing actions with a wasm module from the outside.
use crate::compiler::Compiler;
use crate::instantiate::SetupError;
use std::cmp::max;
use std::{fmt, mem, ptr, slice};
use thiserror::Error;
use wasmtime_environ::ir;
use wasmtime_runtime::{wasmtime_call_trampoline, Export, InstanceHandle, Trap, VMInvokeArgument};
/// A runtime value.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RuntimeValue {
/// A runtime value with type i32.
I32(i32),
/// A runtime value with type i64.
I64(i64),
/// A runtime value with type f32.
F32(u32),
/// A runtime value with type f64.
F64(u64),
/// A runtime value with type v128
V128([u8; 16]),
}
impl RuntimeValue {
/// Return the type of this `RuntimeValue`.
pub fn value_type(self) -> ir::Type {
match self {
Self::I32(_) => ir::types::I32,
Self::I64(_) => ir::types::I64,
Self::F32(_) => ir::types::F32,
Self::F64(_) => ir::types::F64,
Self::V128(_) => ir::types::I8X16,
}
}
/// Assuming this `RuntimeValue` holds an `i32`, return that value.
pub fn unwrap_i32(self) -> i32 {
match self {
Self::I32(x) => x,
_ => panic!("unwrapping value of type {} as i32", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `i64`, return that value.
pub fn unwrap_i64(self) -> i64 {
match self {
Self::I64(x) => x,
_ => panic!("unwrapping value of type {} as i64", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `f32`, return that value.
pub fn unwrap_f32(self) -> f32 {
f32::from_bits(self.unwrap_f32_bits())
}
/// Assuming this `RuntimeValue` holds an `f32`, return the bits of that value as a `u32`.
pub fn unwrap_f32_bits(self) -> u32 {
match self {
Self::F32(x) => x,
_ => panic!("unwrapping value of type {} as f32", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `f64`, return that value.
pub fn unwrap_f64(self) -> f64 {
f64::from_bits(self.unwrap_f64_bits())
}
/// Assuming this `RuntimeValue` holds an `f64`, return the bits of that value as a `u64`.
pub fn unwrap_f64_bits(self) -> u64 {
match self {
Self::F64(x) => x,
_ => panic!("unwrapping value of type {} as f64", self.value_type()),
}
}
}
impl fmt::Display for RuntimeValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::I32(x) => write!(f, "{}: i32", x),
Self::I64(x) => write!(f, "{}: i64", x),
Self::F32(x) => write!(f, "{}: f32", x),
Self::F64(x) => write!(f, "{}: f64", x),
Self::V128(x) => write!(f, "{:?}: v128", x.to_vec()),
}
}
}
/// The result of invoking a wasm function or reading a wasm global.
#[derive(Debug)]
pub enum ActionOutcome {
/// The action returned normally. Its return values are provided.
Returned {
/// The return values.
values: Vec<RuntimeValue>,
},
/// A trap occurred while the action was executing.
Trapped(Trap),
}
/// An error detected while invoking a wasm function or reading a wasm global.
/// Note that at this level, traps are not reported errors, but are rather
/// returned through `ActionOutcome`.
#[derive(Error, Debug)]
pub enum ActionError {
/// An internal implementation error occurred.
#[error("Failed to setup a module")]
Setup(#[from] SetupError),
/// No field with the specified name was present.
#[error("Unknown field: {0}")]
Field(String),
/// The field was present but was the wrong kind (eg. function, table, global, or memory).
#[error("Kind error: {0}")]
Kind(String),
/// The field was present but was the wrong type (eg. i32, i64, f32, or f64).
#[error("Type error: {0}")]
Type(String),
}
/// Invoke a function through an `InstanceHandle` identified by an export name.
pub fn invoke(
compiler: &mut Compiler,
instance: &mut InstanceHandle,
function_name: &str,
args: &[RuntimeValue],
) -> Result<ActionOutcome, ActionError> {
let (address, signature, callee_vmctx) = match instance.lookup(function_name) {
Some(Export::Function {
address,
signature,
vmctx,
}) => (address, signature, vmctx),
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a function",
function_name
)));
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
function_name
)));
}
};
for (index, value) in args.iter().enumerate() {
// Add one to account for the leading vmctx argument.
assert_eq!(value.value_type(), signature.params[index + 1].value_type);
}
// TODO: Support values larger than v128. And pack the values into memory
// instead of just using fixed-sized slots.
// Subtract one because we don't pass the vmctx argument in `values_vec`.
let value_size = mem::size_of::<VMInvokeArgument>();
let mut values_vec: Vec<VMInvokeArgument> =
vec![VMInvokeArgument::new(); max(signature.params.len() - 1, signature.returns.len())];
// Store the argument values into `values_vec`.
for (index, arg) in args.iter().enumerate() {
unsafe {
let ptr = values_vec.as_mut_ptr().add(index);
match arg {
RuntimeValue::I32(x) => ptr::write(ptr as *mut i32, *x),
RuntimeValue::I64(x) => ptr::write(ptr as *mut i64, *x),
RuntimeValue::F32(x) => ptr::write(ptr as *mut u32, *x),
RuntimeValue::F64(x) => ptr::write(ptr as *mut u64, *x),
RuntimeValue::V128(x) => ptr::write(ptr as *mut [u8; 16], *x),
}
}
}
// Get the trampoline to call for this function.
let exec_code_buf = compiler
.get_trampoline(address, &signature, value_size)
.map_err(ActionError::Setup)?;
// Make all JIT code produced thus far executable.
compiler.publish_compiled_code();
// Call the trampoline. Pass a null `caller_vmctx` argument as `invoke` is
// all about calling from the outside world rather than from an instance.
if let Err(trap) = unsafe {
wasmtime_call_trampoline(
callee_vmctx,
ptr::null_mut(),
exec_code_buf,
values_vec.as_mut_ptr() as *mut u8,
)
} {
return Ok(ActionOutcome::Trapped(trap));
}
// Load the return values out of `values_vec`.
let values = signature
.returns
.iter()
.enumerate()
.map(|(index, abi_param)| unsafe {
let ptr = values_vec.as_ptr().add(index);
match abi_param.value_type {
ir::types::I32 => RuntimeValue::I32(ptr::read(ptr as *const i32)),
ir::types::I64 => RuntimeValue::I64(ptr::read(ptr as *const i64)),
ir::types::F32 => RuntimeValue::F32(ptr::read(ptr as *const u32)),
ir::types::F64 => RuntimeValue::F64(ptr::read(ptr as *const u64)),
ir::types::I8X16 => RuntimeValue::V128(ptr::read(ptr as *const [u8; 16])),
other => panic!("unsupported value type {:?}", other),
}
})
.collect();
Ok(ActionOutcome::Returned { values })
}
/// Returns a slice of the contents of allocated linear memory.
pub fn inspect_memory<'instance>(
instance: &'instance InstanceHandle,
memory_name: &str,
start: usize,
len: usize,
) -> Result<&'instance [u8], ActionError> {
let definition = match instance.lookup(memory_name) {
Some(Export::Memory {
definition,
memory: _memory,
vmctx: _vmctx,
}) => definition,
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a linear memory",
memory_name
)));
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
memory_name
)));
}
};
Ok(unsafe {
let memory_def = &*definition;
&slice::from_raw_parts(memory_def.base, memory_def.current_length)[start..start + len]
})
}
/// Read a global in the given instance identified by an export name.
pub fn get(instance: &InstanceHandle, global_name: &str) -> Result<RuntimeValue, ActionError> {
let (definition, global) = match instance.lookup(global_name) {
Some(Export::Global {
definition,
vmctx: _,
global,
}) => (definition, global),
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a global variable",
global_name
)));
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
global_name
)));
}
};
unsafe {
let global_def = &*definition;
Ok(match global.ty {
ir::types::I32 => RuntimeValue::I32(*global_def.as_i32()),
ir::types::I64 => RuntimeValue::I64(*global_def.as_i64()),
ir::types::F32 => RuntimeValue::F32(*global_def.as_f32_bits()),
ir::types::F64 => RuntimeValue::F64(*global_def.as_f64_bits()),
other => {
return Err(ActionError::Type(format!(
"global with type {} not supported",
other
)));
}
})
}
}

View File

@@ -1,222 +0,0 @@
use crate::action::{get, inspect_memory, invoke};
use crate::{
instantiate, ActionError, ActionOutcome, CompiledModule, Compiler, InstanceHandle, Namespace,
RuntimeValue, SetupError,
};
use thiserror::Error;
use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig};
/// Indicates an unknown instance was specified.
#[derive(Error, Debug)]
#[error("no instance {instance_name} present")]
pub struct UnknownInstance {
instance_name: String,
}
/// Error message used by `WastContext`.
#[derive(Error, Debug)]
pub enum ContextError {
/// An unknown instance name was used.
#[error("An error occured due to an unknown instance being specified")]
Instance(#[from] UnknownInstance),
/// An error occured while performing an action.
#[error("An error occurred while performing an action")]
Action(#[from] ActionError),
}
/// The collection of features configurable during compilation
#[derive(Clone, Default, Debug)]
pub struct Features {
/// marks whether the proposed thread feature is enabled or disabled
pub threads: bool,
/// marks whether the proposed reference type feature is enabled or disabled
pub reference_types: bool,
/// marks whether the proposed SIMD feature is enabled or disabled
pub simd: bool,
/// marks whether the proposed bulk memory feature is enabled or disabled
pub bulk_memory: bool,
/// marks whether the proposed multi-value feature is enabled or disabled
pub multi_value: bool,
}
impl Into<ValidatingParserConfig> for Features {
fn into(self) -> ValidatingParserConfig {
ValidatingParserConfig {
operator_config: OperatorValidatorConfig {
enable_threads: self.threads,
enable_reference_types: self.reference_types,
enable_bulk_memory: self.bulk_memory,
enable_simd: self.simd,
enable_multi_value: self.multi_value,
},
}
}
}
/// A convenient context for compiling and executing WebAssembly instances.
pub struct Context {
namespace: Namespace,
compiler: Box<Compiler>,
debug_info: bool,
features: Features,
}
impl Context {
/// Construct a new instance of `Context`.
pub fn new(compiler: Box<Compiler>) -> Self {
Self {
namespace: Namespace::new(),
compiler,
debug_info: false,
features: Default::default(),
}
}
/// Get debug_info settings.
pub fn debug_info(&self) -> bool {
self.debug_info
}
/// Set debug_info settings.
pub fn set_debug_info(&mut self, value: bool) {
self.debug_info = value;
}
/// Retrieve the context features
pub fn features(&self) -> &Features {
&self.features
}
/// Construct a new instance with the given features from the current `Context`
pub fn with_features(self, features: Features) -> Self {
Self { features, ..self }
}
fn validate(&mut self, data: &[u8]) -> Result<(), String> {
// TODO: Fix Cranelift to be able to perform validation itself, rather
// than calling into wasmparser ourselves here.
validate(data, Some(self.features.clone().into()))
.map_err(|e| format!("module did not validate: {}", e.to_string()))
}
unsafe fn instantiate(&mut self, data: &[u8]) -> Result<InstanceHandle, SetupError> {
self.validate(&data).map_err(SetupError::Validate)?;
let debug_info = self.debug_info();
instantiate(&mut *self.compiler, &data, &mut self.namespace, debug_info)
}
/// Return the instance associated with the given name.
pub fn get_instance(
&mut self,
instance_name: &str,
) -> Result<&mut InstanceHandle, UnknownInstance> {
self.namespace
.get_instance(instance_name)
.ok_or_else(|| UnknownInstance {
instance_name: instance_name.to_string(),
})
}
/// Instantiate a module instance and register the instance.
///
/// # Unsafety
///
/// See `InstanceHandle::new`
pub unsafe fn instantiate_module(
&mut self,
instance_name: Option<String>,
data: &[u8],
) -> Result<InstanceHandle, ActionError> {
let instance = self.instantiate(data).map_err(ActionError::Setup)?;
self.optionally_name_instance(instance_name, instance.clone());
Ok(instance)
}
/// Compile a module.
pub fn compile_module(&mut self, data: &[u8]) -> Result<CompiledModule, SetupError> {
self.validate(&data).map_err(SetupError::Validate)?;
let debug_info = self.debug_info();
CompiledModule::new(&mut *self.compiler, data, debug_info)
}
/// If `name` isn't None, register it for the given instance.
pub fn optionally_name_instance(&mut self, name: Option<String>, instance: InstanceHandle) {
if let Some(name) = name {
self.namespace.name_instance(name, instance);
}
}
/// Register a name for the given instance.
pub fn name_instance(&mut self, name: String, instance: InstanceHandle) {
self.namespace.name_instance(name, instance);
}
/// Register an additional name for an existing registered instance.
pub fn alias(&mut self, name: &str, as_name: String) -> Result<(), UnknownInstance> {
let instance = self.get_instance(&name)?.clone();
self.name_instance(as_name, instance);
Ok(())
}
/// Invoke an exported function from a named instance.
pub fn invoke_named(
&mut self,
instance_name: &str,
field: &str,
args: &[RuntimeValue],
) -> Result<ActionOutcome, ContextError> {
let mut instance = self
.get_instance(&instance_name)
.map_err(ContextError::Instance)?
.clone();
self.invoke(&mut instance, field, args)
.map_err(ContextError::Action)
}
/// Invoke an exported function from an instance.
pub fn invoke(
&mut self,
instance: &mut InstanceHandle,
field: &str,
args: &[RuntimeValue],
) -> Result<ActionOutcome, ActionError> {
invoke(&mut *self.compiler, instance, field, &args)
}
/// Get the value of an exported global variable from an instance.
pub fn get_named(
&mut self,
instance_name: &str,
field: &str,
) -> Result<ActionOutcome, ContextError> {
let instance = self
.get_instance(&instance_name)
.map_err(ContextError::Instance)?
.clone();
self.get(&instance, field).map_err(ContextError::Action)
}
/// Get the value of an exported global variable from an instance.
pub fn get(
&mut self,
instance: &InstanceHandle,
field: &str,
) -> Result<ActionOutcome, ActionError> {
get(instance, field).map(|value| ActionOutcome::Returned {
values: vec![value],
})
}
/// Get a slice of memory from an instance.
pub fn inspect_memory<'instance>(
&self,
instance: &'instance InstanceHandle,
field_name: &str,
start: usize,
len: usize,
) -> Result<&'instance [u8], ActionError> {
inspect_memory(instance, field_name, start, len)
}
}

View File

@@ -21,28 +21,22 @@
) )
)] )]
mod action;
mod code_memory; mod code_memory;
mod compiler; mod compiler;
mod context;
mod function_table; mod function_table;
mod imports; mod imports;
mod instantiate; mod instantiate;
mod link; mod link;
mod namespace;
mod resolver; mod resolver;
mod target_tunables; mod target_tunables;
pub mod native; pub mod native;
pub mod trampoline; pub mod trampoline;
pub use crate::action::{invoke, ActionError, ActionOutcome, RuntimeValue};
pub use crate::code_memory::CodeMemory; pub use crate::code_memory::CodeMemory;
pub use crate::compiler::{CompilationStrategy, Compiler}; pub use crate::compiler::{CompilationStrategy, Compiler};
pub use crate::context::{Context, ContextError, Features, UnknownInstance};
pub use crate::instantiate::{instantiate, CompiledModule, SetupError}; pub use crate::instantiate::{instantiate, CompiledModule, SetupError};
pub use crate::link::link_module; pub use crate::link::link_module;
pub use crate::namespace::Namespace;
pub use crate::resolver::{NullResolver, Resolver}; pub use crate::resolver::{NullResolver, Resolver};
pub use crate::target_tunables::target_tunables; pub use crate::target_tunables::target_tunables;

View File

@@ -1,46 +0,0 @@
//! The core WebAssembly spec does not specify how imports are to be resolved
//! to exports. This file provides one possible way to manage multiple instances
//! and resolve imports to exports among them.
use crate::resolver::Resolver;
use std::collections::HashMap;
use wasmtime_runtime::{Export, InstanceHandle};
/// A namespace containing instances keyed by name.
///
/// Note that `Namespace` implements the `Resolver` trait, so it can resolve
/// imports using defined exports.
pub struct Namespace {
/// Mapping from identifiers to indices in `self.instances`.
names: HashMap<String, InstanceHandle>,
}
impl Namespace {
/// Construct a new `Namespace`.
pub fn new() -> Self {
Self {
names: HashMap::new(),
}
}
/// Install a new `InstanceHandle` in this `Namespace`, optionally with the
/// given name.
pub fn name_instance(&mut self, name: String, instance: InstanceHandle) {
self.names.insert(name, instance);
}
/// Get the instance registered with the given `instance_name`.
pub fn get_instance(&mut self, name: &str) -> Option<&mut InstanceHandle> {
self.names.get_mut(name)
}
}
impl Resolver for Namespace {
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 {
None
}
}
}