wasmi/src/func.rs

350 lines
12 KiB
Rust
Raw Normal View History

#[allow(unused_imports)]
Extract validation into a separate crate (#176) * Add some docs. * return_type isn't failable * Add comment about safety of top_label * Attempt number 10 * Rework. Now we will a compiler which wraps and uses info from a evaluation simulator. * Get rid of outcome * Introduce StartedWith * Actually use started_with. * Mirror label_stack. * Avoid using frame_type. * Finally get rid from frame_type. * Extract compilation * Refactoring cleaning * Validation separated from compilation. * Move sink to FunctionReader * Rename to compiler. * fmt * Move push_label under validation context. * Add Validation traits * Express the compiler using validation trait * Move code under prepare * Comments. * WIP * The great move of validation * Make validation compile * Make it compile. * Format it. * Fix warnings. * Clean. * Make it work under no_std * Move deny_floating_point to wasmi * Rename validate_module2 → validate_module * Make validation tests work * Make wasmi compilation tests work * Renamings. * Get rid of memory_units dependency in validation * Rename. * Clean. * Estimate capacity. * fmt. * Clean and detail End opcode. * Add comment about top_label safety * Remove another TODO * Comment access to require_target * Remove redundant PartialEq * Print value that can't be coerced to u32 * s/with_instruction_capacity/with_capacity * fmt. * fmt * Proofs * Add better proof * Get rid of unreachable in StackValueType * Propagate error if frame stack overflown on create * use checked sub instead of - * Keep::count
2019-04-19 14:05:09 +00:00
use alloc::prelude::v1::*;
use alloc::rc::{Rc, Weak};
use core::fmt;
2018-01-17 15:32:33 +00:00
use host::Externals;
2018-12-11 11:54:06 +00:00
use isa;
use module::ModuleInstance;
use parity_wasm::elements::Local;
use runner::{check_function_args, Interpreter, InterpreterState, StackRecycler};
use types::ValueType;
2018-12-11 11:54:06 +00:00
use value::RuntimeValue;
use {Signature, Trap};
2018-01-17 15:32:33 +00:00
/// Reference to a function (See [`FuncInstance`] for details).
2018-01-23 16:38:49 +00:00
///
/// This reference has a reference-counting semantics.
///
/// [`FuncInstance`]: struct.FuncInstance.html
2018-01-17 15:32:33 +00:00
#[derive(Clone, Debug)]
pub struct FuncRef(Rc<FuncInstance>);
impl ::core::ops::Deref for FuncRef {
2018-12-11 11:54:06 +00:00
type Target = FuncInstance;
fn deref(&self) -> &FuncInstance {
&self.0
}
2018-01-17 15:32:33 +00:00
}
2018-01-23 16:38:49 +00:00
/// Runtime representation of a function.
///
/// Functions are the unit of organization of code in WebAssembly. Each function takes a sequence of values
2018-01-23 16:38:49 +00:00
/// as parameters and either optionally return a value or trap.
2018-01-25 15:10:39 +00:00
/// Functions can call other function including itself (i.e recursive calls are allowed) and imported functions
/// (i.e functions defined in another module or by the host environment).
2018-01-23 16:38:49 +00:00
///
/// Functions can be defined either:
///
/// - by a wasm module,
/// - by the host environment and passed to a wasm module as an import.
/// See more in [`Externals`].
///
/// [`Externals`]: trait.Externals.html
pub struct FuncInstance(FuncInstanceInternal);
2018-01-17 15:32:33 +00:00
#[derive(Clone)]
pub(crate) enum FuncInstanceInternal {
2018-12-11 11:54:06 +00:00
Internal {
signature: Rc<Signature>,
module: Weak<ModuleInstance>,
body: Rc<FuncBody>,
},
Host {
signature: Signature,
host_func_index: usize,
},
2018-01-17 15:32:33 +00:00
}
impl fmt::Debug for FuncInstance {
2018-12-11 11:54:06 +00:00
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.as_internal() {
&FuncInstanceInternal::Internal { ref signature, .. } => {
// We can't write description of self.module here, because it generate
// debug string for function instances and this will lead to infinite loop.
write!(f, "Internal {{ signature={:?} }}", signature,)
}
&FuncInstanceInternal::Host { ref signature, .. } => {
write!(f, "Host {{ signature={:?} }}", signature)
}
}
}
2018-01-17 15:32:33 +00:00
}
impl FuncInstance {
2018-12-11 11:54:06 +00:00
/// Allocate a function instance for a host function.
///
/// When this function instance will be called by the wasm code,
/// the instance of [`Externals`] will be invoked by calling `invoke_index`
/// with specified `host_func_index` here.
/// This call will be made with the `signature` provided here.
///
/// [`Externals`]: trait.Externals.html
pub fn alloc_host(signature: Signature, host_func_index: usize) -> FuncRef {
let func = FuncInstanceInternal::Host {
signature,
host_func_index,
};
FuncRef(Rc::new(FuncInstance(func)))
}
2018-12-11 11:54:06 +00:00
/// Returns [signature] of this function instance.
///
/// This function instance can only be called with matching signatures.
///
/// [signature]: struct.Signature.html
pub fn signature(&self) -> &Signature {
match *self.as_internal() {
FuncInstanceInternal::Internal { ref signature, .. } => signature,
FuncInstanceInternal::Host { ref signature, .. } => signature,
}
}
2018-12-11 11:54:06 +00:00
pub(crate) fn as_internal(&self) -> &FuncInstanceInternal {
&self.0
}
2018-12-11 11:54:06 +00:00
pub(crate) fn alloc_internal(
module: Weak<ModuleInstance>,
signature: Rc<Signature>,
body: FuncBody,
) -> FuncRef {
let func = FuncInstanceInternal::Internal {
signature,
module: module,
body: Rc::new(body),
};
FuncRef(Rc::new(FuncInstance(func)))
}
2018-01-17 15:32:33 +00:00
2018-12-11 11:54:06 +00:00
pub(crate) fn body(&self) -> Option<Rc<FuncBody>> {
match *self.as_internal() {
FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)),
FuncInstanceInternal::Host { .. } => None,
}
}
2018-01-17 15:32:33 +00:00
2018-12-11 11:54:06 +00:00
/// Invoke this function.
///
/// # Errors
///
/// Returns `Err` if `args` types is not match function [`signature`] or
/// if [`Trap`] at execution time occured.
///
/// [`signature`]: #method.signature
/// [`Trap`]: #enum.Trap.html
pub fn invoke<E: Externals>(
func: &FuncRef,
args: &[RuntimeValue],
externals: &mut E,
) -> Result<Option<RuntimeValue>, Trap> {
check_function_args(func.signature(), &args)?;
match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => {
let mut interpreter = Interpreter::new(func, args, None)?;
2018-12-11 11:54:06 +00:00
interpreter.start_execution(externals)
}
FuncInstanceInternal::Host {
ref host_func_index,
..
} => externals.invoke_index(*host_func_index, args.into()),
}
}
/// Invoke this function using recycled stacks.
///
/// # Errors
///
/// Same as [`invoke`].
///
/// [`invoke`]: #method.invoke
pub fn invoke_with_stack<E: Externals>(
func: &FuncRef,
args: &[RuntimeValue],
externals: &mut E,
stack_recycler: &mut StackRecycler,
) -> Result<Option<RuntimeValue>, Trap> {
check_function_args(func.signature(), &args)?;
match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => {
let mut interpreter = Interpreter::new(func, args, Some(stack_recycler))?;
let return_value = interpreter.start_execution(externals);
stack_recycler.recycle(interpreter);
return_value
}
FuncInstanceInternal::Host {
ref host_func_index,
..
} => externals.invoke_index(*host_func_index, args.into()),
}
}
2018-12-11 11:54:06 +00:00
/// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a
/// Host trap happens, caller can use [`resume_execution`] to feed the expected return value back in, and then
/// continue the execution.
///
/// This is an experimental API, and this functionality may not be available in other WebAssembly engines.
///
/// # Errors
///
/// Returns `Err` if `args` types is not match function [`signature`].
///
/// [`signature`]: #method.signature
/// [`Trap`]: #enum.Trap.html
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
pub fn invoke_resumable<'args>(
func: &FuncRef,
args: &'args [RuntimeValue],
) -> Result<FuncInvocation<'args>, Trap> {
check_function_args(func.signature(), &args)?;
match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => {
let interpreter = Interpreter::new(func, args, None)?;
2018-12-11 11:54:06 +00:00
Ok(FuncInvocation {
kind: FuncInvocationKind::Internal(interpreter),
})
}
FuncInstanceInternal::Host {
ref host_func_index,
..
} => Ok(FuncInvocation {
kind: FuncInvocationKind::Host {
args,
host_func_index: *host_func_index,
finished: false,
},
}),
}
}
}
/// A resumable invocation error.
#[derive(Debug)]
pub enum ResumableError {
2018-12-11 11:54:06 +00:00
/// Trap happened.
Trap(Trap),
/// The invocation is not resumable.
///
/// Invocations are only resumable if a host function is called, and the host function returns a trap of `Host` kind. For other cases, this error will be returned. This includes:
/// - The invocation is directly a host function.
/// - The invocation has not been started.
/// - The invocation returns normally or returns any trap other than `Host` kind.
///
/// This error is returned by [`resume_execution`].
///
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
NotResumable,
/// The invocation has already been started.
///
/// This error is returned by [`start_execution`].
///
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
AlreadyStarted,
}
impl From<Trap> for ResumableError {
2018-12-11 11:54:06 +00:00
fn from(trap: Trap) -> Self {
ResumableError::Trap(trap)
}
}
/// A resumable invocation handle. This struct is returned by `FuncInstance::invoke_resumable`.
pub struct FuncInvocation<'args> {
2018-12-11 11:54:06 +00:00
kind: FuncInvocationKind<'args>,
}
enum FuncInvocationKind<'args> {
2018-12-11 11:54:06 +00:00
Internal(Interpreter),
Host {
args: &'args [RuntimeValue],
host_func_index: usize,
finished: bool,
},
}
impl<'args> FuncInvocation<'args> {
2018-12-11 11:54:06 +00:00
/// Whether this invocation is currently resumable.
pub fn is_resumable(&self) -> bool {
match &self.kind {
&FuncInvocationKind::Internal(ref interpreter) => interpreter.state().is_resumable(),
&FuncInvocationKind::Host { .. } => false,
}
}
2018-12-11 11:54:06 +00:00
/// If the invocation is resumable, the expected return value type to be feed back in.
pub fn resumable_value_type(&self) -> Option<ValueType> {
match &self.kind {
&FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
&InterpreterState::Resumable(ref value_type) => value_type.clone(),
_ => None,
},
&FuncInvocationKind::Host { .. } => None,
}
}
2018-12-11 11:54:06 +00:00
/// Start the invocation execution.
pub fn start_execution<'externals, E: Externals + 'externals>(
&mut self,
externals: &'externals mut E,
) -> Result<Option<RuntimeValue>, ResumableError> {
match self.kind {
FuncInvocationKind::Internal(ref mut interpreter) => {
if interpreter.state() != &InterpreterState::Initialized {
return Err(ResumableError::AlreadyStarted);
}
Ok(interpreter.start_execution(externals)?)
}
FuncInvocationKind::Host {
ref args,
ref mut finished,
ref host_func_index,
} => {
if *finished {
return Err(ResumableError::AlreadyStarted);
}
*finished = true;
Ok(externals.invoke_index(*host_func_index, args.clone().into())?)
}
}
}
2018-12-11 11:54:06 +00:00
/// Resume an execution if a previous trap of Host kind happened.
///
/// `return_val` must be of the value type [`resumable_value_type`], defined by the host function import. Otherwise,
/// `UnexpectedSignature` trap will be returned. The current invocation must also be resumable
/// [`is_resumable`]. Otherwise, a `NotResumable` error will be returned.
///
/// [`resumable_value_type`]: #method.resumable_value_type
/// [`is_resumable`]: #method.is_resumable
pub fn resume_execution<'externals, E: Externals + 'externals>(
&mut self,
return_val: Option<RuntimeValue>,
externals: &'externals mut E,
) -> Result<Option<RuntimeValue>, ResumableError> {
use crate::TrapKind;
if return_val.map(|v| v.value_type()) != self.resumable_value_type() {
return Err(ResumableError::Trap(Trap::new(
TrapKind::UnexpectedSignature,
)));
}
match &mut self.kind {
FuncInvocationKind::Internal(interpreter) => {
if interpreter.state().is_resumable() {
Ok(interpreter.resume_execution(return_val, externals)?)
} else {
Err(ResumableError::AlreadyStarted)
2018-12-11 11:54:06 +00:00
}
}
FuncInvocationKind::Host { .. } => Err(ResumableError::NotResumable),
2018-12-11 11:54:06 +00:00
}
}
2018-01-17 15:32:33 +00:00
}
#[derive(Clone, Debug)]
pub struct FuncBody {
2018-12-11 11:54:06 +00:00
pub locals: Vec<Local>,
pub code: isa::Instructions,
2018-01-17 15:32:33 +00:00
}