1use crate::{Tunables, WasmResult, wasm_unsupported};
2use alloc::borrow::Cow;
3use alloc::boxed::Box;
4use core::{fmt, ops::Range};
5use cranelift_entity::entity_impl;
6use serde_derive::{Deserialize, Serialize};
7use smallvec::SmallVec;
8
9pub trait TypeTrace {
12 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
16 where
17 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>;
18
19 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
25 where
26 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>;
27
28 fn trace_engine_indices<F, E>(&self, func: &mut F) -> Result<(), E>
30 where
31 F: FnMut(VMSharedTypeIndex) -> Result<(), E>,
32 {
33 self.trace(&mut |idx| match idx {
34 EngineOrModuleTypeIndex::Engine(idx) => func(idx),
35 EngineOrModuleTypeIndex::Module(_) | EngineOrModuleTypeIndex::RecGroup(_) => Ok(()),
36 })
37 }
38
39 fn canonicalize_for_runtime_usage<F>(&mut self, module_to_engine: &mut F)
50 where
51 F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex,
52 {
53 self.trace_mut::<_, ()>(&mut |idx| match idx {
54 EngineOrModuleTypeIndex::Engine(_) => Ok(()),
55 EngineOrModuleTypeIndex::Module(module_index) => {
56 let engine_index = module_to_engine(*module_index);
57 *idx = EngineOrModuleTypeIndex::Engine(engine_index);
58 Ok(())
59 }
60 EngineOrModuleTypeIndex::RecGroup(_) => {
61 panic!("should not already be canonicalized for hash consing")
62 }
63 })
64 .unwrap()
65 }
66
67 fn is_canonicalized_for_runtime_usage(&self) -> bool {
69 self.trace(&mut |idx| match idx {
70 EngineOrModuleTypeIndex::Engine(_) => Ok(()),
71 EngineOrModuleTypeIndex::Module(_) | EngineOrModuleTypeIndex::RecGroup(_) => Err(()),
72 })
73 .is_ok()
74 }
75
76 fn canonicalize_for_hash_consing<F>(
87 &mut self,
88 rec_group_range: Range<ModuleInternedTypeIndex>,
89 module_to_engine: &mut F,
90 ) where
91 F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex,
92 {
93 self.trace_mut::<_, ()>(&mut |idx| match *idx {
94 EngineOrModuleTypeIndex::Engine(_) => Ok(()),
95 EngineOrModuleTypeIndex::Module(module_index) => {
96 *idx = if rec_group_range.start <= module_index {
97 debug_assert!(module_index < rec_group_range.end);
100 let relative = module_index.as_u32() - rec_group_range.start.as_u32();
101 let relative = RecGroupRelativeTypeIndex::from_u32(relative);
102 EngineOrModuleTypeIndex::RecGroup(relative)
103 } else {
104 debug_assert!(module_index < rec_group_range.start);
107 EngineOrModuleTypeIndex::Engine(module_to_engine(module_index))
108 };
109 Ok(())
110 }
111 EngineOrModuleTypeIndex::RecGroup(_) => {
112 panic!("should not already be canonicalized for hash consing")
113 }
114 })
115 .unwrap()
116 }
117
118 fn is_canonicalized_for_hash_consing(&self) -> bool {
120 self.trace(&mut |idx| match idx {
121 EngineOrModuleTypeIndex::Engine(_) | EngineOrModuleTypeIndex::RecGroup(_) => Ok(()),
122 EngineOrModuleTypeIndex::Module(_) => Err(()),
123 })
124 .is_ok()
125 }
126}
127
128#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
130pub enum WasmValType {
131 I32,
133 I64,
135 F32,
137 F64,
139 V128,
141 Ref(WasmRefType),
143}
144
145impl fmt::Display for WasmValType {
146 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147 match self {
148 WasmValType::I32 => write!(f, "i32"),
149 WasmValType::I64 => write!(f, "i64"),
150 WasmValType::F32 => write!(f, "f32"),
151 WasmValType::F64 => write!(f, "f64"),
152 WasmValType::V128 => write!(f, "v128"),
153 WasmValType::Ref(rt) => write!(f, "{rt}"),
154 }
155 }
156}
157
158impl TypeTrace for WasmValType {
159 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
160 where
161 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
162 {
163 match self {
164 WasmValType::Ref(r) => r.trace(func),
165 WasmValType::I32
166 | WasmValType::I64
167 | WasmValType::F32
168 | WasmValType::F64
169 | WasmValType::V128 => Ok(()),
170 }
171 }
172
173 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
174 where
175 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
176 {
177 match self {
178 WasmValType::Ref(r) => r.trace_mut(func),
179 WasmValType::I32
180 | WasmValType::I64
181 | WasmValType::F32
182 | WasmValType::F64
183 | WasmValType::V128 => Ok(()),
184 }
185 }
186}
187
188impl WasmValType {
189 #[inline]
191 pub fn is_vmgcref_type(&self) -> bool {
192 match self {
193 WasmValType::Ref(r) => r.is_vmgcref_type(),
194 _ => false,
195 }
196 }
197
198 #[inline]
204 pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
205 match self {
206 WasmValType::Ref(r) => r.is_vmgcref_type_and_not_i31(),
207 _ => false,
208 }
209 }
210
211 fn trampoline_type(&self) -> Self {
212 match self {
213 WasmValType::Ref(r) => WasmValType::Ref(WasmRefType {
214 nullable: true,
215 heap_type: r.heap_type.top().into(),
216 }),
217 WasmValType::I32
218 | WasmValType::I64
219 | WasmValType::F32
220 | WasmValType::F64
221 | WasmValType::V128 => *self,
222 }
223 }
224
225 pub fn int_from_bits(bits: u8) -> Self {
229 match bits {
230 32 => Self::I32,
231 64 => Self::I64,
232 size => panic!("invalid int bits for WasmValType: {size}"),
233 }
234 }
235
236 pub fn unwrap_ref_type(&self) -> WasmRefType {
240 match self {
241 WasmValType::Ref(ref_type) => *ref_type,
242 _ => panic!("Called WasmValType::unwrap_ref_type on non-reference type"),
243 }
244 }
245}
246
247#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
249pub struct WasmRefType {
250 pub nullable: bool,
252 pub heap_type: WasmHeapType,
254}
255
256impl TypeTrace for WasmRefType {
257 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
258 where
259 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
260 {
261 self.heap_type.trace(func)
262 }
263
264 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
265 where
266 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
267 {
268 self.heap_type.trace_mut(func)
269 }
270}
271
272impl WasmRefType {
273 pub const EXTERNREF: WasmRefType = WasmRefType {
275 nullable: true,
276 heap_type: WasmHeapType::Extern,
277 };
278 pub const FUNCREF: WasmRefType = WasmRefType {
280 nullable: true,
281 heap_type: WasmHeapType::Func,
282 };
283
284 #[inline]
286 pub fn is_vmgcref_type(&self) -> bool {
287 self.heap_type.is_vmgcref_type()
288 }
289
290 #[inline]
296 pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
297 self.heap_type.is_vmgcref_type_and_not_i31()
298 }
299}
300
301impl fmt::Display for WasmRefType {
302 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303 match *self {
304 Self::FUNCREF => write!(f, "funcref"),
305 Self::EXTERNREF => write!(f, "externref"),
306 _ => {
307 if self.nullable {
308 write!(f, "(ref null {})", self.heap_type)
309 } else {
310 write!(f, "(ref {})", self.heap_type)
311 }
312 }
313 }
314 }
315}
316
317#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
322pub enum EngineOrModuleTypeIndex {
323 Engine(VMSharedTypeIndex),
326
327 Module(ModuleInternedTypeIndex),
330
331 RecGroup(RecGroupRelativeTypeIndex),
335}
336
337impl From<ModuleInternedTypeIndex> for EngineOrModuleTypeIndex {
338 #[inline]
339 fn from(i: ModuleInternedTypeIndex) -> Self {
340 Self::Module(i)
341 }
342}
343
344impl From<VMSharedTypeIndex> for EngineOrModuleTypeIndex {
345 #[inline]
346 fn from(i: VMSharedTypeIndex) -> Self {
347 Self::Engine(i)
348 }
349}
350
351impl From<RecGroupRelativeTypeIndex> for EngineOrModuleTypeIndex {
352 #[inline]
353 fn from(i: RecGroupRelativeTypeIndex) -> Self {
354 Self::RecGroup(i)
355 }
356}
357
358impl fmt::Display for EngineOrModuleTypeIndex {
359 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360 match self {
361 Self::Engine(i) => write!(f, "(engine {})", i.bits()),
362 Self::Module(i) => write!(f, "(module {})", i.as_u32()),
363 Self::RecGroup(i) => write!(f, "(recgroup {})", i.as_u32()),
364 }
365 }
366}
367
368impl EngineOrModuleTypeIndex {
369 pub fn is_engine_type_index(self) -> bool {
371 matches!(self, Self::Engine(_))
372 }
373
374 pub fn as_engine_type_index(self) -> Option<VMSharedTypeIndex> {
376 match self {
377 Self::Engine(e) => Some(e),
378 Self::RecGroup(_) | Self::Module(_) => None,
379 }
380 }
381
382 #[track_caller]
384 pub fn unwrap_engine_type_index(self) -> VMSharedTypeIndex {
385 match self.as_engine_type_index() {
386 Some(x) => x,
387 None => panic!("`unwrap_engine_type_index` on {self:?}"),
388 }
389 }
390
391 pub fn is_module_type_index(self) -> bool {
393 matches!(self, Self::Module(_))
394 }
395
396 pub fn as_module_type_index(self) -> Option<ModuleInternedTypeIndex> {
398 match self {
399 Self::Module(e) => Some(e),
400 Self::RecGroup(_) | Self::Engine(_) => None,
401 }
402 }
403
404 #[track_caller]
406 pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex {
407 match self.as_module_type_index() {
408 Some(x) => x,
409 None => panic!("`unwrap_module_type_index` on {self:?}"),
410 }
411 }
412
413 pub fn is_rec_group_type_index(self) -> bool {
415 matches!(self, Self::RecGroup(_))
416 }
417
418 pub fn as_rec_group_type_index(self) -> Option<RecGroupRelativeTypeIndex> {
420 match self {
421 Self::RecGroup(r) => Some(r),
422 Self::Module(_) | Self::Engine(_) => None,
423 }
424 }
425
426 #[track_caller]
428 pub fn unwrap_rec_group_type_index(self) -> RecGroupRelativeTypeIndex {
429 match self.as_rec_group_type_index() {
430 Some(x) => x,
431 None => panic!("`unwrap_rec_group_type_index` on {self:?}"),
432 }
433 }
434}
435
436#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
438#[expect(missing_docs, reason = "self-describing variants")]
439pub enum WasmHeapType {
440 Extern,
442 NoExtern,
443
444 Func,
446 ConcreteFunc(EngineOrModuleTypeIndex),
447 NoFunc,
448
449 Cont,
451 ConcreteCont(EngineOrModuleTypeIndex),
452 NoCont,
453
454 Any,
456 Eq,
457 I31,
458 Array,
459 ConcreteArray(EngineOrModuleTypeIndex),
460 Struct,
461 ConcreteStruct(EngineOrModuleTypeIndex),
462 None,
463}
464
465impl From<WasmHeapTopType> for WasmHeapType {
466 #[inline]
467 fn from(value: WasmHeapTopType) -> Self {
468 match value {
469 WasmHeapTopType::Extern => Self::Extern,
470 WasmHeapTopType::Any => Self::Any,
471 WasmHeapTopType::Func => Self::Func,
472 WasmHeapTopType::Cont => Self::Cont,
473 }
474 }
475}
476
477impl From<WasmHeapBottomType> for WasmHeapType {
478 #[inline]
479 fn from(value: WasmHeapBottomType) -> Self {
480 match value {
481 WasmHeapBottomType::NoExtern => Self::NoExtern,
482 WasmHeapBottomType::None => Self::None,
483 WasmHeapBottomType::NoFunc => Self::NoFunc,
484 WasmHeapBottomType::NoCont => Self::NoCont,
485 }
486 }
487}
488
489impl fmt::Display for WasmHeapType {
490 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
491 match self {
492 Self::Extern => write!(f, "extern"),
493 Self::NoExtern => write!(f, "noextern"),
494 Self::Func => write!(f, "func"),
495 Self::ConcreteFunc(i) => write!(f, "func {i}"),
496 Self::NoFunc => write!(f, "nofunc"),
497 Self::Cont => write!(f, "cont"),
498 Self::ConcreteCont(i) => write!(f, "cont {i}"),
499 Self::NoCont => write!(f, "nocont"),
500 Self::Any => write!(f, "any"),
501 Self::Eq => write!(f, "eq"),
502 Self::I31 => write!(f, "i31"),
503 Self::Array => write!(f, "array"),
504 Self::ConcreteArray(i) => write!(f, "array {i}"),
505 Self::Struct => write!(f, "struct"),
506 Self::ConcreteStruct(i) => write!(f, "struct {i}"),
507 Self::None => write!(f, "none"),
508 }
509 }
510}
511
512impl TypeTrace for WasmHeapType {
513 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
514 where
515 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
516 {
517 match *self {
518 Self::ConcreteArray(i) => func(i),
519 Self::ConcreteFunc(i) => func(i),
520 Self::ConcreteStruct(i) => func(i),
521 Self::ConcreteCont(i) => func(i),
522 _ => Ok(()),
523 }
524 }
525
526 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
527 where
528 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
529 {
530 match self {
531 Self::ConcreteArray(i) => func(i),
532 Self::ConcreteFunc(i) => func(i),
533 Self::ConcreteStruct(i) => func(i),
534 Self::ConcreteCont(i) => func(i),
535 _ => Ok(()),
536 }
537 }
538}
539
540impl WasmHeapType {
541 #[inline]
543 pub fn is_vmgcref_type(&self) -> bool {
544 match self.top() {
545 WasmHeapTopType::Any | WasmHeapTopType::Extern => true,
548
549 WasmHeapTopType::Func => false,
551 WasmHeapTopType::Cont => false,
552 }
553 }
554
555 #[inline]
561 pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
562 self.is_vmgcref_type() && *self != Self::I31
563 }
564
565 #[inline]
567 pub fn is_top(&self) -> bool {
568 *self == Self::from(self.top())
569 }
570
571 #[inline]
573 pub fn top(&self) -> WasmHeapTopType {
574 match self {
575 WasmHeapType::Extern | WasmHeapType::NoExtern => WasmHeapTopType::Extern,
576
577 WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
578 WasmHeapTopType::Func
579 }
580
581 WasmHeapType::Cont | WasmHeapType::ConcreteCont(_) | WasmHeapType::NoCont => {
582 WasmHeapTopType::Cont
583 }
584
585 WasmHeapType::Any
586 | WasmHeapType::Eq
587 | WasmHeapType::I31
588 | WasmHeapType::Array
589 | WasmHeapType::ConcreteArray(_)
590 | WasmHeapType::Struct
591 | WasmHeapType::ConcreteStruct(_)
592 | WasmHeapType::None => WasmHeapTopType::Any,
593 }
594 }
595
596 #[inline]
598 pub fn is_bottom(&self) -> bool {
599 *self == Self::from(self.bottom())
600 }
601
602 #[inline]
604 pub fn bottom(&self) -> WasmHeapBottomType {
605 match self {
606 WasmHeapType::Extern | WasmHeapType::NoExtern => WasmHeapBottomType::NoExtern,
607
608 WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
609 WasmHeapBottomType::NoFunc
610 }
611
612 WasmHeapType::Cont | WasmHeapType::ConcreteCont(_) | WasmHeapType::NoCont => {
613 WasmHeapBottomType::NoCont
614 }
615
616 WasmHeapType::Any
617 | WasmHeapType::Eq
618 | WasmHeapType::I31
619 | WasmHeapType::Array
620 | WasmHeapType::ConcreteArray(_)
621 | WasmHeapType::Struct
622 | WasmHeapType::ConcreteStruct(_)
623 | WasmHeapType::None => WasmHeapBottomType::None,
624 }
625 }
626}
627
628#[derive(Debug, Clone, Copy, Eq, PartialEq)]
630pub enum WasmHeapTopType {
631 Extern,
633 Any,
635 Func,
637 Cont,
639}
640
641#[derive(Debug, Clone, Copy, Eq, PartialEq)]
643pub enum WasmHeapBottomType {
644 NoExtern,
646 None,
648 NoFunc,
650 NoCont,
652}
653
654#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
656pub struct WasmFuncType {
657 params: Box<[WasmValType]>,
658 non_i31_gc_ref_params_count: usize,
659 returns: Box<[WasmValType]>,
660 non_i31_gc_ref_returns_count: usize,
661}
662
663impl fmt::Display for WasmFuncType {
664 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
665 write!(f, "(func")?;
666 if !self.params.is_empty() {
667 write!(f, " (param")?;
668 for p in self.params.iter() {
669 write!(f, " {p}")?;
670 }
671 write!(f, ")")?;
672 }
673 if !self.returns.is_empty() {
674 write!(f, " (result")?;
675 for r in self.returns.iter() {
676 write!(f, " {r}")?;
677 }
678 write!(f, ")")?;
679 }
680 write!(f, ")")
681 }
682}
683
684impl TypeTrace for WasmFuncType {
685 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
686 where
687 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
688 {
689 for p in self.params.iter() {
690 p.trace(func)?;
691 }
692 for r in self.returns.iter() {
693 r.trace(func)?;
694 }
695 Ok(())
696 }
697
698 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
699 where
700 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
701 {
702 for p in self.params.iter_mut() {
703 p.trace_mut(func)?;
704 }
705 for r in self.returns.iter_mut() {
706 r.trace_mut(func)?;
707 }
708 Ok(())
709 }
710}
711
712impl WasmFuncType {
713 #[inline]
715 pub fn new(params: Box<[WasmValType]>, returns: Box<[WasmValType]>) -> Self {
716 let non_i31_gc_ref_params_count = params
717 .iter()
718 .filter(|p| p.is_vmgcref_type_and_not_i31())
719 .count();
720 let non_i31_gc_ref_returns_count = returns
721 .iter()
722 .filter(|r| r.is_vmgcref_type_and_not_i31())
723 .count();
724 WasmFuncType {
725 params,
726 non_i31_gc_ref_params_count,
727 returns,
728 non_i31_gc_ref_returns_count,
729 }
730 }
731
732 #[inline]
734 pub fn params(&self) -> &[WasmValType] {
735 &self.params
736 }
737
738 #[inline]
740 pub fn non_i31_gc_ref_params_count(&self) -> usize {
741 self.non_i31_gc_ref_params_count
742 }
743
744 #[inline]
746 pub fn returns(&self) -> &[WasmValType] {
747 &self.returns
748 }
749
750 #[inline]
752 pub fn non_i31_gc_ref_returns_count(&self) -> usize {
753 self.non_i31_gc_ref_returns_count
754 }
755
756 pub fn is_trampoline_type(&self) -> bool {
758 self.params().iter().all(|p| *p == p.trampoline_type())
759 && self.returns().iter().all(|r| *r == r.trampoline_type())
760 }
761
762 pub fn trampoline_type(&self) -> Cow<'_, Self> {
788 if self.is_trampoline_type() {
789 return Cow::Borrowed(self);
790 }
791
792 Cow::Owned(Self::new(
793 self.params().iter().map(|p| p.trampoline_type()).collect(),
794 self.returns().iter().map(|r| r.trampoline_type()).collect(),
795 ))
796 }
797}
798
799#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
801pub struct WasmContType(EngineOrModuleTypeIndex);
802
803impl fmt::Display for WasmContType {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 write!(f, "(cont {})", self.0)
806 }
807}
808
809impl WasmContType {
810 pub fn new(idx: EngineOrModuleTypeIndex) -> Self {
812 WasmContType(idx)
813 }
814
815 pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex {
817 match self.0 {
818 EngineOrModuleTypeIndex::Engine(_) => panic!("not module interned"),
819 EngineOrModuleTypeIndex::Module(idx) => idx,
820 EngineOrModuleTypeIndex::RecGroup(_) => todo!(),
821 }
822 }
823}
824
825impl TypeTrace for WasmContType {
826 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
827 where
828 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
829 {
830 func(self.0)
831 }
832
833 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
834 where
835 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
836 {
837 func(&mut self.0)
838 }
839}
840
841#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
843pub enum WasmStorageType {
844 I8,
846 I16,
848 Val(WasmValType),
850}
851
852impl fmt::Display for WasmStorageType {
853 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
854 match self {
855 WasmStorageType::I8 => write!(f, "i8"),
856 WasmStorageType::I16 => write!(f, "i16"),
857 WasmStorageType::Val(v) => fmt::Display::fmt(v, f),
858 }
859 }
860}
861
862impl TypeTrace for WasmStorageType {
863 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
864 where
865 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
866 {
867 match self {
868 WasmStorageType::I8 | WasmStorageType::I16 => Ok(()),
869 WasmStorageType::Val(v) => v.trace(func),
870 }
871 }
872
873 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
874 where
875 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
876 {
877 match self {
878 WasmStorageType::I8 | WasmStorageType::I16 => Ok(()),
879 WasmStorageType::Val(v) => v.trace_mut(func),
880 }
881 }
882}
883
884impl WasmStorageType {
885 pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
891 match self {
892 WasmStorageType::I8 | WasmStorageType::I16 => false,
893 WasmStorageType::Val(v) => v.is_vmgcref_type_and_not_i31(),
894 }
895 }
896}
897
898#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
900pub struct WasmFieldType {
901 pub element_type: WasmStorageType,
903
904 pub mutable: bool,
906}
907
908impl fmt::Display for WasmFieldType {
909 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
910 if self.mutable {
911 write!(f, "(mut {})", self.element_type)
912 } else {
913 fmt::Display::fmt(&self.element_type, f)
914 }
915 }
916}
917
918impl TypeTrace for WasmFieldType {
919 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
920 where
921 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
922 {
923 self.element_type.trace(func)
924 }
925
926 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
927 where
928 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
929 {
930 self.element_type.trace_mut(func)
931 }
932}
933
934#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
936pub struct WasmArrayType(pub WasmFieldType);
937
938impl fmt::Display for WasmArrayType {
939 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
940 write!(f, "(array {})", self.0)
941 }
942}
943
944impl TypeTrace for WasmArrayType {
945 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
946 where
947 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
948 {
949 self.0.trace(func)
950 }
951
952 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
953 where
954 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
955 {
956 self.0.trace_mut(func)
957 }
958}
959
960#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
962pub struct WasmStructType {
963 pub fields: Box<[WasmFieldType]>,
965}
966
967impl fmt::Display for WasmStructType {
968 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
969 write!(f, "(struct")?;
970 for ty in self.fields.iter() {
971 write!(f, " {ty}")?;
972 }
973 write!(f, ")")
974 }
975}
976
977impl TypeTrace for WasmStructType {
978 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
979 where
980 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
981 {
982 for f in self.fields.iter() {
983 f.trace(func)?;
984 }
985 Ok(())
986 }
987
988 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
989 where
990 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
991 {
992 for f in self.fields.iter_mut() {
993 f.trace_mut(func)?;
994 }
995 Ok(())
996 }
997}
998
999#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1000#[expect(missing_docs, reason = "self-describing type")]
1001pub struct WasmCompositeType {
1002 pub inner: WasmCompositeInnerType,
1004 pub shared: bool,
1007}
1008
1009impl fmt::Display for WasmCompositeType {
1010 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1011 if self.shared {
1012 write!(f, "(shared ")?;
1013 }
1014 fmt::Display::fmt(&self.inner, f)?;
1015 if self.shared {
1016 write!(f, ")")?;
1017 }
1018 Ok(())
1019 }
1020}
1021
1022#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1024#[expect(missing_docs, reason = "self-describing variants")]
1025pub enum WasmCompositeInnerType {
1026 Array(WasmArrayType),
1027 Func(WasmFuncType),
1028 Struct(WasmStructType),
1029 Cont(WasmContType),
1030}
1031
1032impl fmt::Display for WasmCompositeInnerType {
1033 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1034 match self {
1035 Self::Array(ty) => fmt::Display::fmt(ty, f),
1036 Self::Func(ty) => fmt::Display::fmt(ty, f),
1037 Self::Struct(ty) => fmt::Display::fmt(ty, f),
1038 Self::Cont(ty) => fmt::Display::fmt(ty, f),
1039 }
1040 }
1041}
1042
1043#[expect(missing_docs, reason = "self-describing functions")]
1044impl WasmCompositeInnerType {
1045 #[inline]
1046 pub fn is_array(&self) -> bool {
1047 matches!(self, Self::Array(_))
1048 }
1049
1050 #[inline]
1051 pub fn as_array(&self) -> Option<&WasmArrayType> {
1052 match self {
1053 Self::Array(f) => Some(f),
1054 _ => None,
1055 }
1056 }
1057
1058 #[inline]
1059 pub fn unwrap_array(&self) -> &WasmArrayType {
1060 self.as_array().unwrap()
1061 }
1062
1063 #[inline]
1064 pub fn is_func(&self) -> bool {
1065 matches!(self, Self::Func(_))
1066 }
1067
1068 #[inline]
1069 pub fn as_func(&self) -> Option<&WasmFuncType> {
1070 match self {
1071 Self::Func(f) => Some(f),
1072 _ => None,
1073 }
1074 }
1075
1076 #[inline]
1077 pub fn unwrap_func(&self) -> &WasmFuncType {
1078 self.as_func().unwrap()
1079 }
1080
1081 #[inline]
1082 pub fn is_struct(&self) -> bool {
1083 matches!(self, Self::Struct(_))
1084 }
1085
1086 #[inline]
1087 pub fn as_struct(&self) -> Option<&WasmStructType> {
1088 match self {
1089 Self::Struct(f) => Some(f),
1090 _ => None,
1091 }
1092 }
1093
1094 #[inline]
1095 pub fn unwrap_struct(&self) -> &WasmStructType {
1096 self.as_struct().unwrap()
1097 }
1098
1099 #[inline]
1100 pub fn is_cont(&self) -> bool {
1101 matches!(self, Self::Cont(_))
1102 }
1103
1104 #[inline]
1105 pub fn as_cont(&self) -> Option<&WasmContType> {
1106 match self {
1107 Self::Cont(f) => Some(f),
1108 _ => None,
1109 }
1110 }
1111
1112 #[inline]
1113 pub fn unwrap_cont(&self) -> &WasmContType {
1114 self.as_cont().unwrap()
1115 }
1116}
1117
1118impl TypeTrace for WasmCompositeType {
1119 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1120 where
1121 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1122 {
1123 match &self.inner {
1124 WasmCompositeInnerType::Array(a) => a.trace(func),
1125 WasmCompositeInnerType::Func(f) => f.trace(func),
1126 WasmCompositeInnerType::Struct(a) => a.trace(func),
1127 WasmCompositeInnerType::Cont(c) => c.trace(func),
1128 }
1129 }
1130
1131 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1132 where
1133 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1134 {
1135 match &mut self.inner {
1136 WasmCompositeInnerType::Array(a) => a.trace_mut(func),
1137 WasmCompositeInnerType::Func(f) => f.trace_mut(func),
1138 WasmCompositeInnerType::Struct(a) => a.trace_mut(func),
1139 WasmCompositeInnerType::Cont(c) => c.trace_mut(func),
1140 }
1141 }
1142}
1143
1144#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1146pub struct WasmSubType {
1147 pub is_final: bool,
1150
1151 pub supertype: Option<EngineOrModuleTypeIndex>,
1153
1154 pub composite_type: WasmCompositeType,
1156}
1157
1158impl fmt::Display for WasmSubType {
1159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1160 if self.is_final && self.supertype.is_none() {
1161 fmt::Display::fmt(&self.composite_type, f)
1162 } else {
1163 write!(f, "(sub")?;
1164 if self.is_final {
1165 write!(f, " final")?;
1166 }
1167 if let Some(sup) = self.supertype {
1168 write!(f, " {sup}")?;
1169 }
1170 write!(f, " {})", self.composite_type)
1171 }
1172 }
1173}
1174
1175#[expect(missing_docs, reason = "self-describing functions")]
1179impl WasmSubType {
1180 #[inline]
1181 pub fn is_func(&self) -> bool {
1182 self.composite_type.inner.is_func() && !self.composite_type.shared
1183 }
1184
1185 #[inline]
1186 pub fn as_func(&self) -> Option<&WasmFuncType> {
1187 if self.composite_type.shared {
1188 None
1189 } else {
1190 self.composite_type.inner.as_func()
1191 }
1192 }
1193
1194 #[inline]
1195 pub fn unwrap_func(&self) -> &WasmFuncType {
1196 assert!(!self.composite_type.shared);
1197 self.composite_type.inner.unwrap_func()
1198 }
1199
1200 #[inline]
1201 pub fn is_array(&self) -> bool {
1202 self.composite_type.inner.is_array() && !self.composite_type.shared
1203 }
1204
1205 #[inline]
1206 pub fn as_array(&self) -> Option<&WasmArrayType> {
1207 if self.composite_type.shared {
1208 None
1209 } else {
1210 self.composite_type.inner.as_array()
1211 }
1212 }
1213
1214 #[inline]
1215 pub fn unwrap_array(&self) -> &WasmArrayType {
1216 assert!(!self.composite_type.shared);
1217 self.composite_type.inner.unwrap_array()
1218 }
1219
1220 #[inline]
1221 pub fn is_struct(&self) -> bool {
1222 self.composite_type.inner.is_struct() && !self.composite_type.shared
1223 }
1224
1225 #[inline]
1226 pub fn as_struct(&self) -> Option<&WasmStructType> {
1227 if self.composite_type.shared {
1228 None
1229 } else {
1230 self.composite_type.inner.as_struct()
1231 }
1232 }
1233
1234 #[inline]
1235 pub fn unwrap_struct(&self) -> &WasmStructType {
1236 assert!(!self.composite_type.shared);
1237 self.composite_type.inner.unwrap_struct()
1238 }
1239
1240 #[inline]
1241 pub fn is_cont(&self) -> bool {
1242 self.composite_type.inner.is_cont() && !self.composite_type.shared
1243 }
1244
1245 #[inline]
1246 pub fn as_cont(&self) -> Option<&WasmContType> {
1247 if self.composite_type.shared {
1248 None
1249 } else {
1250 self.composite_type.inner.as_cont()
1251 }
1252 }
1253
1254 #[inline]
1255 pub fn unwrap_cont(&self) -> &WasmContType {
1256 assert!(!self.composite_type.shared);
1257 self.composite_type.inner.unwrap_cont()
1258 }
1259}
1260
1261impl TypeTrace for WasmSubType {
1262 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1263 where
1264 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1265 {
1266 if let Some(sup) = self.supertype {
1267 func(sup)?;
1268 }
1269 self.composite_type.trace(func)
1270 }
1271
1272 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1273 where
1274 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1275 {
1276 if let Some(sup) = self.supertype.as_mut() {
1277 func(sup)?;
1278 }
1279 self.composite_type.trace_mut(func)
1280 }
1281}
1282
1283#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1294pub struct WasmRecGroup {
1295 pub types: Box<[WasmSubType]>,
1297}
1298
1299impl TypeTrace for WasmRecGroup {
1300 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1301 where
1302 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1303 {
1304 for ty in self.types.iter() {
1305 ty.trace(func)?;
1306 }
1307 Ok(())
1308 }
1309
1310 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1311 where
1312 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1313 {
1314 for ty in self.types.iter_mut() {
1315 ty.trace_mut(func)?;
1316 }
1317 Ok(())
1318 }
1319}
1320
1321#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1323pub struct FuncIndex(u32);
1324entity_impl!(FuncIndex);
1325
1326#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1328pub struct DefinedFuncIndex(u32);
1329entity_impl!(DefinedFuncIndex);
1330
1331#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1333pub struct DefinedTableIndex(u32);
1334entity_impl!(DefinedTableIndex);
1335
1336#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1338pub struct DefinedMemoryIndex(u32);
1339entity_impl!(DefinedMemoryIndex);
1340
1341#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1343pub struct OwnedMemoryIndex(u32);
1344entity_impl!(OwnedMemoryIndex);
1345
1346#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1348pub struct DefinedGlobalIndex(u32);
1349entity_impl!(DefinedGlobalIndex);
1350
1351#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1353pub struct TableIndex(u32);
1354entity_impl!(TableIndex);
1355
1356#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1358pub struct GlobalIndex(u32);
1359entity_impl!(GlobalIndex);
1360
1361#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1363pub struct MemoryIndex(u32);
1364entity_impl!(MemoryIndex);
1365
1366#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1369pub struct ModuleInternedRecGroupIndex(u32);
1370entity_impl!(ModuleInternedRecGroupIndex);
1371
1372#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1375pub struct EngineInternedRecGroupIndex(u32);
1376entity_impl!(EngineInternedRecGroupIndex);
1377
1378#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1380pub struct TypeIndex(u32);
1381entity_impl!(TypeIndex);
1382
1383#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1389pub struct RecGroupRelativeTypeIndex(u32);
1390entity_impl!(RecGroupRelativeTypeIndex);
1391
1392#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1400pub struct ModuleInternedTypeIndex(u32);
1401entity_impl!(ModuleInternedTypeIndex);
1402
1403#[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1412pub struct VMSharedTypeIndex(u32);
1413entity_impl!(VMSharedTypeIndex);
1414
1415impl VMSharedTypeIndex {
1416 #[inline]
1418 pub fn new(value: u32) -> Self {
1419 assert_ne!(
1420 value,
1421 u32::MAX,
1422 "u32::MAX is reserved for the default value"
1423 );
1424 Self(value)
1425 }
1426
1427 #[inline]
1429 pub fn bits(&self) -> u32 {
1430 self.0
1431 }
1432}
1433
1434impl Default for VMSharedTypeIndex {
1435 #[inline]
1436 fn default() -> Self {
1437 Self(u32::MAX)
1438 }
1439}
1440
1441#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1443pub struct DataIndex(u32);
1444entity_impl!(DataIndex);
1445
1446#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1448pub struct ElemIndex(u32);
1449entity_impl!(ElemIndex);
1450
1451#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1453pub struct DefinedTagIndex(u32);
1454entity_impl!(DefinedTagIndex);
1455
1456#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1458pub struct TagIndex(u32);
1459entity_impl!(TagIndex);
1460
1461#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1466pub struct StaticModuleIndex(u32);
1467entity_impl!(StaticModuleIndex);
1468
1469#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1471pub enum EntityIndex {
1472 Function(FuncIndex),
1474 Table(TableIndex),
1476 Memory(MemoryIndex),
1478 Global(GlobalIndex),
1480 Tag(TagIndex),
1482}
1483
1484impl From<FuncIndex> for EntityIndex {
1485 fn from(idx: FuncIndex) -> EntityIndex {
1486 EntityIndex::Function(idx)
1487 }
1488}
1489
1490impl From<TableIndex> for EntityIndex {
1491 fn from(idx: TableIndex) -> EntityIndex {
1492 EntityIndex::Table(idx)
1493 }
1494}
1495
1496impl From<MemoryIndex> for EntityIndex {
1497 fn from(idx: MemoryIndex) -> EntityIndex {
1498 EntityIndex::Memory(idx)
1499 }
1500}
1501
1502impl From<GlobalIndex> for EntityIndex {
1503 fn from(idx: GlobalIndex) -> EntityIndex {
1504 EntityIndex::Global(idx)
1505 }
1506}
1507
1508impl From<TagIndex> for EntityIndex {
1509 fn from(idx: TagIndex) -> EntityIndex {
1510 EntityIndex::Tag(idx)
1511 }
1512}
1513
1514#[derive(Clone, Debug, Serialize, Deserialize)]
1517pub enum EntityType {
1518 Global(Global),
1520 Memory(Memory),
1522 Tag(Tag),
1524 Table(Table),
1526 Function(EngineOrModuleTypeIndex),
1529}
1530
1531impl TypeTrace for EntityType {
1532 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1533 where
1534 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1535 {
1536 match self {
1537 Self::Global(g) => g.trace(func),
1538 Self::Table(t) => t.trace(func),
1539 Self::Function(idx) => func(*idx),
1540 Self::Memory(_) => Ok(()),
1541 Self::Tag(t) => t.trace(func),
1542 }
1543 }
1544
1545 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1546 where
1547 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1548 {
1549 match self {
1550 Self::Global(g) => g.trace_mut(func),
1551 Self::Table(t) => t.trace_mut(func),
1552 Self::Function(idx) => func(idx),
1553 Self::Memory(_) => Ok(()),
1554 Self::Tag(t) => t.trace_mut(func),
1555 }
1556 }
1557}
1558
1559impl EntityType {
1560 pub fn unwrap_global(&self) -> &Global {
1562 match self {
1563 EntityType::Global(g) => g,
1564 _ => panic!("not a global"),
1565 }
1566 }
1567
1568 pub fn unwrap_memory(&self) -> &Memory {
1570 match self {
1571 EntityType::Memory(g) => g,
1572 _ => panic!("not a memory"),
1573 }
1574 }
1575
1576 pub fn unwrap_tag(&self) -> &Tag {
1578 match self {
1579 EntityType::Tag(g) => g,
1580 _ => panic!("not a tag"),
1581 }
1582 }
1583
1584 pub fn unwrap_table(&self) -> &Table {
1586 match self {
1587 EntityType::Table(g) => g,
1588 _ => panic!("not a table"),
1589 }
1590 }
1591
1592 pub fn unwrap_func(&self) -> EngineOrModuleTypeIndex {
1594 match self {
1595 EntityType::Function(g) => *g,
1596 _ => panic!("not a func"),
1597 }
1598 }
1599}
1600
1601#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1609pub struct Global {
1610 pub wasm_ty: crate::WasmValType,
1612 pub mutability: bool,
1614}
1615
1616impl TypeTrace for Global {
1617 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1618 where
1619 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1620 {
1621 let Global {
1622 wasm_ty,
1623 mutability: _,
1624 } = self;
1625 wasm_ty.trace(func)
1626 }
1627
1628 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1629 where
1630 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1631 {
1632 let Global {
1633 wasm_ty,
1634 mutability: _,
1635 } = self;
1636 wasm_ty.trace_mut(func)
1637 }
1638}
1639
1640#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1644pub struct ConstExpr {
1645 ops: SmallVec<[ConstOp; 2]>,
1646}
1647
1648impl ConstExpr {
1649 pub fn new(ops: impl IntoIterator<Item = ConstOp>) -> Self {
1655 let ops = ops.into_iter().collect::<SmallVec<[ConstOp; 2]>>();
1656 assert!(!ops.is_empty());
1657 ConstExpr { ops }
1658 }
1659
1660 pub fn from_wasmparser(
1665 expr: wasmparser::ConstExpr<'_>,
1666 ) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)> {
1667 let mut iter = expr
1668 .get_operators_reader()
1669 .into_iter_with_offsets()
1670 .peekable();
1671
1672 let mut ops = SmallVec::<[ConstOp; 2]>::new();
1673 let mut escaped = SmallVec::<[FuncIndex; 1]>::new();
1674 while let Some(res) = iter.next() {
1675 let (op, offset) = res?;
1676
1677 if matches!(op, wasmparser::Operator::End) && iter.peek().is_none() {
1681 break;
1682 }
1683
1684 if let wasmparser::Operator::RefFunc { function_index } = &op {
1687 escaped.push(FuncIndex::from_u32(*function_index));
1688 }
1689
1690 ops.push(ConstOp::from_wasmparser(op, offset)?);
1691 }
1692 Ok((Self { ops }, escaped))
1693 }
1694
1695 pub fn ops(&self) -> &[ConstOp] {
1697 &self.ops
1698 }
1699
1700 pub fn provably_nonzero_i32(&self) -> bool {
1710 assert!(self.ops.len() > 0);
1711 if self.ops.len() > 1 {
1712 return false;
1715 }
1716 match self.ops[0] {
1718 ConstOp::I32Const(0) => false,
1720 ConstOp::I32Const(_) => true,
1723 _ => false,
1725 }
1726 }
1727}
1728
1729#[expect(missing_docs, reason = "self-describing variants")]
1731#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1732pub enum ConstOp {
1733 I32Const(i32),
1734 I64Const(i64),
1735 F32Const(u32),
1736 F64Const(u64),
1737 V128Const(u128),
1738 GlobalGet(GlobalIndex),
1739 RefI31,
1740 RefNull,
1741 RefFunc(FuncIndex),
1742 I32Add,
1743 I32Sub,
1744 I32Mul,
1745 I64Add,
1746 I64Sub,
1747 I64Mul,
1748 StructNew {
1749 struct_type_index: TypeIndex,
1750 },
1751 StructNewDefault {
1752 struct_type_index: TypeIndex,
1753 },
1754 ArrayNew {
1755 array_type_index: TypeIndex,
1756 },
1757 ArrayNewDefault {
1758 array_type_index: TypeIndex,
1759 },
1760 ArrayNewFixed {
1761 array_type_index: TypeIndex,
1762 array_size: u32,
1763 },
1764 ExternConvertAny,
1765 AnyConvertExtern,
1766}
1767
1768impl ConstOp {
1769 pub fn from_wasmparser(op: wasmparser::Operator<'_>, offset: usize) -> WasmResult<Self> {
1771 use wasmparser::Operator as O;
1772 Ok(match op {
1773 O::I32Const { value } => Self::I32Const(value),
1774 O::I64Const { value } => Self::I64Const(value),
1775 O::F32Const { value } => Self::F32Const(value.bits()),
1776 O::F64Const { value } => Self::F64Const(value.bits()),
1777 O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())),
1778 O::RefNull { hty: _ } => Self::RefNull,
1779 O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
1780 O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
1781 O::RefI31 => Self::RefI31,
1782 O::I32Add => Self::I32Add,
1783 O::I32Sub => Self::I32Sub,
1784 O::I32Mul => Self::I32Mul,
1785 O::I64Add => Self::I64Add,
1786 O::I64Sub => Self::I64Sub,
1787 O::I64Mul => Self::I64Mul,
1788 O::StructNew { struct_type_index } => Self::StructNew {
1789 struct_type_index: TypeIndex::from_u32(struct_type_index),
1790 },
1791 O::StructNewDefault { struct_type_index } => Self::StructNewDefault {
1792 struct_type_index: TypeIndex::from_u32(struct_type_index),
1793 },
1794 O::ArrayNew { array_type_index } => Self::ArrayNew {
1795 array_type_index: TypeIndex::from_u32(array_type_index),
1796 },
1797 O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault {
1798 array_type_index: TypeIndex::from_u32(array_type_index),
1799 },
1800 O::ArrayNewFixed {
1801 array_type_index,
1802 array_size,
1803 } => Self::ArrayNewFixed {
1804 array_type_index: TypeIndex::from_u32(array_type_index),
1805 array_size,
1806 },
1807 O::ExternConvertAny => Self::ExternConvertAny,
1808 O::AnyConvertExtern => Self::AnyConvertExtern,
1809 op => {
1810 return Err(wasm_unsupported!(
1811 "unsupported opcode in const expression at offset {offset:#x}: {op:?}",
1812 ));
1813 }
1814 })
1815 }
1816}
1817
1818#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1820#[expect(missing_docs, reason = "self-describing variants")]
1821pub enum IndexType {
1822 I32,
1823 I64,
1824}
1825
1826#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1828#[expect(missing_docs, reason = "self-describing fields")]
1829pub struct Limits {
1830 pub min: u64,
1831 pub max: Option<u64>,
1832}
1833
1834#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1836pub struct Table {
1837 pub idx_type: IndexType,
1839 pub limits: Limits,
1842 pub ref_type: WasmRefType,
1844}
1845
1846impl TypeTrace for Table {
1847 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1848 where
1849 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1850 {
1851 let Table {
1852 ref_type: wasm_ty,
1853 idx_type: _,
1854 limits: _,
1855 } = self;
1856 wasm_ty.trace(func)
1857 }
1858
1859 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1860 where
1861 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1862 {
1863 let Table {
1864 ref_type: wasm_ty,
1865 idx_type: _,
1866 limits: _,
1867 } = self;
1868 wasm_ty.trace_mut(func)
1869 }
1870}
1871
1872#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1874pub struct Memory {
1875 pub idx_type: IndexType,
1877 pub limits: Limits,
1880 pub shared: bool,
1882 pub page_size_log2: u8,
1887}
1888
1889pub const WASM32_MAX_SIZE: u64 = 1 << 32;
1891
1892impl Memory {
1893 pub const DEFAULT_PAGE_SIZE: u32 = 0x10000;
1895
1896 pub const DEFAULT_PAGE_SIZE_LOG2: u8 = {
1898 let log2 = 16;
1899 assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE);
1900 log2
1901 };
1902
1903 pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> {
1911 self.limits
1912 .min
1913 .checked_mul(self.page_size())
1914 .ok_or(SizeOverflow)
1915 }
1916
1917 pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> {
1932 match self.limits.max {
1933 Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow),
1934 None => {
1935 let min = self.minimum_byte_size()?;
1936 Ok(min.max(self.max_size_based_on_index_type()))
1937 }
1938 }
1939 }
1940
1941 pub fn page_size(&self) -> u64 {
1943 debug_assert!(
1944 self.page_size_log2 == 16 || self.page_size_log2 == 0,
1945 "invalid page_size_log2: {}; must be 16 or 0",
1946 self.page_size_log2
1947 );
1948 1 << self.page_size_log2
1949 }
1950
1951 pub fn max_size_based_on_index_type(&self) -> u64 {
1956 match self.idx_type {
1957 IndexType::I64 =>
1958 {
1966 0_u64.wrapping_sub(self.page_size())
1967 }
1968 IndexType::I32 => WASM32_MAX_SIZE,
1969 }
1970 }
1971
1972 pub fn can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
1984 tunables.signals_based_traps && self.page_size_log2 >= host_page_size_log2
1985 }
1986
1987 pub fn can_elide_bounds_check(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
2007 self.can_use_virtual_memory(tunables, host_page_size_log2)
2008 && self.idx_type == IndexType::I32
2009 && tunables.memory_reservation >= (1 << 32)
2010 }
2011
2012 pub fn static_heap_size(&self) -> Option<u64> {
2016 let min = self.minimum_byte_size().ok()?;
2017 let max = self.maximum_byte_size().ok()?;
2018 if min == max { Some(min) } else { None }
2019 }
2020
2021 pub fn memory_may_move(&self, tunables: &Tunables) -> bool {
2028 if self.shared {
2032 return false;
2033 }
2034
2035 if !tunables.memory_may_move {
2038 return false;
2039 }
2040
2041 if self.limits.max.is_some_and(|max| self.limits.min == max) {
2044 return false;
2045 }
2046
2047 let max = self.maximum_byte_size().unwrap_or(u64::MAX);
2050 max > tunables.memory_reservation
2051 }
2052}
2053
2054#[derive(Copy, Clone, Debug)]
2055#[expect(missing_docs, reason = "self-describing error struct")]
2056pub struct SizeOverflow;
2057
2058impl fmt::Display for SizeOverflow {
2059 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2060 f.write_str("size overflow calculating memory size")
2061 }
2062}
2063
2064impl core::error::Error for SizeOverflow {}
2065
2066impl From<wasmparser::MemoryType> for Memory {
2067 fn from(ty: wasmparser::MemoryType) -> Memory {
2068 let idx_type = match ty.memory64 {
2069 false => IndexType::I32,
2070 true => IndexType::I64,
2071 };
2072 let limits = Limits {
2073 min: ty.initial,
2074 max: ty.maximum,
2075 };
2076 let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap();
2077 debug_assert!(
2078 page_size_log2 == 16 || page_size_log2 == 0,
2079 "invalid page_size_log2: {page_size_log2}; must be 16 or 0"
2080 );
2081 Memory {
2082 idx_type,
2083 limits,
2084 shared: ty.shared,
2085 page_size_log2,
2086 }
2087 }
2088}
2089
2090#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2092pub struct Tag {
2093 pub signature: EngineOrModuleTypeIndex,
2095}
2096
2097impl TypeTrace for Tag {
2098 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2099 where
2100 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2101 {
2102 func(self.signature)
2103 }
2104
2105 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2106 where
2107 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2108 {
2109 func(&mut self.signature)
2110 }
2111}
2112
2113#[expect(missing_docs, reason = "self-describing functions")]
2115pub trait TypeConvert {
2116 fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global> {
2118 Ok(Global {
2119 wasm_ty: self.convert_valtype(ty.content_type)?,
2120 mutability: ty.mutable,
2121 })
2122 }
2123
2124 fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> {
2126 let idx_type = match ty.table64 {
2127 false => IndexType::I32,
2128 true => IndexType::I64,
2129 };
2130 let limits = Limits {
2131 min: ty.initial,
2132 max: ty.maximum,
2133 };
2134 Ok(Table {
2135 idx_type,
2136 limits,
2137 ref_type: self.convert_ref_type(ty.element_type)?,
2138 })
2139 }
2140
2141 fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
2142 Ok(WasmSubType {
2143 is_final: ty.is_final,
2144 supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())),
2145 composite_type: self.convert_composite_type(&ty.composite_type)?,
2146 })
2147 }
2148
2149 fn convert_composite_type(
2150 &self,
2151 ty: &wasmparser::CompositeType,
2152 ) -> WasmResult<WasmCompositeType> {
2153 let inner = match &ty.inner {
2154 wasmparser::CompositeInnerType::Func(f) => {
2155 WasmCompositeInnerType::Func(self.convert_func_type(f)?)
2156 }
2157 wasmparser::CompositeInnerType::Array(a) => {
2158 WasmCompositeInnerType::Array(self.convert_array_type(a)?)
2159 }
2160 wasmparser::CompositeInnerType::Struct(s) => {
2161 WasmCompositeInnerType::Struct(self.convert_struct_type(s)?)
2162 }
2163 wasmparser::CompositeInnerType::Cont(c) => {
2164 WasmCompositeInnerType::Cont(self.convert_cont_type(c))
2165 }
2166 };
2167 Ok(WasmCompositeType {
2168 inner,
2169 shared: ty.shared,
2170 })
2171 }
2172
2173 fn convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType {
2175 if let WasmHeapType::ConcreteFunc(sigidx) = self.lookup_heap_type(ty.0.unpack()) {
2176 WasmContType::new(sigidx)
2177 } else {
2178 panic!("Failed to extract signature index for continuation type.")
2179 }
2180 }
2181
2182 fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType> {
2183 Ok(WasmStructType {
2184 fields: ty
2185 .fields
2186 .iter()
2187 .map(|f| self.convert_field_type(f))
2188 .collect::<WasmResult<_>>()?,
2189 })
2190 }
2191
2192 fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType> {
2193 Ok(WasmArrayType(self.convert_field_type(&ty.0)?))
2194 }
2195
2196 fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType> {
2197 Ok(WasmFieldType {
2198 element_type: self.convert_storage_type(&ty.element_type)?,
2199 mutable: ty.mutable,
2200 })
2201 }
2202
2203 fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType> {
2204 Ok(match ty {
2205 wasmparser::StorageType::I8 => WasmStorageType::I8,
2206 wasmparser::StorageType::I16 => WasmStorageType::I16,
2207 wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)?),
2208 })
2209 }
2210
2211 fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType> {
2213 let params = ty
2214 .params()
2215 .iter()
2216 .map(|t| self.convert_valtype(*t))
2217 .collect::<WasmResult<_>>()?;
2218 let results = ty
2219 .results()
2220 .iter()
2221 .map(|t| self.convert_valtype(*t))
2222 .collect::<WasmResult<_>>()?;
2223 Ok(WasmFuncType::new(params, results))
2224 }
2225
2226 fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType> {
2228 Ok(match ty {
2229 wasmparser::ValType::I32 => WasmValType::I32,
2230 wasmparser::ValType::I64 => WasmValType::I64,
2231 wasmparser::ValType::F32 => WasmValType::F32,
2232 wasmparser::ValType::F64 => WasmValType::F64,
2233 wasmparser::ValType::V128 => WasmValType::V128,
2234 wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)?),
2235 })
2236 }
2237
2238 fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType> {
2240 Ok(WasmRefType {
2241 nullable: ty.is_nullable(),
2242 heap_type: self.convert_heap_type(ty.heap_type())?,
2243 })
2244 }
2245
2246 fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType> {
2248 Ok(match ty {
2249 wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i),
2250 wasmparser::HeapType::Abstract { ty, shared: false } => match ty {
2251 wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern,
2252 wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern,
2253 wasmparser::AbstractHeapType::Func => WasmHeapType::Func,
2254 wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc,
2255 wasmparser::AbstractHeapType::Any => WasmHeapType::Any,
2256 wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq,
2257 wasmparser::AbstractHeapType::I31 => WasmHeapType::I31,
2258 wasmparser::AbstractHeapType::Array => WasmHeapType::Array,
2259 wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct,
2260 wasmparser::AbstractHeapType::None => WasmHeapType::None,
2261 wasmparser::AbstractHeapType::Cont => WasmHeapType::Cont,
2262 wasmparser::AbstractHeapType::NoCont => WasmHeapType::NoCont,
2263 wasmparser::AbstractHeapType::Exn | wasmparser::AbstractHeapType::NoExn => {
2264 return Err(wasm_unsupported!("unsupported heap type {ty:?}"));
2265 }
2266 },
2267 _ => return Err(wasm_unsupported!("unsupported heap type {ty:?}")),
2268 })
2269 }
2270
2271 fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;
2274
2275 fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex;
2278}