1#[cfg(feature = "simd")]
26use crate::VisitSimdOperator;
27use crate::{
28 AbstractHeapType, BinaryReaderError, BlockType, BrTable, Catch, ContType, FieldType, FrameKind,
29 FrameStack, FuncType, GlobalType, Handle, HeapType, Ieee32, Ieee64, MemArg, ModuleArity,
30 RefType, Result, ResumeTable, StorageType, StructType, SubType, TableType, TryTable,
31 UnpackedIndex, ValType, VisitOperator, WasmFeatures, WasmModuleResources,
32 limits::MAX_WASM_FUNCTION_LOCALS,
33};
34use crate::{CompositeInnerType, Ordering, prelude::*};
35use core::ops::{Deref, DerefMut};
36use core::{cmp, iter, mem};
37
38#[cfg(feature = "simd")]
39mod simd;
40
41#[cfg(feature = "try-op")]
42mod transaction;
43#[cfg(not(feature = "try-op"))]
44mod transaction_disabled;
45#[cfg(not(feature = "try-op"))]
46use transaction_disabled as transaction;
47
48use transaction::{RollbackLogAllocations, Transaction};
49
50#[derive(Clone, PartialEq)]
51pub(crate) struct OperatorValidator {
52 pub(super) locals: Locals,
53 local_inits: LocalInits,
54
55 pub(crate) features: WasmFeatures,
58
59 popped_types_tmp: Vec<MaybeType>,
61
62 control: Vec<Frame>,
64 operands: Vec<MaybeType>,
66
67 shared: bool,
69
70 #[cfg(debug_assertions)]
75 pub(crate) pop_push_log: Vec<bool>,
76
77 transaction: Transaction,
81}
82
83#[derive(Clone, PartialEq)]
85struct LocalInits {
86 local_inits: Vec<bool>,
88 inits: Vec<u32>,
91 first_non_default_local: u32,
98}
99
100impl Default for LocalInits {
101 fn default() -> Self {
102 Self {
103 local_inits: Vec::default(),
104 inits: Vec::default(),
105 first_non_default_local: u32::MAX,
106 }
107 }
108}
109
110impl LocalInits {
111 pub fn define_params(&mut self, count: usize) {
113 let Some(new_len) = self.local_inits.len().checked_add(count) else {
114 panic!("tried to define too many function locals as parameters: {count}");
115 };
116 self.local_inits.resize(new_len, true);
117 }
118
119 pub fn define_locals(&mut self, count: u32, ty: ValType) {
121 let Ok(count) = usize::try_from(count) else {
122 panic!("tried to define too many function locals: {count}");
123 };
124 let len = self.local_inits.len();
125 let Some(new_len) = len.checked_add(count) else {
126 panic!("tried to define too many function locals: {count}");
127 };
128 let is_defaultable = ty.is_defaultable();
129 if !is_defaultable && self.first_non_default_local == u32::MAX {
130 self.first_non_default_local = len as u32;
131 }
132 self.local_inits.resize(new_len, is_defaultable);
133 }
134
135 #[inline]
137 pub fn is_uninit(&self, local_index: u32) -> bool {
138 if local_index < self.first_non_default_local {
139 return false;
140 }
141 !self.local_inits[local_index as usize]
142 }
143
144 #[inline]
146 pub fn set_init(&mut self, local_index: u32) {
147 if self.is_uninit(local_index) {
148 self.local_inits[local_index as usize] = true;
149 self.inits.push(local_index);
150 }
151 }
152
153 pub fn height(&self) -> usize {
155 self.inits.len()
156 }
157
158 #[inline]
163 pub fn pop_ctrl(&mut self, height: usize) -> Vec<u32> {
164 let inits = self.inits.split_off(height);
165 for local_index in &inits {
166 self.local_inits[*local_index as usize] = false;
167 }
168 inits
169 }
170
171 pub fn clear(&mut self) {
175 self.local_inits.clear();
176 self.inits.clear();
177 self.first_non_default_local = u32::MAX;
178 }
179
180 pub fn is_empty(&self) -> bool {
182 self.local_inits.is_empty()
183 }
184}
185
186const MAX_LOCALS_TO_TRACK: u32 = 50;
189
190#[derive(Clone, PartialEq)]
191pub(super) struct Locals {
192 num_locals: u32,
194
195 first: Vec<ValType>,
200
201 uncached: Vec<(u32, ValType)>,
212}
213
214#[derive(Debug, Copy, Clone, PartialEq)]
221pub struct Frame {
222 pub kind: FrameKind,
224 pub block_type: BlockType,
227 pub height: usize,
229 pub unreachable: bool,
231 pub init_height: usize,
233}
234
235struct OperatorValidatorTemp<'validator, 'resources, T> {
236 offset: usize,
237 inner: &'validator mut OperatorValidator,
238 resources: &'resources T,
239}
240
241#[derive(Default)]
242pub struct OperatorValidatorAllocations {
243 popped_types_tmp: Vec<MaybeType>,
244 control: Vec<Frame>,
245 operands: Vec<MaybeType>,
246 local_inits: LocalInits,
247 locals_first: Vec<ValType>,
248 locals_uncached: Vec<(u32, ValType)>,
249 rollback_log: RollbackLogAllocations,
250}
251
252#[derive(Debug, Copy, Clone, PartialEq)]
259enum MaybeType<T = ValType> {
260 Bottom,
266 UnknownRef(Option<AbstractHeapType>),
277 Known(T),
279}
280
281#[test]
285fn assert_maybe_type_small() {
286 assert!(core::mem::size_of::<MaybeType>() == 8);
287}
288
289impl core::fmt::Display for MaybeType {
290 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
291 match self {
292 MaybeType::Bottom => write!(f, "bot"),
293 MaybeType::UnknownRef(ty) => {
294 write!(f, "(ref shared? ")?;
295 match ty {
296 Some(ty) => write!(f, "{}bot", ty.as_str(true))?,
297 None => write!(f, "bot")?,
298 }
299 write!(f, ")")
300 }
301 MaybeType::Known(ty) => core::fmt::Display::fmt(ty, f),
302 }
303 }
304}
305
306impl From<ValType> for MaybeType {
307 fn from(ty: ValType) -> MaybeType {
308 MaybeType::Known(ty)
309 }
310}
311
312impl From<RefType> for MaybeType {
313 fn from(ty: RefType) -> MaybeType {
314 let ty: ValType = ty.into();
315 ty.into()
316 }
317}
318impl From<MaybeType<RefType>> for MaybeType<ValType> {
319 fn from(ty: MaybeType<RefType>) -> MaybeType<ValType> {
320 match ty {
321 MaybeType::Bottom => MaybeType::Bottom,
322 MaybeType::UnknownRef(ty) => MaybeType::UnknownRef(ty),
323 MaybeType::Known(t) => MaybeType::Known(t.into()),
324 }
325 }
326}
327
328impl MaybeType<RefType> {
329 fn as_non_null(&self) -> MaybeType<RefType> {
330 match self {
331 MaybeType::Bottom => MaybeType::Bottom,
332 MaybeType::UnknownRef(ty) => MaybeType::UnknownRef(*ty),
333 MaybeType::Known(ty) => MaybeType::Known(ty.as_non_null()),
334 }
335 }
336
337 fn is_maybe_shared(&self, resources: &impl WasmModuleResources) -> Option<bool> {
338 match self {
339 MaybeType::Bottom => None,
340 MaybeType::UnknownRef(_) => None,
341 MaybeType::Known(ty) => Some(resources.is_shared(*ty)),
342 }
343 }
344}
345
346impl OperatorValidator {
347 fn new(features: &WasmFeatures, allocs: OperatorValidatorAllocations) -> Self {
348 let OperatorValidatorAllocations {
349 popped_types_tmp,
350 control,
351 operands,
352 local_inits,
353 locals_first,
354 locals_uncached,
355 rollback_log,
356 } = allocs;
357 debug_assert!(popped_types_tmp.is_empty());
358 debug_assert!(control.is_empty());
359 debug_assert!(operands.is_empty());
360 debug_assert!(local_inits.is_empty());
361 debug_assert!(locals_first.is_empty());
362 debug_assert!(locals_uncached.is_empty());
363 OperatorValidator {
364 locals: Locals {
365 num_locals: 0,
366 first: locals_first,
367 uncached: locals_uncached,
368 },
369 local_inits,
370 features: *features,
371 popped_types_tmp,
372 operands,
373 control,
374 shared: false,
375 #[cfg(debug_assertions)]
376 pop_push_log: vec![],
377 transaction: Transaction::new(rollback_log),
378 }
379 }
380
381 pub fn new_func<T>(
387 ty: u32,
388 offset: usize,
389 features: &WasmFeatures,
390 resources: &T,
391 allocs: OperatorValidatorAllocations,
392 ) -> Result<Self>
393 where
394 T: WasmModuleResources,
395 {
396 let mut ret = OperatorValidator::new(features, allocs);
397 ret.control.push(Frame {
398 kind: FrameKind::Block,
399 block_type: BlockType::FuncType(ty),
400 height: 0,
401 unreachable: false,
402 init_height: 0,
403 });
404
405 let sub_ty = OperatorValidatorTemp {
408 offset,
409 inner: &mut ret,
410 resources,
411 }
412 .sub_type_at(ty)?;
413
414 if let CompositeInnerType::Func(func_ty) = &sub_ty.composite_type.inner {
416 for ty in func_ty.params() {
417 ret.locals.define(1, *ty);
418 }
419 ret.local_inits.define_params(func_ty.params().len());
420 } else {
421 bail!(offset, "expected func type at index {ty}, found {sub_ty}")
422 }
423
424 if sub_ty.composite_type.shared {
427 ret.shared = true;
428 }
429 Ok(ret)
430 }
431
432 pub fn new_const_expr(
436 features: &WasmFeatures,
437 ty: ValType,
438 allocs: OperatorValidatorAllocations,
439 ) -> Self {
440 let mut ret = OperatorValidator::new(features, allocs);
441 ret.control.push(Frame {
442 kind: FrameKind::Block,
443 block_type: BlockType::Type(ty),
444 height: 0,
445 unreachable: false,
446 init_height: 0,
447 });
448 ret
449 }
450
451 pub fn define_locals(
452 &mut self,
453 offset: usize,
454 count: u32,
455 mut ty: ValType,
456 resources: &impl WasmModuleResources,
457 ) -> Result<()> {
458 resources.check_value_type(&mut ty, &self.features, offset)?;
459 if count == 0 {
460 return Ok(());
461 }
462 if !self.locals.define(count, ty) {
463 return Err(BinaryReaderError::new(
464 "too many locals: locals exceed maximum",
465 offset,
466 ));
467 }
468 self.local_inits.define_locals(count, ty);
469 Ok(())
470 }
471
472 pub fn operand_stack_height(&self) -> usize {
474 self.operands.len()
475 }
476
477 pub fn peek_operand_at(&self, depth: usize) -> Option<Option<ValType>> {
488 Some(match self.operands.iter().rev().nth(depth)? {
489 MaybeType::Known(t) => Some(*t),
490 MaybeType::Bottom | MaybeType::UnknownRef(..) => None,
491 })
492 }
493
494 pub fn control_stack_height(&self) -> usize {
496 self.control.len()
497 }
498
499 pub(crate) fn jump(&self, depth: u32) -> Option<(BlockType, FrameKind)> {
504 assert!(!self.control.is_empty());
505 let i = (self.control.len() - 1).checked_sub(depth as usize)?;
506 let frame = &self.control[i];
507 Some((frame.block_type, frame.kind))
508 }
509
510 pub fn get_frame(&self, depth: usize) -> Option<&Frame> {
511 self.control.iter().rev().nth(depth)
512 }
513
514 pub fn with_resources<'a, 'validator, 'resources, T>(
516 &'validator mut self,
517 resources: &'resources T,
518 offset: usize,
519 ) -> impl VisitOperator<'a, Output = Result<()>> + ModuleArity + FrameStack + 'validator
520 where
521 T: WasmModuleResources,
522 'resources: 'validator,
523 {
524 WasmProposalValidator(OperatorValidatorTemp {
525 offset,
526 inner: self,
527 resources,
528 })
529 }
530
531 #[cfg(feature = "simd")]
534 pub fn with_resources_simd<'a, 'validator, 'resources, T>(
535 &'validator mut self,
536 resources: &'resources T,
537 offset: usize,
538 ) -> impl VisitSimdOperator<'a, Output = Result<()>> + ModuleArity + 'validator
539 where
540 T: WasmModuleResources,
541 'resources: 'validator,
542 {
543 WasmProposalValidator(OperatorValidatorTemp {
544 offset,
545 inner: self,
546 resources,
547 })
548 }
549
550 pub fn into_allocations(mut self) -> OperatorValidatorAllocations {
551 fn clear<T>(mut tmp: Vec<T>) -> Vec<T> {
552 tmp.clear();
553 tmp
554 }
555 OperatorValidatorAllocations {
556 popped_types_tmp: clear(self.popped_types_tmp),
557 control: clear(self.control),
558 operands: clear(self.operands),
559 local_inits: {
560 self.local_inits.clear();
561 self.local_inits
562 },
563 locals_first: clear(self.locals.first),
564 locals_uncached: clear(self.locals.uncached),
565 rollback_log: self.transaction.into_allocations(),
566 }
567 }
568
569 fn record_pop(&mut self, ty: MaybeType) {
571 self.transaction.map(|log| log.record_pop(ty));
572 self.record_any_pop();
573 }
574
575 fn record_any_pop(&mut self) {
577 #[cfg(debug_assertions)]
578 {
579 self.pop_push_log.push(false);
580 }
581 }
582
583 fn record_push(&mut self) {
584 self.transaction.map(|log| log.record_push());
585 #[cfg(debug_assertions)]
586 {
587 self.pop_push_log.push(true);
588 }
589 }
590
591 #[allow(dead_code)]
592 pub(super) fn begin_try_op(&mut self) {
593 self.transaction.begin(self.local_inits.height());
594 }
595
596 #[allow(dead_code)]
597 pub(super) fn commit(&mut self) {
598 self.transaction.end();
599 }
600
601 #[cfg(feature = "try-op")]
605 pub(super) fn rollback(&mut self) {
606 let Transaction::Active(rollback_log) = &self.transaction else {
607 panic!("no transaction pending");
608 };
609
610 if rollback_log.unreachable {
611 self.control.last_mut().unwrap().unreachable = false;
612 }
613
614 for x in rollback_log.operands.iter().rev() {
615 match x {
616 None => {
617 self.operands.pop();
618 }
619 Some(mt) => self.operands.push(*mt),
620 }
621 }
622
623 for x in rollback_log.frames.iter().rev() {
624 match x {
625 None => {
626 let frame = self.control.pop().unwrap();
627 self.local_inits.pop_ctrl(frame.init_height);
628 }
629 Some(frame) => {
630 self.control.push(*frame);
631 }
632 }
633 }
634
635 for idx in &rollback_log.inits {
636 self.local_inits.set_init(*idx);
637 }
638
639 if self.local_inits.height() > rollback_log.init_height {
640 self.local_inits.pop_ctrl(rollback_log.init_height);
641 }
642
643 self.transaction.end();
644 }
645}
646
647impl<R> Deref for OperatorValidatorTemp<'_, '_, R> {
648 type Target = OperatorValidator;
649 fn deref(&self) -> &OperatorValidator {
650 self.inner
651 }
652}
653
654impl<R> DerefMut for OperatorValidatorTemp<'_, '_, R> {
655 fn deref_mut(&mut self) -> &mut OperatorValidator {
656 self.inner
657 }
658}
659
660impl<'resources, R> OperatorValidatorTemp<'_, 'resources, R>
661where
662 R: WasmModuleResources,
663{
664 fn push_operand<T>(&mut self, ty: T) -> Result<()>
670 where
671 T: Into<MaybeType>,
672 {
673 let maybe_ty = ty.into();
674
675 if cfg!(debug_assertions) {
676 match maybe_ty {
677 MaybeType::Known(ValType::Ref(r)) => match r.heap_type() {
678 HeapType::Concrete(index) | HeapType::Exact(index) => {
679 debug_assert!(
680 matches!(index, UnpackedIndex::Id(_)),
681 "only ref types referencing `CoreTypeId`s can \
682 be pushed to the operand stack"
683 );
684 }
685 _ => {}
686 },
687 _ => {}
688 }
689 }
690
691 self.operands.push(maybe_ty);
692 self.record_push();
693 Ok(())
694 }
695
696 fn push_concrete_ref(&mut self, nullable: bool, type_index: u32) -> Result<()> {
697 let mut heap_ty = HeapType::Concrete(UnpackedIndex::Module(type_index));
698
699 self.resources.check_heap_type(&mut heap_ty, self.offset)?;
701 debug_assert!(matches!(heap_ty, HeapType::Concrete(UnpackedIndex::Id(_))));
702
703 let ref_ty = RefType::new(nullable, heap_ty).ok_or_else(|| {
704 format_err!(self.offset, "implementation limit: type index too large")
705 })?;
706
707 self.push_operand(ref_ty)
708 }
709
710 fn push_exact_ref(&mut self, nullable: bool, type_index: u32) -> Result<()> {
711 let mut heap_ty = HeapType::Exact(UnpackedIndex::Module(type_index));
712
713 self.resources.check_heap_type(&mut heap_ty, self.offset)?;
715 debug_assert!(matches!(heap_ty, HeapType::Exact(UnpackedIndex::Id(_))));
716
717 let ref_ty = RefType::new(nullable, heap_ty).ok_or_else(|| {
718 format_err!(self.offset, "implementation limit: type index too large")
719 })?;
720
721 self.push_operand(ref_ty)
722 }
723
724 fn push_exact_ref_if_available(&mut self, nullable: bool, type_index: u32) -> Result<()> {
725 if self.features.custom_descriptors() {
726 self.push_exact_ref(nullable, type_index)
727 } else {
728 self.push_concrete_ref(nullable, type_index)
729 }
730 }
731
732 fn pop_concrete_ref(&mut self, nullable: bool, type_index: u32) -> Result<MaybeType> {
733 let mut heap_ty = HeapType::Concrete(UnpackedIndex::Module(type_index));
734
735 self.resources.check_heap_type(&mut heap_ty, self.offset)?;
737 debug_assert!(matches!(heap_ty, HeapType::Concrete(UnpackedIndex::Id(_))));
738
739 let ref_ty = RefType::new(nullable, heap_ty).ok_or_else(|| {
740 format_err!(self.offset, "implementation limit: type index too large")
741 })?;
742
743 self.pop_operand(Some(ref_ty.into()))
744 }
745
746 fn pop_concrete_or_exact_ref(
747 &mut self,
748 nullable: bool,
749 type_index: u32,
750 ) -> Result<(MaybeType, bool)> {
751 let ty = self.pop_concrete_ref(nullable, type_index)?;
752 let is_exact = match ty {
753 MaybeType::Known(ValType::Ref(rt)) if rt.is_exact_type_ref() || rt.is_none_ref() => {
754 let mut heap_ty = HeapType::Exact(UnpackedIndex::Module(type_index));
755 self.resources.check_heap_type(&mut heap_ty, self.offset)?;
756 let expected = RefType::new(nullable, heap_ty).ok_or_else(|| {
757 format_err!(self.offset, "implementation limit: type index too large")
758 })?;
759 self.resources.is_subtype(rt.into(), expected.into())
760 }
761 MaybeType::Bottom => true,
762 _ => false,
763 };
764 Ok((ty, is_exact))
765 }
766
767 fn pop_push_label_types(
770 &mut self,
771 label_types: impl PreciseIterator<Item = ValType>,
772 ) -> Result<()> {
773 for ty in label_types.clone().rev() {
774 self.pop_operand(Some(ty))?;
775 }
776 for ty in label_types {
777 self.push_operand(ty)?;
778 }
779 Ok(())
780 }
781
782 fn pop_operand(&mut self, expected: Option<ValType>) -> Result<MaybeType> {
801 let popped = match self.operands.pop() {
813 Some(MaybeType::Known(actual_ty)) => {
814 if Some(actual_ty) == expected {
815 if let Some(control) = self.control.last() {
816 if self.operands.len() >= control.height {
817 self.record_pop(MaybeType::Known(actual_ty));
818 return Ok(MaybeType::Known(actual_ty));
819 }
820 }
821 }
822 Some(MaybeType::Known(actual_ty))
823 }
824 other => other,
825 };
826
827 self._pop_operand(expected, popped)
828 }
829
830 #[cold]
834 fn _pop_operand(
835 &mut self,
836 expected: Option<ValType>,
837 popped: Option<MaybeType>,
838 ) -> Result<MaybeType> {
839 self.operands.extend(popped);
840 let control = self.control.last().unwrap();
841 let actual = if self.operands.len() == control.height && control.unreachable {
842 self.record_any_pop();
843 MaybeType::Bottom
844 } else {
845 if self.operands.len() == control.height {
846 let desc = match expected {
847 Some(ty) => ty_to_str(ty),
848 None => "a type".into(),
849 };
850 bail!(
851 self.offset,
852 "type mismatch: expected {desc} but nothing on stack"
853 )
854 } else {
855 let ty = self.operands.pop().unwrap();
856 self.record_pop(ty);
857 ty
858 }
859 };
860 if let Some(expected) = expected {
861 match (actual, expected) {
862 (MaybeType::Bottom, _) => {}
864
865 (MaybeType::UnknownRef(actual_ty), ValType::Ref(expected)) => {
870 if let Some(actual) = actual_ty {
871 let expected_shared = self.resources.is_shared(expected);
872 let actual = RefType::new(
873 false,
874 HeapType::Abstract {
875 shared: expected_shared,
876 ty: actual,
877 },
878 )
879 .unwrap();
880 if !self.resources.is_subtype(actual.into(), expected.into()) {
881 bail!(
882 self.offset,
883 "type mismatch: expected {}, found {}",
884 ty_to_str(expected.into()),
885 ty_to_str(actual.into())
886 );
887 }
888 }
889 }
890
891 (MaybeType::Known(actual), expected) => {
894 if !self.resources.is_subtype(actual, expected) {
895 bail!(
896 self.offset,
897 "type mismatch: expected {}, found {}",
898 ty_to_str(expected),
899 ty_to_str(actual)
900 );
901 }
902 }
903
904 (
906 MaybeType::UnknownRef(..),
907 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128,
908 ) => {
909 bail!(
910 self.offset,
911 "type mismatch: expected {}, found heap type",
912 ty_to_str(expected)
913 )
914 }
915 }
916 }
917 Ok(actual)
918 }
919
920 fn match_operand(
922 &mut self,
923 actual: ValType,
924 expected: ValType,
925 ) -> Result<(), BinaryReaderError> {
926 self.push_operand(actual)?;
927 self.pop_operand(Some(expected))?;
928 Ok(())
929 }
930
931 fn match_stack_operands(
933 &mut self,
934 expected_tys: impl PreciseIterator<Item = ValType> + 'resources,
935 ) -> Result<()> {
936 let mut popped_types_tmp = mem::take(&mut self.popped_types_tmp);
937 debug_assert!(popped_types_tmp.is_empty());
938 popped_types_tmp.reserve(expected_tys.len());
939
940 for expected_ty in expected_tys.rev() {
941 let actual_ty = self.pop_operand(Some(expected_ty))?;
942 popped_types_tmp.push(actual_ty);
943 }
944 for ty in popped_types_tmp.drain(..).rev() {
945 self.push_operand(ty)?;
946 }
947
948 debug_assert!(self.popped_types_tmp.is_empty());
949 self.popped_types_tmp = popped_types_tmp;
950 Ok(())
951 }
952
953 fn pop_ref(&mut self, expected: Option<RefType>) -> Result<MaybeType<RefType>> {
955 match self.pop_operand(expected.map(|t| t.into()))? {
956 MaybeType::Bottom => Ok(MaybeType::UnknownRef(None)),
957 MaybeType::UnknownRef(ty) => Ok(MaybeType::UnknownRef(ty)),
958 MaybeType::Known(ValType::Ref(rt)) => Ok(MaybeType::Known(rt)),
959 MaybeType::Known(ty) => bail!(
960 self.offset,
961 "type mismatch: expected ref but found {}",
962 ty_to_str(ty)
963 ),
964 }
965 }
966
967 fn pop_maybe_shared_ref(&mut self, expected: AbstractHeapType) -> Result<MaybeType<RefType>> {
973 let actual = match self.pop_ref(None)? {
974 MaybeType::Bottom => return Ok(MaybeType::Bottom),
975 MaybeType::UnknownRef(None) => return Ok(MaybeType::UnknownRef(None)),
976 MaybeType::UnknownRef(Some(actual)) => {
977 if !actual.is_subtype_of(expected) {
978 bail!(
979 self.offset,
980 "type mismatch: expected subtype of {}, found {}",
981 expected.as_str(false),
982 actual.as_str(false),
983 )
984 }
985 return Ok(MaybeType::UnknownRef(Some(actual)));
986 }
987 MaybeType::Known(ty) => ty,
988 };
989 let is_actual_shared = self.resources.is_shared(actual);
992 let expected = RefType::new(
993 true,
994 HeapType::Abstract {
995 shared: is_actual_shared,
996 ty: expected,
997 },
998 )
999 .unwrap();
1000
1001 if !self.resources.is_subtype(actual.into(), expected.into()) {
1005 bail!(
1006 self.offset,
1007 "type mismatch: expected subtype of {expected}, found {actual}",
1008 )
1009 }
1010 Ok(MaybeType::Known(actual))
1011 }
1012
1013 fn local(&self, idx: u32) -> Result<ValType> {
1016 match self.locals.get(idx) {
1017 Some(ty) => Ok(ty),
1018 None => bail!(
1019 self.offset,
1020 "unknown local {}: local index out of bounds",
1021 idx
1022 ),
1023 }
1024 }
1025
1026 fn unreachable(&mut self) -> Result<()> {
1029 if !self.control.last().unwrap().unreachable {
1030 self.transaction.map(|log| log.set_unreachable());
1031 }
1032
1033 let control = self.control.last_mut().unwrap();
1034 control.unreachable = true;
1035 let new_height = control.height;
1036
1037 let operands = self.operands.split_off(new_height);
1038 self.transaction.map(|log| {
1039 for op in operands.iter().rev() {
1040 log.record_pop(*op);
1041 }
1042 });
1043
1044 self.operands.truncate(new_height);
1045 Ok(())
1046 }
1047
1048 fn push_ctrl(&mut self, kind: FrameKind, ty: BlockType) -> Result<()> {
1055 self.push_bare_ctrl(kind, ty);
1056 for ty in self.params(ty)? {
1059 self.push_operand(ty)?;
1060 }
1061 Ok(())
1062 }
1063
1064 fn push_bare_ctrl(&mut self, kind: FrameKind, ty: BlockType) {
1067 let height = self.operands.len();
1070 let init_height = self.local_inits.height();
1071 self.control.push(Frame {
1072 kind,
1073 block_type: ty,
1074 height,
1075 unreachable: false,
1076 init_height,
1077 });
1078 self.transaction.map(|log| log.push_ctrl());
1079 }
1080
1081 fn pop_ctrl(&mut self) -> Result<Frame> {
1086 let frame = self.control.last().unwrap();
1089 let ty = frame.block_type;
1090 let height = frame.height;
1091
1092 for ty in self.results(ty)?.rev() {
1095 self.pop_operand(Some(ty))?;
1096 }
1097
1098 if self.operands.len() != height {
1101 bail!(
1102 self.offset,
1103 "type mismatch: values remaining on stack at end of block"
1104 );
1105 }
1106
1107 let frame = self.control.pop().unwrap();
1109 let _inits = self.local_inits.pop_ctrl(frame.init_height);
1110 self.transaction.map(|log| log.pop_ctrl(frame, _inits));
1111
1112 Ok(frame)
1113 }
1114
1115 fn jump(&self, depth: u32) -> Result<(BlockType, FrameKind)> {
1120 match self.inner.jump(depth) {
1121 Some(tup) => Ok(tup),
1122 None => bail!(self.offset, "unknown label: branch depth too large"),
1123 }
1124 }
1125
1126 fn check_memory_index(&self, memory_index: u32) -> Result<ValType> {
1129 match self.resources.memory_at(memory_index) {
1130 Some(mem) => Ok(mem.index_type()),
1131 None => bail!(self.offset, "unknown memory {}", memory_index),
1132 }
1133 }
1134
1135 fn check_memarg(&self, memarg: MemArg) -> Result<ValType> {
1138 let index_ty = self.check_memory_index(memarg.memory)?;
1139 if memarg.align > memarg.max_align {
1140 bail!(
1141 self.offset,
1142 "invalid memop alignment: alignment must not be larger than natural"
1143 );
1144 }
1145 if index_ty == ValType::I32 && memarg.offset > u64::from(u32::MAX) {
1146 bail!(self.offset, "offset out of range: must be <= 2**32");
1147 }
1148 Ok(index_ty)
1149 }
1150
1151 fn check_floats_enabled(&self) -> Result<()> {
1152 if !self.features.floats() {
1153 bail!(self.offset, "floating-point instruction disallowed");
1154 }
1155 Ok(())
1156 }
1157
1158 fn check_shared_memarg(&self, memarg: MemArg) -> Result<ValType> {
1159 if memarg.align != memarg.max_align {
1160 bail!(
1161 self.offset,
1162 "atomic instructions must always specify maximum alignment"
1163 );
1164 }
1165 self.check_memarg(memarg)
1166 }
1167
1168 fn check_block_type(&self, ty: &mut BlockType) -> Result<()> {
1170 match ty {
1171 BlockType::Empty => Ok(()),
1172 BlockType::Type(t) => self
1173 .resources
1174 .check_value_type(t, &self.features, self.offset),
1175 BlockType::FuncType(idx) => {
1176 if !self.features.multi_value() {
1177 bail!(
1178 self.offset,
1179 "blocks, loops, and ifs may only produce a resulttype \
1180 when multi-value is not enabled",
1181 );
1182 }
1183 self.func_type_at(*idx)?;
1184 Ok(())
1185 }
1186 }
1187 }
1188
1189 fn type_of_function(&self, function_index: u32) -> Result<&'resources FuncType> {
1192 if let Some(type_index) = self.resources.type_index_of_function(function_index) {
1193 self.func_type_at(type_index)
1194 } else {
1195 bail!(
1196 self.offset,
1197 "unknown function {function_index}: function index out of bounds",
1198 )
1199 }
1200 }
1201
1202 fn check_call_ty(&mut self, ty: &FuncType) -> Result<()> {
1208 for &ty in ty.params().iter().rev() {
1209 debug_assert_type_indices_are_ids(ty);
1210 self.pop_operand(Some(ty))?;
1211 }
1212 for &ty in ty.results() {
1213 debug_assert_type_indices_are_ids(ty);
1214 self.push_operand(ty)?;
1215 }
1216 Ok(())
1217 }
1218
1219 fn check_return_call_ty(&mut self, ty: &FuncType) -> Result<()> {
1221 self.check_func_type_same_results(ty)?;
1222 for &ty in ty.params().iter().rev() {
1223 debug_assert_type_indices_are_ids(ty);
1224 self.pop_operand(Some(ty))?;
1225 }
1226
1227 for &ty in ty.results() {
1229 debug_assert_type_indices_are_ids(ty);
1230 self.push_operand(ty)?;
1231 }
1232 self.check_return()?;
1233
1234 Ok(())
1235 }
1236
1237 fn check_call_ref_ty(&mut self, type_index: u32) -> Result<&'resources FuncType> {
1245 let unpacked_index = UnpackedIndex::Module(type_index);
1246 let mut hty = HeapType::Concrete(unpacked_index);
1247 self.resources.check_heap_type(&mut hty, self.offset)?;
1248 let expected = RefType::new(true, hty).expect("hty should be previously validated");
1249 self.pop_ref(Some(expected))?;
1250 self.func_type_at(type_index)
1251 }
1252
1253 fn check_call_indirect_ty(
1263 &mut self,
1264 type_index: u32,
1265 table_index: u32,
1266 ) -> Result<&'resources FuncType> {
1267 let tab = self.table_type_at(table_index)?;
1268 if !self
1269 .resources
1270 .is_subtype(ValType::Ref(tab.element_type), ValType::FUNCREF)
1271 {
1272 bail!(
1273 self.offset,
1274 "type mismatch: indirect calls must go through a table with type <= funcref",
1275 );
1276 }
1277 self.pop_operand(Some(tab.index_type()))?;
1278 self.func_type_at(type_index)
1279 }
1280
1281 fn check_return(&mut self) -> Result<()> {
1284 assert!(!self.control.is_empty());
1285 for ty in self.results(self.control[0].block_type)?.rev() {
1286 self.pop_operand(Some(ty))?;
1287 }
1288 self.unreachable()?;
1289 Ok(())
1290 }
1291
1292 fn check_func_type_same_results(&self, callee_ty: &FuncType) -> Result<()> {
1295 assert!(!self.control.is_empty());
1296 let caller_rets = self.results(self.control[0].block_type)?;
1297 if callee_ty.results().len() != caller_rets.len()
1298 || !caller_rets
1299 .zip(callee_ty.results())
1300 .all(|(caller_ty, callee_ty)| self.resources.is_subtype(*callee_ty, caller_ty))
1301 {
1302 let caller_rets = self
1303 .results(self.control[0].block_type)?
1304 .map(|ty| format!("{ty}"))
1305 .collect::<Vec<_>>()
1306 .join(" ");
1307 let callee_rets = callee_ty
1308 .results()
1309 .iter()
1310 .map(|ty| format!("{ty}"))
1311 .collect::<Vec<_>>()
1312 .join(" ");
1313 bail!(
1314 self.offset,
1315 "type mismatch: current function requires result type \
1316 [{caller_rets}] but callee returns [{callee_rets}]"
1317 );
1318 }
1319 Ok(())
1320 }
1321
1322 fn check_cmp_op(&mut self, ty: ValType) -> Result<()> {
1324 self.pop_operand(Some(ty))?;
1325 self.pop_operand(Some(ty))?;
1326 self.push_operand(ValType::I32)?;
1327 Ok(())
1328 }
1329
1330 fn check_fcmp_op(&mut self, ty: ValType) -> Result<()> {
1332 debug_assert!(matches!(ty, ValType::F32 | ValType::F64));
1333 self.check_floats_enabled()?;
1334 self.check_cmp_op(ty)
1335 }
1336
1337 fn check_unary_op(&mut self, ty: ValType) -> Result<()> {
1339 self.pop_operand(Some(ty))?;
1340 self.push_operand(ty)?;
1341 Ok(())
1342 }
1343
1344 fn check_funary_op(&mut self, ty: ValType) -> Result<()> {
1346 debug_assert!(matches!(ty, ValType::F32 | ValType::F64));
1347 self.check_floats_enabled()?;
1348 self.check_unary_op(ty)
1349 }
1350
1351 fn check_conversion_op(&mut self, into: ValType, from: ValType) -> Result<()> {
1353 self.pop_operand(Some(from))?;
1354 self.push_operand(into)?;
1355 Ok(())
1356 }
1357
1358 fn check_fconversion_op(&mut self, into: ValType, from: ValType) -> Result<()> {
1360 debug_assert!(matches!(into, ValType::F32 | ValType::F64));
1361 self.check_floats_enabled()?;
1362 self.check_conversion_op(into, from)
1363 }
1364
1365 fn check_binary_op(&mut self, ty: ValType) -> Result<()> {
1367 self.pop_operand(Some(ty))?;
1368 self.pop_operand(Some(ty))?;
1369 self.push_operand(ty)?;
1370 Ok(())
1371 }
1372
1373 fn check_fbinary_op(&mut self, ty: ValType) -> Result<()> {
1375 debug_assert!(matches!(ty, ValType::F32 | ValType::F64));
1376 self.check_floats_enabled()?;
1377 self.check_binary_op(ty)
1378 }
1379
1380 fn check_atomic_load(&mut self, memarg: MemArg, load_ty: ValType) -> Result<()> {
1382 let ty = self.check_shared_memarg(memarg)?;
1383 self.pop_operand(Some(ty))?;
1384 self.push_operand(load_ty)?;
1385 Ok(())
1386 }
1387
1388 fn check_atomic_store(&mut self, memarg: MemArg, store_ty: ValType) -> Result<()> {
1390 let ty = self.check_shared_memarg(memarg)?;
1391 self.pop_operand(Some(store_ty))?;
1392 self.pop_operand(Some(ty))?;
1393 Ok(())
1394 }
1395
1396 fn check_atomic_binary_memory_op(&mut self, memarg: MemArg, op_ty: ValType) -> Result<()> {
1398 let ty = self.check_shared_memarg(memarg)?;
1399 self.pop_operand(Some(op_ty))?;
1400 self.pop_operand(Some(ty))?;
1401 self.push_operand(op_ty)?;
1402 Ok(())
1403 }
1404
1405 fn check_atomic_binary_memory_cmpxchg(&mut self, memarg: MemArg, op_ty: ValType) -> Result<()> {
1407 let ty = self.check_shared_memarg(memarg)?;
1408 self.pop_operand(Some(op_ty))?;
1409 self.pop_operand(Some(op_ty))?;
1410 self.pop_operand(Some(ty))?;
1411 self.push_operand(op_ty)?;
1412 Ok(())
1413 }
1414
1415 fn check_downcast(&mut self, nullable: bool, mut heap_type: HeapType) -> Result<RefType> {
1418 self.resources
1419 .check_heap_type(&mut heap_type, self.offset)?;
1420
1421 let sub_ty = RefType::new(nullable, heap_type).ok_or_else(|| {
1422 BinaryReaderError::new("implementation limit: type index too large", self.offset)
1423 })?;
1424 let top = self.resources.top_type(&heap_type);
1425 self.check_cast_to_allowed(top)?;
1426 let sup_ty = RefType::new(true, top).expect("can't panic with non-concrete heap types");
1427
1428 self.pop_ref(Some(sup_ty))?;
1429 Ok(sub_ty)
1430 }
1431
1432 fn check_cast_to_allowed(&self, ty: HeapType) -> Result<()> {
1433 let top = self.resources.top_type(&ty);
1434 if matches!(
1435 top,
1436 HeapType::Abstract {
1437 ty: AbstractHeapType::Cont,
1438 ..
1439 }
1440 ) {
1441 bail!(
1442 self.offset,
1443 "invalid cast: cannot cast to a continuation type"
1444 );
1445 }
1446 Ok(())
1447 }
1448
1449 fn check_ref_test(&mut self, nullable: bool, heap_type: HeapType) -> Result<()> {
1452 self.check_downcast(nullable, heap_type)?;
1453 self.push_operand(ValType::I32)
1454 }
1455
1456 fn check_ref_cast(&mut self, nullable: bool, heap_type: HeapType) -> Result<()> {
1459 let sub_ty = self.check_downcast(nullable, heap_type)?;
1460 self.push_operand(sub_ty)
1461 }
1462
1463 fn check_br_on_cast_type_hierarchy(
1465 &self,
1466 from_ref_type: RefType,
1467 to_ref_type: RefType,
1468 ) -> Result<()> {
1469 if self.features.custom_descriptors() {
1470 let from_ref_type_top = self.resources.top_type(&from_ref_type.heap_type());
1475 let to_ref_type_top = self.resources.top_type(&to_ref_type.heap_type());
1476 if from_ref_type_top != to_ref_type_top {
1477 bail!(
1478 self.offset,
1479 "type mismatch: {from_ref_type} and {to_ref_type} have different heap type hierarchies"
1480 );
1481 }
1482 return Ok(());
1483 }
1484
1485 self.check_cast_to_allowed(to_ref_type.heap_type())?;
1486
1487 if !self
1488 .resources
1489 .is_subtype(to_ref_type.into(), from_ref_type.into())
1490 {
1491 bail!(
1492 self.offset,
1493 "type mismatch: expected {from_ref_type}, found {to_ref_type}"
1494 );
1495 }
1496 Ok(())
1497 }
1498
1499 fn check_descriptor(&self, heap_type: HeapType) -> Result<u32> {
1501 Ok(match heap_type {
1502 HeapType::Exact(idx) | HeapType::Concrete(idx) => {
1503 if let Some(descriptor_idx) = self
1504 .sub_type_at(idx.as_module_index().unwrap())?
1505 .composite_type
1506 .descriptor_idx
1507 {
1508 u32::try_from(crate::validator::types::TypeIdentifier::index(
1509 &descriptor_idx.as_core_type_id().unwrap(),
1510 ))
1511 .unwrap()
1512 } else {
1513 bail!(self.offset, "cast target must have descriptor")
1514 }
1515 }
1516 _ => bail!(self.offset, "unexpected heap type"),
1517 })
1518 }
1519
1520 fn check_maybe_exact_descriptor_ref(&mut self, heap_type: HeapType) -> Result<bool> {
1521 let descriptor_idx = self.check_descriptor(heap_type)?;
1522 let (ty, _is_exact) = self.pop_concrete_or_exact_ref(true, descriptor_idx)?;
1523 let is_exact = if let HeapType::Exact(_) = heap_type {
1524 let mut descriptor_ty = HeapType::Exact(UnpackedIndex::Module(descriptor_idx));
1525 self.resources
1526 .check_heap_type(&mut descriptor_ty, self.offset)?;
1527 let descriptor_ty = ValType::Ref(
1528 RefType::new(true, descriptor_ty)
1529 .expect("existing heap types should be within our limits"),
1530 );
1531
1532 match ty {
1533 MaybeType::Known(actual) if !self.resources.is_subtype(actual, descriptor_ty) => {
1534 bail!(
1535 self.offset,
1536 "type mismatch: expected descriptor of exact type {descriptor_ty} found {actual}",
1537 );
1538 }
1539 _ => (),
1540 }
1541 true
1542 } else {
1543 false
1544 };
1545 Ok(is_exact)
1546 }
1547
1548 fn check_ref_cast_desc_eq(&mut self, nullable: bool, heap_type: HeapType) -> Result<()> {
1551 let is_exact = self.check_maybe_exact_descriptor_ref(heap_type)?;
1552
1553 self.check_downcast(nullable, heap_type)?;
1554
1555 let idx = {
1556 let mut heap_type = heap_type;
1557 self.resources
1558 .check_heap_type(&mut heap_type, self.offset)?;
1559 match heap_type {
1560 HeapType::Concrete(index) | HeapType::Exact(index) => {
1561 index.pack().ok_or_else(|| {
1562 BinaryReaderError::new(
1563 "implementation limit: type index too large",
1564 self.offset,
1565 )
1566 })?
1567 }
1568 _ => panic!(),
1569 }
1570 };
1571
1572 self.push_operand(if is_exact {
1573 RefType::exact(nullable, idx)
1574 } else {
1575 RefType::concrete(nullable, idx)
1576 })
1577 }
1578
1579 fn check_atomic_global_rmw_ty(&self, global_index: u32) -> Result<ValType> {
1582 let global = self.global_type_at(global_index)?;
1583 if !global.mutable {
1584 bail!(
1585 self.offset,
1586 "global is immutable: cannot modify it with `global.atomic.rmw.*`"
1587 );
1588 }
1589 let ty = global.content_type;
1590 if !(ty == ValType::I32 || ty == ValType::I64) {
1591 bail!(
1592 self.offset,
1593 "invalid type: `global.atomic.rmw.*` only allows `i32` and `i64`"
1594 );
1595 }
1596 Ok(ty)
1597 }
1598
1599 fn check_struct_atomic_rmw(
1602 &mut self,
1603 op: &'static str,
1604 struct_type_index: u32,
1605 field_index: u32,
1606 ) -> Result<()> {
1607 let field = self.mutable_struct_field_at(struct_type_index, field_index)?;
1608 let field_ty = match field.element_type {
1609 StorageType::Val(ValType::I32) => ValType::I32,
1610 StorageType::Val(ValType::I64) => ValType::I64,
1611 _ => bail!(
1612 self.offset,
1613 "invalid type: `struct.atomic.rmw.{}` only allows `i32` and `i64`",
1614 op
1615 ),
1616 };
1617 self.pop_operand(Some(field_ty))?;
1618 self.pop_concrete_ref(true, struct_type_index)?;
1619 self.push_operand(field_ty)?;
1620 Ok(())
1621 }
1622
1623 fn check_array_atomic_rmw(&mut self, op: &'static str, type_index: u32) -> Result<()> {
1626 let field = self.mutable_array_type_at(type_index)?;
1627 let elem_ty = match field.element_type {
1628 StorageType::Val(ValType::I32) => ValType::I32,
1629 StorageType::Val(ValType::I64) => ValType::I64,
1630 _ => bail!(
1631 self.offset,
1632 "invalid type: `array.atomic.rmw.{}` only allows `i32` and `i64`",
1633 op
1634 ),
1635 };
1636 self.pop_operand(Some(elem_ty))?;
1637 self.pop_operand(Some(ValType::I32))?;
1638 self.pop_concrete_ref(true, type_index)?;
1639 self.push_operand(elem_ty)?;
1640 Ok(())
1641 }
1642
1643 fn element_type_at(&self, elem_index: u32) -> Result<RefType> {
1644 match self.resources.element_type_at(elem_index) {
1645 Some(ty) => Ok(ty),
1646 None => bail!(
1647 self.offset,
1648 "unknown elem segment {}: segment index out of bounds",
1649 elem_index
1650 ),
1651 }
1652 }
1653
1654 fn sub_type_at(&self, at: u32) -> Result<&'resources SubType> {
1655 self.resources
1656 .sub_type_at(at)
1657 .ok_or_else(|| format_err!(self.offset, "unknown type: type index out of bounds"))
1658 }
1659
1660 fn struct_type_at(&self, at: u32) -> Result<&'resources StructType> {
1661 let sub_ty = self.sub_type_at(at)?;
1662 if let CompositeInnerType::Struct(struct_ty) = &sub_ty.composite_type.inner {
1663 if self.inner.shared && !sub_ty.composite_type.shared {
1664 bail!(
1665 self.offset,
1666 "shared functions cannot access unshared structs",
1667 );
1668 }
1669 Ok(struct_ty)
1670 } else {
1671 bail!(
1672 self.offset,
1673 "expected struct type at index {at}, found {sub_ty}"
1674 )
1675 }
1676 }
1677
1678 fn struct_field_at(&self, struct_type_index: u32, field_index: u32) -> Result<FieldType> {
1679 let field_index = usize::try_from(field_index).map_err(|_| {
1680 BinaryReaderError::new("unknown field: field index out of bounds", self.offset)
1681 })?;
1682 self.struct_type_at(struct_type_index)?
1683 .fields
1684 .get(field_index)
1685 .copied()
1686 .ok_or_else(|| {
1687 BinaryReaderError::new("unknown field: field index out of bounds", self.offset)
1688 })
1689 }
1690
1691 fn mutable_struct_field_at(
1692 &self,
1693 struct_type_index: u32,
1694 field_index: u32,
1695 ) -> Result<FieldType> {
1696 let field = self.struct_field_at(struct_type_index, field_index)?;
1697 if !field.mutable {
1698 bail!(
1699 self.offset,
1700 "invalid struct modification: struct field is immutable"
1701 )
1702 }
1703 Ok(field)
1704 }
1705
1706 fn array_type_at(&self, at: u32) -> Result<FieldType> {
1707 let sub_ty = self.sub_type_at(at)?;
1708 if let CompositeInnerType::Array(array_ty) = &sub_ty.composite_type.inner {
1709 if self.inner.shared && !sub_ty.composite_type.shared {
1710 bail!(
1711 self.offset,
1712 "shared functions cannot access unshared arrays",
1713 );
1714 }
1715 Ok(array_ty.0)
1716 } else {
1717 bail!(
1718 self.offset,
1719 "expected array type at index {at}, found {sub_ty}"
1720 )
1721 }
1722 }
1723
1724 fn mutable_array_type_at(&self, at: u32) -> Result<FieldType> {
1725 let field = self.array_type_at(at)?;
1726 if !field.mutable {
1727 bail!(
1728 self.offset,
1729 "invalid array modification: array is immutable"
1730 )
1731 }
1732 Ok(field)
1733 }
1734
1735 fn func_type_at(&self, at: u32) -> Result<&'resources FuncType> {
1736 let sub_ty = self.sub_type_at(at)?;
1737 if let CompositeInnerType::Func(func_ty) = &sub_ty.composite_type.inner {
1738 if self.inner.shared && !sub_ty.composite_type.shared {
1739 bail!(
1740 self.offset,
1741 "shared functions cannot access unshared functions",
1742 );
1743 }
1744 Ok(func_ty)
1745 } else {
1746 bail!(
1747 self.offset,
1748 "expected func type at index {at}, found {sub_ty}"
1749 )
1750 }
1751 }
1752
1753 fn cont_type_at(&self, at: u32) -> Result<&ContType> {
1754 let sub_ty = self.sub_type_at(at)?;
1755 if let CompositeInnerType::Cont(cont_ty) = &sub_ty.composite_type.inner {
1756 if self.inner.shared && !sub_ty.composite_type.shared {
1757 bail!(
1758 self.offset,
1759 "shared continuations cannot access unshared continuations",
1760 );
1761 }
1762 Ok(cont_ty)
1763 } else {
1764 bail!(self.offset, "non-continuation type {at}",)
1765 }
1766 }
1767
1768 fn func_type_of_cont_type(&self, cont_ty: &ContType) -> &'resources FuncType {
1769 let func_id = cont_ty.0.as_core_type_id().expect("valid core type id");
1770 self.resources.sub_type_at_id(func_id).unwrap_func()
1771 }
1772
1773 fn tag_at(&self, at: u32) -> Result<&'resources FuncType> {
1774 self.resources
1775 .tag_at(at)
1776 .ok_or_else(|| format_err!(self.offset, "unknown tag {}: tag index out of bounds", at))
1777 }
1778
1779 fn exception_tag_at(&self, at: u32) -> Result<&'resources FuncType> {
1783 let func_ty = self.tag_at(at)?;
1784 if func_ty.results().len() != 0 {
1785 bail!(
1786 self.offset,
1787 "invalid exception type: non-empty tag result type"
1788 );
1789 }
1790 Ok(func_ty)
1791 }
1792
1793 fn global_type_at(&self, at: u32) -> Result<GlobalType> {
1794 if let Some(ty) = self.resources.global_at(at) {
1795 if self.inner.shared && !ty.shared {
1796 bail!(
1797 self.offset,
1798 "shared functions cannot access unshared globals",
1799 );
1800 }
1801 Ok(ty)
1802 } else {
1803 bail!(self.offset, "unknown global: global index out of bounds");
1804 }
1805 }
1806
1807 fn table_type_at(&self, table: u32) -> Result<TableType> {
1809 match self.resources.table_at(table) {
1810 Some(ty) => {
1811 if self.inner.shared && !ty.shared {
1812 bail!(
1813 self.offset,
1814 "shared functions cannot access unshared tables",
1815 );
1816 }
1817 Ok(ty)
1818 }
1819 None => bail!(
1820 self.offset,
1821 "unknown table {table}: table index out of bounds"
1822 ),
1823 }
1824 }
1825
1826 fn params(&self, ty: BlockType) -> Result<impl PreciseIterator<Item = ValType> + 'resources> {
1827 Ok(match ty {
1828 BlockType::Empty | BlockType::Type(_) => Either::B(None.into_iter()),
1829 BlockType::FuncType(t) => Either::A(self.func_type_at(t)?.params().iter().copied()),
1830 })
1831 }
1832
1833 fn results(&self, ty: BlockType) -> Result<impl PreciseIterator<Item = ValType> + 'resources> {
1834 Ok(match ty {
1835 BlockType::Empty => Either::B(None.into_iter()),
1836 BlockType::Type(t) => Either::B(Some(t).into_iter()),
1837 BlockType::FuncType(t) => Either::A(self.func_type_at(t)?.results().iter().copied()),
1838 })
1839 }
1840
1841 fn label_types(
1842 &self,
1843 ty: BlockType,
1844 kind: FrameKind,
1845 ) -> Result<impl PreciseIterator<Item = ValType> + 'resources> {
1846 Ok(match kind {
1847 FrameKind::Loop => Either::A(self.params(ty)?),
1848 _ => Either::B(self.results(ty)?),
1849 })
1850 }
1851
1852 fn check_data_segment(&self, data_index: u32) -> Result<()> {
1853 match self.resources.data_count() {
1854 None => bail!(self.offset, "data count section required"),
1855 Some(count) if data_index < count => Ok(()),
1856 Some(_) => bail!(self.offset, "unknown data segment {data_index}"),
1857 }
1858 }
1859
1860 fn check_resume_table(
1861 &mut self,
1862 table: ResumeTable,
1863 type_index: u32, ) -> Result<&'resources FuncType> {
1865 let cont_ty = self.cont_type_at(type_index)?;
1871 let old_func_ty = self.func_type_of_cont_type(cont_ty);
1872
1873 for handle in table.handlers {
1874 match handle {
1875 Handle::OnLabel { tag, label } => {
1876 let tag_ty = self.tag_at(tag)?;
1878 let (ty, kind) = self.jump(label)?;
1879
1880 let mut label_tys = self.label_types(ty, kind)?;
1882 let ct = match label_tys.next_back() {
1883 Some(ValType::Ref(rt)) if rt.is_concrete_type_ref() => rt,
1884 Some(ty) => bail!(self.offset, "type mismatch: {}", ty_to_str(ty)),
1885 None => bail!(
1886 self.offset,
1887 "type mismatch: instruction requires continuation reference type but label has none"
1888 ),
1889 };
1890 if tag_ty.params().len() != label_tys.len()
1892 || tag_ty
1893 .params()
1894 .iter()
1895 .copied()
1896 .zip(label_tys)
1897 .any(|(t1, t2)| !self.resources.is_subtype(t1, t2))
1898 {
1899 bail!(self.offset, "type mismatch between tag type and label type")
1900 }
1901
1902 let sub_ty = self.resources.sub_type_at_id(
1904 ct.type_index()
1905 .unwrap()
1906 .as_core_type_id()
1907 .expect("canonicalized index"),
1908 );
1909 let new_cont =
1910 if let CompositeInnerType::Cont(cont) = &sub_ty.composite_type.inner {
1911 cont
1912 } else {
1913 bail!(self.offset, "non-continuation type");
1914 };
1915 let new_func_ty = self.func_type_of_cont_type(&new_cont);
1916
1917 if !self.is_func_subtype(
1919 (tag_ty.results(), old_func_ty.results()),
1920 (new_func_ty.params(), new_func_ty.results()),
1921 ) {
1922 bail!(self.offset, "type mismatch in continuation type")
1923 }
1924 }
1925 Handle::OnSwitch { tag } => {
1926 let tag_ty = self.tag_at(tag)?;
1927 if !self.is_func_subtype(
1928 (tag_ty.params(), tag_ty.results()),
1929 (&[], old_func_ty.results()),
1930 ) {
1931 bail!(
1932 self.offset,
1933 "type mismatch: switch tag does not match continuation"
1934 )
1935 }
1936 }
1937 }
1938 }
1939 Ok(old_func_ty)
1940 }
1941
1942 fn is_func_subtype(
1944 &mut self,
1945 (p1, r1): (&[ValType], &[ValType]),
1946 (p2, r2): (&[ValType], &[ValType]),
1947 ) -> bool {
1948 p1.len() == p2.len()
1951 && p1
1952 .iter()
1953 .zip(p2.iter())
1954 .all(|(t1, t2)| self.resources.is_subtype(*t2, *t1))
1955 && r1.len() == r2.len()
1956 && r1
1957 .iter()
1958 .zip(r2.iter())
1959 .all(|(r1, r2)| self.resources.is_subtype(*r1, *r2))
1960 }
1961
1962 fn check_binop128(&mut self) -> Result<()> {
1963 self.pop_operand(Some(ValType::I64))?;
1964 self.pop_operand(Some(ValType::I64))?;
1965 self.pop_operand(Some(ValType::I64))?;
1966 self.pop_operand(Some(ValType::I64))?;
1967 self.push_operand(ValType::I64)?;
1968 self.push_operand(ValType::I64)?;
1969 Ok(())
1970 }
1971
1972 fn check_i64_mul_wide(&mut self) -> Result<()> {
1973 self.pop_operand(Some(ValType::I64))?;
1974 self.pop_operand(Some(ValType::I64))?;
1975 self.push_operand(ValType::I64)?;
1976 self.push_operand(ValType::I64)?;
1977 Ok(())
1978 }
1979
1980 fn check_enabled(&self, flag: bool, desc: &str) -> Result<()> {
1981 if flag {
1982 return Ok(());
1983 }
1984 bail!(self.offset, "{desc} support is not enabled");
1985 }
1986}
1987
1988pub fn ty_to_str(ty: ValType) -> &'static str {
1989 match ty {
1990 ValType::I32 => "i32",
1991 ValType::I64 => "i64",
1992 ValType::F32 => "f32",
1993 ValType::F64 => "f64",
1994 ValType::V128 => "v128",
1995 ValType::Ref(r) => r.wat(),
1996 }
1997}
1998
1999struct WasmProposalValidator<'validator, 'resources, T>(
2008 OperatorValidatorTemp<'validator, 'resources, T>,
2009);
2010
2011#[cfg_attr(not(feature = "simd"), allow(unused_macro_rules))]
2012macro_rules! validate_proposal {
2013 ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
2014 $(
2015 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Result<()> {
2016 validate_proposal!(validate self $proposal / $op);
2017 self.0.$visit($( $($arg),* )?)
2018 }
2019 )*
2020 };
2021
2022 (validate self mvp / $op:ident) => {};
2023
2024 (validate self $proposal:ident / MemoryFill) => {};
2028 (validate self $proposal:ident / MemoryCopy) => {};
2029
2030 (validate $self:ident $proposal:ident / $op:ident) => {
2031 $self.0.check_enabled($self.0.features.$proposal(), validate_proposal!(desc $proposal))?
2032 };
2033
2034 (desc simd) => ("SIMD");
2035 (desc relaxed_simd) => ("relaxed SIMD");
2036 (desc threads) => ("threads");
2037 (desc shared_everything_threads) => ("shared-everything-threads");
2038 (desc saturating_float_to_int) => ("saturating float to int conversions");
2039 (desc reference_types) => ("reference types");
2040 (desc bulk_memory) => ("bulk memory");
2041 (desc sign_extension) => ("sign extension operations");
2042 (desc exceptions) => ("exceptions");
2043 (desc tail_call) => ("tail calls");
2044 (desc function_references) => ("function references");
2045 (desc memory_control) => ("memory control");
2046 (desc gc) => ("gc");
2047 (desc legacy_exceptions) => ("legacy exceptions");
2048 (desc stack_switching) => ("stack switching");
2049 (desc wide_arithmetic) => ("wide arithmetic");
2050 (desc custom_descriptors) => ("custom descriptors operations");
2051}
2052
2053impl<'a, T> VisitOperator<'a> for WasmProposalValidator<'_, '_, T>
2054where
2055 T: WasmModuleResources,
2056{
2057 type Output = Result<()>;
2058
2059 #[cfg(feature = "simd")]
2060 fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
2061 Some(self)
2062 }
2063
2064 crate::for_each_visit_operator!(validate_proposal);
2065}
2066
2067#[cfg(feature = "simd")]
2068impl<'a, T> VisitSimdOperator<'a> for WasmProposalValidator<'_, '_, T>
2069where
2070 T: WasmModuleResources,
2071{
2072 crate::for_each_visit_simd_operator!(validate_proposal);
2073}
2074
2075#[track_caller]
2076#[inline]
2077fn debug_assert_type_indices_are_ids(ty: ValType) {
2078 if cfg!(debug_assertions) {
2079 if let ValType::Ref(r) = ty {
2080 if let HeapType::Concrete(idx) = r.heap_type() {
2081 debug_assert!(
2082 matches!(idx, UnpackedIndex::Id(_)),
2083 "type reference should be a `CoreTypeId`, found {idx:?}"
2084 );
2085 }
2086 }
2087 }
2088}
2089
2090impl<'a, T> VisitOperator<'a> for OperatorValidatorTemp<'_, '_, T>
2091where
2092 T: WasmModuleResources,
2093{
2094 type Output = Result<()>;
2095
2096 #[cfg(feature = "simd")]
2097 fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> {
2098 Some(self)
2099 }
2100
2101 fn visit_nop(&mut self) -> Self::Output {
2102 Ok(())
2103 }
2104 fn visit_unreachable(&mut self) -> Self::Output {
2105 self.unreachable()?;
2106 Ok(())
2107 }
2108 fn visit_block(&mut self, mut ty: BlockType) -> Self::Output {
2109 self.check_block_type(&mut ty)?;
2110 for ty in self.params(ty)?.rev() {
2111 self.pop_operand(Some(ty))?;
2112 }
2113 self.push_ctrl(FrameKind::Block, ty)?;
2114 Ok(())
2115 }
2116 fn visit_loop(&mut self, mut ty: BlockType) -> Self::Output {
2117 self.check_block_type(&mut ty)?;
2118 for ty in self.params(ty)?.rev() {
2119 self.pop_operand(Some(ty))?;
2120 }
2121 self.push_ctrl(FrameKind::Loop, ty)?;
2122 Ok(())
2123 }
2124 fn visit_if(&mut self, mut ty: BlockType) -> Self::Output {
2125 self.check_block_type(&mut ty)?;
2126 self.pop_operand(Some(ValType::I32))?;
2127 for ty in self.params(ty)?.rev() {
2128 self.pop_operand(Some(ty))?;
2129 }
2130 self.push_ctrl(FrameKind::If, ty)?;
2131 Ok(())
2132 }
2133 fn visit_else(&mut self) -> Self::Output {
2134 let frame = self.pop_ctrl()?;
2135 debug_assert_eq!(frame.kind, FrameKind::If); self.push_ctrl(FrameKind::Else, frame.block_type)?;
2137 Ok(())
2138 }
2139 fn visit_try_table(&mut self, mut ty: TryTable) -> Self::Output {
2140 self.check_block_type(&mut ty.ty)?;
2141 for ty in self.params(ty.ty)?.rev() {
2142 self.pop_operand(Some(ty))?;
2143 }
2144 let exn_type = ValType::from(RefType::EXN);
2145 for catch in ty.catches {
2146 match catch {
2147 Catch::One { tag, label } => {
2148 let tag = self.exception_tag_at(tag)?;
2149 let (ty, kind) = self.jump(label)?;
2150 let params = tag.params();
2151 let types = self.label_types(ty, kind)?;
2152 if params.len() != types.len() {
2153 bail!(
2154 self.offset,
2155 "type mismatch: catch label must have same number of types as tag"
2156 );
2157 }
2158 for (expected, actual) in types.zip(params) {
2159 self.match_operand(*actual, expected)?;
2160 }
2161 }
2162 Catch::OneRef { tag, label } => {
2163 let tag = self.exception_tag_at(tag)?;
2164 let (ty, kind) = self.jump(label)?;
2165 let tag_params = tag.params().iter().copied();
2166 let label_types = self.label_types(ty, kind)?;
2167 if tag_params.len() + 1 != label_types.len() {
2168 bail!(
2169 self.offset,
2170 "type mismatch: catch_ref label must have one \
2171 more type than tag types",
2172 );
2173 }
2174 for (expected_label_type, actual_tag_param) in
2175 label_types.zip(tag_params.chain([exn_type]))
2176 {
2177 self.match_operand(actual_tag_param, expected_label_type)?;
2178 }
2179 }
2180
2181 Catch::All { label } => {
2182 let (ty, kind) = self.jump(label)?;
2183 if self.label_types(ty, kind)?.len() != 0 {
2184 bail!(
2185 self.offset,
2186 "type mismatch: catch_all label must have no result types"
2187 );
2188 }
2189 }
2190
2191 Catch::AllRef { label } => {
2192 let (ty, kind) = self.jump(label)?;
2193 let mut types = self.label_types(ty, kind)?;
2194 let ty = match (types.next(), types.next()) {
2195 (Some(ty), None) => ty,
2196 _ => {
2197 bail!(
2198 self.offset,
2199 "type mismatch: catch_all_ref label must have \
2200 exactly one result type"
2201 );
2202 }
2203 };
2204 if !self.resources.is_subtype(exn_type, ty) {
2205 bail!(
2206 self.offset,
2207 "type mismatch: catch_all_ref label must a \
2208 subtype of (ref exn)"
2209 );
2210 }
2211 }
2212 }
2213 }
2214 self.push_ctrl(FrameKind::TryTable, ty.ty)?;
2215 Ok(())
2216 }
2217 fn visit_throw(&mut self, index: u32) -> Self::Output {
2218 let ty = self.exception_tag_at(index)?;
2220 for ty in ty.clone().params().iter().rev() {
2221 self.pop_operand(Some(*ty))?;
2222 }
2223 debug_assert!(ty.results().is_empty());
2225 self.unreachable()?;
2226 Ok(())
2227 }
2228 fn visit_throw_ref(&mut self) -> Self::Output {
2229 self.pop_operand(Some(ValType::EXNREF))?;
2230 self.unreachable()?;
2231 Ok(())
2232 }
2233 fn visit_end(&mut self) -> Self::Output {
2234 let mut frame = self.pop_ctrl()?;
2235
2236 if frame.kind == FrameKind::If {
2243 self.push_ctrl(FrameKind::Else, frame.block_type)?;
2244 frame = self.pop_ctrl()?;
2245 }
2246 for ty in self.results(frame.block_type)? {
2247 self.push_operand(ty)?;
2248 }
2249 if self.control.is_empty() {
2250 assert_ne!(self.offset, 0);
2251 }
2252 Ok(())
2253 }
2254 fn visit_br(&mut self, relative_depth: u32) -> Self::Output {
2255 let (ty, kind) = self.jump(relative_depth)?;
2256 for ty in self.label_types(ty, kind)?.rev() {
2257 self.pop_operand(Some(ty))?;
2258 }
2259 self.unreachable()?;
2260 Ok(())
2261 }
2262 fn visit_br_if(&mut self, relative_depth: u32) -> Self::Output {
2263 self.pop_operand(Some(ValType::I32))?;
2264 let (ty, kind) = self.jump(relative_depth)?;
2265 let label_types = self.label_types(ty, kind)?;
2266 self.pop_push_label_types(label_types)?;
2267 Ok(())
2268 }
2269 fn visit_br_table(&mut self, table: BrTable) -> Self::Output {
2270 self.pop_operand(Some(ValType::I32))?;
2271 let default = self.jump(table.default())?;
2272 let default_types = self.label_types(default.0, default.1)?;
2273 for element in table.targets() {
2274 let relative_depth = element?;
2275 let block = self.jump(relative_depth)?;
2276 let label_tys = self.label_types(block.0, block.1)?;
2277 if label_tys.len() != default_types.len() {
2278 bail!(
2279 self.offset,
2280 "type mismatch: br_table target labels have different number of types"
2281 );
2282 }
2283 self.match_stack_operands(label_tys)?;
2284 }
2285 for ty in default_types.rev() {
2286 self.pop_operand(Some(ty))?;
2287 }
2288 self.unreachable()?;
2289 Ok(())
2290 }
2291 fn visit_return(&mut self) -> Self::Output {
2292 self.check_return()?;
2293 Ok(())
2294 }
2295 fn visit_call(&mut self, function_index: u32) -> Self::Output {
2296 let ty = self.type_of_function(function_index)?;
2297 self.check_call_ty(ty)?;
2298 Ok(())
2299 }
2300 fn visit_return_call(&mut self, function_index: u32) -> Self::Output {
2301 let ty = self.type_of_function(function_index)?;
2302 self.check_return_call_ty(ty)?;
2303 Ok(())
2304 }
2305 fn visit_call_ref(&mut self, type_index: u32) -> Self::Output {
2306 let ty = self.check_call_ref_ty(type_index)?;
2307 self.check_call_ty(ty)?;
2308 Ok(())
2309 }
2310 fn visit_return_call_ref(&mut self, type_index: u32) -> Self::Output {
2311 let ty = self.check_call_ref_ty(type_index)?;
2312 self.check_return_call_ty(ty)?;
2313 Ok(())
2314 }
2315 fn visit_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output {
2316 let ty = self.check_call_indirect_ty(type_index, table_index)?;
2317 self.check_call_ty(ty)?;
2318 Ok(())
2319 }
2320 fn visit_return_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output {
2321 let ty = self.check_call_indirect_ty(type_index, table_index)?;
2322 self.check_return_call_ty(ty)?;
2323 Ok(())
2324 }
2325 fn visit_drop(&mut self) -> Self::Output {
2326 self.pop_operand(None)?;
2327 Ok(())
2328 }
2329 fn visit_select(&mut self) -> Self::Output {
2330 self.pop_operand(Some(ValType::I32))?;
2331 let ty1 = self.pop_operand(None)?;
2332 let ty2 = self.pop_operand(None)?;
2333
2334 let ty = match (ty1, ty2) {
2335 (MaybeType::UnknownRef(..), _)
2338 | (_, MaybeType::UnknownRef(..))
2339 | (MaybeType::Known(ValType::Ref(_)), _)
2340 | (_, MaybeType::Known(ValType::Ref(_))) => {
2341 bail!(
2342 self.offset,
2343 "type mismatch: select only takes integral types"
2344 )
2345 }
2346
2347 (MaybeType::Bottom, t) | (t, MaybeType::Bottom) => t,
2350
2351 (t @ MaybeType::Known(t1), MaybeType::Known(t2)) => {
2354 if t1 != t2 {
2355 bail!(
2356 self.offset,
2357 "type mismatch: select operands have different types"
2358 );
2359 }
2360 t
2361 }
2362 };
2363 self.push_operand(ty)?;
2364 Ok(())
2365 }
2366 fn visit_typed_select(&mut self, mut ty: ValType) -> Self::Output {
2367 self.resources
2368 .check_value_type(&mut ty, &self.features, self.offset)?;
2369 self.pop_operand(Some(ValType::I32))?;
2370 self.pop_operand(Some(ty))?;
2371 self.pop_operand(Some(ty))?;
2372 self.push_operand(ty)?;
2373 Ok(())
2374 }
2375 fn visit_typed_select_multi(&mut self, tys: Vec<ValType>) -> Self::Output {
2376 debug_assert!(tys.len() != 1);
2377 bail!(self.offset, "invalid result arity");
2378 }
2379 fn visit_local_get(&mut self, local_index: u32) -> Self::Output {
2380 let ty = self.local(local_index)?;
2381 debug_assert_type_indices_are_ids(ty);
2382 if self.local_inits.is_uninit(local_index) {
2383 bail!(self.offset, "uninitialized local: {}", local_index);
2384 }
2385 self.push_operand(ty)?;
2386 Ok(())
2387 }
2388 fn visit_local_set(&mut self, local_index: u32) -> Self::Output {
2389 let ty = self.local(local_index)?;
2390 self.pop_operand(Some(ty))?;
2391 self.local_inits.set_init(local_index);
2392 Ok(())
2393 }
2394 fn visit_local_tee(&mut self, local_index: u32) -> Self::Output {
2395 let expected_ty = self.local(local_index)?;
2396 self.pop_operand(Some(expected_ty))?;
2397 self.local_inits.set_init(local_index);
2398 self.push_operand(expected_ty)?;
2399 Ok(())
2400 }
2401 fn visit_global_get(&mut self, global_index: u32) -> Self::Output {
2402 let ty = self.global_type_at(global_index)?.content_type;
2403 debug_assert_type_indices_are_ids(ty);
2404 self.push_operand(ty)?;
2405 Ok(())
2406 }
2407 fn visit_global_atomic_get(&mut self, _ordering: Ordering, global_index: u32) -> Self::Output {
2408 self.visit_global_get(global_index)?;
2409 let ty = self.global_type_at(global_index)?.content_type;
2413 let supertype = RefType::ANYREF.into();
2414 if !(ty == ValType::I32 || ty == ValType::I64 || self.resources.is_subtype(ty, supertype)) {
2415 bail!(
2416 self.offset,
2417 "invalid type: `global.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`"
2418 );
2419 }
2420 Ok(())
2421 }
2422 fn visit_global_set(&mut self, global_index: u32) -> Self::Output {
2423 let ty = self.global_type_at(global_index)?;
2424 if !ty.mutable {
2425 bail!(
2426 self.offset,
2427 "global is immutable: cannot modify it with `global.set`"
2428 );
2429 }
2430 self.pop_operand(Some(ty.content_type))?;
2431 Ok(())
2432 }
2433 fn visit_global_atomic_set(&mut self, _ordering: Ordering, global_index: u32) -> Self::Output {
2434 self.visit_global_set(global_index)?;
2435 let ty = self.global_type_at(global_index)?.content_type;
2438 let supertype = RefType::ANYREF.into();
2439 if !(ty == ValType::I32 || ty == ValType::I64 || self.resources.is_subtype(ty, supertype)) {
2440 bail!(
2441 self.offset,
2442 "invalid type: `global.atomic.set` only allows `i32`, `i64` and subtypes of `anyref`"
2443 );
2444 }
2445 Ok(())
2446 }
2447 fn visit_global_atomic_rmw_add(
2448 &mut self,
2449 _ordering: crate::Ordering,
2450 global_index: u32,
2451 ) -> Self::Output {
2452 let ty = self.check_atomic_global_rmw_ty(global_index)?;
2453 self.check_unary_op(ty)
2454 }
2455 fn visit_global_atomic_rmw_sub(
2456 &mut self,
2457 _ordering: crate::Ordering,
2458 global_index: u32,
2459 ) -> Self::Output {
2460 let ty = self.check_atomic_global_rmw_ty(global_index)?;
2461 self.check_unary_op(ty)
2462 }
2463 fn visit_global_atomic_rmw_and(
2464 &mut self,
2465 _ordering: crate::Ordering,
2466 global_index: u32,
2467 ) -> Self::Output {
2468 let ty = self.check_atomic_global_rmw_ty(global_index)?;
2469 self.check_unary_op(ty)
2470 }
2471 fn visit_global_atomic_rmw_or(
2472 &mut self,
2473 _ordering: crate::Ordering,
2474 global_index: u32,
2475 ) -> Self::Output {
2476 let ty = self.check_atomic_global_rmw_ty(global_index)?;
2477 self.check_unary_op(ty)
2478 }
2479 fn visit_global_atomic_rmw_xor(
2480 &mut self,
2481 _ordering: crate::Ordering,
2482 global_index: u32,
2483 ) -> Self::Output {
2484 let ty = self.check_atomic_global_rmw_ty(global_index)?;
2485 self.check_unary_op(ty)
2486 }
2487 fn visit_global_atomic_rmw_xchg(
2488 &mut self,
2489 _ordering: crate::Ordering,
2490 global_index: u32,
2491 ) -> Self::Output {
2492 let global = self.global_type_at(global_index)?;
2493 if !global.mutable {
2494 bail!(
2495 self.offset,
2496 "global is immutable: cannot modify it with `global.atomic.rmw.xchg`"
2497 );
2498 }
2499 let ty = global.content_type;
2500 if !(ty == ValType::I32
2501 || ty == ValType::I64
2502 || self.resources.is_subtype(ty, RefType::ANYREF.into()))
2503 {
2504 bail!(
2505 self.offset,
2506 "invalid type: `global.atomic.rmw.xchg` only allows `i32`, `i64` and subtypes of `anyref`"
2507 );
2508 }
2509 self.check_unary_op(ty)
2510 }
2511 fn visit_global_atomic_rmw_cmpxchg(
2512 &mut self,
2513 _ordering: crate::Ordering,
2514 global_index: u32,
2515 ) -> Self::Output {
2516 let global = self.global_type_at(global_index)?;
2517 if !global.mutable {
2518 bail!(
2519 self.offset,
2520 "global is immutable: cannot modify it with `global.atomic.rmw.cmpxchg`"
2521 );
2522 }
2523 let ty = global.content_type;
2524 if !(ty == ValType::I32
2525 || ty == ValType::I64
2526 || self.resources.is_subtype(ty, RefType::EQREF.into()))
2527 {
2528 bail!(
2529 self.offset,
2530 "invalid type: `global.atomic.rmw.cmpxchg` only allows `i32`, `i64` and subtypes of `eqref`"
2531 );
2532 }
2533 self.check_binary_op(ty)
2534 }
2535
2536 fn visit_i32_load(&mut self, memarg: MemArg) -> Self::Output {
2537 let ty = self.check_memarg(memarg)?;
2538 self.pop_operand(Some(ty))?;
2539 self.push_operand(ValType::I32)?;
2540 Ok(())
2541 }
2542 fn visit_i64_load(&mut self, memarg: MemArg) -> Self::Output {
2543 let ty = self.check_memarg(memarg)?;
2544 self.pop_operand(Some(ty))?;
2545 self.push_operand(ValType::I64)?;
2546 Ok(())
2547 }
2548 fn visit_f32_load(&mut self, memarg: MemArg) -> Self::Output {
2549 self.check_floats_enabled()?;
2550 let ty = self.check_memarg(memarg)?;
2551 self.pop_operand(Some(ty))?;
2552 self.push_operand(ValType::F32)?;
2553 Ok(())
2554 }
2555 fn visit_f64_load(&mut self, memarg: MemArg) -> Self::Output {
2556 self.check_floats_enabled()?;
2557 let ty = self.check_memarg(memarg)?;
2558 self.pop_operand(Some(ty))?;
2559 self.push_operand(ValType::F64)?;
2560 Ok(())
2561 }
2562 fn visit_i32_load8_s(&mut self, memarg: MemArg) -> Self::Output {
2563 let ty = self.check_memarg(memarg)?;
2564 self.pop_operand(Some(ty))?;
2565 self.push_operand(ValType::I32)?;
2566 Ok(())
2567 }
2568 fn visit_i32_load8_u(&mut self, memarg: MemArg) -> Self::Output {
2569 self.visit_i32_load8_s(memarg)
2570 }
2571 fn visit_i32_load16_s(&mut self, memarg: MemArg) -> Self::Output {
2572 let ty = self.check_memarg(memarg)?;
2573 self.pop_operand(Some(ty))?;
2574 self.push_operand(ValType::I32)?;
2575 Ok(())
2576 }
2577 fn visit_i32_load16_u(&mut self, memarg: MemArg) -> Self::Output {
2578 self.visit_i32_load16_s(memarg)
2579 }
2580 fn visit_i64_load8_s(&mut self, memarg: MemArg) -> Self::Output {
2581 let ty = self.check_memarg(memarg)?;
2582 self.pop_operand(Some(ty))?;
2583 self.push_operand(ValType::I64)?;
2584 Ok(())
2585 }
2586 fn visit_i64_load8_u(&mut self, memarg: MemArg) -> Self::Output {
2587 self.visit_i64_load8_s(memarg)
2588 }
2589 fn visit_i64_load16_s(&mut self, memarg: MemArg) -> Self::Output {
2590 let ty = self.check_memarg(memarg)?;
2591 self.pop_operand(Some(ty))?;
2592 self.push_operand(ValType::I64)?;
2593 Ok(())
2594 }
2595 fn visit_i64_load16_u(&mut self, memarg: MemArg) -> Self::Output {
2596 self.visit_i64_load16_s(memarg)
2597 }
2598 fn visit_i64_load32_s(&mut self, memarg: MemArg) -> Self::Output {
2599 let ty = self.check_memarg(memarg)?;
2600 self.pop_operand(Some(ty))?;
2601 self.push_operand(ValType::I64)?;
2602 Ok(())
2603 }
2604 fn visit_i64_load32_u(&mut self, memarg: MemArg) -> Self::Output {
2605 self.visit_i64_load32_s(memarg)
2606 }
2607 fn visit_i32_store(&mut self, memarg: MemArg) -> Self::Output {
2608 let ty = self.check_memarg(memarg)?;
2609 self.pop_operand(Some(ValType::I32))?;
2610 self.pop_operand(Some(ty))?;
2611 Ok(())
2612 }
2613 fn visit_i64_store(&mut self, memarg: MemArg) -> Self::Output {
2614 let ty = self.check_memarg(memarg)?;
2615 self.pop_operand(Some(ValType::I64))?;
2616 self.pop_operand(Some(ty))?;
2617 Ok(())
2618 }
2619 fn visit_f32_store(&mut self, memarg: MemArg) -> Self::Output {
2620 self.check_floats_enabled()?;
2621 let ty = self.check_memarg(memarg)?;
2622 self.pop_operand(Some(ValType::F32))?;
2623 self.pop_operand(Some(ty))?;
2624 Ok(())
2625 }
2626 fn visit_f64_store(&mut self, memarg: MemArg) -> Self::Output {
2627 self.check_floats_enabled()?;
2628 let ty = self.check_memarg(memarg)?;
2629 self.pop_operand(Some(ValType::F64))?;
2630 self.pop_operand(Some(ty))?;
2631 Ok(())
2632 }
2633 fn visit_i32_store8(&mut self, memarg: MemArg) -> Self::Output {
2634 let ty = self.check_memarg(memarg)?;
2635 self.pop_operand(Some(ValType::I32))?;
2636 self.pop_operand(Some(ty))?;
2637 Ok(())
2638 }
2639 fn visit_i32_store16(&mut self, memarg: MemArg) -> Self::Output {
2640 let ty = self.check_memarg(memarg)?;
2641 self.pop_operand(Some(ValType::I32))?;
2642 self.pop_operand(Some(ty))?;
2643 Ok(())
2644 }
2645 fn visit_i64_store8(&mut self, memarg: MemArg) -> Self::Output {
2646 let ty = self.check_memarg(memarg)?;
2647 self.pop_operand(Some(ValType::I64))?;
2648 self.pop_operand(Some(ty))?;
2649 Ok(())
2650 }
2651 fn visit_i64_store16(&mut self, memarg: MemArg) -> Self::Output {
2652 let ty = self.check_memarg(memarg)?;
2653 self.pop_operand(Some(ValType::I64))?;
2654 self.pop_operand(Some(ty))?;
2655 Ok(())
2656 }
2657 fn visit_i64_store32(&mut self, memarg: MemArg) -> Self::Output {
2658 let ty = self.check_memarg(memarg)?;
2659 self.pop_operand(Some(ValType::I64))?;
2660 self.pop_operand(Some(ty))?;
2661 Ok(())
2662 }
2663 fn visit_memory_size(&mut self, mem: u32) -> Self::Output {
2664 let index_ty = self.check_memory_index(mem)?;
2665 self.push_operand(index_ty)?;
2666 Ok(())
2667 }
2668 fn visit_memory_grow(&mut self, mem: u32) -> Self::Output {
2669 let index_ty = self.check_memory_index(mem)?;
2670 self.pop_operand(Some(index_ty))?;
2671 self.push_operand(index_ty)?;
2672 Ok(())
2673 }
2674 fn visit_i32_const(&mut self, _value: i32) -> Self::Output {
2675 self.push_operand(ValType::I32)?;
2676 Ok(())
2677 }
2678 fn visit_i64_const(&mut self, _value: i64) -> Self::Output {
2679 self.push_operand(ValType::I64)?;
2680 Ok(())
2681 }
2682 fn visit_f32_const(&mut self, _value: Ieee32) -> Self::Output {
2683 self.check_floats_enabled()?;
2684 self.push_operand(ValType::F32)?;
2685 Ok(())
2686 }
2687 fn visit_f64_const(&mut self, _value: Ieee64) -> Self::Output {
2688 self.check_floats_enabled()?;
2689 self.push_operand(ValType::F64)?;
2690 Ok(())
2691 }
2692 fn visit_i32_eqz(&mut self) -> Self::Output {
2693 self.pop_operand(Some(ValType::I32))?;
2694 self.push_operand(ValType::I32)?;
2695 Ok(())
2696 }
2697 fn visit_i32_eq(&mut self) -> Self::Output {
2698 self.check_cmp_op(ValType::I32)
2699 }
2700 fn visit_i32_ne(&mut self) -> Self::Output {
2701 self.check_cmp_op(ValType::I32)
2702 }
2703 fn visit_i32_lt_s(&mut self) -> Self::Output {
2704 self.check_cmp_op(ValType::I32)
2705 }
2706 fn visit_i32_lt_u(&mut self) -> Self::Output {
2707 self.check_cmp_op(ValType::I32)
2708 }
2709 fn visit_i32_gt_s(&mut self) -> Self::Output {
2710 self.check_cmp_op(ValType::I32)
2711 }
2712 fn visit_i32_gt_u(&mut self) -> Self::Output {
2713 self.check_cmp_op(ValType::I32)
2714 }
2715 fn visit_i32_le_s(&mut self) -> Self::Output {
2716 self.check_cmp_op(ValType::I32)
2717 }
2718 fn visit_i32_le_u(&mut self) -> Self::Output {
2719 self.check_cmp_op(ValType::I32)
2720 }
2721 fn visit_i32_ge_s(&mut self) -> Self::Output {
2722 self.check_cmp_op(ValType::I32)
2723 }
2724 fn visit_i32_ge_u(&mut self) -> Self::Output {
2725 self.check_cmp_op(ValType::I32)
2726 }
2727 fn visit_i64_eqz(&mut self) -> Self::Output {
2728 self.pop_operand(Some(ValType::I64))?;
2729 self.push_operand(ValType::I32)?;
2730 Ok(())
2731 }
2732 fn visit_i64_eq(&mut self) -> Self::Output {
2733 self.check_cmp_op(ValType::I64)
2734 }
2735 fn visit_i64_ne(&mut self) -> Self::Output {
2736 self.check_cmp_op(ValType::I64)
2737 }
2738 fn visit_i64_lt_s(&mut self) -> Self::Output {
2739 self.check_cmp_op(ValType::I64)
2740 }
2741 fn visit_i64_lt_u(&mut self) -> Self::Output {
2742 self.check_cmp_op(ValType::I64)
2743 }
2744 fn visit_i64_gt_s(&mut self) -> Self::Output {
2745 self.check_cmp_op(ValType::I64)
2746 }
2747 fn visit_i64_gt_u(&mut self) -> Self::Output {
2748 self.check_cmp_op(ValType::I64)
2749 }
2750 fn visit_i64_le_s(&mut self) -> Self::Output {
2751 self.check_cmp_op(ValType::I64)
2752 }
2753 fn visit_i64_le_u(&mut self) -> Self::Output {
2754 self.check_cmp_op(ValType::I64)
2755 }
2756 fn visit_i64_ge_s(&mut self) -> Self::Output {
2757 self.check_cmp_op(ValType::I64)
2758 }
2759 fn visit_i64_ge_u(&mut self) -> Self::Output {
2760 self.check_cmp_op(ValType::I64)
2761 }
2762 fn visit_f32_eq(&mut self) -> Self::Output {
2763 self.check_fcmp_op(ValType::F32)
2764 }
2765 fn visit_f32_ne(&mut self) -> Self::Output {
2766 self.check_fcmp_op(ValType::F32)
2767 }
2768 fn visit_f32_lt(&mut self) -> Self::Output {
2769 self.check_fcmp_op(ValType::F32)
2770 }
2771 fn visit_f32_gt(&mut self) -> Self::Output {
2772 self.check_fcmp_op(ValType::F32)
2773 }
2774 fn visit_f32_le(&mut self) -> Self::Output {
2775 self.check_fcmp_op(ValType::F32)
2776 }
2777 fn visit_f32_ge(&mut self) -> Self::Output {
2778 self.check_fcmp_op(ValType::F32)
2779 }
2780 fn visit_f64_eq(&mut self) -> Self::Output {
2781 self.check_fcmp_op(ValType::F64)
2782 }
2783 fn visit_f64_ne(&mut self) -> Self::Output {
2784 self.check_fcmp_op(ValType::F64)
2785 }
2786 fn visit_f64_lt(&mut self) -> Self::Output {
2787 self.check_fcmp_op(ValType::F64)
2788 }
2789 fn visit_f64_gt(&mut self) -> Self::Output {
2790 self.check_fcmp_op(ValType::F64)
2791 }
2792 fn visit_f64_le(&mut self) -> Self::Output {
2793 self.check_fcmp_op(ValType::F64)
2794 }
2795 fn visit_f64_ge(&mut self) -> Self::Output {
2796 self.check_fcmp_op(ValType::F64)
2797 }
2798 fn visit_i32_clz(&mut self) -> Self::Output {
2799 self.check_unary_op(ValType::I32)
2800 }
2801 fn visit_i32_ctz(&mut self) -> Self::Output {
2802 self.check_unary_op(ValType::I32)
2803 }
2804 fn visit_i32_popcnt(&mut self) -> Self::Output {
2805 self.check_unary_op(ValType::I32)
2806 }
2807 fn visit_i32_add(&mut self) -> Self::Output {
2808 self.check_binary_op(ValType::I32)
2809 }
2810 fn visit_i32_sub(&mut self) -> Self::Output {
2811 self.check_binary_op(ValType::I32)
2812 }
2813 fn visit_i32_mul(&mut self) -> Self::Output {
2814 self.check_binary_op(ValType::I32)
2815 }
2816 fn visit_i32_div_s(&mut self) -> Self::Output {
2817 self.check_binary_op(ValType::I32)
2818 }
2819 fn visit_i32_div_u(&mut self) -> Self::Output {
2820 self.check_binary_op(ValType::I32)
2821 }
2822 fn visit_i32_rem_s(&mut self) -> Self::Output {
2823 self.check_binary_op(ValType::I32)
2824 }
2825 fn visit_i32_rem_u(&mut self) -> Self::Output {
2826 self.check_binary_op(ValType::I32)
2827 }
2828 fn visit_i32_and(&mut self) -> Self::Output {
2829 self.check_binary_op(ValType::I32)
2830 }
2831 fn visit_i32_or(&mut self) -> Self::Output {
2832 self.check_binary_op(ValType::I32)
2833 }
2834 fn visit_i32_xor(&mut self) -> Self::Output {
2835 self.check_binary_op(ValType::I32)
2836 }
2837 fn visit_i32_shl(&mut self) -> Self::Output {
2838 self.check_binary_op(ValType::I32)
2839 }
2840 fn visit_i32_shr_s(&mut self) -> Self::Output {
2841 self.check_binary_op(ValType::I32)
2842 }
2843 fn visit_i32_shr_u(&mut self) -> Self::Output {
2844 self.check_binary_op(ValType::I32)
2845 }
2846 fn visit_i32_rotl(&mut self) -> Self::Output {
2847 self.check_binary_op(ValType::I32)
2848 }
2849 fn visit_i32_rotr(&mut self) -> Self::Output {
2850 self.check_binary_op(ValType::I32)
2851 }
2852 fn visit_i64_clz(&mut self) -> Self::Output {
2853 self.check_unary_op(ValType::I64)
2854 }
2855 fn visit_i64_ctz(&mut self) -> Self::Output {
2856 self.check_unary_op(ValType::I64)
2857 }
2858 fn visit_i64_popcnt(&mut self) -> Self::Output {
2859 self.check_unary_op(ValType::I64)
2860 }
2861 fn visit_i64_add(&mut self) -> Self::Output {
2862 self.check_binary_op(ValType::I64)
2863 }
2864 fn visit_i64_sub(&mut self) -> Self::Output {
2865 self.check_binary_op(ValType::I64)
2866 }
2867 fn visit_i64_mul(&mut self) -> Self::Output {
2868 self.check_binary_op(ValType::I64)
2869 }
2870 fn visit_i64_div_s(&mut self) -> Self::Output {
2871 self.check_binary_op(ValType::I64)
2872 }
2873 fn visit_i64_div_u(&mut self) -> Self::Output {
2874 self.check_binary_op(ValType::I64)
2875 }
2876 fn visit_i64_rem_s(&mut self) -> Self::Output {
2877 self.check_binary_op(ValType::I64)
2878 }
2879 fn visit_i64_rem_u(&mut self) -> Self::Output {
2880 self.check_binary_op(ValType::I64)
2881 }
2882 fn visit_i64_and(&mut self) -> Self::Output {
2883 self.check_binary_op(ValType::I64)
2884 }
2885 fn visit_i64_or(&mut self) -> Self::Output {
2886 self.check_binary_op(ValType::I64)
2887 }
2888 fn visit_i64_xor(&mut self) -> Self::Output {
2889 self.check_binary_op(ValType::I64)
2890 }
2891 fn visit_i64_shl(&mut self) -> Self::Output {
2892 self.check_binary_op(ValType::I64)
2893 }
2894 fn visit_i64_shr_s(&mut self) -> Self::Output {
2895 self.check_binary_op(ValType::I64)
2896 }
2897 fn visit_i64_shr_u(&mut self) -> Self::Output {
2898 self.check_binary_op(ValType::I64)
2899 }
2900 fn visit_i64_rotl(&mut self) -> Self::Output {
2901 self.check_binary_op(ValType::I64)
2902 }
2903 fn visit_i64_rotr(&mut self) -> Self::Output {
2904 self.check_binary_op(ValType::I64)
2905 }
2906 fn visit_f32_abs(&mut self) -> Self::Output {
2907 self.check_funary_op(ValType::F32)
2908 }
2909 fn visit_f32_neg(&mut self) -> Self::Output {
2910 self.check_funary_op(ValType::F32)
2911 }
2912 fn visit_f32_ceil(&mut self) -> Self::Output {
2913 self.check_funary_op(ValType::F32)
2914 }
2915 fn visit_f32_floor(&mut self) -> Self::Output {
2916 self.check_funary_op(ValType::F32)
2917 }
2918 fn visit_f32_trunc(&mut self) -> Self::Output {
2919 self.check_funary_op(ValType::F32)
2920 }
2921 fn visit_f32_nearest(&mut self) -> Self::Output {
2922 self.check_funary_op(ValType::F32)
2923 }
2924 fn visit_f32_sqrt(&mut self) -> Self::Output {
2925 self.check_funary_op(ValType::F32)
2926 }
2927 fn visit_f32_add(&mut self) -> Self::Output {
2928 self.check_fbinary_op(ValType::F32)
2929 }
2930 fn visit_f32_sub(&mut self) -> Self::Output {
2931 self.check_fbinary_op(ValType::F32)
2932 }
2933 fn visit_f32_mul(&mut self) -> Self::Output {
2934 self.check_fbinary_op(ValType::F32)
2935 }
2936 fn visit_f32_div(&mut self) -> Self::Output {
2937 self.check_fbinary_op(ValType::F32)
2938 }
2939 fn visit_f32_min(&mut self) -> Self::Output {
2940 self.check_fbinary_op(ValType::F32)
2941 }
2942 fn visit_f32_max(&mut self) -> Self::Output {
2943 self.check_fbinary_op(ValType::F32)
2944 }
2945 fn visit_f32_copysign(&mut self) -> Self::Output {
2946 self.check_fbinary_op(ValType::F32)
2947 }
2948 fn visit_f64_abs(&mut self) -> Self::Output {
2949 self.check_funary_op(ValType::F64)
2950 }
2951 fn visit_f64_neg(&mut self) -> Self::Output {
2952 self.check_funary_op(ValType::F64)
2953 }
2954 fn visit_f64_ceil(&mut self) -> Self::Output {
2955 self.check_funary_op(ValType::F64)
2956 }
2957 fn visit_f64_floor(&mut self) -> Self::Output {
2958 self.check_funary_op(ValType::F64)
2959 }
2960 fn visit_f64_trunc(&mut self) -> Self::Output {
2961 self.check_funary_op(ValType::F64)
2962 }
2963 fn visit_f64_nearest(&mut self) -> Self::Output {
2964 self.check_funary_op(ValType::F64)
2965 }
2966 fn visit_f64_sqrt(&mut self) -> Self::Output {
2967 self.check_funary_op(ValType::F64)
2968 }
2969 fn visit_f64_add(&mut self) -> Self::Output {
2970 self.check_fbinary_op(ValType::F64)
2971 }
2972 fn visit_f64_sub(&mut self) -> Self::Output {
2973 self.check_fbinary_op(ValType::F64)
2974 }
2975 fn visit_f64_mul(&mut self) -> Self::Output {
2976 self.check_fbinary_op(ValType::F64)
2977 }
2978 fn visit_f64_div(&mut self) -> Self::Output {
2979 self.check_fbinary_op(ValType::F64)
2980 }
2981 fn visit_f64_min(&mut self) -> Self::Output {
2982 self.check_fbinary_op(ValType::F64)
2983 }
2984 fn visit_f64_max(&mut self) -> Self::Output {
2985 self.check_fbinary_op(ValType::F64)
2986 }
2987 fn visit_f64_copysign(&mut self) -> Self::Output {
2988 self.check_fbinary_op(ValType::F64)
2989 }
2990 fn visit_i32_wrap_i64(&mut self) -> Self::Output {
2991 self.check_conversion_op(ValType::I32, ValType::I64)
2992 }
2993 fn visit_i32_trunc_f32_s(&mut self) -> Self::Output {
2994 self.check_conversion_op(ValType::I32, ValType::F32)
2995 }
2996 fn visit_i32_trunc_f32_u(&mut self) -> Self::Output {
2997 self.check_conversion_op(ValType::I32, ValType::F32)
2998 }
2999 fn visit_i32_trunc_f64_s(&mut self) -> Self::Output {
3000 self.check_conversion_op(ValType::I32, ValType::F64)
3001 }
3002 fn visit_i32_trunc_f64_u(&mut self) -> Self::Output {
3003 self.check_conversion_op(ValType::I32, ValType::F64)
3004 }
3005 fn visit_i64_extend_i32_s(&mut self) -> Self::Output {
3006 self.check_conversion_op(ValType::I64, ValType::I32)
3007 }
3008 fn visit_i64_extend_i32_u(&mut self) -> Self::Output {
3009 self.check_conversion_op(ValType::I64, ValType::I32)
3010 }
3011 fn visit_i64_trunc_f32_s(&mut self) -> Self::Output {
3012 self.check_conversion_op(ValType::I64, ValType::F32)
3013 }
3014 fn visit_i64_trunc_f32_u(&mut self) -> Self::Output {
3015 self.check_conversion_op(ValType::I64, ValType::F32)
3016 }
3017 fn visit_i64_trunc_f64_s(&mut self) -> Self::Output {
3018 self.check_conversion_op(ValType::I64, ValType::F64)
3019 }
3020 fn visit_i64_trunc_f64_u(&mut self) -> Self::Output {
3021 self.check_conversion_op(ValType::I64, ValType::F64)
3022 }
3023 fn visit_f32_convert_i32_s(&mut self) -> Self::Output {
3024 self.check_fconversion_op(ValType::F32, ValType::I32)
3025 }
3026 fn visit_f32_convert_i32_u(&mut self) -> Self::Output {
3027 self.check_fconversion_op(ValType::F32, ValType::I32)
3028 }
3029 fn visit_f32_convert_i64_s(&mut self) -> Self::Output {
3030 self.check_fconversion_op(ValType::F32, ValType::I64)
3031 }
3032 fn visit_f32_convert_i64_u(&mut self) -> Self::Output {
3033 self.check_fconversion_op(ValType::F32, ValType::I64)
3034 }
3035 fn visit_f32_demote_f64(&mut self) -> Self::Output {
3036 self.check_fconversion_op(ValType::F32, ValType::F64)
3037 }
3038 fn visit_f64_convert_i32_s(&mut self) -> Self::Output {
3039 self.check_fconversion_op(ValType::F64, ValType::I32)
3040 }
3041 fn visit_f64_convert_i32_u(&mut self) -> Self::Output {
3042 self.check_fconversion_op(ValType::F64, ValType::I32)
3043 }
3044 fn visit_f64_convert_i64_s(&mut self) -> Self::Output {
3045 self.check_fconversion_op(ValType::F64, ValType::I64)
3046 }
3047 fn visit_f64_convert_i64_u(&mut self) -> Self::Output {
3048 self.check_fconversion_op(ValType::F64, ValType::I64)
3049 }
3050 fn visit_f64_promote_f32(&mut self) -> Self::Output {
3051 self.check_fconversion_op(ValType::F64, ValType::F32)
3052 }
3053 fn visit_i32_reinterpret_f32(&mut self) -> Self::Output {
3054 self.check_conversion_op(ValType::I32, ValType::F32)
3055 }
3056 fn visit_i64_reinterpret_f64(&mut self) -> Self::Output {
3057 self.check_conversion_op(ValType::I64, ValType::F64)
3058 }
3059 fn visit_f32_reinterpret_i32(&mut self) -> Self::Output {
3060 self.check_fconversion_op(ValType::F32, ValType::I32)
3061 }
3062 fn visit_f64_reinterpret_i64(&mut self) -> Self::Output {
3063 self.check_fconversion_op(ValType::F64, ValType::I64)
3064 }
3065 fn visit_i32_trunc_sat_f32_s(&mut self) -> Self::Output {
3066 self.check_conversion_op(ValType::I32, ValType::F32)
3067 }
3068 fn visit_i32_trunc_sat_f32_u(&mut self) -> Self::Output {
3069 self.check_conversion_op(ValType::I32, ValType::F32)
3070 }
3071 fn visit_i32_trunc_sat_f64_s(&mut self) -> Self::Output {
3072 self.check_conversion_op(ValType::I32, ValType::F64)
3073 }
3074 fn visit_i32_trunc_sat_f64_u(&mut self) -> Self::Output {
3075 self.check_conversion_op(ValType::I32, ValType::F64)
3076 }
3077 fn visit_i64_trunc_sat_f32_s(&mut self) -> Self::Output {
3078 self.check_conversion_op(ValType::I64, ValType::F32)
3079 }
3080 fn visit_i64_trunc_sat_f32_u(&mut self) -> Self::Output {
3081 self.check_conversion_op(ValType::I64, ValType::F32)
3082 }
3083 fn visit_i64_trunc_sat_f64_s(&mut self) -> Self::Output {
3084 self.check_conversion_op(ValType::I64, ValType::F64)
3085 }
3086 fn visit_i64_trunc_sat_f64_u(&mut self) -> Self::Output {
3087 self.check_conversion_op(ValType::I64, ValType::F64)
3088 }
3089 fn visit_i32_extend8_s(&mut self) -> Self::Output {
3090 self.check_unary_op(ValType::I32)
3091 }
3092 fn visit_i32_extend16_s(&mut self) -> Self::Output {
3093 self.check_unary_op(ValType::I32)
3094 }
3095 fn visit_i64_extend8_s(&mut self) -> Self::Output {
3096 self.check_unary_op(ValType::I64)
3097 }
3098 fn visit_i64_extend16_s(&mut self) -> Self::Output {
3099 self.check_unary_op(ValType::I64)
3100 }
3101 fn visit_i64_extend32_s(&mut self) -> Self::Output {
3102 self.check_unary_op(ValType::I64)
3103 }
3104 fn visit_i32_atomic_load(&mut self, memarg: MemArg) -> Self::Output {
3105 self.check_atomic_load(memarg, ValType::I32)
3106 }
3107 fn visit_i32_atomic_load16_u(&mut self, memarg: MemArg) -> Self::Output {
3108 self.check_atomic_load(memarg, ValType::I32)
3109 }
3110 fn visit_i32_atomic_load8_u(&mut self, memarg: MemArg) -> Self::Output {
3111 self.check_atomic_load(memarg, ValType::I32)
3112 }
3113 fn visit_i64_atomic_load(&mut self, memarg: MemArg) -> Self::Output {
3114 self.check_atomic_load(memarg, ValType::I64)
3115 }
3116 fn visit_i64_atomic_load32_u(&mut self, memarg: MemArg) -> Self::Output {
3117 self.check_atomic_load(memarg, ValType::I64)
3118 }
3119 fn visit_i64_atomic_load16_u(&mut self, memarg: MemArg) -> Self::Output {
3120 self.check_atomic_load(memarg, ValType::I64)
3121 }
3122 fn visit_i64_atomic_load8_u(&mut self, memarg: MemArg) -> Self::Output {
3123 self.check_atomic_load(memarg, ValType::I64)
3124 }
3125 fn visit_i32_atomic_store(&mut self, memarg: MemArg) -> Self::Output {
3126 self.check_atomic_store(memarg, ValType::I32)
3127 }
3128 fn visit_i32_atomic_store16(&mut self, memarg: MemArg) -> Self::Output {
3129 self.check_atomic_store(memarg, ValType::I32)
3130 }
3131 fn visit_i32_atomic_store8(&mut self, memarg: MemArg) -> Self::Output {
3132 self.check_atomic_store(memarg, ValType::I32)
3133 }
3134 fn visit_i64_atomic_store(&mut self, memarg: MemArg) -> Self::Output {
3135 self.check_atomic_store(memarg, ValType::I64)
3136 }
3137 fn visit_i64_atomic_store32(&mut self, memarg: MemArg) -> Self::Output {
3138 self.check_atomic_store(memarg, ValType::I64)
3139 }
3140 fn visit_i64_atomic_store16(&mut self, memarg: MemArg) -> Self::Output {
3141 self.check_atomic_store(memarg, ValType::I64)
3142 }
3143 fn visit_i64_atomic_store8(&mut self, memarg: MemArg) -> Self::Output {
3144 self.check_atomic_store(memarg, ValType::I64)
3145 }
3146 fn visit_i32_atomic_rmw_add(&mut self, memarg: MemArg) -> Self::Output {
3147 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3148 }
3149 fn visit_i32_atomic_rmw_sub(&mut self, memarg: MemArg) -> Self::Output {
3150 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3151 }
3152 fn visit_i32_atomic_rmw_and(&mut self, memarg: MemArg) -> Self::Output {
3153 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3154 }
3155 fn visit_i32_atomic_rmw_or(&mut self, memarg: MemArg) -> Self::Output {
3156 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3157 }
3158 fn visit_i32_atomic_rmw_xor(&mut self, memarg: MemArg) -> Self::Output {
3159 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3160 }
3161 fn visit_i32_atomic_rmw16_add_u(&mut self, memarg: MemArg) -> Self::Output {
3162 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3163 }
3164 fn visit_i32_atomic_rmw16_sub_u(&mut self, memarg: MemArg) -> Self::Output {
3165 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3166 }
3167 fn visit_i32_atomic_rmw16_and_u(&mut self, memarg: MemArg) -> Self::Output {
3168 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3169 }
3170 fn visit_i32_atomic_rmw16_or_u(&mut self, memarg: MemArg) -> Self::Output {
3171 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3172 }
3173 fn visit_i32_atomic_rmw16_xor_u(&mut self, memarg: MemArg) -> Self::Output {
3174 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3175 }
3176 fn visit_i32_atomic_rmw8_add_u(&mut self, memarg: MemArg) -> Self::Output {
3177 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3178 }
3179 fn visit_i32_atomic_rmw8_sub_u(&mut self, memarg: MemArg) -> Self::Output {
3180 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3181 }
3182 fn visit_i32_atomic_rmw8_and_u(&mut self, memarg: MemArg) -> Self::Output {
3183 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3184 }
3185 fn visit_i32_atomic_rmw8_or_u(&mut self, memarg: MemArg) -> Self::Output {
3186 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3187 }
3188 fn visit_i32_atomic_rmw8_xor_u(&mut self, memarg: MemArg) -> Self::Output {
3189 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3190 }
3191 fn visit_i64_atomic_rmw_add(&mut self, memarg: MemArg) -> Self::Output {
3192 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3193 }
3194 fn visit_i64_atomic_rmw_sub(&mut self, memarg: MemArg) -> Self::Output {
3195 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3196 }
3197 fn visit_i64_atomic_rmw_and(&mut self, memarg: MemArg) -> Self::Output {
3198 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3199 }
3200 fn visit_i64_atomic_rmw_or(&mut self, memarg: MemArg) -> Self::Output {
3201 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3202 }
3203 fn visit_i64_atomic_rmw_xor(&mut self, memarg: MemArg) -> Self::Output {
3204 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3205 }
3206 fn visit_i64_atomic_rmw32_add_u(&mut self, memarg: MemArg) -> Self::Output {
3207 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3208 }
3209 fn visit_i64_atomic_rmw32_sub_u(&mut self, memarg: MemArg) -> Self::Output {
3210 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3211 }
3212 fn visit_i64_atomic_rmw32_and_u(&mut self, memarg: MemArg) -> Self::Output {
3213 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3214 }
3215 fn visit_i64_atomic_rmw32_or_u(&mut self, memarg: MemArg) -> Self::Output {
3216 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3217 }
3218 fn visit_i64_atomic_rmw32_xor_u(&mut self, memarg: MemArg) -> Self::Output {
3219 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3220 }
3221 fn visit_i64_atomic_rmw16_add_u(&mut self, memarg: MemArg) -> Self::Output {
3222 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3223 }
3224 fn visit_i64_atomic_rmw16_sub_u(&mut self, memarg: MemArg) -> Self::Output {
3225 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3226 }
3227 fn visit_i64_atomic_rmw16_and_u(&mut self, memarg: MemArg) -> Self::Output {
3228 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3229 }
3230 fn visit_i64_atomic_rmw16_or_u(&mut self, memarg: MemArg) -> Self::Output {
3231 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3232 }
3233 fn visit_i64_atomic_rmw16_xor_u(&mut self, memarg: MemArg) -> Self::Output {
3234 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3235 }
3236 fn visit_i64_atomic_rmw8_add_u(&mut self, memarg: MemArg) -> Self::Output {
3237 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3238 }
3239 fn visit_i64_atomic_rmw8_sub_u(&mut self, memarg: MemArg) -> Self::Output {
3240 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3241 }
3242 fn visit_i64_atomic_rmw8_and_u(&mut self, memarg: MemArg) -> Self::Output {
3243 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3244 }
3245 fn visit_i64_atomic_rmw8_or_u(&mut self, memarg: MemArg) -> Self::Output {
3246 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3247 }
3248 fn visit_i64_atomic_rmw8_xor_u(&mut self, memarg: MemArg) -> Self::Output {
3249 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3250 }
3251 fn visit_i32_atomic_rmw_xchg(&mut self, memarg: MemArg) -> Self::Output {
3252 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3253 }
3254 fn visit_i32_atomic_rmw16_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
3255 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3256 }
3257 fn visit_i32_atomic_rmw8_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
3258 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3259 }
3260 fn visit_i32_atomic_rmw_cmpxchg(&mut self, memarg: MemArg) -> Self::Output {
3261 self.check_atomic_binary_memory_cmpxchg(memarg, ValType::I32)
3262 }
3263 fn visit_i32_atomic_rmw16_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
3264 self.check_atomic_binary_memory_cmpxchg(memarg, ValType::I32)
3265 }
3266 fn visit_i32_atomic_rmw8_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
3267 self.check_atomic_binary_memory_cmpxchg(memarg, ValType::I32)
3268 }
3269 fn visit_i64_atomic_rmw_xchg(&mut self, memarg: MemArg) -> Self::Output {
3270 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3271 }
3272 fn visit_i64_atomic_rmw32_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
3273 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3274 }
3275 fn visit_i64_atomic_rmw16_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
3276 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3277 }
3278 fn visit_i64_atomic_rmw8_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
3279 self.check_atomic_binary_memory_op(memarg, ValType::I64)
3280 }
3281 fn visit_i64_atomic_rmw_cmpxchg(&mut self, memarg: MemArg) -> Self::Output {
3282 self.check_atomic_binary_memory_cmpxchg(memarg, ValType::I64)
3283 }
3284 fn visit_i64_atomic_rmw32_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
3285 self.check_atomic_binary_memory_cmpxchg(memarg, ValType::I64)
3286 }
3287 fn visit_i64_atomic_rmw16_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
3288 self.check_atomic_binary_memory_cmpxchg(memarg, ValType::I64)
3289 }
3290 fn visit_i64_atomic_rmw8_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
3291 self.check_atomic_binary_memory_cmpxchg(memarg, ValType::I64)
3292 }
3293 fn visit_memory_atomic_notify(&mut self, memarg: MemArg) -> Self::Output {
3294 self.check_atomic_binary_memory_op(memarg, ValType::I32)
3295 }
3296 fn visit_memory_atomic_wait32(&mut self, memarg: MemArg) -> Self::Output {
3297 let ty = self.check_shared_memarg(memarg)?;
3298 self.pop_operand(Some(ValType::I64))?;
3299 self.pop_operand(Some(ValType::I32))?;
3300 self.pop_operand(Some(ty))?;
3301 self.push_operand(ValType::I32)?;
3302 Ok(())
3303 }
3304 fn visit_memory_atomic_wait64(&mut self, memarg: MemArg) -> Self::Output {
3305 let ty = self.check_shared_memarg(memarg)?;
3306 self.pop_operand(Some(ValType::I64))?;
3307 self.pop_operand(Some(ValType::I64))?;
3308 self.pop_operand(Some(ty))?;
3309 self.push_operand(ValType::I32)?;
3310 Ok(())
3311 }
3312 fn visit_atomic_fence(&mut self) -> Self::Output {
3313 Ok(())
3314 }
3315 fn visit_ref_null(&mut self, mut heap_type: HeapType) -> Self::Output {
3316 if let Some(ty) = RefType::new(true, heap_type) {
3317 self.features
3318 .check_ref_type(ty)
3319 .map_err(|e| BinaryReaderError::new(e, self.offset))?;
3320 }
3321 self.resources
3322 .check_heap_type(&mut heap_type, self.offset)?;
3323 let ty = ValType::Ref(
3324 RefType::new(true, heap_type).expect("existing heap types should be within our limits"),
3325 );
3326 self.push_operand(ty)?;
3327 Ok(())
3328 }
3329
3330 fn visit_ref_as_non_null(&mut self) -> Self::Output {
3331 let ty = self.pop_ref(None)?.as_non_null();
3332 self.push_operand(ty)?;
3333 Ok(())
3334 }
3335 fn visit_br_on_null(&mut self, relative_depth: u32) -> Self::Output {
3336 let ref_ty = self.pop_ref(None)?.as_non_null();
3337 let (ft, kind) = self.jump(relative_depth)?;
3338 let label_types = self.label_types(ft, kind)?;
3339 self.pop_push_label_types(label_types)?;
3340 self.push_operand(ref_ty)?;
3341 Ok(())
3342 }
3343 fn visit_br_on_non_null(&mut self, relative_depth: u32) -> Self::Output {
3344 let (ft, kind) = self.jump(relative_depth)?;
3345
3346 let mut label_types = self.label_types(ft, kind)?;
3347 let expected = match label_types.next_back() {
3348 None => bail!(
3349 self.offset,
3350 "type mismatch: br_on_non_null target has no label types",
3351 ),
3352 Some(ValType::Ref(ty)) => ty,
3353 Some(_) => bail!(
3354 self.offset,
3355 "type mismatch: br_on_non_null target does not end with heap type",
3356 ),
3357 };
3358 self.pop_ref(Some(expected.nullable()))?;
3359
3360 self.pop_push_label_types(label_types)?;
3361 Ok(())
3362 }
3363 fn visit_ref_is_null(&mut self) -> Self::Output {
3364 self.pop_ref(None)?;
3365 self.push_operand(ValType::I32)?;
3366 Ok(())
3367 }
3368 fn visit_ref_func(&mut self, function_index: u32) -> Self::Output {
3369 let type_id = match self.resources.type_id_of_function(function_index) {
3370 Some(id) => id,
3371 None => bail!(
3372 self.offset,
3373 "unknown function {}: function index out of bounds",
3374 function_index,
3375 ),
3376 };
3377 if !self.resources.is_function_referenced(function_index) {
3378 bail!(self.offset, "undeclared function reference");
3379 }
3380
3381 let index = UnpackedIndex::Id(type_id);
3382 let hty = if self.features.custom_descriptors()
3383 && self.resources.has_function_exact_type(function_index)
3384 {
3385 HeapType::Exact(index)
3386 } else {
3387 HeapType::Concrete(index)
3388 };
3389 let ty = ValType::Ref(RefType::new(false, hty).ok_or_else(|| {
3390 BinaryReaderError::new("implementation limit: type index too large", self.offset)
3391 })?);
3392 self.push_operand(ty)?;
3393 Ok(())
3394 }
3395 fn visit_ref_eq(&mut self) -> Self::Output {
3396 let a = self.pop_maybe_shared_ref(AbstractHeapType::Eq)?;
3397 let b = self.pop_maybe_shared_ref(AbstractHeapType::Eq)?;
3398 let a_is_shared = a.is_maybe_shared(&self.resources);
3399 let b_is_shared = b.is_maybe_shared(&self.resources);
3400 match (a_is_shared, b_is_shared) {
3401 (None, Some(_)) | (Some(_), None) | (None, None) => {}
3404
3405 (Some(is_a_shared), Some(is_b_shared)) => {
3406 if is_a_shared != is_b_shared {
3407 bail!(
3408 self.offset,
3409 "type mismatch: expected `ref.eq` types to match `shared`-ness"
3410 );
3411 }
3412 }
3413 }
3414 self.push_operand(ValType::I32)
3415 }
3416 fn visit_memory_init(&mut self, segment: u32, mem: u32) -> Self::Output {
3417 let ty = self.check_memory_index(mem)?;
3418 self.check_data_segment(segment)?;
3419 self.pop_operand(Some(ValType::I32))?;
3420 self.pop_operand(Some(ValType::I32))?;
3421 self.pop_operand(Some(ty))?;
3422 Ok(())
3423 }
3424 fn visit_data_drop(&mut self, segment: u32) -> Self::Output {
3425 self.check_data_segment(segment)?;
3426 Ok(())
3427 }
3428 fn visit_memory_copy(&mut self, dst: u32, src: u32) -> Self::Output {
3429 self.check_enabled(self.features.bulk_memory_opt(), "bulk memory")?;
3430 let dst_ty = self.check_memory_index(dst)?;
3431 let src_ty = self.check_memory_index(src)?;
3432
3433 self.pop_operand(Some(match src_ty {
3436 ValType::I32 => ValType::I32,
3437 _ => dst_ty,
3438 }))?;
3439
3440 self.pop_operand(Some(src_ty))?;
3443 self.pop_operand(Some(dst_ty))?;
3444 Ok(())
3445 }
3446 fn visit_memory_fill(&mut self, mem: u32) -> Self::Output {
3447 self.check_enabled(self.features.bulk_memory_opt(), "bulk memory")?;
3448 let ty = self.check_memory_index(mem)?;
3449 self.pop_operand(Some(ty))?;
3450 self.pop_operand(Some(ValType::I32))?;
3451 self.pop_operand(Some(ty))?;
3452 Ok(())
3453 }
3454 fn visit_memory_discard(&mut self, mem: u32) -> Self::Output {
3455 let ty = self.check_memory_index(mem)?;
3456 self.pop_operand(Some(ty))?;
3457 self.pop_operand(Some(ty))?;
3458 Ok(())
3459 }
3460 fn visit_table_init(&mut self, segment: u32, table: u32) -> Self::Output {
3461 let table = self.table_type_at(table)?;
3462 let segment_ty = self.element_type_at(segment)?;
3463 if !self
3464 .resources
3465 .is_subtype(ValType::Ref(segment_ty), ValType::Ref(table.element_type))
3466 {
3467 bail!(self.offset, "type mismatch");
3468 }
3469 self.pop_operand(Some(ValType::I32))?;
3470 self.pop_operand(Some(ValType::I32))?;
3471 self.pop_operand(Some(table.index_type()))?;
3472 Ok(())
3473 }
3474 fn visit_elem_drop(&mut self, segment: u32) -> Self::Output {
3475 self.element_type_at(segment)?;
3476 Ok(())
3477 }
3478 fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output {
3479 let src = self.table_type_at(src_table)?;
3480 let dst = self.table_type_at(dst_table)?;
3481 if !self.resources.is_subtype(
3482 ValType::Ref(src.element_type),
3483 ValType::Ref(dst.element_type),
3484 ) {
3485 bail!(self.offset, "type mismatch");
3486 }
3487
3488 self.pop_operand(Some(match src.index_type() {
3491 ValType::I32 => ValType::I32,
3492 _ => dst.index_type(),
3493 }))?;
3494
3495 self.pop_operand(Some(src.index_type()))?;
3498 self.pop_operand(Some(dst.index_type()))?;
3499 Ok(())
3500 }
3501 fn visit_table_get(&mut self, table: u32) -> Self::Output {
3502 let table = self.table_type_at(table)?;
3503 debug_assert_type_indices_are_ids(table.element_type.into());
3504 self.pop_operand(Some(table.index_type()))?;
3505 self.push_operand(table.element_type)?;
3506 Ok(())
3507 }
3508 fn visit_table_atomic_get(&mut self, _ordering: Ordering, table: u32) -> Self::Output {
3509 self.visit_table_get(table)?;
3510 let ty = self.table_type_at(table)?.element_type;
3514 let supertype = RefType::ANYREF.shared().unwrap();
3515 if !self.resources.is_subtype(ty.into(), supertype.into()) {
3516 bail!(
3517 self.offset,
3518 "invalid type: `table.atomic.get` only allows subtypes of `anyref`"
3519 );
3520 }
3521 Ok(())
3522 }
3523 fn visit_table_set(&mut self, table: u32) -> Self::Output {
3524 let table = self.table_type_at(table)?;
3525 debug_assert_type_indices_are_ids(table.element_type.into());
3526 self.pop_operand(Some(table.element_type.into()))?;
3527 self.pop_operand(Some(table.index_type()))?;
3528 Ok(())
3529 }
3530 fn visit_table_atomic_set(&mut self, _ordering: Ordering, table: u32) -> Self::Output {
3531 self.visit_table_set(table)?;
3532 let ty = self.table_type_at(table)?.element_type;
3536 let supertype = RefType::ANYREF.shared().unwrap();
3537 if !self.resources.is_subtype(ty.into(), supertype.into()) {
3538 bail!(
3539 self.offset,
3540 "invalid type: `table.atomic.set` only allows subtypes of `anyref`"
3541 );
3542 }
3543 Ok(())
3544 }
3545 fn visit_table_grow(&mut self, table: u32) -> Self::Output {
3546 let table = self.table_type_at(table)?;
3547 debug_assert_type_indices_are_ids(table.element_type.into());
3548 self.pop_operand(Some(table.index_type()))?;
3549 self.pop_operand(Some(table.element_type.into()))?;
3550 self.push_operand(table.index_type())?;
3551 Ok(())
3552 }
3553 fn visit_table_size(&mut self, table: u32) -> Self::Output {
3554 let table = self.table_type_at(table)?;
3555 self.push_operand(table.index_type())?;
3556 Ok(())
3557 }
3558 fn visit_table_fill(&mut self, table: u32) -> Self::Output {
3559 let table = self.table_type_at(table)?;
3560 debug_assert_type_indices_are_ids(table.element_type.into());
3561 self.pop_operand(Some(table.index_type()))?;
3562 self.pop_operand(Some(table.element_type.into()))?;
3563 self.pop_operand(Some(table.index_type()))?;
3564 Ok(())
3565 }
3566 fn visit_table_atomic_rmw_xchg(&mut self, _ordering: Ordering, table: u32) -> Self::Output {
3567 let table = self.table_type_at(table)?;
3568 let elem_ty = table.element_type.into();
3569 debug_assert_type_indices_are_ids(elem_ty);
3570 let supertype = RefType::ANYREF.shared().unwrap();
3571 if !self.resources.is_subtype(elem_ty, supertype.into()) {
3572 bail!(
3573 self.offset,
3574 "invalid type: `table.atomic.rmw.xchg` only allows subtypes of `anyref`"
3575 );
3576 }
3577 self.pop_operand(Some(elem_ty))?;
3578 self.pop_operand(Some(table.index_type()))?;
3579 self.push_operand(elem_ty)?;
3580 Ok(())
3581 }
3582 fn visit_table_atomic_rmw_cmpxchg(&mut self, _ordering: Ordering, table: u32) -> Self::Output {
3583 let table = self.table_type_at(table)?;
3584 let elem_ty = table.element_type.into();
3585 debug_assert_type_indices_are_ids(elem_ty);
3586 let supertype = RefType::EQREF.shared().unwrap();
3587 if !self.resources.is_subtype(elem_ty, supertype.into()) {
3588 bail!(
3589 self.offset,
3590 "invalid type: `table.atomic.rmw.cmpxchg` only allows subtypes of `eqref`"
3591 );
3592 }
3593 self.pop_operand(Some(elem_ty))?;
3594 self.pop_operand(Some(elem_ty))?;
3595 self.pop_operand(Some(table.index_type()))?;
3596 self.push_operand(elem_ty)?;
3597 Ok(())
3598 }
3599 fn visit_struct_new(&mut self, struct_type_index: u32) -> Self::Output {
3600 if let Some(_) = self
3601 .sub_type_at(struct_type_index)?
3602 .composite_type
3603 .descriptor_idx
3604 {
3605 bail!(
3606 self.offset,
3607 "type with descriptor requires descriptor allocation: `struct.new` with type {struct_type_index}"
3608 );
3609 }
3610
3611 let struct_ty = self.struct_type_at(struct_type_index)?;
3612 for ty in struct_ty.fields.iter().rev() {
3613 self.pop_operand(Some(ty.element_type.unpack()))?;
3614 }
3615 self.push_exact_ref_if_available(false, struct_type_index)?;
3616 Ok(())
3617 }
3618 fn visit_struct_new_default(&mut self, type_index: u32) -> Self::Output {
3619 if let Some(_) = self.sub_type_at(type_index)?.composite_type.descriptor_idx {
3620 bail!(
3621 self.offset,
3622 "type with descriptor requires descriptor allocation: `struct.new_default` with type {type_index}"
3623 );
3624 }
3625
3626 let ty = self.struct_type_at(type_index)?;
3627 for field in ty.fields.iter() {
3628 let val_ty = field.element_type.unpack();
3629 if !val_ty.is_defaultable() {
3630 bail!(
3631 self.offset,
3632 "invalid `struct.new_default`: {val_ty} field is not defaultable"
3633 );
3634 }
3635 }
3636 self.push_exact_ref_if_available(false, type_index)?;
3637 Ok(())
3638 }
3639 fn visit_struct_new_desc(&mut self, struct_type_index: u32) -> Self::Output {
3640 if let Some(descriptor_idx) = self
3641 .sub_type_at(struct_type_index)?
3642 .composite_type
3643 .descriptor_idx
3644 {
3645 let ty = ValType::Ref(RefType::exact(true, descriptor_idx));
3646 self.pop_operand(Some(ty))?;
3647 } else {
3648 bail!(
3649 self.offset,
3650 "invalid `struct.new_desc`: type {struct_type_index} is not described"
3651 );
3652 }
3653 let struct_ty = self.struct_type_at(struct_type_index)?;
3654 for ty in struct_ty.fields.iter().rev() {
3655 self.pop_operand(Some(ty.element_type.unpack()))?;
3656 }
3657 self.push_exact_ref_if_available(false, struct_type_index)?;
3658 Ok(())
3659 }
3660 fn visit_struct_new_default_desc(&mut self, type_index: u32) -> Self::Output {
3661 if let Some(descriptor_idx) = self.sub_type_at(type_index)?.composite_type.descriptor_idx {
3662 let ty = ValType::Ref(RefType::exact(true, descriptor_idx));
3663 self.pop_operand(Some(ty))?;
3664 } else {
3665 bail!(
3666 self.offset,
3667 "invalid `struct.new_default_desc`: type {type_index} is not described"
3668 );
3669 }
3670 let ty = self.struct_type_at(type_index)?;
3671 for field in ty.fields.iter() {
3672 let val_ty = field.element_type.unpack();
3673 if !val_ty.is_defaultable() {
3674 bail!(
3675 self.offset,
3676 "invalid `struct.new_default`: {val_ty} field is not defaultable"
3677 );
3678 }
3679 }
3680 self.push_exact_ref_if_available(false, type_index)?;
3681 Ok(())
3682 }
3683 fn visit_struct_get(&mut self, struct_type_index: u32, field_index: u32) -> Self::Output {
3684 let field_ty = self.struct_field_at(struct_type_index, field_index)?;
3685 if field_ty.element_type.is_packed() {
3686 bail!(
3687 self.offset,
3688 "can only use struct `get` with non-packed storage types"
3689 )
3690 }
3691 self.pop_concrete_ref(true, struct_type_index)?;
3692 self.push_operand(field_ty.element_type.unpack())
3693 }
3694 fn visit_struct_atomic_get(
3695 &mut self,
3696 _ordering: Ordering,
3697 struct_type_index: u32,
3698 field_index: u32,
3699 ) -> Self::Output {
3700 self.visit_struct_get(struct_type_index, field_index)?;
3701 let ty = self
3703 .struct_field_at(struct_type_index, field_index)?
3704 .element_type;
3705 let is_valid_type = match ty {
3706 StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
3707 StorageType::Val(v) => self
3708 .resources
3709 .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
3710 _ => false,
3711 };
3712 if !is_valid_type {
3713 bail!(
3714 self.offset,
3715 "invalid type: `struct.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`"
3716 );
3717 }
3718 Ok(())
3719 }
3720 fn visit_struct_get_s(&mut self, struct_type_index: u32, field_index: u32) -> Self::Output {
3721 let field_ty = self.struct_field_at(struct_type_index, field_index)?;
3722 if !field_ty.element_type.is_packed() {
3723 bail!(
3724 self.offset,
3725 "cannot use struct.get_s with non-packed storage types"
3726 )
3727 }
3728 self.pop_concrete_ref(true, struct_type_index)?;
3729 self.push_operand(field_ty.element_type.unpack())
3730 }
3731 fn visit_struct_atomic_get_s(
3732 &mut self,
3733 _ordering: Ordering,
3734 struct_type_index: u32,
3735 field_index: u32,
3736 ) -> Self::Output {
3737 self.visit_struct_get_s(struct_type_index, field_index)?;
3738 debug_assert!(matches!(
3740 self.struct_field_at(struct_type_index, field_index)?
3741 .element_type,
3742 StorageType::I8 | StorageType::I16
3743 ));
3744 Ok(())
3745 }
3746 fn visit_struct_get_u(&mut self, struct_type_index: u32, field_index: u32) -> Self::Output {
3747 let field_ty = self.struct_field_at(struct_type_index, field_index)?;
3748 if !field_ty.element_type.is_packed() {
3749 bail!(
3750 self.offset,
3751 "cannot use struct.get_u with non-packed storage types"
3752 )
3753 }
3754 self.pop_concrete_ref(true, struct_type_index)?;
3755 self.push_operand(field_ty.element_type.unpack())
3756 }
3757 fn visit_struct_atomic_get_u(
3758 &mut self,
3759 _ordering: Ordering,
3760 struct_type_index: u32,
3761 field_index: u32,
3762 ) -> Self::Output {
3763 self.visit_struct_get_u(struct_type_index, field_index)?;
3764 debug_assert!(matches!(
3766 self.struct_field_at(struct_type_index, field_index)?
3767 .element_type,
3768 StorageType::I8 | StorageType::I16
3769 ));
3770 Ok(())
3771 }
3772 fn visit_struct_set(&mut self, struct_type_index: u32, field_index: u32) -> Self::Output {
3773 let field_ty = self.mutable_struct_field_at(struct_type_index, field_index)?;
3774 self.pop_operand(Some(field_ty.element_type.unpack()))?;
3775 self.pop_concrete_ref(true, struct_type_index)?;
3776 Ok(())
3777 }
3778 fn visit_struct_atomic_set(
3779 &mut self,
3780 _ordering: Ordering,
3781 struct_type_index: u32,
3782 field_index: u32,
3783 ) -> Self::Output {
3784 self.visit_struct_set(struct_type_index, field_index)?;
3785 let ty = self
3787 .struct_field_at(struct_type_index, field_index)?
3788 .element_type;
3789 let is_valid_type = match ty {
3790 StorageType::I8 | StorageType::I16 => true,
3791 StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
3792 StorageType::Val(v) => self
3793 .resources
3794 .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
3795 };
3796 if !is_valid_type {
3797 bail!(
3798 self.offset,
3799 "invalid type: `struct.atomic.set` only allows `i8`, `i16`, `i32`, `i64` and subtypes of `anyref`"
3800 );
3801 }
3802 Ok(())
3803 }
3804 fn visit_struct_atomic_rmw_add(
3805 &mut self,
3806 _ordering: Ordering,
3807 struct_type_index: u32,
3808 field_index: u32,
3809 ) -> Self::Output {
3810 self.check_struct_atomic_rmw("add", struct_type_index, field_index)
3811 }
3812 fn visit_struct_atomic_rmw_sub(
3813 &mut self,
3814 _ordering: Ordering,
3815 struct_type_index: u32,
3816 field_index: u32,
3817 ) -> Self::Output {
3818 self.check_struct_atomic_rmw("sub", struct_type_index, field_index)
3819 }
3820 fn visit_struct_atomic_rmw_and(
3821 &mut self,
3822 _ordering: Ordering,
3823 struct_type_index: u32,
3824 field_index: u32,
3825 ) -> Self::Output {
3826 self.check_struct_atomic_rmw("and", struct_type_index, field_index)
3827 }
3828 fn visit_struct_atomic_rmw_or(
3829 &mut self,
3830 _ordering: Ordering,
3831 struct_type_index: u32,
3832 field_index: u32,
3833 ) -> Self::Output {
3834 self.check_struct_atomic_rmw("or", struct_type_index, field_index)
3835 }
3836 fn visit_struct_atomic_rmw_xor(
3837 &mut self,
3838 _ordering: Ordering,
3839 struct_type_index: u32,
3840 field_index: u32,
3841 ) -> Self::Output {
3842 self.check_struct_atomic_rmw("xor", struct_type_index, field_index)
3843 }
3844 fn visit_struct_atomic_rmw_xchg(
3845 &mut self,
3846 _ordering: Ordering,
3847 struct_type_index: u32,
3848 field_index: u32,
3849 ) -> Self::Output {
3850 let field = self.mutable_struct_field_at(struct_type_index, field_index)?;
3851 let is_valid_type = match field.element_type {
3852 StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
3853 StorageType::Val(v) => self
3854 .resources
3855 .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
3856 _ => false,
3857 };
3858 if !is_valid_type {
3859 bail!(
3860 self.offset,
3861 "invalid type: `struct.atomic.rmw.xchg` only allows `i32`, `i64` and subtypes of `anyref`"
3862 );
3863 }
3864 let field_ty = field.element_type.unpack();
3865 self.pop_operand(Some(field_ty))?;
3866 self.pop_concrete_ref(true, struct_type_index)?;
3867 self.push_operand(field_ty)?;
3868 Ok(())
3869 }
3870 fn visit_struct_atomic_rmw_cmpxchg(
3871 &mut self,
3872 _ordering: Ordering,
3873 struct_type_index: u32,
3874 field_index: u32,
3875 ) -> Self::Output {
3876 let field = self.mutable_struct_field_at(struct_type_index, field_index)?;
3877 let is_valid_type = match field.element_type {
3878 StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
3879 StorageType::Val(v) => self
3880 .resources
3881 .is_subtype(v, RefType::EQREF.shared().unwrap().into()),
3882 _ => false,
3883 };
3884 if !is_valid_type {
3885 bail!(
3886 self.offset,
3887 "invalid type: `struct.atomic.rmw.cmpxchg` only allows `i32`, `i64` and subtypes of `eqref`"
3888 );
3889 }
3890 let field_ty = field.element_type.unpack();
3891 self.pop_operand(Some(field_ty))?;
3892 self.pop_operand(Some(field_ty))?;
3893 self.pop_concrete_ref(true, struct_type_index)?;
3894 self.push_operand(field_ty)?;
3895 Ok(())
3896 }
3897 fn visit_array_new(&mut self, type_index: u32) -> Self::Output {
3898 let array_ty = self.array_type_at(type_index)?;
3899 self.pop_operand(Some(ValType::I32))?;
3900 self.pop_operand(Some(array_ty.element_type.unpack()))?;
3901 self.push_exact_ref_if_available(false, type_index)
3902 }
3903 fn visit_array_new_default(&mut self, type_index: u32) -> Self::Output {
3904 let ty = self.array_type_at(type_index)?;
3905 let val_ty = ty.element_type.unpack();
3906 if !val_ty.is_defaultable() {
3907 bail!(
3908 self.offset,
3909 "invalid `array.new_default`: {val_ty} field is not defaultable"
3910 );
3911 }
3912 self.pop_operand(Some(ValType::I32))?;
3913 self.push_exact_ref_if_available(false, type_index)
3914 }
3915 fn visit_array_new_fixed(&mut self, type_index: u32, n: u32) -> Self::Output {
3916 let array_ty = self.array_type_at(type_index)?;
3917 let elem_ty = array_ty.element_type.unpack();
3918 for _ in 0..n {
3919 self.pop_operand(Some(elem_ty))?;
3920 }
3921 self.push_exact_ref_if_available(false, type_index)
3922 }
3923 fn visit_array_new_data(&mut self, type_index: u32, data_index: u32) -> Self::Output {
3924 let array_ty = self.array_type_at(type_index)?;
3925 let elem_ty = array_ty.element_type.unpack();
3926 match elem_ty {
3927 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {}
3928 ValType::Ref(_) => bail!(
3929 self.offset,
3930 "type mismatch: array.new_data can only create arrays with numeric and vector elements"
3931 ),
3932 }
3933 self.check_data_segment(data_index)?;
3934 self.pop_operand(Some(ValType::I32))?;
3935 self.pop_operand(Some(ValType::I32))?;
3936 self.push_exact_ref_if_available(false, type_index)
3937 }
3938 fn visit_array_new_elem(&mut self, type_index: u32, elem_index: u32) -> Self::Output {
3939 let array_ty = self.array_type_at(type_index)?;
3940 let array_ref_ty = match array_ty.element_type.unpack() {
3941 ValType::Ref(rt) => rt,
3942 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => bail!(
3943 self.offset,
3944 "type mismatch: array.new_elem can only create arrays with reference elements"
3945 ),
3946 };
3947 let elem_ref_ty = self.element_type_at(elem_index)?;
3948 if !self
3949 .resources
3950 .is_subtype(elem_ref_ty.into(), array_ref_ty.into())
3951 {
3952 bail!(
3953 self.offset,
3954 "invalid array.new_elem instruction: element segment {elem_index} type mismatch: \
3955 expected {array_ref_ty}, found {elem_ref_ty}"
3956 )
3957 }
3958 self.pop_operand(Some(ValType::I32))?;
3959 self.pop_operand(Some(ValType::I32))?;
3960 self.push_exact_ref_if_available(false, type_index)
3961 }
3962 fn visit_array_get(&mut self, type_index: u32) -> Self::Output {
3963 let array_ty = self.array_type_at(type_index)?;
3964 let elem_ty = array_ty.element_type;
3965 if elem_ty.is_packed() {
3966 bail!(
3967 self.offset,
3968 "cannot use array.get with packed storage types"
3969 )
3970 }
3971 self.pop_operand(Some(ValType::I32))?;
3972 self.pop_concrete_ref(true, type_index)?;
3973 self.push_operand(elem_ty.unpack())
3974 }
3975 fn visit_array_atomic_get(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
3976 self.visit_array_get(type_index)?;
3977 let elem_ty = self.array_type_at(type_index)?.element_type;
3979 let is_valid_type = match elem_ty {
3980 StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
3981 StorageType::Val(v) => self
3982 .resources
3983 .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
3984 _ => false,
3985 };
3986 if !is_valid_type {
3987 bail!(
3988 self.offset,
3989 "invalid type: `array.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`"
3990 );
3991 }
3992 Ok(())
3993 }
3994 fn visit_array_get_s(&mut self, type_index: u32) -> Self::Output {
3995 let array_ty = self.array_type_at(type_index)?;
3996 let elem_ty = array_ty.element_type;
3997 if !elem_ty.is_packed() {
3998 bail!(
3999 self.offset,
4000 "cannot use array.get_s with non-packed storage types"
4001 )
4002 }
4003 self.pop_operand(Some(ValType::I32))?;
4004 self.pop_concrete_ref(true, type_index)?;
4005 self.push_operand(elem_ty.unpack())
4006 }
4007 fn visit_array_atomic_get_s(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
4008 self.visit_array_get_s(type_index)?;
4009 debug_assert!(matches!(
4011 self.array_type_at(type_index)?.element_type,
4012 StorageType::I8 | StorageType::I16
4013 ));
4014 Ok(())
4015 }
4016 fn visit_array_get_u(&mut self, type_index: u32) -> Self::Output {
4017 let array_ty = self.array_type_at(type_index)?;
4018 let elem_ty = array_ty.element_type;
4019 if !elem_ty.is_packed() {
4020 bail!(
4021 self.offset,
4022 "cannot use array.get_u with non-packed storage types"
4023 )
4024 }
4025 self.pop_operand(Some(ValType::I32))?;
4026 self.pop_concrete_ref(true, type_index)?;
4027 self.push_operand(elem_ty.unpack())
4028 }
4029 fn visit_array_atomic_get_u(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
4030 self.visit_array_get_u(type_index)?;
4031 debug_assert!(matches!(
4033 self.array_type_at(type_index)?.element_type,
4034 StorageType::I8 | StorageType::I16
4035 ));
4036 Ok(())
4037 }
4038 fn visit_array_set(&mut self, type_index: u32) -> Self::Output {
4039 let array_ty = self.mutable_array_type_at(type_index)?;
4040 self.pop_operand(Some(array_ty.element_type.unpack()))?;
4041 self.pop_operand(Some(ValType::I32))?;
4042 self.pop_concrete_ref(true, type_index)?;
4043 Ok(())
4044 }
4045 fn visit_array_atomic_set(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
4046 self.visit_array_set(type_index)?;
4047 let elem_ty = self.array_type_at(type_index)?.element_type;
4049 let is_valid_type = match elem_ty {
4050 StorageType::I8 | StorageType::I16 => true,
4051 StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
4052 StorageType::Val(v) => self
4053 .resources
4054 .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
4055 };
4056 if !is_valid_type {
4057 bail!(
4058 self.offset,
4059 "invalid type: `array.atomic.set` only allows `i8`, `i16`, `i32`, `i64` and subtypes of `anyref`"
4060 );
4061 }
4062 Ok(())
4063 }
4064 fn visit_array_len(&mut self) -> Self::Output {
4065 self.pop_maybe_shared_ref(AbstractHeapType::Array)?;
4066 self.push_operand(ValType::I32)
4067 }
4068 fn visit_array_fill(&mut self, array_type_index: u32) -> Self::Output {
4069 let array_ty = self.mutable_array_type_at(array_type_index)?;
4070 self.pop_operand(Some(ValType::I32))?;
4071 self.pop_operand(Some(array_ty.element_type.unpack()))?;
4072 self.pop_operand(Some(ValType::I32))?;
4073 self.pop_concrete_ref(true, array_type_index)?;
4074 Ok(())
4075 }
4076 fn visit_array_copy(&mut self, type_index_dst: u32, type_index_src: u32) -> Self::Output {
4077 let array_ty_dst = self.mutable_array_type_at(type_index_dst)?;
4078 let array_ty_src = self.array_type_at(type_index_src)?;
4079 match (array_ty_dst.element_type, array_ty_src.element_type) {
4080 (StorageType::I8, StorageType::I8) => {}
4081 (StorageType::I8, ty) => bail!(
4082 self.offset,
4083 "array types do not match: expected i8, found {ty}"
4084 ),
4085 (StorageType::I16, StorageType::I16) => {}
4086 (StorageType::I16, ty) => bail!(
4087 self.offset,
4088 "array types do not match: expected i16, found {ty}"
4089 ),
4090 (StorageType::Val(dst), StorageType::Val(src)) => {
4091 if !self.resources.is_subtype(src, dst) {
4092 bail!(
4093 self.offset,
4094 "array types do not match: expected {dst}, found {src}"
4095 )
4096 }
4097 }
4098 (StorageType::Val(dst), src) => {
4099 bail!(
4100 self.offset,
4101 "array types do not match: expected {dst}, found {src}"
4102 )
4103 }
4104 }
4105 self.pop_operand(Some(ValType::I32))?;
4106 self.pop_operand(Some(ValType::I32))?;
4107 self.pop_concrete_ref(true, type_index_src)?;
4108 self.pop_operand(Some(ValType::I32))?;
4109 self.pop_concrete_ref(true, type_index_dst)?;
4110 Ok(())
4111 }
4112 fn visit_array_init_data(
4113 &mut self,
4114 array_type_index: u32,
4115 array_data_index: u32,
4116 ) -> Self::Output {
4117 let array_ty = self.mutable_array_type_at(array_type_index)?;
4118 let val_ty = array_ty.element_type.unpack();
4119 match val_ty {
4120 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {}
4121 ValType::Ref(_) => bail!(
4122 self.offset,
4123 "invalid array.init_data: array type is not numeric or vector"
4124 ),
4125 }
4126 self.check_data_segment(array_data_index)?;
4127 self.pop_operand(Some(ValType::I32))?;
4128 self.pop_operand(Some(ValType::I32))?;
4129 self.pop_operand(Some(ValType::I32))?;
4130 self.pop_concrete_ref(true, array_type_index)?;
4131 Ok(())
4132 }
4133 fn visit_array_init_elem(&mut self, type_index: u32, elem_index: u32) -> Self::Output {
4134 let array_ty = self.mutable_array_type_at(type_index)?;
4135 let array_ref_ty = match array_ty.element_type.unpack() {
4136 ValType::Ref(rt) => rt,
4137 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => bail!(
4138 self.offset,
4139 "type mismatch: array.init_elem can only create arrays with reference elements"
4140 ),
4141 };
4142 let elem_ref_ty = self.element_type_at(elem_index)?;
4143 if !self
4144 .resources
4145 .is_subtype(elem_ref_ty.into(), array_ref_ty.into())
4146 {
4147 bail!(
4148 self.offset,
4149 "invalid array.init_elem instruction: element segment {elem_index} type mismatch: \
4150 expected {array_ref_ty}, found {elem_ref_ty}"
4151 )
4152 }
4153 self.pop_operand(Some(ValType::I32))?;
4154 self.pop_operand(Some(ValType::I32))?;
4155 self.pop_operand(Some(ValType::I32))?;
4156 self.pop_concrete_ref(true, type_index)?;
4157 Ok(())
4158 }
4159 fn visit_array_atomic_rmw_add(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
4160 self.check_array_atomic_rmw("add", type_index)
4161 }
4162 fn visit_array_atomic_rmw_sub(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
4163 self.check_array_atomic_rmw("sub", type_index)
4164 }
4165 fn visit_array_atomic_rmw_and(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
4166 self.check_array_atomic_rmw("and", type_index)
4167 }
4168 fn visit_array_atomic_rmw_or(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
4169 self.check_array_atomic_rmw("or", type_index)
4170 }
4171 fn visit_array_atomic_rmw_xor(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
4172 self.check_array_atomic_rmw("xor", type_index)
4173 }
4174 fn visit_array_atomic_rmw_xchg(
4175 &mut self,
4176 _ordering: Ordering,
4177 type_index: u32,
4178 ) -> Self::Output {
4179 let field = self.mutable_array_type_at(type_index)?;
4180 let is_valid_type = match field.element_type {
4181 StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
4182 StorageType::Val(v) => self
4183 .resources
4184 .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
4185 _ => false,
4186 };
4187 if !is_valid_type {
4188 bail!(
4189 self.offset,
4190 "invalid type: `array.atomic.rmw.xchg` only allows `i32`, `i64` and subtypes of `anyref`"
4191 );
4192 }
4193 let elem_ty = field.element_type.unpack();
4194 self.pop_operand(Some(elem_ty))?;
4195 self.pop_operand(Some(ValType::I32))?;
4196 self.pop_concrete_ref(true, type_index)?;
4197 self.push_operand(elem_ty)?;
4198 Ok(())
4199 }
4200 fn visit_array_atomic_rmw_cmpxchg(
4201 &mut self,
4202 _ordering: Ordering,
4203 type_index: u32,
4204 ) -> Self::Output {
4205 let field = self.mutable_array_type_at(type_index)?;
4206 let is_valid_type = match field.element_type {
4207 StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
4208 StorageType::Val(v) => self
4209 .resources
4210 .is_subtype(v, RefType::EQREF.shared().unwrap().into()),
4211 _ => false,
4212 };
4213 if !is_valid_type {
4214 bail!(
4215 self.offset,
4216 "invalid type: `array.atomic.rmw.cmpxchg` only allows `i32`, `i64` and subtypes of `eqref`"
4217 );
4218 }
4219 let elem_ty = field.element_type.unpack();
4220 self.pop_operand(Some(elem_ty))?;
4221 self.pop_operand(Some(elem_ty))?;
4222 self.pop_operand(Some(ValType::I32))?;
4223 self.pop_concrete_ref(true, type_index)?;
4224 self.push_operand(elem_ty)?;
4225 Ok(())
4226 }
4227 fn visit_any_convert_extern(&mut self) -> Self::Output {
4228 let any_ref = match self.pop_maybe_shared_ref(AbstractHeapType::Extern)? {
4229 MaybeType::Bottom | MaybeType::UnknownRef(_) => {
4230 MaybeType::UnknownRef(Some(AbstractHeapType::Any))
4231 }
4232 MaybeType::Known(ty) => {
4233 let shared = self.resources.is_shared(ty);
4234 let heap_type = HeapType::Abstract {
4235 shared,
4236 ty: AbstractHeapType::Any,
4237 };
4238 let any_ref = RefType::new(ty.is_nullable(), heap_type).unwrap();
4239 MaybeType::Known(any_ref)
4240 }
4241 };
4242 self.push_operand(any_ref)
4243 }
4244 fn visit_extern_convert_any(&mut self) -> Self::Output {
4245 let extern_ref = match self.pop_maybe_shared_ref(AbstractHeapType::Any)? {
4246 MaybeType::Bottom | MaybeType::UnknownRef(_) => {
4247 MaybeType::UnknownRef(Some(AbstractHeapType::Extern))
4248 }
4249 MaybeType::Known(ty) => {
4250 let shared = self.resources.is_shared(ty);
4251 let heap_type = HeapType::Abstract {
4252 shared,
4253 ty: AbstractHeapType::Extern,
4254 };
4255 let extern_ref = RefType::new(ty.is_nullable(), heap_type).unwrap();
4256 MaybeType::Known(extern_ref)
4257 }
4258 };
4259 self.push_operand(extern_ref)
4260 }
4261 fn visit_ref_test_non_null(&mut self, heap_type: HeapType) -> Self::Output {
4262 self.check_ref_test(false, heap_type)
4263 }
4264 fn visit_ref_test_nullable(&mut self, heap_type: HeapType) -> Self::Output {
4265 self.check_ref_test(true, heap_type)
4266 }
4267 fn visit_ref_cast_non_null(&mut self, heap_type: HeapType) -> Self::Output {
4268 self.check_ref_cast(false, heap_type)
4269 }
4270 fn visit_ref_cast_nullable(&mut self, heap_type: HeapType) -> Self::Output {
4271 self.check_ref_cast(true, heap_type)
4272 }
4273 fn visit_br_on_cast(
4274 &mut self,
4275 relative_depth: u32,
4276 mut from_ref_type: RefType,
4277 mut to_ref_type: RefType,
4278 ) -> Self::Output {
4279 self.resources
4280 .check_ref_type(&mut from_ref_type, self.offset)?;
4281 self.resources
4282 .check_ref_type(&mut to_ref_type, self.offset)?;
4283
4284 self.check_br_on_cast_type_hierarchy(from_ref_type, to_ref_type)?;
4285
4286 let (block_ty, frame_kind) = self.jump(relative_depth)?;
4287 let mut label_types = self.label_types(block_ty, frame_kind)?;
4288
4289 match label_types.next_back() {
4290 Some(label_ty) if self.resources.is_subtype(to_ref_type.into(), label_ty) => {
4291 self.pop_operand(Some(from_ref_type.into()))?;
4292 }
4293 Some(label_ty) => bail!(
4294 self.offset,
4295 "type mismatch: casting to type {to_ref_type}, but it does not match \
4296 label result type {label_ty}"
4297 ),
4298 None => bail!(
4299 self.offset,
4300 "type mismatch: br_on_cast to label with empty types, must have a reference type"
4301 ),
4302 };
4303
4304 self.pop_push_label_types(label_types)?;
4305 let diff_ty = RefType::difference(from_ref_type, to_ref_type);
4306 self.push_operand(diff_ty)?;
4307 Ok(())
4308 }
4309 fn visit_br_on_cast_fail(
4310 &mut self,
4311 relative_depth: u32,
4312 mut from_ref_type: RefType,
4313 mut to_ref_type: RefType,
4314 ) -> Self::Output {
4315 self.resources
4316 .check_ref_type(&mut from_ref_type, self.offset)?;
4317 self.resources
4318 .check_ref_type(&mut to_ref_type, self.offset)?;
4319
4320 self.check_br_on_cast_type_hierarchy(from_ref_type, to_ref_type)?;
4321
4322 let (block_ty, frame_kind) = self.jump(relative_depth)?;
4323 let mut label_tys = self.label_types(block_ty, frame_kind)?;
4324
4325 let diff_ty = RefType::difference(from_ref_type, to_ref_type);
4326 match label_tys.next_back() {
4327 Some(label_ty) if self.resources.is_subtype(diff_ty.into(), label_ty) => {
4328 self.pop_operand(Some(from_ref_type.into()))?;
4329 }
4330 Some(label_ty) => bail!(
4331 self.offset,
4332 "type mismatch: expected label result type {label_ty}, found {diff_ty}"
4333 ),
4334 None => bail!(
4335 self.offset,
4336 "type mismatch: expected a reference type, found nothing"
4337 ),
4338 }
4339
4340 self.pop_push_label_types(label_tys)?;
4341 self.push_operand(to_ref_type)?;
4342 Ok(())
4343 }
4344 fn visit_ref_i31(&mut self) -> Self::Output {
4345 self.pop_operand(Some(ValType::I32))?;
4346 self.push_operand(ValType::Ref(RefType::I31))
4347 }
4348 fn visit_ref_i31_shared(&mut self) -> Self::Output {
4349 self.pop_operand(Some(ValType::I32))?;
4350 self.push_operand(ValType::Ref(
4351 RefType::I31.shared().expect("i31 is abstract"),
4352 ))
4353 }
4354 fn visit_i31_get_s(&mut self) -> Self::Output {
4355 self.pop_maybe_shared_ref(AbstractHeapType::I31)?;
4356 self.push_operand(ValType::I32)
4357 }
4358 fn visit_i31_get_u(&mut self) -> Self::Output {
4359 self.pop_maybe_shared_ref(AbstractHeapType::I31)?;
4360 self.push_operand(ValType::I32)
4361 }
4362 fn visit_try(&mut self, mut ty: BlockType) -> Self::Output {
4363 self.check_block_type(&mut ty)?;
4364 for ty in self.params(ty)?.rev() {
4365 self.pop_operand(Some(ty))?;
4366 }
4367 self.push_ctrl(FrameKind::LegacyTry, ty)?;
4368 Ok(())
4369 }
4370 fn visit_catch(&mut self, index: u32) -> Self::Output {
4371 let frame = self.pop_ctrl()?;
4372 debug_assert!(frame.kind == FrameKind::LegacyTry || frame.kind == FrameKind::LegacyCatch);
4373 self.push_bare_ctrl(FrameKind::LegacyCatch, frame.block_type);
4375 let ty = self.exception_tag_at(index)?;
4376 for ty in ty.params() {
4377 self.push_operand(*ty)?;
4378 }
4379 Ok(())
4380 }
4381 fn visit_rethrow(&mut self, relative_depth: u32) -> Self::Output {
4382 let (_, kind) = self.jump(relative_depth)?;
4385 if kind != FrameKind::LegacyCatch && kind != FrameKind::LegacyCatchAll {
4386 bail!(
4387 self.offset,
4388 "invalid rethrow label: target was not a `catch` block"
4389 );
4390 }
4391 self.unreachable()?;
4392 Ok(())
4393 }
4394 fn visit_delegate(&mut self, relative_depth: u32) -> Self::Output {
4395 let frame = self.pop_ctrl()?;
4396 debug_assert_eq!(frame.kind, FrameKind::LegacyTry);
4397 let _ = self.jump(relative_depth)?;
4400 for ty in self.results(frame.block_type)? {
4401 self.push_operand(ty)?;
4402 }
4403 Ok(())
4404 }
4405 fn visit_catch_all(&mut self) -> Self::Output {
4406 let frame = self.pop_ctrl()?;
4407 debug_assert!(frame.kind == FrameKind::LegacyTry || frame.kind == FrameKind::LegacyCatch);
4408 self.push_bare_ctrl(FrameKind::LegacyCatchAll, frame.block_type);
4409 Ok(())
4410 }
4411 fn visit_cont_new(&mut self, type_index: u32) -> Self::Output {
4412 let cont_ty = self.cont_type_at(type_index)?;
4413 let rt = RefType::concrete(true, cont_ty.0);
4414 self.pop_ref(Some(rt))?;
4415 self.push_concrete_ref(false, type_index)?;
4416 Ok(())
4417 }
4418 fn visit_cont_bind(&mut self, argument_index: u32, result_index: u32) -> Self::Output {
4419 let arg_cont = self.cont_type_at(argument_index)?;
4421 let arg_func = self.func_type_of_cont_type(arg_cont);
4422 let res_cont = self.cont_type_at(result_index)?;
4424 let res_func = self.func_type_of_cont_type(res_cont);
4425
4426 if arg_func.params().len() < res_func.params().len() {
4429 bail!(self.offset, "type mismatch in continuation arguments");
4430 }
4431
4432 let argcnt = arg_func.params().len() - res_func.params().len();
4433
4434 if !self.is_func_subtype(
4436 (&arg_func.params()[argcnt..], arg_func.results()),
4437 (res_func.params(), res_func.results()),
4438 ) {
4439 bail!(self.offset, "type mismatch in continuation types");
4440 }
4441
4442 self.pop_concrete_ref(true, argument_index)?;
4444
4445 for &ty in arg_func.params().iter().take(argcnt).rev() {
4447 self.pop_operand(Some(ty))?;
4448 }
4449
4450 self.push_concrete_ref(false, result_index)?;
4452
4453 Ok(())
4454 }
4455 fn visit_suspend(&mut self, tag_index: u32) -> Self::Output {
4456 let ft = &self.tag_at(tag_index)?;
4457 for &ty in ft.params().iter().rev() {
4458 self.pop_operand(Some(ty))?;
4459 }
4460 for &ty in ft.results() {
4461 self.push_operand(ty)?;
4462 }
4463 Ok(())
4464 }
4465 fn visit_resume(&mut self, type_index: u32, table: ResumeTable) -> Self::Output {
4466 let ft = self.check_resume_table(table, type_index)?;
4468 self.pop_concrete_ref(true, type_index)?;
4469 for &ty in ft.params().iter().rev() {
4471 self.pop_operand(Some(ty))?;
4472 }
4473
4474 for &ty in ft.results() {
4476 self.push_operand(ty)?;
4477 }
4478 Ok(())
4479 }
4480 fn visit_resume_throw(
4481 &mut self,
4482 type_index: u32,
4483 tag_index: u32,
4484 table: ResumeTable,
4485 ) -> Self::Output {
4486 let ft = self.check_resume_table(table, type_index)?;
4488 let tag_ty = self.exception_tag_at(tag_index)?;
4490 self.pop_concrete_ref(true, type_index)?;
4491 for &ty in tag_ty.params().iter().rev() {
4493 self.pop_operand(Some(ty))?;
4494 }
4495
4496 for &ty in ft.results() {
4498 self.push_operand(ty)?;
4499 }
4500 Ok(())
4501 }
4502 fn visit_resume_throw_ref(&mut self, type_index: u32, table: ResumeTable) -> Self::Output {
4503 let ft = self.check_resume_table(table, type_index)?;
4504 self.pop_concrete_ref(true, type_index)?;
4505 self.pop_operand(Some(ValType::EXNREF))?;
4506
4507 for &ty in ft.results() {
4508 self.push_operand(ty)?
4509 }
4510 Ok(())
4511 }
4512 fn visit_switch(&mut self, type_index: u32, tag_index: u32) -> Self::Output {
4513 let cont_ty = self.cont_type_at(type_index)?;
4515 let func_ty = self.func_type_of_cont_type(cont_ty);
4516 let tag_ty = self.tag_at(tag_index)?;
4518 if tag_ty.params().len() != 0 {
4519 bail!(self.offset, "type mismatch: non-empty tag parameter type")
4520 }
4521 match func_ty.params().last() {
4523 Some(ValType::Ref(rt)) if rt.is_concrete_type_ref() => {
4524 let other_cont_id = rt
4525 .type_index()
4526 .unwrap()
4527 .unpack()
4528 .as_core_type_id()
4529 .expect("expected canonicalized index");
4530 let sub_ty = self.resources.sub_type_at_id(other_cont_id);
4531 let other_cont_ty =
4532 if let CompositeInnerType::Cont(cont) = &sub_ty.composite_type.inner {
4533 cont
4534 } else {
4535 bail!(self.offset, "non-continuation type");
4536 };
4537 let other_func_ty = self.func_type_of_cont_type(&other_cont_ty);
4538 if !self.is_func_subtype(
4539 (tag_ty.results(), tag_ty.results()),
4540 (func_ty.results(), other_func_ty.results()),
4541 ) {
4542 bail!(self.offset, "type mismatch in continuation types")
4543 }
4544
4545 self.pop_concrete_ref(true, type_index)?;
4547
4548 for &ty in func_ty.params().iter().rev().skip(1) {
4551 self.pop_operand(Some(ty))?;
4552 }
4553
4554 for &ty in other_func_ty.params() {
4556 self.push_operand(ty)?;
4557 }
4558 }
4559 Some(ty) => bail!(
4560 self.offset,
4561 "type mismatch: expected a continuation reference, found {}",
4562 ty_to_str(*ty)
4563 ),
4564 None => bail!(
4565 self.offset,
4566 "type mismatch: instruction requires a continuation reference"
4567 ),
4568 }
4569 Ok(())
4570 }
4571 fn visit_i64_add128(&mut self) -> Result<()> {
4572 self.check_binop128()
4573 }
4574 fn visit_i64_sub128(&mut self) -> Result<()> {
4575 self.check_binop128()
4576 }
4577 fn visit_i64_mul_wide_s(&mut self) -> Result<()> {
4578 self.check_i64_mul_wide()
4579 }
4580 fn visit_i64_mul_wide_u(&mut self) -> Result<()> {
4581 self.check_i64_mul_wide()
4582 }
4583
4584 fn visit_ref_get_desc(&mut self, type_index: u32) -> Self::Output {
4585 let (_, is_exact) = self.pop_concrete_or_exact_ref(true, type_index)?;
4586 match self.sub_type_at(type_index)?.composite_type.descriptor_idx {
4587 Some(descriptor_idx) => {
4588 let ref_ty = if is_exact {
4589 RefType::exact(false, descriptor_idx)
4590 } else {
4591 RefType::concrete(false, descriptor_idx)
4592 };
4593 self.push_operand(ref_ty)
4594 }
4595 None => bail!(self.offset, "expected type with descriptor"),
4596 }
4597 }
4598
4599 fn visit_ref_cast_desc_eq_non_null(&mut self, heap_type: HeapType) -> Self::Output {
4600 self.check_ref_cast_desc_eq(false, heap_type)
4601 }
4602 fn visit_ref_cast_desc_eq_nullable(&mut self, heap_type: HeapType) -> Self::Output {
4603 self.check_ref_cast_desc_eq(true, heap_type)
4604 }
4605 fn visit_br_on_cast_desc_eq(
4606 &mut self,
4607 relative_depth: u32,
4608 mut from_ref_type: RefType,
4609 mut to_ref_type: RefType,
4610 ) -> Self::Output {
4611 let described_ty = to_ref_type.heap_type();
4612
4613 self.resources
4614 .check_ref_type(&mut from_ref_type, self.offset)?;
4615 self.resources
4616 .check_ref_type(&mut to_ref_type, self.offset)?;
4617
4618 self.check_br_on_cast_type_hierarchy(from_ref_type, to_ref_type)?;
4619
4620 self.check_maybe_exact_descriptor_ref(described_ty)?;
4621
4622 let (block_ty, frame_kind) = self.jump(relative_depth)?;
4623 let mut label_types = self.label_types(block_ty, frame_kind)?;
4624
4625 match label_types.next_back() {
4626 Some(label_ty) if self.resources.is_subtype(to_ref_type.into(), label_ty) => {
4627 self.pop_operand(Some(from_ref_type.into()))?;
4628 }
4629 Some(label_ty) => bail!(
4630 self.offset,
4631 "type mismatch: casting to type {to_ref_type}, but it does not match \
4632 label result type {label_ty}"
4633 ),
4634 None => bail!(
4635 self.offset,
4636 "type mismatch: br_on_cast to label with empty types, must have a reference type"
4637 ),
4638 };
4639
4640 self.pop_push_label_types(label_types)?;
4641 let diff_ty = RefType::difference(from_ref_type, to_ref_type);
4642 self.push_operand(diff_ty)?;
4643 Ok(())
4644 }
4645 fn visit_br_on_cast_desc_eq_fail(
4646 &mut self,
4647 relative_depth: u32,
4648 mut from_ref_type: RefType,
4649 mut to_ref_type: RefType,
4650 ) -> Self::Output {
4651 let described_ty = to_ref_type.heap_type();
4652
4653 self.resources
4654 .check_ref_type(&mut from_ref_type, self.offset)?;
4655 self.resources
4656 .check_ref_type(&mut to_ref_type, self.offset)?;
4657
4658 self.check_br_on_cast_type_hierarchy(from_ref_type, to_ref_type)?;
4659
4660 self.check_maybe_exact_descriptor_ref(described_ty)?;
4661
4662 let (block_ty, frame_kind) = self.jump(relative_depth)?;
4663 let mut label_tys = self.label_types(block_ty, frame_kind)?;
4664
4665 let diff_ty = RefType::difference(from_ref_type, to_ref_type);
4666 match label_tys.next_back() {
4667 Some(label_ty) if self.resources.is_subtype(diff_ty.into(), label_ty) => {
4668 self.pop_operand(Some(from_ref_type.into()))?;
4669 }
4670 Some(label_ty) => bail!(
4671 self.offset,
4672 "type mismatch: expected label result type {label_ty}, found {diff_ty}"
4673 ),
4674 None => bail!(
4675 self.offset,
4676 "type mismatch: expected a reference type, found nothing"
4677 ),
4678 }
4679
4680 self.pop_push_label_types(label_tys)?;
4681 self.push_operand(to_ref_type)?;
4682 Ok(())
4683 }
4684}
4685
4686#[derive(Clone, Debug)]
4687enum Either<A, B> {
4688 A(A),
4689 B(B),
4690}
4691
4692impl<A, B> Iterator for Either<A, B>
4693where
4694 A: Iterator,
4695 B: Iterator<Item = A::Item>,
4696{
4697 type Item = A::Item;
4698 fn next(&mut self) -> Option<A::Item> {
4699 match self {
4700 Either::A(a) => a.next(),
4701 Either::B(b) => b.next(),
4702 }
4703 }
4704}
4705
4706impl<A, B> DoubleEndedIterator for Either<A, B>
4707where
4708 A: DoubleEndedIterator,
4709 B: DoubleEndedIterator<Item = A::Item>,
4710{
4711 fn next_back(&mut self) -> Option<A::Item> {
4712 match self {
4713 Either::A(a) => a.next_back(),
4714 Either::B(b) => b.next_back(),
4715 }
4716 }
4717}
4718
4719impl<A, B> ExactSizeIterator for Either<A, B>
4720where
4721 A: ExactSizeIterator,
4722 B: ExactSizeIterator<Item = A::Item>,
4723{
4724 fn len(&self) -> usize {
4725 match self {
4726 Either::A(a) => a.len(),
4727 Either::B(b) => b.len(),
4728 }
4729 }
4730}
4731
4732trait PreciseIterator: ExactSizeIterator + DoubleEndedIterator + Clone + core::fmt::Debug {}
4733impl<T: ExactSizeIterator + DoubleEndedIterator + Clone + core::fmt::Debug> PreciseIterator for T {}
4734
4735impl Locals {
4736 fn define(&mut self, count: u32, ty: ValType) -> bool {
4742 if count == 0 {
4743 return true;
4744 }
4745 let vacant_first = MAX_LOCALS_TO_TRACK.saturating_sub(self.num_locals);
4746 match self.num_locals.checked_add(count) {
4747 Some(num_locals) if num_locals > MAX_WASM_FUNCTION_LOCALS => return false,
4748 None => return false,
4749 Some(num_locals) => self.num_locals = num_locals,
4750 };
4751 let push_to_first = cmp::min(vacant_first, count);
4752 self.first
4753 .extend(iter::repeat(ty).take(push_to_first as usize));
4754 let num_uncached = count - push_to_first;
4755 if num_uncached > 0 {
4756 let max_uncached_idx = self.num_locals - 1;
4757 self.uncached.push((max_uncached_idx, ty));
4758 }
4759 true
4760 }
4761
4762 pub(super) fn len_locals(&self) -> u32 {
4764 self.num_locals
4765 }
4766
4767 #[inline]
4769 pub(super) fn get(&self, idx: u32) -> Option<ValType> {
4770 match self.first.get(idx as usize) {
4771 Some(ty) => Some(*ty),
4772 None => self.get_bsearch(idx),
4773 }
4774 }
4775
4776 fn get_bsearch(&self, idx: u32) -> Option<ValType> {
4777 match self.uncached.binary_search_by_key(&idx, |(idx, _)| *idx) {
4778 Err(i) if i == self.uncached.len() => None,
4781
4782 Ok(i) | Err(i) => Some(self.uncached[i].1),
4788 }
4789 }
4790}
4791
4792impl<R> ModuleArity for WasmProposalValidator<'_, '_, R>
4793where
4794 R: WasmModuleResources,
4795{
4796 fn tag_type_arity(&self, at: u32) -> Option<(u32, u32)> {
4797 self.0
4798 .resources
4799 .tag_at(at)
4800 .map(|x| (x.params().len() as u32, x.results().len() as u32))
4801 }
4802
4803 fn type_index_of_function(&self, function_idx: u32) -> Option<u32> {
4804 self.0.resources.type_index_of_function(function_idx)
4805 }
4806
4807 fn sub_type_at(&self, type_idx: u32) -> Option<&SubType> {
4808 Some(self.0.sub_type_at(type_idx).ok()?)
4809 }
4810
4811 fn func_type_of_cont_type(&self, c: &ContType) -> Option<&FuncType> {
4812 Some(self.0.func_type_of_cont_type(c))
4813 }
4814
4815 fn sub_type_of_ref_type(&self, rt: &RefType) -> Option<&SubType> {
4816 let id = rt.type_index()?.as_core_type_id()?;
4817 Some(self.0.resources.sub_type_at_id(id))
4818 }
4819
4820 fn control_stack_height(&self) -> u32 {
4821 self.0.control.len() as u32
4822 }
4823
4824 fn label_block(&self, depth: u32) -> Option<(BlockType, FrameKind)> {
4825 self.0.jump(depth).ok()
4826 }
4827}
4828
4829impl<R> FrameStack for WasmProposalValidator<'_, '_, R>
4830where
4831 R: WasmModuleResources,
4832{
4833 fn current_frame(&self) -> Option<FrameKind> {
4834 Some(self.0.control.last()?.kind)
4835 }
4836}