mod comparator;
mod driver;
mod error;
mod func;
mod utils;
#[cfg(doc)]
use crate::Engine;
pub use self::{
driver::FuncTranslationDriver,
error::TranslationError,
func::{FuncTranslator, FuncTranslatorAllocations},
utils::required_cells_for_tys,
};
use super::code_map::CompiledFuncEntity;
use crate::{
Error,
engine::EngineFunc,
module::{FuncIdx, ModuleHeader},
};
use core::{fmt, mem};
use wasmparser::{
BinaryReaderError,
FuncToValidate,
FuncValidatorAllocations,
ValidatorResources,
VisitOperator,
WasmFeatures,
};
type FuncValidator = wasmparser::FuncValidator<wasmparser::ValidatorResources>;
pub struct ValidatingFuncTranslator<T> {
pos: usize,
validator: FuncValidator,
translator: T,
}
#[derive(Default)]
pub struct ReusableAllocations<T> {
pub translation: T,
pub validation: FuncValidatorAllocations,
}
#[cfg(not(feature = "simd"))]
pub trait VisitSimdOperator<'a> {}
#[cfg(not(feature = "simd"))]
impl<'a, T> VisitSimdOperator<'a> for T where T: WasmTranslator<'a> {}
#[cfg(feature = "simd")]
pub trait VisitSimdOperator<'a>:
wasmparser::VisitSimdOperator<'a, Output = Result<(), Error>>
{
}
#[cfg(feature = "simd")]
impl<'a, T> VisitSimdOperator<'a> for T where
T: WasmTranslator<'a> + wasmparser::VisitSimdOperator<'a, Output = Result<(), Error>>
{
}
pub trait WasmTranslator<'parser>:
VisitOperator<'parser, Output = Result<(), Error>> + VisitSimdOperator<'parser>
{
type Allocations: Default;
fn setup(&mut self, bytes: &[u8]) -> Result<bool, Error>;
fn features(&self) -> WasmFeatures;
fn translate_locals(
&mut self,
amount: u32,
value_type: wasmparser::ValType,
) -> Result<(), Error>;
fn finish_translate_locals(&mut self) -> Result<(), Error>;
fn update_pos(&mut self, pos: usize);
fn finish(self, finalize: impl FnOnce(CompiledFuncEntity)) -> Result<Self::Allocations, Error>;
}
impl<T> ValidatingFuncTranslator<T> {
pub fn new(validator: FuncValidator, translator: T) -> Result<Self, Error> {
Ok(Self {
pos: 0,
validator,
translator,
})
}
fn current_pos(&self) -> usize {
self.pos
}
fn validate_then_translate<Validate, Translate>(
&mut self,
validate: Validate,
translate: Translate,
) -> Result<(), Error>
where
Validate: FnOnce(&mut FuncValidator) -> Result<(), BinaryReaderError>,
Translate: FnOnce(&mut T) -> Result<(), Error>,
{
validate(&mut self.validator)?;
translate(&mut self.translator)?;
Ok(())
}
}
impl<'parser, T> WasmTranslator<'parser> for ValidatingFuncTranslator<T>
where
T: WasmTranslator<'parser>,
{
type Allocations = ReusableAllocations<T::Allocations>;
fn setup(&mut self, bytes: &[u8]) -> Result<bool, Error> {
self.translator.setup(bytes)?;
Ok(false)
}
fn features(&self) -> WasmFeatures {
self.translator.features()
}
fn translate_locals(
&mut self,
amount: u32,
value_type: wasmparser::ValType,
) -> Result<(), Error> {
self.validator
.define_locals(self.current_pos(), amount, value_type)?;
self.translator.translate_locals(amount, value_type)?;
Ok(())
}
fn finish_translate_locals(&mut self) -> Result<(), Error> {
self.translator.finish_translate_locals()?;
Ok(())
}
fn update_pos(&mut self, pos: usize) {
self.pos = pos;
}
fn finish(
mut self,
finalize: impl FnOnce(CompiledFuncEntity),
) -> Result<Self::Allocations, Error> {
let pos = self.current_pos();
self.validator.finish(pos)?;
let translation = self.translator.finish(finalize)?;
let validation = self.validator.into_allocations();
let allocations = ReusableAllocations {
translation,
validation,
};
Ok(allocations)
}
}
macro_rules! impl_visit_operator {
( @mvp BrTable { $arg:ident: $argty:ty } => $visit:ident $_ann:tt $($rest:tt)* ) => {
fn $visit(&mut self, $arg: $argty) -> Self::Output {
let offset = self.current_pos();
self.validate_then_translate(
|validator| validator.visitor(offset).$visit($arg.clone()),
|translator| translator.$visit($arg.clone()),
)
}
impl_visit_operator!($($rest)*);
};
( @mvp $($rest:tt)* ) => {
impl_visit_operator!(@@supported $($rest)*);
};
( @sign_extension $($rest:tt)* ) => {
impl_visit_operator!(@@supported $($rest)*);
};
( @saturating_float_to_int $($rest:tt)* ) => {
impl_visit_operator!(@@supported $($rest)*);
};
( @bulk_memory $($rest:tt)* ) => {
impl_visit_operator!(@@supported $($rest)*);
};
( @reference_types $($rest:tt)* ) => {
impl_visit_operator!(@@supported $($rest)*);
};
( @tail_call $($rest:tt)* ) => {
impl_visit_operator!(@@supported $($rest)*);
};
( @wide_arithmetic $($rest:tt)* ) => {
impl_visit_operator!(@@supported $($rest)*);
};
( @@supported $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $_ann:tt $($rest:tt)* ) => {
fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
let offset = self.current_pos();
self.validate_then_translate(
move |validator| validator.visitor(offset).$visit($($($arg),*)?),
move |translator| translator.$visit($($($arg),*)?),
)
}
impl_visit_operator!($($rest)*);
};
( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $ann:tt $($rest:tt)* ) => {
fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output {
let offset = self.current_pos();
self.validator.visitor(offset).$visit($($($arg),*)?).map_err(::core::convert::Into::into)
}
impl_visit_operator!($($rest)*);
};
() => {};
}
impl<'a, T> VisitOperator<'a> for ValidatingFuncTranslator<T>
where
T: WasmTranslator<'a>,
{
type Output = Result<(), Error>;
#[cfg(feature = "simd")]
fn simd_visitor(
&mut self,
) -> Option<&mut dyn wasmparser::VisitSimdOperator<'a, Output = Self::Output>> {
Some(self)
}
wasmparser::for_each_visit_operator!(impl_visit_operator);
}
#[cfg(feature = "simd")]
macro_rules! impl_visit_simd_operator {
( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $_ann:tt $($rest:tt)* ) => {
fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
let offset = self.current_pos();
self.validate_then_translate(
move |validator| validator.simd_visitor(offset).$visit($($($arg),*)?),
move |translator| translator.$visit($($($arg),*)?),
)
}
impl_visit_simd_operator!($($rest)*);
};
() => {};
}
#[cfg(feature = "simd")]
impl<'a, T> wasmparser::VisitSimdOperator<'a> for ValidatingFuncTranslator<T>
where
T: WasmTranslator<'a>,
{
wasmparser::for_each_visit_simd_operator!(impl_visit_simd_operator);
}
#[derive(Debug)]
pub struct LazyFuncTranslator {
func_idx: FuncIdx,
engine_func: EngineFunc,
module: ModuleHeader,
validation: Validation,
}
enum Validation {
Checked(FuncToValidate<ValidatorResources>),
Unchecked(WasmFeatures),
}
impl Validation {
pub fn is_checked(&self) -> bool {
matches!(self, Self::Checked(_))
}
pub fn features(&self) -> WasmFeatures {
match self {
Validation::Checked(func_to_validate) => func_to_validate.features,
Validation::Unchecked(wasm_features) => *wasm_features,
}
}
pub fn take_func_to_validate(&mut self) -> Option<FuncToValidate<ValidatorResources>> {
let features = self.features();
match mem::replace(self, Self::Unchecked(features)) {
Self::Checked(func_to_validate) => Some(func_to_validate),
Self::Unchecked(_) => None,
}
}
}
impl fmt::Debug for Validation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("LazyFuncTranslator")
.field("validate", &self.is_checked())
.field("features", &self.features())
.finish()
}
}
impl LazyFuncTranslator {
pub fn new(
func_idx: FuncIdx,
engine_func: EngineFunc,
module: ModuleHeader,
func_to_validate: FuncToValidate<ValidatorResources>,
) -> Self {
Self {
func_idx,
engine_func,
module,
validation: Validation::Checked(func_to_validate),
}
}
pub fn new_unchecked(
func_idx: FuncIdx,
engine_func: EngineFunc,
module: ModuleHeader,
features: WasmFeatures,
) -> Self {
Self {
func_idx,
engine_func,
module,
validation: Validation::Unchecked(features),
}
}
}
impl WasmTranslator<'_> for LazyFuncTranslator {
type Allocations = ();
fn setup(&mut self, bytes: &[u8]) -> Result<bool, Error> {
self.module
.engine()
.upgrade()
.unwrap_or_else(|| {
panic!(
"engine does no longer exist for lazy compilation setup: {:?}",
self.module.engine()
)
})
.init_lazy_func(
self.func_idx,
self.engine_func,
bytes,
&self.module,
self.validation.take_func_to_validate(),
);
Ok(true)
}
#[inline]
fn features(&self) -> WasmFeatures {
self.validation.features()
}
#[inline]
fn translate_locals(
&mut self,
_amount: u32,
_value_type: wasmparser::ValType,
) -> Result<(), Error> {
Ok(())
}
#[inline]
fn finish_translate_locals(&mut self) -> Result<(), Error> {
Ok(())
}
#[inline]
fn update_pos(&mut self, _pos: usize) {}
#[inline]
fn finish(
self,
_finalize: impl FnOnce(CompiledFuncEntity),
) -> Result<Self::Allocations, Error> {
Ok(())
}
}
macro_rules! impl_visit_operator {
( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $ann:tt $($rest:tt)* ) => {
#[inline]
fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output {
$( $( let _ = $arg; )* )?
Ok(())
}
impl_visit_operator!($($rest)*);
};
() => {};
}
impl<'a> VisitOperator<'a> for LazyFuncTranslator {
type Output = Result<(), Error>;
#[cfg(feature = "simd")]
fn simd_visitor(
&mut self,
) -> Option<&mut dyn wasmparser::VisitSimdOperator<'a, Output = Self::Output>> {
Some(self)
}
wasmparser::for_each_visit_operator!(impl_visit_operator);
}
#[cfg(feature = "simd")]
impl wasmparser::VisitSimdOperator<'_> for LazyFuncTranslator {
wasmparser::for_each_visit_simd_operator!(impl_visit_operator);
}