1use crate::prelude::*;
4use crate::runtime::vm::{I31, VMGcRef, ValRaw};
5use crate::store::{AutoAssertNoGc, InstanceId, StoreOpaque};
6#[cfg(feature = "gc")]
7use crate::{
8 AnyRef, ArrayRef, ArrayRefPre, ArrayType, ExternRef, StructRef, StructRefPre, StructType, Val,
9};
10use smallvec::SmallVec;
11use wasmtime_environ::{ConstExpr, ConstOp, FuncIndex, GlobalIndex};
12#[cfg(feature = "gc")]
13use wasmtime_environ::{
14 Unsigned, VMSharedTypeIndex, WasmCompositeInnerType, WasmCompositeType, WasmSubType,
15};
16
17#[derive(Default)]
22pub struct ConstExprEvaluator {
23 stack: SmallVec<[ValRaw; 2]>,
24}
25
26pub struct ConstEvalContext {
28 pub(crate) instance: InstanceId,
29}
30
31impl ConstEvalContext {
32 pub fn new(instance: InstanceId) -> Self {
34 Self { instance }
35 }
36
37 fn global_get(&mut self, store: &mut AutoAssertNoGc<'_>, index: GlobalIndex) -> Result<ValRaw> {
38 unsafe {
39 let mut instance = store.instance_mut(self.instance);
40 let global = instance
41 .as_mut()
42 .defined_or_imported_global_ptr(index)
43 .as_ref();
44 let wasm_ty = instance.env_module().globals[index].wasm_ty;
45 global.to_val_raw(store, wasm_ty)
46 }
47 }
48
49 fn ref_func(&mut self, store: &mut AutoAssertNoGc<'_>, index: FuncIndex) -> Result<ValRaw> {
50 Ok(ValRaw::funcref(
51 store
52 .instance_mut(self.instance)
53 .get_func_ref(index)
54 .unwrap()
55 .as_ptr()
56 .cast(),
57 ))
58 }
59
60 #[cfg(feature = "gc")]
61 fn struct_fields_len(
62 &self,
63 store: &mut AutoAssertNoGc<'_>,
64 shared_ty: VMSharedTypeIndex,
65 ) -> usize {
66 let struct_ty = StructType::from_shared_type_index(store.engine(), shared_ty);
67 let fields = struct_ty.fields();
68 fields.len()
69 }
70
71 #[cfg(feature = "gc")]
73 unsafe fn struct_new(
74 &mut self,
75 store: &mut AutoAssertNoGc<'_>,
76 shared_ty: VMSharedTypeIndex,
77 fields: &[ValRaw],
78 ) -> Result<ValRaw> {
79 let struct_ty = StructType::from_shared_type_index(store.engine(), shared_ty);
80 let fields = fields
81 .iter()
82 .zip(struct_ty.fields())
83 .map(|(raw, ty)| {
84 let ty = ty.element_type().unpack();
85 Val::_from_raw(store, *raw, ty)
86 })
87 .collect::<Vec<_>>();
88
89 let allocator = StructRefPre::_new(store, struct_ty);
90 let struct_ref = unsafe { StructRef::new_maybe_async(store, &allocator, &fields)? };
91 let raw = struct_ref.to_anyref()._to_raw(store)?;
92 Ok(ValRaw::anyref(raw))
93 }
94
95 #[cfg(feature = "gc")]
96 fn struct_new_default(
97 &mut self,
98 store: &mut AutoAssertNoGc<'_>,
99 shared_ty: VMSharedTypeIndex,
100 ) -> Result<ValRaw> {
101 let module = store
102 .instance(self.instance)
103 .runtime_module()
104 .expect("should never be allocating a struct type defined in a dummy module");
105
106 let borrowed = module
107 .engine()
108 .signatures()
109 .borrow(shared_ty)
110 .expect("should have a registered type for struct");
111 let WasmSubType {
112 composite_type:
113 WasmCompositeType {
114 shared: false,
115 inner: WasmCompositeInnerType::Struct(struct_ty),
116 },
117 ..
118 } = &*borrowed
119 else {
120 unreachable!("registered type should be a struct");
121 };
122
123 let fields = struct_ty
124 .fields
125 .iter()
126 .map(|ty| match &ty.element_type {
127 wasmtime_environ::WasmStorageType::I8 | wasmtime_environ::WasmStorageType::I16 => {
128 ValRaw::i32(0)
129 }
130 wasmtime_environ::WasmStorageType::Val(v) => match v {
131 wasmtime_environ::WasmValType::I32 => ValRaw::i32(0),
132 wasmtime_environ::WasmValType::I64 => ValRaw::i64(0),
133 wasmtime_environ::WasmValType::F32 => ValRaw::f32(0.0f32.to_bits()),
134 wasmtime_environ::WasmValType::F64 => ValRaw::f64(0.0f64.to_bits()),
135 wasmtime_environ::WasmValType::V128 => ValRaw::v128(0),
136 wasmtime_environ::WasmValType::Ref(r) => {
137 assert!(r.nullable);
138 ValRaw::null()
139 }
140 },
141 })
142 .collect::<SmallVec<[_; 8]>>();
143
144 unsafe { self.struct_new(store, shared_ty, &fields) }
145 }
146}
147
148impl ConstExprEvaluator {
149 pub unsafe fn eval(
164 &mut self,
165 store: &mut StoreOpaque,
166 context: &mut ConstEvalContext,
167 expr: &ConstExpr,
168 ) -> Result<ValRaw> {
169 log::trace!("evaluating const expr: {expr:?}");
170
171 self.stack.clear();
172
173 #[cfg(feature = "gc")]
177 let mut store = crate::OpaqueRootScope::new(store);
178 #[cfg(not(feature = "gc"))]
179 let mut store = store;
180
181 let mut store = AutoAssertNoGc::new(&mut store);
187
188 for op in expr.ops() {
189 log::trace!("const-evaluating op: {op:?}");
190 match op {
191 ConstOp::I32Const(i) => self.stack.push(ValRaw::i32(*i)),
192 ConstOp::I64Const(i) => self.stack.push(ValRaw::i64(*i)),
193 ConstOp::F32Const(f) => self.stack.push(ValRaw::f32(*f)),
194 ConstOp::F64Const(f) => self.stack.push(ValRaw::f64(*f)),
195 ConstOp::V128Const(v) => self.stack.push(ValRaw::v128(*v)),
196 ConstOp::GlobalGet(g) => self.stack.push(context.global_get(&mut store, *g)?),
197 ConstOp::RefNull => self.stack.push(ValRaw::null()),
198 ConstOp::RefFunc(f) => self.stack.push(context.ref_func(&mut store, *f)?),
199 ConstOp::RefI31 => {
200 let i = self.pop()?.get_i32();
201 let i31 = I31::wrapping_i32(i);
202 let raw = VMGcRef::from_i31(i31).as_raw_u32();
203 self.stack.push(ValRaw::anyref(raw));
204 }
205 ConstOp::I32Add => {
206 let b = self.pop()?.get_i32();
207 let a = self.pop()?.get_i32();
208 self.stack.push(ValRaw::i32(a.wrapping_add(b)));
209 }
210 ConstOp::I32Sub => {
211 let b = self.pop()?.get_i32();
212 let a = self.pop()?.get_i32();
213 self.stack.push(ValRaw::i32(a.wrapping_sub(b)));
214 }
215 ConstOp::I32Mul => {
216 let b = self.pop()?.get_i32();
217 let a = self.pop()?.get_i32();
218 self.stack.push(ValRaw::i32(a.wrapping_mul(b)));
219 }
220 ConstOp::I64Add => {
221 let b = self.pop()?.get_i64();
222 let a = self.pop()?.get_i64();
223 self.stack.push(ValRaw::i64(a.wrapping_add(b)));
224 }
225 ConstOp::I64Sub => {
226 let b = self.pop()?.get_i64();
227 let a = self.pop()?.get_i64();
228 self.stack.push(ValRaw::i64(a.wrapping_sub(b)));
229 }
230 ConstOp::I64Mul => {
231 let b = self.pop()?.get_i64();
232 let a = self.pop()?.get_i64();
233 self.stack.push(ValRaw::i64(a.wrapping_mul(b)));
234 }
235
236 #[cfg(not(feature = "gc"))]
237 ConstOp::StructNew { .. }
238 | ConstOp::StructNewDefault { .. }
239 | ConstOp::ArrayNew { .. }
240 | ConstOp::ArrayNewDefault { .. }
241 | ConstOp::ArrayNewFixed { .. }
242 | ConstOp::ExternConvertAny
243 | ConstOp::AnyConvertExtern => {
244 bail!(
245 "const expr evaluation error: struct operations are not \
246 supported without the `gc` feature"
247 )
248 }
249
250 #[cfg(feature = "gc")]
251 ConstOp::StructNew { struct_type_index } => {
252 let interned_type_index = store.instance(context.instance).env_module().types
253 [*struct_type_index]
254 .unwrap_engine_type_index();
255 let len = context.struct_fields_len(&mut store, interned_type_index);
256
257 if self.stack.len() < len {
258 bail!(
259 "const expr evaluation error: expected at least {len} values on the stack, found {}",
260 self.stack.len()
261 )
262 }
263
264 let start = self.stack.len() - len;
265 let s = context.struct_new(
266 &mut store,
267 interned_type_index,
268 &self.stack[start..],
269 )?;
270 self.stack.truncate(start);
271 self.stack.push(s);
272 }
273
274 #[cfg(feature = "gc")]
275 ConstOp::StructNewDefault { struct_type_index } => {
276 let ty = store.instance(context.instance).env_module().types
277 [*struct_type_index]
278 .unwrap_engine_type_index();
279 self.stack.push(context.struct_new_default(&mut store, ty)?);
280 }
281
282 #[cfg(feature = "gc")]
283 ConstOp::ArrayNew { array_type_index } => {
284 let ty = store.instance(context.instance).env_module().types[*array_type_index]
285 .unwrap_engine_type_index();
286 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
287
288 let len = self.pop()?.get_i32().unsigned();
289
290 let elem = Val::_from_raw(&mut store, self.pop()?, ty.element_type().unpack());
291
292 let pre = ArrayRefPre::_new(&mut store, ty);
293 let array = unsafe { ArrayRef::new_maybe_async(&mut store, &pre, &elem, len)? };
294
295 self.stack
296 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
297 }
298
299 #[cfg(feature = "gc")]
300 ConstOp::ArrayNewDefault { array_type_index } => {
301 let ty = store.instance(context.instance).env_module().types[*array_type_index]
302 .unwrap_engine_type_index();
303 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
304
305 let len = self.pop()?.get_i32().unsigned();
306
307 let elem = Val::default_for_ty(ty.element_type().unpack())
308 .expect("type should have a default value");
309
310 let pre = ArrayRefPre::_new(&mut store, ty);
311 let array = unsafe { ArrayRef::new_maybe_async(&mut store, &pre, &elem, len)? };
312
313 self.stack
314 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
315 }
316
317 #[cfg(feature = "gc")]
318 ConstOp::ArrayNewFixed {
319 array_type_index,
320 array_size,
321 } => {
322 let ty = store.instance(context.instance).env_module().types[*array_type_index]
323 .unwrap_engine_type_index();
324 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
325
326 let array_size = usize::try_from(*array_size).unwrap();
327 if self.stack.len() < array_size {
328 bail!(
329 "const expr evaluation error: expected at least {array_size} values on the stack, found {}",
330 self.stack.len()
331 )
332 }
333
334 let start = self.stack.len() - array_size;
335
336 let elem_ty = ty.element_type();
337 let elem_ty = elem_ty.unpack();
338
339 let elems = self
340 .stack
341 .drain(start..)
342 .map(|raw| Val::_from_raw(&mut store, raw, elem_ty))
343 .collect::<SmallVec<[_; 8]>>();
344
345 let pre = ArrayRefPre::_new(&mut store, ty);
346 let array =
347 unsafe { ArrayRef::new_fixed_maybe_async(&mut store, &pre, &elems)? };
348
349 self.stack
350 .push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
351 }
352
353 #[cfg(feature = "gc")]
354 ConstOp::ExternConvertAny => {
355 let result = match AnyRef::_from_raw(&mut store, self.pop()?.get_anyref()) {
356 Some(anyref) => {
357 ExternRef::_convert_any(&mut store, anyref)?._to_raw(&mut store)?
358 }
359 None => 0,
360 };
361 self.stack.push(ValRaw::externref(result));
362 }
363
364 #[cfg(feature = "gc")]
365 ConstOp::AnyConvertExtern => {
366 let result =
367 match ExternRef::_from_raw(&mut store, self.pop()?.get_externref()) {
368 Some(externref) => AnyRef::_convert_extern(&mut store, externref)?
369 ._to_raw(&mut store)?,
370 None => 0,
371 };
372 self.stack.push(ValRaw::anyref(result));
373 }
374 }
375 }
376
377 if self.stack.len() == 1 {
378 log::trace!("const expr evaluated to {:?}", self.stack[0]);
379 Ok(self.stack[0])
380 } else {
381 bail!(
382 "const expr evaluation error: expected 1 resulting value, found {}",
383 self.stack.len()
384 )
385 }
386 }
387
388 fn pop(&mut self) -> Result<ValRaw> {
389 self.stack.pop().ok_or_else(|| {
390 anyhow!(
391 "const expr evaluation error: attempted to pop from an empty \
392 evaluation stack"
393 )
394 })
395 }
396}