* Replace the global-exports mechanism with a caller-vmctx mechanism. This eliminates the global exports mechanism, and instead adds a caller-vmctx argument to wasm functions so that WASI can obtain the memory and other things from the caller rather than looking them up in a global registry. This replaces #390. * Fixup some merge conflicts * Rustfmt * Ensure VMContext is aligned to 16 bytes With the removal of `global_exports` it "just so happens" that this isn't happening naturally any more. * Fixup some bugs with double vmctx in wasmtime crate * Trampoline stub needed adjusting * Use pointer type instead of always using I64 for caller vmctx * Don't store `ir::Signature` in `Func` since we don't know the pointer size at creation time. * Skip the first 2 arguments in IR signatures since that's the two vmctx parameters. * Update cranelift to 0.56.0 * Handle more merge conflicts * Rustfmt Co-authored-by: Alex Crichton <alex@alexcrichton.com>
231 lines
7.4 KiB
Rust
231 lines
7.4 KiB
Rust
use crate::action::{get, inspect_memory, invoke};
|
|
use crate::{
|
|
instantiate, ActionError, ActionOutcome, CompilationStrategy, CompiledModule, Compiler,
|
|
InstanceHandle, Namespace, RuntimeValue, SetupError,
|
|
};
|
|
use thiserror::Error;
|
|
use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig};
|
|
use wasmtime_environ::isa::TargetIsa;
|
|
|
|
/// 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)]
|
|
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;
|
|
}
|
|
|
|
/// Construct a new instance of `Context` with the given target.
|
|
pub fn with_isa(isa: Box<dyn TargetIsa>, strategy: CompilationStrategy) -> Self {
|
|
Self::new(Box::new(Compiler::new(isa, strategy)))
|
|
}
|
|
|
|
/// 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()))
|
|
}
|
|
|
|
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,
|
|
None,
|
|
&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.
|
|
pub 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, None, 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)
|
|
}
|
|
}
|