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:
@@ -9,7 +9,7 @@ use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use wasmparser::{
|
||||
validate, CustomSectionKind, ExternalKind, ImportSectionEntryType, ModuleReader, Name,
|
||||
OperatorValidatorConfig, SectionCode, ValidatingParserConfig,
|
||||
SectionCode,
|
||||
};
|
||||
use wasmtime_jit::CompiledModule;
|
||||
|
||||
@@ -244,16 +244,7 @@ impl Module {
|
||||
///
|
||||
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html
|
||||
pub fn validate(store: &Store, binary: &[u8]) -> Result<()> {
|
||||
let features = store.engine().config().features.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,
|
||||
},
|
||||
};
|
||||
let config = store.engine().config().validating_config.clone();
|
||||
validate(binary, Some(config)).map_err(Error::new)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use wasmparser::{OperatorValidatorConfig, ValidatingParserConfig};
|
||||
use wasmtime_environ::settings::{self, Configurable};
|
||||
use wasmtime_environ::CacheConfig;
|
||||
use wasmtime_jit::{native, CompilationStrategy, Compiler, Features};
|
||||
use wasmtime_jit::{native, CompilationStrategy, Compiler};
|
||||
|
||||
// Runtime Environment
|
||||
|
||||
@@ -20,7 +21,7 @@ use wasmtime_jit::{native, CompilationStrategy, Compiler, Features};
|
||||
#[derive(Clone)]
|
||||
pub struct Config {
|
||||
pub(crate) flags: settings::Builder,
|
||||
pub(crate) features: Features,
|
||||
pub(crate) validating_config: ValidatingParserConfig,
|
||||
pub(crate) debug_info: bool,
|
||||
pub(crate) strategy: CompilationStrategy,
|
||||
pub(crate) cache_config: CacheConfig,
|
||||
@@ -45,7 +46,15 @@ impl Config {
|
||||
|
||||
Config {
|
||||
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,
|
||||
strategy: CompilationStrategy::Auto,
|
||||
cache_config: CacheConfig::new_cache_disabled(),
|
||||
@@ -77,10 +86,10 @@ impl Config {
|
||||
///
|
||||
/// [threads]: https://github.com/webassembly/threads
|
||||
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
|
||||
if enable {
|
||||
self.features.bulk_memory = true;
|
||||
self.wasm_bulk_memory(true);
|
||||
}
|
||||
self
|
||||
}
|
||||
@@ -102,10 +111,12 @@ impl Config {
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/reference-types
|
||||
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
|
||||
if enable {
|
||||
self.features.bulk_memory = true;
|
||||
self.wasm_bulk_memory(true);
|
||||
}
|
||||
self
|
||||
}
|
||||
@@ -126,7 +137,7 @@ impl Config {
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/simd
|
||||
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" };
|
||||
self.flags
|
||||
.set("enable_simd", val)
|
||||
@@ -150,7 +161,7 @@ impl Config {
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/bulk-memory-operations
|
||||
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
|
||||
}
|
||||
|
||||
@@ -170,7 +181,7 @@ impl Config {
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/multi-value
|
||||
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
|
||||
}
|
||||
|
||||
@@ -288,10 +299,15 @@ impl Default for Config {
|
||||
|
||||
impl fmt::Debug for Config {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let features = &self.validating_config.operator_config;
|
||||
f.debug_struct("Config")
|
||||
.field("debug_info", &self.debug_info)
|
||||
.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(
|
||||
"flags",
|
||||
&settings::Flags::new(self.flags.clone()).to_string(),
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::{Func, Store, ValType};
|
||||
use anyhow::{bail, Result};
|
||||
use std::ptr;
|
||||
use wasmtime_environ::ir;
|
||||
use wasmtime_jit::RuntimeValue;
|
||||
|
||||
/// Possible runtime values that a WebAssembly module can either consume or
|
||||
/// 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(
|
||||
val: Val,
|
||||
store: &Store,
|
||||
|
||||
@@ -11,8 +11,8 @@ use anyhow::{bail, format_err, Result};
|
||||
use std::convert::TryFrom;
|
||||
use std::str;
|
||||
use wasm_webidl_bindings::ast;
|
||||
use wasmtime::Val;
|
||||
use wasmtime_environ::ir;
|
||||
use wasmtime_jit::RuntimeValue;
|
||||
use wasmtime_runtime::{Export, InstanceHandle};
|
||||
|
||||
mod value;
|
||||
@@ -147,19 +147,7 @@ impl ModuleData {
|
||||
.into_iter()
|
||||
.map(|rv| rv.into())
|
||||
.collect::<Vec<_>>();
|
||||
let wasm_results = f
|
||||
.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>>();
|
||||
let wasm_results = f.call(&wasm_args)?;
|
||||
translate_outgoing(&mut cx, &outgoing, &wasm_results)
|
||||
}
|
||||
|
||||
@@ -358,7 +346,7 @@ fn translate_incoming(
|
||||
cx: &mut dyn TranslateContext,
|
||||
bindings: &[ast::IncomingBindingExpression],
|
||||
args: &[Value],
|
||||
) -> Result<Vec<RuntimeValue>> {
|
||||
) -> Result<Vec<Val>> {
|
||||
let get = |expr: &ast::IncomingBindingExpression| match expr {
|
||||
ast::IncomingBindingExpression::Get(g) => args
|
||||
.get(g.idx as usize)
|
||||
@@ -387,31 +375,31 @@ fn translate_incoming(
|
||||
_ => bail!("expected a string"),
|
||||
};
|
||||
let (ptr, len) = copy(&g.alloc_func_name, val.as_bytes())?;
|
||||
wasm.push(RuntimeValue::I32(ptr));
|
||||
wasm.push(RuntimeValue::I32(len));
|
||||
wasm.push(Val::I32(ptr));
|
||||
wasm.push(Val::I32(len));
|
||||
}
|
||||
ast::IncomingBindingExpression::As(g) => {
|
||||
let val = get(&g.expr)?;
|
||||
match g.ty {
|
||||
walrus::ValType::I32 => match val {
|
||||
Value::I32(i) => wasm.push(RuntimeValue::I32(*i)),
|
||||
Value::U32(i) => wasm.push(RuntimeValue::I32(*i as i32)),
|
||||
Value::I32(i) => wasm.push(Val::I32(*i)),
|
||||
Value::U32(i) => wasm.push(Val::I32(*i as i32)),
|
||||
_ => bail!("cannot convert {:?} to `i32`", val),
|
||||
},
|
||||
walrus::ValType::I64 => match val {
|
||||
Value::I32(i) => wasm.push(RuntimeValue::I64((*i).into())),
|
||||
Value::U32(i) => wasm.push(RuntimeValue::I64((*i).into())),
|
||||
Value::I64(i) => wasm.push(RuntimeValue::I64(*i)),
|
||||
Value::U64(i) => wasm.push(RuntimeValue::I64(*i as i64)),
|
||||
Value::I32(i) => wasm.push(Val::I64((*i).into())),
|
||||
Value::U32(i) => wasm.push(Val::I64((*i).into())),
|
||||
Value::I64(i) => wasm.push(Val::I64(*i)),
|
||||
Value::U64(i) => wasm.push(Val::I64(*i as i64)),
|
||||
_ => bail!("cannot convert {:?} to `i64`", 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),
|
||||
},
|
||||
walrus::ValType::F64 => match val {
|
||||
Value::F32(i) => wasm.push(RuntimeValue::F64((*i as f64).to_bits())),
|
||||
Value::F64(i) => wasm.push(RuntimeValue::F64(i.to_bits())),
|
||||
Value::F32(i) => wasm.push(Val::F64((*i as f64).to_bits())),
|
||||
Value::F64(i) => wasm.push(Val::F64(i.to_bits())),
|
||||
_ => bail!("cannot convert {:?} to `f64`", val),
|
||||
},
|
||||
walrus::ValType::V128 | walrus::ValType::Anyref => {
|
||||
@@ -429,7 +417,7 @@ fn translate_incoming(
|
||||
fn translate_outgoing(
|
||||
cx: &mut dyn TranslateContext,
|
||||
bindings: &[ast::OutgoingBindingExpression],
|
||||
args: &[RuntimeValue],
|
||||
args: &[Val],
|
||||
) -> Result<Vec<Value>> {
|
||||
let mut values = Vec::new();
|
||||
|
||||
@@ -445,32 +433,32 @@ fn translate_outgoing(
|
||||
let arg = get(a.idx)?;
|
||||
match a.ty {
|
||||
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),
|
||||
},
|
||||
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),
|
||||
},
|
||||
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::LongLong) => match arg {
|
||||
RuntimeValue::I32(a) => values.push(Value::I64(a as i64)),
|
||||
RuntimeValue::I64(a) => values.push(Value::I64(a)),
|
||||
Val::I32(a) => values.push(Value::I64(a as i64)),
|
||||
Val::I64(a) => values.push(Value::I64(a)),
|
||||
_ => bail!("can't convert {:?} to long long", arg),
|
||||
},
|
||||
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::UnsignedLongLong) => {
|
||||
match arg {
|
||||
RuntimeValue::I32(a) => values.push(Value::U64(a as u64)),
|
||||
RuntimeValue::I64(a) => values.push(Value::U64(a as u64)),
|
||||
Val::I32(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),
|
||||
}
|
||||
}
|
||||
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),
|
||||
},
|
||||
ast::WebidlTypeRef::Scalar(ast::WebidlScalarType::Double) => match arg {
|
||||
RuntimeValue::F32(a) => values.push(Value::F64(f32::from_bits(a) as f64)),
|
||||
RuntimeValue::F64(a) => values.push(Value::F64(f64::from_bits(a))),
|
||||
Val::F32(a) => values.push(Value::F64(f32::from_bits(a) as f64)),
|
||||
Val::F64(a) => values.push(Value::F64(f64::from_bits(a))),
|
||||
_ => bail!("can't convert {:?} to double", arg),
|
||||
},
|
||||
_ => bail!("unsupported outgoing binding expr {:?}", expr),
|
||||
@@ -481,11 +469,11 @@ fn translate_outgoing(
|
||||
bail!("utf-8 strings must go into dom-string")
|
||||
}
|
||||
let offset = match get(e.offset)? {
|
||||
RuntimeValue::I32(a) => a,
|
||||
Val::I32(a) => a,
|
||||
_ => bail!("offset must be an i32"),
|
||||
};
|
||||
let length = match get(e.length)? {
|
||||
RuntimeValue::I32(a) => a,
|
||||
Val::I32(a) => a,
|
||||
_ => bail!("length must be an i32"),
|
||||
};
|
||||
let bytes = unsafe { &cx.get_memory()?[offset as usize..][..length as usize] };
|
||||
|
||||
@@ -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
|
||||
)));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -21,28 +21,22 @@
|
||||
)
|
||||
)]
|
||||
|
||||
mod action;
|
||||
mod code_memory;
|
||||
mod compiler;
|
||||
mod context;
|
||||
mod function_table;
|
||||
mod imports;
|
||||
mod instantiate;
|
||||
mod link;
|
||||
mod namespace;
|
||||
mod resolver;
|
||||
mod target_tunables;
|
||||
|
||||
pub mod native;
|
||||
pub mod trampoline;
|
||||
|
||||
pub use crate::action::{invoke, ActionError, ActionOutcome, RuntimeValue};
|
||||
pub use crate::code_memory::CodeMemory;
|
||||
pub use crate::compiler::{CompilationStrategy, Compiler};
|
||||
pub use crate::context::{Context, ContextError, Features, UnknownInstance};
|
||||
pub use crate::instantiate::{instantiate, CompiledModule, SetupError};
|
||||
pub use crate::link::link_module;
|
||||
pub use crate::namespace::Namespace;
|
||||
pub use crate::resolver::{NullResolver, Resolver};
|
||||
pub use crate::target_tunables::target_tunables;
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user