1use crate::prelude::*;
4use crate::runtime::vm;
5use crate::store::{AutoAssertNoGc, InstanceId, StoreOpaque, StoreResourceLimiter};
6#[cfg(feature = "gc")]
7use crate::{
8 AnyRef, ArrayRef, ArrayRefPre, ArrayType, ExternRef, I31, StructRef, StructRefPre, StructType,
9};
10use crate::{OpaqueRootScope, Val};
11use wasmtime_environ::{ConstExpr, ConstOp, FuncIndex, GlobalIndex};
12#[cfg(feature = "gc")]
13use wasmtime_environ::{VMSharedTypeIndex, WasmCompositeInnerType, WasmCompositeType, WasmSubType};
14
15pub struct ConstExprEvaluator {
20 stack: Vec<Val>,
21 simple: Val,
22}
23
24impl Default for ConstExprEvaluator {
25 fn default() -> ConstExprEvaluator {
26 ConstExprEvaluator {
27 stack: Vec::new(),
28 simple: Val::I32(0),
29 }
30 }
31}
32
33pub struct ConstEvalContext {
35 pub(crate) instance: InstanceId,
36}
37
38impl ConstEvalContext {
39 pub fn new(instance: InstanceId) -> Self {
41 Self { instance }
42 }
43
44 fn global_get(&mut self, store: &mut StoreOpaque, index: GlobalIndex) -> Result<Val> {
45 let id = store.id();
46 Ok(store
47 .instance_mut(self.instance)
48 .get_exported_global(id, index)
49 ._get(&mut AutoAssertNoGc::new(store)))
50 }
51
52 fn ref_func(&mut self, store: &mut StoreOpaque, index: FuncIndex) -> Result<Val> {
53 let id = store.id();
54 let func = unsafe {
57 store
58 .instance_mut(self.instance)
59 .get_exported_func(id, index)
60 };
61 Ok(func.into())
62 }
63
64 #[cfg(feature = "gc")]
65 fn struct_fields_len(&self, store: &mut StoreOpaque, shared_ty: VMSharedTypeIndex) -> 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")]
72 async fn struct_new(
73 &mut self,
74 store: &mut StoreOpaque,
75 limiter: Option<&mut StoreResourceLimiter<'_>>,
76 shared_ty: VMSharedTypeIndex,
77 fields: &[Val],
78 ) -> Result<Val> {
79 let struct_ty = StructType::from_shared_type_index(store.engine(), shared_ty);
80 let allocator = StructRefPre::_new(store, struct_ty);
81 let struct_ref = StructRef::_new_async(store, limiter, &allocator, &fields).await?;
82 Ok(Val::AnyRef(Some(struct_ref.into())))
83 }
84
85 #[cfg(feature = "gc")]
86 async fn struct_new_default(
87 &mut self,
88 store: &mut StoreOpaque,
89 limiter: Option<&mut StoreResourceLimiter<'_>>,
90 shared_ty: VMSharedTypeIndex,
91 ) -> Result<Val> {
92 let module = store
93 .instance(self.instance)
94 .runtime_module()
95 .expect("should never be allocating a struct type defined in a dummy module");
96
97 let borrowed = module
98 .engine()
99 .signatures()
100 .borrow(shared_ty)
101 .expect("should have a registered type for struct");
102 let WasmSubType {
103 composite_type:
104 WasmCompositeType {
105 shared: false,
106 inner: WasmCompositeInnerType::Struct(struct_ty),
107 },
108 ..
109 } = &*borrowed
110 else {
111 unreachable!("registered type should be a struct");
112 };
113
114 let fields = struct_ty
115 .fields
116 .iter()
117 .map(|ty| match &ty.element_type {
118 wasmtime_environ::WasmStorageType::I8 | wasmtime_environ::WasmStorageType::I16 => {
119 Val::I32(0)
120 }
121 wasmtime_environ::WasmStorageType::Val(v) => match v {
122 wasmtime_environ::WasmValType::I32 => Val::I32(0),
123 wasmtime_environ::WasmValType::I64 => Val::I64(0),
124 wasmtime_environ::WasmValType::F32 => Val::F32(0.0f32.to_bits()),
125 wasmtime_environ::WasmValType::F64 => Val::F64(0.0f64.to_bits()),
126 wasmtime_environ::WasmValType::V128 => Val::V128(0u128.into()),
127 wasmtime_environ::WasmValType::Ref(r) => {
128 assert!(r.nullable);
129 Val::null_top(r.heap_type.top())
130 }
131 },
132 })
133 .collect::<smallvec::SmallVec<[_; 8]>>();
134
135 self.struct_new(store, limiter, shared_ty, &fields).await
136 }
137}
138
139impl ConstExprEvaluator {
140 pub fn eval_int(
148 &mut self,
149 store: &mut StoreOpaque,
150 context: &mut ConstEvalContext,
151 expr: &ConstExpr,
152 ) -> Result<&Val> {
153 if self.try_simple(expr).is_some() {
156 return Ok(&self.simple);
157 }
158
159 let mut scope = OpaqueRootScope::new(store);
162 vm::assert_ready(self.eval_loop(&mut scope, None, context, expr))
163 }
164
165 #[inline]
168 pub fn try_simple(&mut self, expr: &ConstExpr) -> Option<&Val> {
169 match expr.ops() {
170 [ConstOp::I32Const(i)] => Some(self.return_one(Val::I32(*i))),
171 [ConstOp::I64Const(i)] => Some(self.return_one(Val::I64(*i))),
172 [ConstOp::F32Const(f)] => Some(self.return_one(Val::F32(*f))),
173 [ConstOp::F64Const(f)] => Some(self.return_one(Val::F64(*f))),
174 _ => None,
175 }
176 }
177
178 pub async fn eval(
190 &mut self,
191 store: &mut OpaqueRootScope<&mut StoreOpaque>,
192 limiter: Option<&mut StoreResourceLimiter<'_>>,
193 context: &mut ConstEvalContext,
194 expr: &ConstExpr,
195 ) -> Result<&Val> {
196 if self.try_simple(expr).is_some() {
199 return Ok(&self.simple);
200 }
201 self.eval_loop(store, limiter, context, expr).await
202 }
203
204 #[inline]
205 fn return_one(&mut self, val: Val) -> &Val {
206 self.simple = val;
207 &self.simple
208 }
212
213 #[cold]
214 async fn eval_loop(
215 &mut self,
216 store: &mut OpaqueRootScope<&mut StoreOpaque>,
217 mut limiter: Option<&mut StoreResourceLimiter<'_>>,
218 context: &mut ConstEvalContext,
219 expr: &ConstExpr,
220 ) -> Result<&Val> {
221 log::trace!("evaluating const expr: {expr:?}");
222
223 self.stack.clear();
224
225 let _ = &mut limiter;
228
229 for op in expr.ops() {
230 log::trace!("const-evaluating op: {op:?}");
231 match op {
232 ConstOp::I32Const(i) => self.stack.push(Val::I32(*i)),
233 ConstOp::I64Const(i) => self.stack.push(Val::I64(*i)),
234 ConstOp::F32Const(f) => self.stack.push(Val::F32(*f)),
235 ConstOp::F64Const(f) => self.stack.push(Val::F64(*f)),
236 ConstOp::V128Const(v) => self.stack.push(Val::V128((*v).into())),
237 ConstOp::GlobalGet(g) => self.stack.push(context.global_get(store, *g)?),
238 ConstOp::RefNull(ty) => self.stack.push(Val::null_top(*ty)),
239 ConstOp::RefFunc(f) => self.stack.push(context.ref_func(store, *f)?),
240 #[cfg(feature = "gc")]
241 ConstOp::RefI31 => {
242 let i = self.pop()?.unwrap_i32();
243 let i31 = I31::wrapping_i32(i);
244 let r = AnyRef::_from_i31(&mut AutoAssertNoGc::new(store), i31);
245 self.stack.push(Val::AnyRef(Some(r)));
246 }
247 #[cfg(not(feature = "gc"))]
248 ConstOp::RefI31 => panic!("should not have validated"),
249 ConstOp::I32Add => {
250 let b = self.pop()?.unwrap_i32();
251 let a = self.pop()?.unwrap_i32();
252 self.stack.push(Val::I32(a.wrapping_add(b)));
253 }
254 ConstOp::I32Sub => {
255 let b = self.pop()?.unwrap_i32();
256 let a = self.pop()?.unwrap_i32();
257 self.stack.push(Val::I32(a.wrapping_sub(b)));
258 }
259 ConstOp::I32Mul => {
260 let b = self.pop()?.unwrap_i32();
261 let a = self.pop()?.unwrap_i32();
262 self.stack.push(Val::I32(a.wrapping_mul(b)));
263 }
264 ConstOp::I64Add => {
265 let b = self.pop()?.unwrap_i64();
266 let a = self.pop()?.unwrap_i64();
267 self.stack.push(Val::I64(a.wrapping_add(b)));
268 }
269 ConstOp::I64Sub => {
270 let b = self.pop()?.unwrap_i64();
271 let a = self.pop()?.unwrap_i64();
272 self.stack.push(Val::I64(a.wrapping_sub(b)));
273 }
274 ConstOp::I64Mul => {
275 let b = self.pop()?.unwrap_i64();
276 let a = self.pop()?.unwrap_i64();
277 self.stack.push(Val::I64(a.wrapping_mul(b)));
278 }
279
280 #[cfg(not(feature = "gc"))]
281 ConstOp::StructNew { .. }
282 | ConstOp::StructNewDefault { .. }
283 | ConstOp::ArrayNew { .. }
284 | ConstOp::ArrayNewDefault { .. }
285 | ConstOp::ArrayNewFixed { .. }
286 | ConstOp::ExternConvertAny
287 | ConstOp::AnyConvertExtern => {
288 bail!(
289 "const expr evaluation error: struct operations are not \
290 supported without the `gc` feature"
291 )
292 }
293
294 #[cfg(feature = "gc")]
295 ConstOp::StructNew { struct_type_index } => {
296 let interned_type_index = store.instance(context.instance).env_module().types
297 [*struct_type_index]
298 .unwrap_engine_type_index();
299 let len = context.struct_fields_len(store, interned_type_index);
300
301 if self.stack.len() < len {
302 bail!(
303 "const expr evaluation error: expected at least {len} values on the stack, found {}",
304 self.stack.len()
305 )
306 }
307
308 let start = self.stack.len() - len;
309 let s = context
310 .struct_new(
311 store,
312 limiter.as_deref_mut(),
313 interned_type_index,
314 &self.stack[start..],
315 )
316 .await?;
317 self.stack.truncate(start);
318 self.stack.push(s);
319 }
320
321 #[cfg(feature = "gc")]
322 ConstOp::StructNewDefault { struct_type_index } => {
323 let ty = store.instance(context.instance).env_module().types
324 [*struct_type_index]
325 .unwrap_engine_type_index();
326 self.stack.push(
327 context
328 .struct_new_default(store, limiter.as_deref_mut(), ty)
329 .await?,
330 );
331 }
332
333 #[cfg(feature = "gc")]
334 ConstOp::ArrayNew { array_type_index } => {
335 let ty = store.instance(context.instance).env_module().types[*array_type_index]
336 .unwrap_engine_type_index();
337 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
338
339 let len = self.pop()?.unwrap_i32().cast_unsigned();
340
341 let elem = self.pop()?;
342
343 let pre = ArrayRefPre::_new(store, ty);
344 let array =
345 ArrayRef::_new_async(store, limiter.as_deref_mut(), &pre, &elem, len)
346 .await?;
347
348 self.stack.push(Val::AnyRef(Some(array.into())));
349 }
350
351 #[cfg(feature = "gc")]
352 ConstOp::ArrayNewDefault { array_type_index } => {
353 let ty = store.instance(context.instance).env_module().types[*array_type_index]
354 .unwrap_engine_type_index();
355 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
356
357 let len = self.pop()?.unwrap_i32().cast_unsigned();
358
359 let elem = Val::default_for_ty(ty.element_type().unpack())
360 .expect("type should have a default value");
361
362 let pre = ArrayRefPre::_new(store, ty);
363 let array =
364 ArrayRef::_new_async(store, limiter.as_deref_mut(), &pre, &elem, len)
365 .await?;
366
367 self.stack.push(Val::AnyRef(Some(array.into())));
368 }
369
370 #[cfg(feature = "gc")]
371 ConstOp::ArrayNewFixed {
372 array_type_index,
373 array_size,
374 } => {
375 let ty = store.instance(context.instance).env_module().types[*array_type_index]
376 .unwrap_engine_type_index();
377 let ty = ArrayType::from_shared_type_index(store.engine(), ty);
378
379 let array_size = usize::try_from(*array_size).unwrap();
380 if self.stack.len() < array_size {
381 bail!(
382 "const expr evaluation error: expected at least {array_size} values on the stack, found {}",
383 self.stack.len()
384 )
385 }
386
387 let start = self.stack.len() - array_size;
388
389 let elems = self
390 .stack
391 .drain(start..)
392 .collect::<smallvec::SmallVec<[_; 8]>>();
393
394 let pre = ArrayRefPre::_new(store, ty);
395 let array =
396 ArrayRef::_new_fixed_async(store, limiter.as_deref_mut(), &pre, &elems)
397 .await?;
398
399 self.stack.push(Val::AnyRef(Some(array.into())));
400 }
401
402 #[cfg(feature = "gc")]
403 ConstOp::ExternConvertAny => {
404 let mut store = AutoAssertNoGc::new(store);
405 let result = match self.pop()?.unwrap_anyref() {
406 Some(anyref) => Some(ExternRef::_convert_any(&mut store, *anyref)?),
407 None => None,
408 };
409 self.stack.push(Val::ExternRef(result));
410 }
411
412 #[cfg(feature = "gc")]
413 ConstOp::AnyConvertExtern => {
414 let mut store = AutoAssertNoGc::new(store);
415 let result = match self.pop()?.unwrap_externref() {
416 Some(externref) => Some(AnyRef::_convert_extern(&mut store, *externref)?),
417 None => None,
418 };
419 self.stack.push(result.into());
420 }
421 }
422 }
423
424 if self.stack.len() == 1 {
425 log::trace!("const expr evaluated to {:?}", self.stack[0]);
426 Ok(&self.stack[0])
427 } else {
428 bail!(
429 "const expr evaluation error: expected 1 resulting value, found {}",
430 self.stack.len()
431 )
432 }
433 }
434
435 fn pop(&mut self) -> Result<Val> {
436 self.stack.pop().ok_or_else(|| {
437 anyhow!(
438 "const expr evaluation error: attempted to pop from an empty \
439 evaluation stack"
440 )
441 })
442 }
443}