1use crate::{wasm_unsupported, Tunables, WasmResult};
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
237#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
239pub struct WasmRefType {
240 pub nullable: bool,
242 pub heap_type: WasmHeapType,
244}
245
246impl TypeTrace for WasmRefType {
247 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
248 where
249 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
250 {
251 self.heap_type.trace(func)
252 }
253
254 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
255 where
256 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
257 {
258 self.heap_type.trace_mut(func)
259 }
260}
261
262impl WasmRefType {
263 pub const EXTERNREF: WasmRefType = WasmRefType {
265 nullable: true,
266 heap_type: WasmHeapType::Extern,
267 };
268 pub const FUNCREF: WasmRefType = WasmRefType {
270 nullable: true,
271 heap_type: WasmHeapType::Func,
272 };
273
274 #[inline]
276 pub fn is_vmgcref_type(&self) -> bool {
277 self.heap_type.is_vmgcref_type()
278 }
279
280 #[inline]
286 pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
287 self.heap_type.is_vmgcref_type_and_not_i31()
288 }
289}
290
291impl fmt::Display for WasmRefType {
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 match *self {
294 Self::FUNCREF => write!(f, "funcref"),
295 Self::EXTERNREF => write!(f, "externref"),
296 _ => {
297 if self.nullable {
298 write!(f, "(ref null {})", self.heap_type)
299 } else {
300 write!(f, "(ref {})", self.heap_type)
301 }
302 }
303 }
304 }
305}
306
307#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
312pub enum EngineOrModuleTypeIndex {
313 Engine(VMSharedTypeIndex),
316
317 Module(ModuleInternedTypeIndex),
320
321 RecGroup(RecGroupRelativeTypeIndex),
325}
326
327impl From<ModuleInternedTypeIndex> for EngineOrModuleTypeIndex {
328 #[inline]
329 fn from(i: ModuleInternedTypeIndex) -> Self {
330 Self::Module(i)
331 }
332}
333
334impl From<VMSharedTypeIndex> for EngineOrModuleTypeIndex {
335 #[inline]
336 fn from(i: VMSharedTypeIndex) -> Self {
337 Self::Engine(i)
338 }
339}
340
341impl From<RecGroupRelativeTypeIndex> for EngineOrModuleTypeIndex {
342 #[inline]
343 fn from(i: RecGroupRelativeTypeIndex) -> Self {
344 Self::RecGroup(i)
345 }
346}
347
348impl fmt::Display for EngineOrModuleTypeIndex {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 match self {
351 Self::Engine(i) => write!(f, "(engine {})", i.bits()),
352 Self::Module(i) => write!(f, "(module {})", i.as_u32()),
353 Self::RecGroup(i) => write!(f, "(recgroup {})", i.as_u32()),
354 }
355 }
356}
357
358impl EngineOrModuleTypeIndex {
359 pub fn is_engine_type_index(self) -> bool {
361 matches!(self, Self::Engine(_))
362 }
363
364 pub fn as_engine_type_index(self) -> Option<VMSharedTypeIndex> {
366 match self {
367 Self::Engine(e) => Some(e),
368 Self::RecGroup(_) | Self::Module(_) => None,
369 }
370 }
371
372 #[track_caller]
374 pub fn unwrap_engine_type_index(self) -> VMSharedTypeIndex {
375 match self.as_engine_type_index() {
376 Some(x) => x,
377 None => panic!("`unwrap_engine_type_index` on {self:?}"),
378 }
379 }
380
381 pub fn is_module_type_index(self) -> bool {
383 matches!(self, Self::Module(_))
384 }
385
386 pub fn as_module_type_index(self) -> Option<ModuleInternedTypeIndex> {
388 match self {
389 Self::Module(e) => Some(e),
390 Self::RecGroup(_) | Self::Engine(_) => None,
391 }
392 }
393
394 #[track_caller]
396 pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex {
397 match self.as_module_type_index() {
398 Some(x) => x,
399 None => panic!("`unwrap_module_type_index` on {self:?}"),
400 }
401 }
402
403 pub fn is_rec_group_type_index(self) -> bool {
405 matches!(self, Self::RecGroup(_))
406 }
407
408 pub fn as_rec_group_type_index(self) -> Option<RecGroupRelativeTypeIndex> {
410 match self {
411 Self::RecGroup(r) => Some(r),
412 Self::Module(_) | Self::Engine(_) => None,
413 }
414 }
415
416 #[track_caller]
418 pub fn unwrap_rec_group_type_index(self) -> RecGroupRelativeTypeIndex {
419 match self.as_rec_group_type_index() {
420 Some(x) => x,
421 None => panic!("`unwrap_rec_group_type_index` on {self:?}"),
422 }
423 }
424}
425
426#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
428#[expect(missing_docs, reason = "self-describing variants")]
429pub enum WasmHeapType {
430 Extern,
432 NoExtern,
433
434 Func,
436 ConcreteFunc(EngineOrModuleTypeIndex),
437 NoFunc,
438
439 Cont,
441 ConcreteCont(EngineOrModuleTypeIndex),
442 NoCont,
443
444 Any,
446 Eq,
447 I31,
448 Array,
449 ConcreteArray(EngineOrModuleTypeIndex),
450 Struct,
451 ConcreteStruct(EngineOrModuleTypeIndex),
452 None,
453}
454
455impl From<WasmHeapTopType> for WasmHeapType {
456 #[inline]
457 fn from(value: WasmHeapTopType) -> Self {
458 match value {
459 WasmHeapTopType::Extern => Self::Extern,
460 WasmHeapTopType::Any => Self::Any,
461 WasmHeapTopType::Func => Self::Func,
462 WasmHeapTopType::Cont => Self::Cont,
463 }
464 }
465}
466
467impl From<WasmHeapBottomType> for WasmHeapType {
468 #[inline]
469 fn from(value: WasmHeapBottomType) -> Self {
470 match value {
471 WasmHeapBottomType::NoExtern => Self::NoExtern,
472 WasmHeapBottomType::None => Self::None,
473 WasmHeapBottomType::NoFunc => Self::NoFunc,
474 WasmHeapBottomType::NoCont => Self::NoCont,
475 }
476 }
477}
478
479impl fmt::Display for WasmHeapType {
480 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481 match self {
482 Self::Extern => write!(f, "extern"),
483 Self::NoExtern => write!(f, "noextern"),
484 Self::Func => write!(f, "func"),
485 Self::ConcreteFunc(i) => write!(f, "func {i}"),
486 Self::NoFunc => write!(f, "nofunc"),
487 Self::Cont => write!(f, "cont"),
488 Self::ConcreteCont(i) => write!(f, "cont {i}"),
489 Self::NoCont => write!(f, "nocont"),
490 Self::Any => write!(f, "any"),
491 Self::Eq => write!(f, "eq"),
492 Self::I31 => write!(f, "i31"),
493 Self::Array => write!(f, "array"),
494 Self::ConcreteArray(i) => write!(f, "array {i}"),
495 Self::Struct => write!(f, "struct"),
496 Self::ConcreteStruct(i) => write!(f, "struct {i}"),
497 Self::None => write!(f, "none"),
498 }
499 }
500}
501
502impl TypeTrace for WasmHeapType {
503 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
504 where
505 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
506 {
507 match *self {
508 Self::ConcreteArray(i) => func(i),
509 Self::ConcreteFunc(i) => func(i),
510 Self::ConcreteStruct(i) => func(i),
511 Self::ConcreteCont(i) => func(i),
512 _ => Ok(()),
513 }
514 }
515
516 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
517 where
518 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
519 {
520 match self {
521 Self::ConcreteArray(i) => func(i),
522 Self::ConcreteFunc(i) => func(i),
523 Self::ConcreteStruct(i) => func(i),
524 Self::ConcreteCont(i) => func(i),
525 _ => Ok(()),
526 }
527 }
528}
529
530impl WasmHeapType {
531 #[inline]
533 pub fn is_vmgcref_type(&self) -> bool {
534 match self.top() {
535 WasmHeapTopType::Any | WasmHeapTopType::Extern => true,
538
539 WasmHeapTopType::Func => false,
541 WasmHeapTopType::Cont => false,
542 }
543 }
544
545 #[inline]
551 pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
552 self.is_vmgcref_type() && *self != Self::I31
553 }
554
555 #[inline]
557 pub fn is_top(&self) -> bool {
558 *self == Self::from(self.top())
559 }
560
561 #[inline]
563 pub fn top(&self) -> WasmHeapTopType {
564 match self {
565 WasmHeapType::Extern | WasmHeapType::NoExtern => WasmHeapTopType::Extern,
566
567 WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
568 WasmHeapTopType::Func
569 }
570
571 WasmHeapType::Cont | WasmHeapType::ConcreteCont(_) | WasmHeapType::NoCont => {
572 WasmHeapTopType::Cont
573 }
574
575 WasmHeapType::Any
576 | WasmHeapType::Eq
577 | WasmHeapType::I31
578 | WasmHeapType::Array
579 | WasmHeapType::ConcreteArray(_)
580 | WasmHeapType::Struct
581 | WasmHeapType::ConcreteStruct(_)
582 | WasmHeapType::None => WasmHeapTopType::Any,
583 }
584 }
585
586 #[inline]
588 pub fn is_bottom(&self) -> bool {
589 *self == Self::from(self.bottom())
590 }
591
592 #[inline]
594 pub fn bottom(&self) -> WasmHeapBottomType {
595 match self {
596 WasmHeapType::Extern | WasmHeapType::NoExtern => WasmHeapBottomType::NoExtern,
597
598 WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
599 WasmHeapBottomType::NoFunc
600 }
601
602 WasmHeapType::Cont | WasmHeapType::ConcreteCont(_) | WasmHeapType::NoCont => {
603 WasmHeapBottomType::NoCont
604 }
605
606 WasmHeapType::Any
607 | WasmHeapType::Eq
608 | WasmHeapType::I31
609 | WasmHeapType::Array
610 | WasmHeapType::ConcreteArray(_)
611 | WasmHeapType::Struct
612 | WasmHeapType::ConcreteStruct(_)
613 | WasmHeapType::None => WasmHeapBottomType::None,
614 }
615 }
616}
617
618#[derive(Debug, Clone, Copy, Eq, PartialEq)]
620pub enum WasmHeapTopType {
621 Extern,
623 Any,
625 Func,
627 Cont,
629}
630
631#[derive(Debug, Clone, Copy, Eq, PartialEq)]
633pub enum WasmHeapBottomType {
634 NoExtern,
636 None,
638 NoFunc,
640 NoCont,
642}
643
644#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
646pub struct WasmFuncType {
647 params: Box<[WasmValType]>,
648 non_i31_gc_ref_params_count: usize,
649 returns: Box<[WasmValType]>,
650 non_i31_gc_ref_returns_count: usize,
651}
652
653impl fmt::Display for WasmFuncType {
654 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655 write!(f, "(func")?;
656 if !self.params.is_empty() {
657 write!(f, " (param")?;
658 for p in self.params.iter() {
659 write!(f, " {p}")?;
660 }
661 write!(f, ")")?;
662 }
663 if !self.returns.is_empty() {
664 write!(f, " (result")?;
665 for r in self.returns.iter() {
666 write!(f, " {r}")?;
667 }
668 write!(f, ")")?;
669 }
670 write!(f, ")")
671 }
672}
673
674impl TypeTrace for WasmFuncType {
675 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
676 where
677 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
678 {
679 for p in self.params.iter() {
680 p.trace(func)?;
681 }
682 for r in self.returns.iter() {
683 r.trace(func)?;
684 }
685 Ok(())
686 }
687
688 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
689 where
690 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
691 {
692 for p in self.params.iter_mut() {
693 p.trace_mut(func)?;
694 }
695 for r in self.returns.iter_mut() {
696 r.trace_mut(func)?;
697 }
698 Ok(())
699 }
700}
701
702impl WasmFuncType {
703 #[inline]
705 pub fn new(params: Box<[WasmValType]>, returns: Box<[WasmValType]>) -> Self {
706 let non_i31_gc_ref_params_count = params
707 .iter()
708 .filter(|p| p.is_vmgcref_type_and_not_i31())
709 .count();
710 let non_i31_gc_ref_returns_count = returns
711 .iter()
712 .filter(|r| r.is_vmgcref_type_and_not_i31())
713 .count();
714 WasmFuncType {
715 params,
716 non_i31_gc_ref_params_count,
717 returns,
718 non_i31_gc_ref_returns_count,
719 }
720 }
721
722 #[inline]
724 pub fn params(&self) -> &[WasmValType] {
725 &self.params
726 }
727
728 #[inline]
730 pub fn non_i31_gc_ref_params_count(&self) -> usize {
731 self.non_i31_gc_ref_params_count
732 }
733
734 #[inline]
736 pub fn returns(&self) -> &[WasmValType] {
737 &self.returns
738 }
739
740 #[inline]
742 pub fn non_i31_gc_ref_returns_count(&self) -> usize {
743 self.non_i31_gc_ref_returns_count
744 }
745
746 pub fn is_trampoline_type(&self) -> bool {
748 self.params().iter().all(|p| *p == p.trampoline_type())
749 && self.returns().iter().all(|r| *r == r.trampoline_type())
750 }
751
752 pub fn trampoline_type(&self) -> Cow<'_, Self> {
778 if self.is_trampoline_type() {
779 return Cow::Borrowed(self);
780 }
781
782 Cow::Owned(Self::new(
783 self.params().iter().map(|p| p.trampoline_type()).collect(),
784 self.returns().iter().map(|r| r.trampoline_type()).collect(),
785 ))
786 }
787}
788
789#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
791pub struct WasmContType(EngineOrModuleTypeIndex);
792
793impl fmt::Display for WasmContType {
794 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
795 write!(f, "(cont {})", self.0)
796 }
797}
798
799impl WasmContType {
800 pub fn new(idx: EngineOrModuleTypeIndex) -> Self {
802 WasmContType(idx)
803 }
804}
805
806impl TypeTrace for WasmContType {
807 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
808 where
809 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
810 {
811 func(self.0)
812 }
813
814 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
815 where
816 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
817 {
818 func(&mut self.0)
819 }
820}
821
822#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
824pub enum WasmStorageType {
825 I8,
827 I16,
829 Val(WasmValType),
831}
832
833impl fmt::Display for WasmStorageType {
834 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
835 match self {
836 WasmStorageType::I8 => write!(f, "i8"),
837 WasmStorageType::I16 => write!(f, "i16"),
838 WasmStorageType::Val(v) => fmt::Display::fmt(v, f),
839 }
840 }
841}
842
843impl TypeTrace for WasmStorageType {
844 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
845 where
846 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
847 {
848 match self {
849 WasmStorageType::I8 | WasmStorageType::I16 => Ok(()),
850 WasmStorageType::Val(v) => v.trace(func),
851 }
852 }
853
854 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
855 where
856 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
857 {
858 match self {
859 WasmStorageType::I8 | WasmStorageType::I16 => Ok(()),
860 WasmStorageType::Val(v) => v.trace_mut(func),
861 }
862 }
863}
864
865impl WasmStorageType {
866 pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
872 match self {
873 WasmStorageType::I8 | WasmStorageType::I16 => false,
874 WasmStorageType::Val(v) => v.is_vmgcref_type_and_not_i31(),
875 }
876 }
877}
878
879#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
881pub struct WasmFieldType {
882 pub element_type: WasmStorageType,
884
885 pub mutable: bool,
887}
888
889impl fmt::Display for WasmFieldType {
890 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
891 if self.mutable {
892 write!(f, "(mut {})", self.element_type)
893 } else {
894 fmt::Display::fmt(&self.element_type, f)
895 }
896 }
897}
898
899impl TypeTrace for WasmFieldType {
900 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
901 where
902 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
903 {
904 self.element_type.trace(func)
905 }
906
907 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
908 where
909 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
910 {
911 self.element_type.trace_mut(func)
912 }
913}
914
915#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
917pub struct WasmArrayType(pub WasmFieldType);
918
919impl fmt::Display for WasmArrayType {
920 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
921 write!(f, "(array {})", self.0)
922 }
923}
924
925impl TypeTrace for WasmArrayType {
926 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
927 where
928 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
929 {
930 self.0.trace(func)
931 }
932
933 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
934 where
935 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
936 {
937 self.0.trace_mut(func)
938 }
939}
940
941#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
943pub struct WasmStructType {
944 pub fields: Box<[WasmFieldType]>,
946}
947
948impl fmt::Display for WasmStructType {
949 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
950 write!(f, "(struct")?;
951 for ty in self.fields.iter() {
952 write!(f, " {ty}")?;
953 }
954 write!(f, ")")
955 }
956}
957
958impl TypeTrace for WasmStructType {
959 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
960 where
961 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
962 {
963 for f in self.fields.iter() {
964 f.trace(func)?;
965 }
966 Ok(())
967 }
968
969 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
970 where
971 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
972 {
973 for f in self.fields.iter_mut() {
974 f.trace_mut(func)?;
975 }
976 Ok(())
977 }
978}
979
980#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
981#[expect(missing_docs, reason = "self-describing type")]
982pub struct WasmCompositeType {
983 pub inner: WasmCompositeInnerType,
985 pub shared: bool,
988}
989
990impl fmt::Display for WasmCompositeType {
991 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
992 if self.shared {
993 write!(f, "(shared ")?;
994 }
995 fmt::Display::fmt(&self.inner, f)?;
996 if self.shared {
997 write!(f, ")")?;
998 }
999 Ok(())
1000 }
1001}
1002
1003#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1005#[expect(missing_docs, reason = "self-describing variants")]
1006pub enum WasmCompositeInnerType {
1007 Array(WasmArrayType),
1008 Func(WasmFuncType),
1009 Struct(WasmStructType),
1010 Cont(WasmContType),
1011}
1012
1013impl fmt::Display for WasmCompositeInnerType {
1014 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1015 match self {
1016 Self::Array(ty) => fmt::Display::fmt(ty, f),
1017 Self::Func(ty) => fmt::Display::fmt(ty, f),
1018 Self::Struct(ty) => fmt::Display::fmt(ty, f),
1019 Self::Cont(ty) => fmt::Display::fmt(ty, f),
1020 }
1021 }
1022}
1023
1024#[expect(missing_docs, reason = "self-describing functions")]
1025impl WasmCompositeInnerType {
1026 #[inline]
1027 pub fn is_array(&self) -> bool {
1028 matches!(self, Self::Array(_))
1029 }
1030
1031 #[inline]
1032 pub fn as_array(&self) -> Option<&WasmArrayType> {
1033 match self {
1034 Self::Array(f) => Some(f),
1035 _ => None,
1036 }
1037 }
1038
1039 #[inline]
1040 pub fn unwrap_array(&self) -> &WasmArrayType {
1041 self.as_array().unwrap()
1042 }
1043
1044 #[inline]
1045 pub fn is_func(&self) -> bool {
1046 matches!(self, Self::Func(_))
1047 }
1048
1049 #[inline]
1050 pub fn as_func(&self) -> Option<&WasmFuncType> {
1051 match self {
1052 Self::Func(f) => Some(f),
1053 _ => None,
1054 }
1055 }
1056
1057 #[inline]
1058 pub fn unwrap_func(&self) -> &WasmFuncType {
1059 self.as_func().unwrap()
1060 }
1061
1062 #[inline]
1063 pub fn is_struct(&self) -> bool {
1064 matches!(self, Self::Struct(_))
1065 }
1066
1067 #[inline]
1068 pub fn as_struct(&self) -> Option<&WasmStructType> {
1069 match self {
1070 Self::Struct(f) => Some(f),
1071 _ => None,
1072 }
1073 }
1074
1075 #[inline]
1076 pub fn unwrap_struct(&self) -> &WasmStructType {
1077 self.as_struct().unwrap()
1078 }
1079
1080 #[inline]
1081 pub fn is_cont(&self) -> bool {
1082 matches!(self, Self::Cont(_))
1083 }
1084
1085 #[inline]
1086 pub fn as_cont(&self) -> Option<&WasmContType> {
1087 match self {
1088 Self::Cont(f) => Some(f),
1089 _ => None,
1090 }
1091 }
1092
1093 #[inline]
1094 pub fn unwrap_cont(&self) -> &WasmContType {
1095 self.as_cont().unwrap()
1096 }
1097}
1098
1099impl TypeTrace for WasmCompositeType {
1100 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1101 where
1102 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1103 {
1104 match &self.inner {
1105 WasmCompositeInnerType::Array(a) => a.trace(func),
1106 WasmCompositeInnerType::Func(f) => f.trace(func),
1107 WasmCompositeInnerType::Struct(a) => a.trace(func),
1108 WasmCompositeInnerType::Cont(c) => c.trace(func),
1109 }
1110 }
1111
1112 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1113 where
1114 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1115 {
1116 match &mut self.inner {
1117 WasmCompositeInnerType::Array(a) => a.trace_mut(func),
1118 WasmCompositeInnerType::Func(f) => f.trace_mut(func),
1119 WasmCompositeInnerType::Struct(a) => a.trace_mut(func),
1120 WasmCompositeInnerType::Cont(c) => c.trace_mut(func),
1121 }
1122 }
1123}
1124
1125#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1127pub struct WasmSubType {
1128 pub is_final: bool,
1131
1132 pub supertype: Option<EngineOrModuleTypeIndex>,
1134
1135 pub composite_type: WasmCompositeType,
1137}
1138
1139impl fmt::Display for WasmSubType {
1140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1141 if self.is_final && self.supertype.is_none() {
1142 fmt::Display::fmt(&self.composite_type, f)
1143 } else {
1144 write!(f, "(sub")?;
1145 if self.is_final {
1146 write!(f, " final")?;
1147 }
1148 if let Some(sup) = self.supertype {
1149 write!(f, " {sup}")?;
1150 }
1151 write!(f, " {})", self.composite_type)
1152 }
1153 }
1154}
1155
1156#[expect(missing_docs, reason = "self-describing functions")]
1160impl WasmSubType {
1161 #[inline]
1162 pub fn is_func(&self) -> bool {
1163 self.composite_type.inner.is_func() && !self.composite_type.shared
1164 }
1165
1166 #[inline]
1167 pub fn as_func(&self) -> Option<&WasmFuncType> {
1168 if self.composite_type.shared {
1169 None
1170 } else {
1171 self.composite_type.inner.as_func()
1172 }
1173 }
1174
1175 #[inline]
1176 pub fn unwrap_func(&self) -> &WasmFuncType {
1177 assert!(!self.composite_type.shared);
1178 self.composite_type.inner.unwrap_func()
1179 }
1180
1181 #[inline]
1182 pub fn is_array(&self) -> bool {
1183 self.composite_type.inner.is_array() && !self.composite_type.shared
1184 }
1185
1186 #[inline]
1187 pub fn as_array(&self) -> Option<&WasmArrayType> {
1188 if self.composite_type.shared {
1189 None
1190 } else {
1191 self.composite_type.inner.as_array()
1192 }
1193 }
1194
1195 #[inline]
1196 pub fn unwrap_array(&self) -> &WasmArrayType {
1197 assert!(!self.composite_type.shared);
1198 self.composite_type.inner.unwrap_array()
1199 }
1200
1201 #[inline]
1202 pub fn is_struct(&self) -> bool {
1203 self.composite_type.inner.is_struct() && !self.composite_type.shared
1204 }
1205
1206 #[inline]
1207 pub fn as_struct(&self) -> Option<&WasmStructType> {
1208 if self.composite_type.shared {
1209 None
1210 } else {
1211 self.composite_type.inner.as_struct()
1212 }
1213 }
1214
1215 #[inline]
1216 pub fn unwrap_struct(&self) -> &WasmStructType {
1217 assert!(!self.composite_type.shared);
1218 self.composite_type.inner.unwrap_struct()
1219 }
1220
1221 #[inline]
1222 pub fn is_cont(&self) -> bool {
1223 self.composite_type.inner.is_cont() && !self.composite_type.shared
1224 }
1225
1226 #[inline]
1227 pub fn as_cont(&self) -> Option<&WasmContType> {
1228 if self.composite_type.shared {
1229 None
1230 } else {
1231 self.composite_type.inner.as_cont()
1232 }
1233 }
1234
1235 #[inline]
1236 pub fn unwrap_cont(&self) -> &WasmContType {
1237 assert!(!self.composite_type.shared);
1238 self.composite_type.inner.unwrap_cont()
1239 }
1240}
1241
1242impl TypeTrace for WasmSubType {
1243 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1244 where
1245 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1246 {
1247 if let Some(sup) = self.supertype {
1248 func(sup)?;
1249 }
1250 self.composite_type.trace(func)
1251 }
1252
1253 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1254 where
1255 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1256 {
1257 if let Some(sup) = self.supertype.as_mut() {
1258 func(sup)?;
1259 }
1260 self.composite_type.trace_mut(func)
1261 }
1262}
1263
1264#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1275pub struct WasmRecGroup {
1276 pub types: Box<[WasmSubType]>,
1278}
1279
1280impl TypeTrace for WasmRecGroup {
1281 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1282 where
1283 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1284 {
1285 for ty in self.types.iter() {
1286 ty.trace(func)?;
1287 }
1288 Ok(())
1289 }
1290
1291 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1292 where
1293 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1294 {
1295 for ty in self.types.iter_mut() {
1296 ty.trace_mut(func)?;
1297 }
1298 Ok(())
1299 }
1300}
1301
1302#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1304pub struct FuncIndex(u32);
1305entity_impl!(FuncIndex);
1306
1307#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1309pub struct DefinedFuncIndex(u32);
1310entity_impl!(DefinedFuncIndex);
1311
1312#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1314pub struct DefinedTableIndex(u32);
1315entity_impl!(DefinedTableIndex);
1316
1317#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1319pub struct DefinedMemoryIndex(u32);
1320entity_impl!(DefinedMemoryIndex);
1321
1322#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1324pub struct OwnedMemoryIndex(u32);
1325entity_impl!(OwnedMemoryIndex);
1326
1327#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1329pub struct DefinedGlobalIndex(u32);
1330entity_impl!(DefinedGlobalIndex);
1331
1332#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1334pub struct TableIndex(u32);
1335entity_impl!(TableIndex);
1336
1337#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1339pub struct GlobalIndex(u32);
1340entity_impl!(GlobalIndex);
1341
1342#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1344pub struct MemoryIndex(u32);
1345entity_impl!(MemoryIndex);
1346
1347#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1350pub struct ModuleInternedRecGroupIndex(u32);
1351entity_impl!(ModuleInternedRecGroupIndex);
1352
1353#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1356pub struct EngineInternedRecGroupIndex(u32);
1357entity_impl!(EngineInternedRecGroupIndex);
1358
1359#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1361pub struct TypeIndex(u32);
1362entity_impl!(TypeIndex);
1363
1364#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1370pub struct RecGroupRelativeTypeIndex(u32);
1371entity_impl!(RecGroupRelativeTypeIndex);
1372
1373#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1381pub struct ModuleInternedTypeIndex(u32);
1382entity_impl!(ModuleInternedTypeIndex);
1383
1384#[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1393pub struct VMSharedTypeIndex(u32);
1394entity_impl!(VMSharedTypeIndex);
1395
1396impl VMSharedTypeIndex {
1397 #[inline]
1399 pub fn new(value: u32) -> Self {
1400 assert_ne!(
1401 value,
1402 u32::MAX,
1403 "u32::MAX is reserved for the default value"
1404 );
1405 Self(value)
1406 }
1407
1408 #[inline]
1410 pub fn bits(&self) -> u32 {
1411 self.0
1412 }
1413}
1414
1415impl Default for VMSharedTypeIndex {
1416 #[inline]
1417 fn default() -> Self {
1418 Self(u32::MAX)
1419 }
1420}
1421
1422#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1424pub struct DataIndex(u32);
1425entity_impl!(DataIndex);
1426
1427#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1429pub struct ElemIndex(u32);
1430entity_impl!(ElemIndex);
1431
1432#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1434pub struct DefinedTagIndex(u32);
1435entity_impl!(DefinedTagIndex);
1436
1437#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1439pub struct TagIndex(u32);
1440entity_impl!(TagIndex);
1441
1442#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1447pub struct StaticModuleIndex(u32);
1448entity_impl!(StaticModuleIndex);
1449
1450#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1452pub enum EntityIndex {
1453 Function(FuncIndex),
1455 Table(TableIndex),
1457 Memory(MemoryIndex),
1459 Global(GlobalIndex),
1461 Tag(TagIndex),
1463}
1464
1465impl From<FuncIndex> for EntityIndex {
1466 fn from(idx: FuncIndex) -> EntityIndex {
1467 EntityIndex::Function(idx)
1468 }
1469}
1470
1471impl From<TableIndex> for EntityIndex {
1472 fn from(idx: TableIndex) -> EntityIndex {
1473 EntityIndex::Table(idx)
1474 }
1475}
1476
1477impl From<MemoryIndex> for EntityIndex {
1478 fn from(idx: MemoryIndex) -> EntityIndex {
1479 EntityIndex::Memory(idx)
1480 }
1481}
1482
1483impl From<GlobalIndex> for EntityIndex {
1484 fn from(idx: GlobalIndex) -> EntityIndex {
1485 EntityIndex::Global(idx)
1486 }
1487}
1488
1489impl From<TagIndex> for EntityIndex {
1490 fn from(idx: TagIndex) -> EntityIndex {
1491 EntityIndex::Tag(idx)
1492 }
1493}
1494
1495#[derive(Clone, Debug, Serialize, Deserialize)]
1498pub enum EntityType {
1499 Global(Global),
1501 Memory(Memory),
1503 Tag(Tag),
1505 Table(Table),
1507 Function(EngineOrModuleTypeIndex),
1510}
1511
1512impl TypeTrace for EntityType {
1513 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1514 where
1515 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1516 {
1517 match self {
1518 Self::Global(g) => g.trace(func),
1519 Self::Table(t) => t.trace(func),
1520 Self::Function(idx) => func(*idx),
1521 Self::Memory(_) => Ok(()),
1522 Self::Tag(t) => t.trace(func),
1523 }
1524 }
1525
1526 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1527 where
1528 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1529 {
1530 match self {
1531 Self::Global(g) => g.trace_mut(func),
1532 Self::Table(t) => t.trace_mut(func),
1533 Self::Function(idx) => func(idx),
1534 Self::Memory(_) => Ok(()),
1535 Self::Tag(t) => t.trace_mut(func),
1536 }
1537 }
1538}
1539
1540impl EntityType {
1541 pub fn unwrap_global(&self) -> &Global {
1543 match self {
1544 EntityType::Global(g) => g,
1545 _ => panic!("not a global"),
1546 }
1547 }
1548
1549 pub fn unwrap_memory(&self) -> &Memory {
1551 match self {
1552 EntityType::Memory(g) => g,
1553 _ => panic!("not a memory"),
1554 }
1555 }
1556
1557 pub fn unwrap_tag(&self) -> &Tag {
1559 match self {
1560 EntityType::Tag(g) => g,
1561 _ => panic!("not a tag"),
1562 }
1563 }
1564
1565 pub fn unwrap_table(&self) -> &Table {
1567 match self {
1568 EntityType::Table(g) => g,
1569 _ => panic!("not a table"),
1570 }
1571 }
1572
1573 pub fn unwrap_func(&self) -> EngineOrModuleTypeIndex {
1575 match self {
1576 EntityType::Function(g) => *g,
1577 _ => panic!("not a func"),
1578 }
1579 }
1580}
1581
1582#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1590pub struct Global {
1591 pub wasm_ty: crate::WasmValType,
1593 pub mutability: bool,
1595}
1596
1597impl TypeTrace for Global {
1598 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1599 where
1600 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1601 {
1602 let Global {
1603 wasm_ty,
1604 mutability: _,
1605 } = self;
1606 wasm_ty.trace(func)
1607 }
1608
1609 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1610 where
1611 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1612 {
1613 let Global {
1614 wasm_ty,
1615 mutability: _,
1616 } = self;
1617 wasm_ty.trace_mut(func)
1618 }
1619}
1620
1621#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1625pub struct ConstExpr {
1626 ops: SmallVec<[ConstOp; 2]>,
1627}
1628
1629impl ConstExpr {
1630 pub fn new(ops: impl IntoIterator<Item = ConstOp>) -> Self {
1636 let ops = ops.into_iter().collect::<SmallVec<[ConstOp; 2]>>();
1637 assert!(!ops.is_empty());
1638 ConstExpr { ops }
1639 }
1640
1641 pub fn from_wasmparser(
1646 expr: wasmparser::ConstExpr<'_>,
1647 ) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)> {
1648 let mut iter = expr
1649 .get_operators_reader()
1650 .into_iter_with_offsets()
1651 .peekable();
1652
1653 let mut ops = SmallVec::<[ConstOp; 2]>::new();
1654 let mut escaped = SmallVec::<[FuncIndex; 1]>::new();
1655 while let Some(res) = iter.next() {
1656 let (op, offset) = res?;
1657
1658 if matches!(op, wasmparser::Operator::End) && iter.peek().is_none() {
1662 break;
1663 }
1664
1665 if let wasmparser::Operator::RefFunc { function_index } = &op {
1668 escaped.push(FuncIndex::from_u32(*function_index));
1669 }
1670
1671 ops.push(ConstOp::from_wasmparser(op, offset)?);
1672 }
1673 Ok((Self { ops }, escaped))
1674 }
1675
1676 pub fn ops(&self) -> &[ConstOp] {
1678 &self.ops
1679 }
1680
1681 pub fn provably_nonzero_i32(&self) -> bool {
1691 assert!(self.ops.len() > 0);
1692 if self.ops.len() > 1 {
1693 return false;
1696 }
1697 match self.ops[0] {
1699 ConstOp::I32Const(0) => false,
1701 ConstOp::I32Const(_) => true,
1704 _ => false,
1706 }
1707 }
1708}
1709
1710#[expect(missing_docs, reason = "self-describing variants")]
1712#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1713pub enum ConstOp {
1714 I32Const(i32),
1715 I64Const(i64),
1716 F32Const(u32),
1717 F64Const(u64),
1718 V128Const(u128),
1719 GlobalGet(GlobalIndex),
1720 RefI31,
1721 RefNull,
1722 RefFunc(FuncIndex),
1723 I32Add,
1724 I32Sub,
1725 I32Mul,
1726 I64Add,
1727 I64Sub,
1728 I64Mul,
1729 StructNew {
1730 struct_type_index: TypeIndex,
1731 },
1732 StructNewDefault {
1733 struct_type_index: TypeIndex,
1734 },
1735 ArrayNew {
1736 array_type_index: TypeIndex,
1737 },
1738 ArrayNewDefault {
1739 array_type_index: TypeIndex,
1740 },
1741 ArrayNewFixed {
1742 array_type_index: TypeIndex,
1743 array_size: u32,
1744 },
1745 ExternConvertAny,
1746 AnyConvertExtern,
1747}
1748
1749impl ConstOp {
1750 pub fn from_wasmparser(op: wasmparser::Operator<'_>, offset: usize) -> WasmResult<Self> {
1752 use wasmparser::Operator as O;
1753 Ok(match op {
1754 O::I32Const { value } => Self::I32Const(value),
1755 O::I64Const { value } => Self::I64Const(value),
1756 O::F32Const { value } => Self::F32Const(value.bits()),
1757 O::F64Const { value } => Self::F64Const(value.bits()),
1758 O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())),
1759 O::RefNull { hty: _ } => Self::RefNull,
1760 O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
1761 O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
1762 O::RefI31 => Self::RefI31,
1763 O::I32Add => Self::I32Add,
1764 O::I32Sub => Self::I32Sub,
1765 O::I32Mul => Self::I32Mul,
1766 O::I64Add => Self::I64Add,
1767 O::I64Sub => Self::I64Sub,
1768 O::I64Mul => Self::I64Mul,
1769 O::StructNew { struct_type_index } => Self::StructNew {
1770 struct_type_index: TypeIndex::from_u32(struct_type_index),
1771 },
1772 O::StructNewDefault { struct_type_index } => Self::StructNewDefault {
1773 struct_type_index: TypeIndex::from_u32(struct_type_index),
1774 },
1775 O::ArrayNew { array_type_index } => Self::ArrayNew {
1776 array_type_index: TypeIndex::from_u32(array_type_index),
1777 },
1778 O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault {
1779 array_type_index: TypeIndex::from_u32(array_type_index),
1780 },
1781 O::ArrayNewFixed {
1782 array_type_index,
1783 array_size,
1784 } => Self::ArrayNewFixed {
1785 array_type_index: TypeIndex::from_u32(array_type_index),
1786 array_size,
1787 },
1788 O::ExternConvertAny => Self::ExternConvertAny,
1789 O::AnyConvertExtern => Self::AnyConvertExtern,
1790 op => {
1791 return Err(wasm_unsupported!(
1792 "unsupported opcode in const expression at offset {offset:#x}: {op:?}",
1793 ));
1794 }
1795 })
1796 }
1797}
1798
1799#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1801#[expect(missing_docs, reason = "self-describing variants")]
1802pub enum IndexType {
1803 I32,
1804 I64,
1805}
1806
1807#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1809#[expect(missing_docs, reason = "self-describing fields")]
1810pub struct Limits {
1811 pub min: u64,
1812 pub max: Option<u64>,
1813}
1814
1815#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1817pub struct Table {
1818 pub idx_type: IndexType,
1820 pub limits: Limits,
1823 pub ref_type: WasmRefType,
1825}
1826
1827impl TypeTrace for Table {
1828 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1829 where
1830 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1831 {
1832 let Table {
1833 ref_type: wasm_ty,
1834 idx_type: _,
1835 limits: _,
1836 } = self;
1837 wasm_ty.trace(func)
1838 }
1839
1840 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1841 where
1842 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1843 {
1844 let Table {
1845 ref_type: wasm_ty,
1846 idx_type: _,
1847 limits: _,
1848 } = self;
1849 wasm_ty.trace_mut(func)
1850 }
1851}
1852
1853#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1855pub struct Memory {
1856 pub idx_type: IndexType,
1858 pub limits: Limits,
1861 pub shared: bool,
1863 pub page_size_log2: u8,
1868}
1869
1870pub const WASM32_MAX_SIZE: u64 = 1 << 32;
1872
1873impl Memory {
1874 pub const DEFAULT_PAGE_SIZE: u32 = 0x10000;
1876
1877 pub const DEFAULT_PAGE_SIZE_LOG2: u8 = {
1879 let log2 = 16;
1880 assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE);
1881 log2
1882 };
1883
1884 pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> {
1892 self.limits
1893 .min
1894 .checked_mul(self.page_size())
1895 .ok_or(SizeOverflow)
1896 }
1897
1898 pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> {
1913 match self.limits.max {
1914 Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow),
1915 None => {
1916 let min = self.minimum_byte_size()?;
1917 Ok(min.max(self.max_size_based_on_index_type()))
1918 }
1919 }
1920 }
1921
1922 pub fn page_size(&self) -> u64 {
1924 debug_assert!(
1925 self.page_size_log2 == 16 || self.page_size_log2 == 0,
1926 "invalid page_size_log2: {}; must be 16 or 0",
1927 self.page_size_log2
1928 );
1929 1 << self.page_size_log2
1930 }
1931
1932 pub fn max_size_based_on_index_type(&self) -> u64 {
1937 match self.idx_type {
1938 IndexType::I64 =>
1939 {
1947 0_u64.wrapping_sub(self.page_size())
1948 }
1949 IndexType::I32 => WASM32_MAX_SIZE,
1950 }
1951 }
1952
1953 pub fn can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
1965 tunables.signals_based_traps && self.page_size_log2 >= host_page_size_log2
1966 }
1967
1968 pub fn can_elide_bounds_check(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
1988 self.can_use_virtual_memory(tunables, host_page_size_log2)
1989 && self.idx_type == IndexType::I32
1990 && tunables.memory_reservation >= (1 << 32)
1991 }
1992
1993 pub fn static_heap_size(&self) -> Option<u64> {
1997 let min = self.minimum_byte_size().ok()?;
1998 let max = self.maximum_byte_size().ok()?;
1999 if min == max {
2000 Some(min)
2001 } else {
2002 None
2003 }
2004 }
2005
2006 pub fn memory_may_move(&self, tunables: &Tunables) -> bool {
2013 if self.shared {
2017 return false;
2018 }
2019
2020 if !tunables.memory_may_move {
2023 return false;
2024 }
2025
2026 if self.limits.max.is_some_and(|max| self.limits.min == max) {
2029 return false;
2030 }
2031
2032 let max = self.maximum_byte_size().unwrap_or(u64::MAX);
2035 max > tunables.memory_reservation
2036 }
2037}
2038
2039#[derive(Copy, Clone, Debug)]
2040#[expect(missing_docs, reason = "self-describing error struct")]
2041pub struct SizeOverflow;
2042
2043impl fmt::Display for SizeOverflow {
2044 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2045 f.write_str("size overflow calculating memory size")
2046 }
2047}
2048
2049impl core::error::Error for SizeOverflow {}
2050
2051impl From<wasmparser::MemoryType> for Memory {
2052 fn from(ty: wasmparser::MemoryType) -> Memory {
2053 let idx_type = match ty.memory64 {
2054 false => IndexType::I32,
2055 true => IndexType::I64,
2056 };
2057 let limits = Limits {
2058 min: ty.initial,
2059 max: ty.maximum,
2060 };
2061 let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap();
2062 debug_assert!(
2063 page_size_log2 == 16 || page_size_log2 == 0,
2064 "invalid page_size_log2: {page_size_log2}; must be 16 or 0"
2065 );
2066 Memory {
2067 idx_type,
2068 limits,
2069 shared: ty.shared,
2070 page_size_log2,
2071 }
2072 }
2073}
2074
2075#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2077pub struct Tag {
2078 pub signature: EngineOrModuleTypeIndex,
2080}
2081
2082impl TypeTrace for Tag {
2083 fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2084 where
2085 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2086 {
2087 func(self.signature)
2088 }
2089
2090 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2091 where
2092 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2093 {
2094 func(&mut self.signature)
2095 }
2096}
2097
2098#[expect(missing_docs, reason = "self-describing functions")]
2100pub trait TypeConvert {
2101 fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global> {
2103 Ok(Global {
2104 wasm_ty: self.convert_valtype(ty.content_type)?,
2105 mutability: ty.mutable,
2106 })
2107 }
2108
2109 fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> {
2111 let idx_type = match ty.table64 {
2112 false => IndexType::I32,
2113 true => IndexType::I64,
2114 };
2115 let limits = Limits {
2116 min: ty.initial.try_into().unwrap(),
2117 max: ty.maximum.map(|i| i.try_into().unwrap()),
2118 };
2119 Ok(Table {
2120 idx_type,
2121 limits,
2122 ref_type: self.convert_ref_type(ty.element_type)?,
2123 })
2124 }
2125
2126 fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
2127 Ok(WasmSubType {
2128 is_final: ty.is_final,
2129 supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())),
2130 composite_type: self.convert_composite_type(&ty.composite_type)?,
2131 })
2132 }
2133
2134 fn convert_composite_type(
2135 &self,
2136 ty: &wasmparser::CompositeType,
2137 ) -> WasmResult<WasmCompositeType> {
2138 let inner = match &ty.inner {
2139 wasmparser::CompositeInnerType::Func(f) => {
2140 WasmCompositeInnerType::Func(self.convert_func_type(f)?)
2141 }
2142 wasmparser::CompositeInnerType::Array(a) => {
2143 WasmCompositeInnerType::Array(self.convert_array_type(a)?)
2144 }
2145 wasmparser::CompositeInnerType::Struct(s) => {
2146 WasmCompositeInnerType::Struct(self.convert_struct_type(s)?)
2147 }
2148 wasmparser::CompositeInnerType::Cont(c) => {
2149 WasmCompositeInnerType::Cont(self.convert_cont_type(c))
2150 }
2151 };
2152 Ok(WasmCompositeType {
2153 inner,
2154 shared: ty.shared,
2155 })
2156 }
2157
2158 fn convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType {
2160 if let WasmHeapType::ConcreteFunc(sigidx) = self.lookup_heap_type(ty.0.unpack()) {
2161 WasmContType::new(sigidx)
2162 } else {
2163 panic!("Failed to extract signature index for continuation type.")
2164 }
2165 }
2166
2167 fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType> {
2168 Ok(WasmStructType {
2169 fields: ty
2170 .fields
2171 .iter()
2172 .map(|f| self.convert_field_type(f))
2173 .collect::<WasmResult<_>>()?,
2174 })
2175 }
2176
2177 fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType> {
2178 Ok(WasmArrayType(self.convert_field_type(&ty.0)?))
2179 }
2180
2181 fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType> {
2182 Ok(WasmFieldType {
2183 element_type: self.convert_storage_type(&ty.element_type)?,
2184 mutable: ty.mutable,
2185 })
2186 }
2187
2188 fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType> {
2189 Ok(match ty {
2190 wasmparser::StorageType::I8 => WasmStorageType::I8,
2191 wasmparser::StorageType::I16 => WasmStorageType::I16,
2192 wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)?),
2193 })
2194 }
2195
2196 fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType> {
2198 let params = ty
2199 .params()
2200 .iter()
2201 .map(|t| self.convert_valtype(*t))
2202 .collect::<WasmResult<_>>()?;
2203 let results = ty
2204 .results()
2205 .iter()
2206 .map(|t| self.convert_valtype(*t))
2207 .collect::<WasmResult<_>>()?;
2208 Ok(WasmFuncType::new(params, results))
2209 }
2210
2211 fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType> {
2213 Ok(match ty {
2214 wasmparser::ValType::I32 => WasmValType::I32,
2215 wasmparser::ValType::I64 => WasmValType::I64,
2216 wasmparser::ValType::F32 => WasmValType::F32,
2217 wasmparser::ValType::F64 => WasmValType::F64,
2218 wasmparser::ValType::V128 => WasmValType::V128,
2219 wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)?),
2220 })
2221 }
2222
2223 fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType> {
2225 Ok(WasmRefType {
2226 nullable: ty.is_nullable(),
2227 heap_type: self.convert_heap_type(ty.heap_type())?,
2228 })
2229 }
2230
2231 fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType> {
2233 Ok(match ty {
2234 wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i),
2235 wasmparser::HeapType::Abstract { ty, shared: false } => match ty {
2236 wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern,
2237 wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern,
2238 wasmparser::AbstractHeapType::Func => WasmHeapType::Func,
2239 wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc,
2240 wasmparser::AbstractHeapType::Any => WasmHeapType::Any,
2241 wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq,
2242 wasmparser::AbstractHeapType::I31 => WasmHeapType::I31,
2243 wasmparser::AbstractHeapType::Array => WasmHeapType::Array,
2244 wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct,
2245 wasmparser::AbstractHeapType::None => WasmHeapType::None,
2246
2247 wasmparser::AbstractHeapType::Exn
2248 | wasmparser::AbstractHeapType::NoExn
2249 | wasmparser::AbstractHeapType::Cont
2250 | wasmparser::AbstractHeapType::NoCont => {
2251 return Err(wasm_unsupported!("unsupported heap type {ty:?}"))
2252 }
2253 },
2254 _ => return Err(wasm_unsupported!("unsupported heap type {ty:?}")),
2255 })
2256 }
2257
2258 fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;
2261
2262 fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex;
2265}