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