use crate::runtime::vm::{Instance, VMGcRef, ValRaw, I31};
use anyhow::{anyhow, bail, Result};
use smallvec::SmallVec;
use wasmtime_environ::{ConstExpr, FuncIndex, GlobalIndex, Module};
#[derive(Default)]
pub struct ConstExprEvaluator {
stack: SmallVec<[ValRaw; 2]>,
}
pub struct ConstEvalContext<'a, 'b> {
instance: &'a mut Instance,
module: &'b Module,
}
impl<'a, 'b> ConstEvalContext<'a, 'b> {
pub fn new(instance: &'a mut Instance, module: &'b Module) -> Self {
Self { instance, module }
}
fn global_get(&mut self, index: GlobalIndex) -> Result<ValRaw> {
unsafe {
let gc_store = (*self.instance.store()).gc_store();
let global = self
.instance
.defined_or_imported_global_ptr(index)
.as_ref()
.unwrap();
Ok(global.to_val_raw(gc_store, self.module.globals[index].wasm_ty))
}
}
fn ref_func(&mut self, index: FuncIndex) -> Result<ValRaw> {
Ok(ValRaw::funcref(
self.instance.get_func_ref(index).unwrap().cast(),
))
}
}
impl ConstExprEvaluator {
pub unsafe fn eval(
&mut self,
context: &mut ConstEvalContext<'_, '_>,
expr: &ConstExpr,
) -> Result<ValRaw> {
self.stack.clear();
for op in expr.ops() {
match op {
wasmtime_environ::ConstOp::I32Const(i) => self.stack.push(ValRaw::i32(*i)),
wasmtime_environ::ConstOp::I64Const(i) => self.stack.push(ValRaw::i64(*i)),
wasmtime_environ::ConstOp::F32Const(f) => self.stack.push(ValRaw::f32(*f)),
wasmtime_environ::ConstOp::F64Const(f) => self.stack.push(ValRaw::f64(*f)),
wasmtime_environ::ConstOp::V128Const(v) => self.stack.push(ValRaw::v128(*v)),
wasmtime_environ::ConstOp::GlobalGet(g) => self.stack.push(context.global_get(*g)?),
wasmtime_environ::ConstOp::RefNull => self.stack.push(ValRaw::null()),
wasmtime_environ::ConstOp::RefFunc(f) => self.stack.push(context.ref_func(*f)?),
wasmtime_environ::ConstOp::RefI31 => {
let i = self.pop()?.get_i32();
let i31 = I31::wrapping_i32(i);
let raw = VMGcRef::from_i31(i31).as_raw_u32();
self.stack.push(ValRaw::anyref(raw));
}
}
}
if self.stack.len() == 1 {
Ok(self.stack[0])
} else {
bail!(
"const expr evaluation error: expected 1 resulting value, found {}",
self.stack.len()
)
}
}
fn pop(&mut self) -> Result<ValRaw> {
self.stack.pop().ok_or_else(|| {
anyhow!(
"const expr evaluation error: attempted to pop from an empty \
evaluation stack"
)
})
}
}