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