1 //! Evaluating const expressions.
2 
3 use crate::prelude::*;
4 use crate::runtime::vm::{Instance, VMGcRef, ValRaw, I31};
5 use smallvec::SmallVec;
6 use wasmtime_environ::{ConstExpr, FuncIndex, GlobalIndex, Module};
7 
8 /// An interpreter for const expressions.
9 ///
10 /// This can be reused across many const expression evaluations to reuse
11 /// allocated resources, if any.
12 #[derive(Default)]
13 pub struct ConstExprEvaluator {
14     stack: SmallVec<[ValRaw; 2]>,
15 }
16 
17 /// The context within which a particular const expression is evaluated.
18 pub struct ConstEvalContext<'a, 'b> {
19     instance: &'a mut Instance,
20     module: &'b Module,
21 }
22 
23 impl<'a, 'b> ConstEvalContext<'a, 'b> {
24     /// Create a new context.
25     pub fn new(instance: &'a mut Instance, module: &'b Module) -> Self {
26         Self { instance, module }
27     }
28 
29     fn global_get(&mut self, index: GlobalIndex) -> Result<ValRaw> {
30         unsafe {
31             let gc_store = (*self.instance.store()).gc_store();
32             let global = self
33                 .instance
34                 .defined_or_imported_global_ptr(index)
35                 .as_ref()
36                 .unwrap();
37             Ok(global.to_val_raw(gc_store, self.module.globals[index].wasm_ty))
38         }
39     }
40 
41     fn ref_func(&mut self, index: FuncIndex) -> Result<ValRaw> {
42         Ok(ValRaw::funcref(
43             self.instance.get_func_ref(index).unwrap().cast(),
44         ))
45     }
46 }
47 
48 impl ConstExprEvaluator {
49     /// Evaluate the given const expression in the given context.
50     ///
51     /// # Unsafety
52     ///
53     /// The given const expression must be valid within the given context,
54     /// e.g. the const expression must be well-typed and the context must return
55     /// global values of the expected types. This evaluator operates directly on
56     /// untyped `ValRaw`s and does not and cannot check that its operands are of
57     /// the correct type.
58     pub unsafe fn eval(
59         &mut self,
60         context: &mut ConstEvalContext<'_, '_>,
61         expr: &ConstExpr,
62     ) -> Result<ValRaw> {
63         self.stack.clear();
64 
65         for op in expr.ops() {
66             match op {
67                 wasmtime_environ::ConstOp::I32Const(i) => self.stack.push(ValRaw::i32(*i)),
68                 wasmtime_environ::ConstOp::I64Const(i) => self.stack.push(ValRaw::i64(*i)),
69                 wasmtime_environ::ConstOp::F32Const(f) => self.stack.push(ValRaw::f32(*f)),
70                 wasmtime_environ::ConstOp::F64Const(f) => self.stack.push(ValRaw::f64(*f)),
71                 wasmtime_environ::ConstOp::V128Const(v) => self.stack.push(ValRaw::v128(*v)),
72                 wasmtime_environ::ConstOp::GlobalGet(g) => self.stack.push(context.global_get(*g)?),
73                 wasmtime_environ::ConstOp::RefNull => self.stack.push(ValRaw::null()),
74                 wasmtime_environ::ConstOp::RefFunc(f) => self.stack.push(context.ref_func(*f)?),
75                 wasmtime_environ::ConstOp::RefI31 => {
76                     let i = self.pop()?.get_i32();
77                     let i31 = I31::wrapping_i32(i);
78                     let raw = VMGcRef::from_i31(i31).as_raw_u32();
79                     self.stack.push(ValRaw::anyref(raw));
80                 }
81             }
82         }
83 
84         if self.stack.len() == 1 {
85             Ok(self.stack[0])
86         } else {
87             bail!(
88                 "const expr evaluation error: expected 1 resulting value, found {}",
89                 self.stack.len()
90             )
91         }
92     }
93 
94     fn pop(&mut self) -> Result<ValRaw> {
95         self.stack.pop().ok_or_else(|| {
96             anyhow!(
97                 "const expr evaluation error: attempted to pop from an empty \
98                  evaluation stack"
99             )
100         })
101     }
102 }
103