1mod canonical;
5pub(crate) use canonical::InternRecGroup;
6
7use self::arc::MaybeOwned;
8use super::{
9 check_max, combine_type_sizes,
10 operators::{OperatorValidator, OperatorValidatorAllocations, ty_to_str},
11 types::{CoreTypeId, EntityType, RecGroupId, TypeAlloc, TypeList},
12};
13#[cfg(feature = "simd")]
14use crate::VisitSimdOperator;
15use crate::{
16 BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FrameKind,
17 FrameStack, FuncType, Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result,
18 SubType, Table, TableInit, TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator,
19 WasmFeatures, WasmModuleResources, limits::*,
20};
21use crate::{CompositeInnerType, prelude::*};
22use alloc::sync::Arc;
23use core::mem;
24
25pub(crate) struct ModuleState {
26 pub module: arc::MaybeOwned<Module>,
32
33 const_expr_allocs: OperatorValidatorAllocations,
34
35 code_section_index: Option<usize>,
37}
38
39impl ModuleState {
40 pub fn new(features: WasmFeatures) -> ModuleState {
41 ModuleState {
42 module: arc::MaybeOwned::new(Module::new(features)),
43 const_expr_allocs: OperatorValidatorAllocations::default(),
44 code_section_index: None,
45 }
46 }
47
48 pub fn next_code_index_and_type(&mut self) -> (u32, u32) {
49 let index = self
50 .code_section_index
51 .get_or_insert(self.module.num_imported_functions as usize);
52
53 assert!(*index < self.module.functions.len());
54
55 let ty = self.module.functions[*index];
56 *index += 1;
57
58 ((*index - 1) as u32, ty)
59 }
60
61 pub fn add_global(
62 &mut self,
63 mut global: Global,
64 types: &TypeList,
65 offset: usize,
66 ) -> Result<()> {
67 self.module
68 .check_global_type(&mut global.ty, types, offset)?;
69 self.check_const_expr(&global.init_expr, global.ty.content_type, types)?;
70 self.module.assert_mut().globals.push(global.ty);
71 Ok(())
72 }
73
74 pub fn add_table(
75 &mut self,
76 mut table: Table<'_>,
77 types: &TypeList,
78 offset: usize,
79 ) -> Result<()> {
80 self.module.check_table_type(&mut table.ty, types, offset)?;
81
82 match &table.init {
83 TableInit::RefNull => {
84 if !table.ty.element_type.is_nullable() {
85 bail!(offset, "type mismatch: non-defaultable element type");
86 }
87 }
88 TableInit::Expr(expr) => {
89 if !self.module.features.function_references() {
90 bail!(
91 offset,
92 "tables with expression initializers require \
93 the function-references proposal"
94 );
95 }
96 self.check_const_expr(expr, table.ty.element_type.into(), types)?;
97 }
98 }
99 self.module.assert_mut().tables.push(table.ty);
100 Ok(())
101 }
102
103 pub fn add_data_segment(&mut self, data: Data, types: &TypeList, offset: usize) -> Result<()> {
104 match data.kind {
105 DataKind::Passive => {
106 if !self.module.features.bulk_memory() {
107 bail!(
108 offset,
109 "passive data segments require the bulk-memory proposal"
110 );
111 }
112 Ok(())
113 }
114 DataKind::Active {
115 memory_index,
116 offset_expr,
117 } => {
118 let ty = self.module.memory_at(memory_index, offset)?.index_type();
119 self.check_const_expr(&offset_expr, ty, types)
120 }
121 }
122 }
123
124 pub fn add_element_segment(
125 &mut self,
126 mut e: Element,
127 types: &TypeList,
128 offset: usize,
129 ) -> Result<()> {
130 let element_ty = match &mut e.items {
133 crate::ElementItems::Functions(_) => RefType::FUNC,
134 crate::ElementItems::Expressions(ty, _) => {
135 self.module.check_ref_type(ty, offset)?;
136 *ty
137 }
138 };
139
140 match e.kind {
141 ElementKind::Active {
142 table_index,
143 offset_expr,
144 } => {
145 let table = self.module.table_at(table_index.unwrap_or(0), offset)?;
146 if !types.reftype_is_subtype(element_ty, table.element_type) {
147 return Err(BinaryReaderError::new(
148 format!(
149 "type mismatch: invalid element type `{}` for table type `{}`",
150 ty_to_str(element_ty.into()),
151 ty_to_str(table.element_type.into()),
152 ),
153 offset,
154 ));
155 }
156
157 self.check_const_expr(&offset_expr, table.index_type(), types)?;
158 }
159 ElementKind::Passive | ElementKind::Declared => {
160 if !self.module.features.bulk_memory() {
161 return Err(BinaryReaderError::new(
162 "bulk memory must be enabled",
163 offset,
164 ));
165 }
166 }
167 }
168
169 let validate_count = |count: u32| -> Result<(), BinaryReaderError> {
170 if count > MAX_WASM_TABLE_ENTRIES as u32 {
171 Err(BinaryReaderError::new(
172 "number of elements is out of bounds",
173 offset,
174 ))
175 } else {
176 Ok(())
177 }
178 };
179 match e.items {
180 crate::ElementItems::Functions(reader) => {
181 let count = reader.count();
182 validate_count(count)?;
183 for f in reader.into_iter_with_offsets() {
184 let (offset, f) = f?;
185 self.module.get_func_type(f, types, offset)?;
186 self.module.assert_mut().function_references.insert(f);
187 }
188 }
189 crate::ElementItems::Expressions(ty, reader) => {
190 validate_count(reader.count())?;
191 for expr in reader {
192 self.check_const_expr(&expr?, ValType::Ref(ty), types)?;
193 }
194 }
195 }
196 self.module.assert_mut().element_types.push(element_ty);
197 Ok(())
198 }
199
200 fn check_const_expr(
201 &mut self,
202 expr: &ConstExpr<'_>,
203 expected_ty: ValType,
204 types: &TypeList,
205 ) -> Result<()> {
206 let mut validator = VisitConstOperator {
207 offset: 0,
208 uninserted_funcref: false,
209 ops: OperatorValidator::new_const_expr(
210 &self.module.features,
211 expected_ty,
212 mem::take(&mut self.const_expr_allocs),
213 ),
214 resources: OperatorValidatorResources {
215 types,
216 module: &mut self.module,
217 },
218 };
219
220 let mut ops = expr.get_binary_reader();
221 while !ops.eof() {
222 validator.offset = ops.original_position();
223 ops.visit_operator(&mut validator)??;
224 }
225
226 assert!(!validator.uninserted_funcref);
228
229 self.const_expr_allocs = validator.ops.into_allocations();
230
231 return Ok(());
232
233 struct VisitConstOperator<'a> {
234 offset: usize,
235 uninserted_funcref: bool,
236 ops: OperatorValidator,
237 resources: OperatorValidatorResources<'a>,
238 }
239
240 impl VisitConstOperator<'_> {
241 fn validator(&mut self) -> impl VisitOperator<'_, Output = Result<()>> {
242 self.ops.with_resources(&self.resources, self.offset)
243 }
244
245 fn validate_extended_const(&mut self, op: &str) -> Result<()> {
246 if self.ops.features.extended_const() {
247 Ok(())
248 } else {
249 Err(BinaryReaderError::new(
250 format!("constant expression required: non-constant operator: {op}"),
251 self.offset,
252 ))
253 }
254 }
255
256 fn validate_gc(&mut self, op: &str) -> Result<()> {
257 if self.ops.features.gc() {
258 Ok(())
259 } else {
260 Err(BinaryReaderError::new(
261 format!("constant expression required: non-constant operator: {op}"),
262 self.offset,
263 ))
264 }
265 }
266
267 fn validate_shared_everything_threads(&mut self, op: &str) -> Result<()> {
268 if self.ops.features.shared_everything_threads() {
269 Ok(())
270 } else {
271 Err(BinaryReaderError::new(
272 format!("constant expression required: non-constant operator: {op}"),
273 self.offset,
274 ))
275 }
276 }
277
278 fn validate_global(&mut self, index: u32) -> Result<()> {
279 let module = &self.resources.module;
280 let global = module.global_at(index, self.offset)?;
281
282 if index >= module.num_imported_globals && !self.ops.features.gc() {
283 return Err(BinaryReaderError::new(
284 "constant expression required: global.get of locally defined global",
285 self.offset,
286 ));
287 }
288 if global.mutable {
289 return Err(BinaryReaderError::new(
290 "constant expression required: global.get of mutable global",
291 self.offset,
292 ));
293 }
294 Ok(())
295 }
296
297 fn insert_ref_func(&mut self, index: u32) {
315 if self.resources.module.as_mut().is_none() {
316 self.uninserted_funcref = true;
317 } else {
318 self.resources
319 .module
320 .assert_mut()
321 .function_references
322 .insert(index);
323 }
324 }
325
326 fn not_const(&self, instr: &str) -> BinaryReaderError {
327 BinaryReaderError::new(
328 format!("constant expression required: non-constant operator: {instr}"),
329 self.offset,
330 )
331 }
332 }
333
334 #[cfg_attr(not(feature = "simd"), allow(unused_macro_rules))]
335 macro_rules! define_visit_operator {
336 ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
337 $(
338 #[allow(unused_variables)]
339 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
340 define_visit_operator!(@visit self $visit $($($arg)*)?)
341 }
342 )*
343 };
344
345 (@visit $self:ident visit_i32_const $val:ident) => {{
347 $self.validator().visit_i32_const($val)
348 }};
349 (@visit $self:ident visit_i64_const $val:ident) => {{
350 $self.validator().visit_i64_const($val)
351 }};
352 (@visit $self:ident visit_f32_const $val:ident) => {{
353 $self.validator().visit_f32_const($val)
354 }};
355 (@visit $self:ident visit_f64_const $val:ident) => {{
356 $self.validator().visit_f64_const($val)
357 }};
358 (@visit $self:ident visit_v128_const $val:ident) => {{
359 $self.validator().simd_visitor().unwrap().visit_v128_const($val)
360 }};
361 (@visit $self:ident visit_ref_null $val:ident) => {{
362 $self.validator().visit_ref_null($val)
363 }};
364 (@visit $self:ident visit_end) => {{
365 $self.validator().visit_end()
366 }};
367
368
369 (@visit $self:ident visit_i32_add) => {{
371 $self.validate_extended_const("i32.add")?;
372 $self.validator().visit_i32_add()
373 }};
374 (@visit $self:ident visit_i32_sub) => {{
375 $self.validate_extended_const("i32.sub")?;
376 $self.validator().visit_i32_sub()
377 }};
378 (@visit $self:ident visit_i32_mul) => {{
379 $self.validate_extended_const("i32.mul")?;
380 $self.validator().visit_i32_mul()
381 }};
382 (@visit $self:ident visit_i64_add) => {{
383 $self.validate_extended_const("i64.add")?;
384 $self.validator().visit_i64_add()
385 }};
386 (@visit $self:ident visit_i64_sub) => {{
387 $self.validate_extended_const("i64.sub")?;
388 $self.validator().visit_i64_sub()
389 }};
390 (@visit $self:ident visit_i64_mul) => {{
391 $self.validate_extended_const("i64.mul")?;
392 $self.validator().visit_i64_mul()
393 }};
394
395 (@visit $self:ident visit_struct_new $type_index:ident) => {{
398 $self.validate_gc("struct.new")?;
399 $self.validator().visit_struct_new($type_index)
400 }};
401 (@visit $self:ident visit_struct_new_default $type_index:ident) => {{
402 $self.validate_gc("struct.new_default")?;
403 $self.validator().visit_struct_new_default($type_index)
404 }};
405 (@visit $self:ident visit_array_new $type_index:ident) => {{
406 $self.validate_gc("array.new")?;
407 $self.validator().visit_array_new($type_index)
408 }};
409 (@visit $self:ident visit_array_new_default $type_index:ident) => {{
410 $self.validate_gc("array.new_default")?;
411 $self.validator().visit_array_new_default($type_index)
412 }};
413 (@visit $self:ident visit_array_new_fixed $type_index:ident $n:ident) => {{
414 $self.validate_gc("array.new_fixed")?;
415 $self.validator().visit_array_new_fixed($type_index, $n)
416 }};
417 (@visit $self:ident visit_ref_i31) => {{
418 $self.validate_gc("ref.i31")?;
419 $self.validator().visit_ref_i31()
420 }};
421 (@visit $self:ident visit_extern_convert_any) => {{
422 $self.validate_gc("extern.convert_any")?;
423 $self.validator().visit_extern_convert_any()
424 }};
425 (@visit $self:ident visit_any_convert_extern) => {{
426 $self.validate_gc("any.convert_extern")?;
427 $self.validator().visit_any_convert_extern()
428 }};
429 (@visit $self:ident visit_ref_i31_shared) => {{
430 $self.validate_shared_everything_threads("ref.i31_shared")?;
431 $self.validator().visit_ref_i31_shared()
432 }};
433
434 (@visit $self:ident visit_global_get $idx:ident) => {{
437 $self.validate_global($idx)?;
438 $self.validator().visit_global_get($idx)
439 }};
440 (@visit $self:ident visit_ref_func $idx:ident) => {{
443 $self.insert_ref_func($idx);
444 $self.validator().visit_ref_func($idx)
445 }};
446
447 (@visit $self:ident $op:ident $($args:tt)*) => {{
448 Err($self.not_const(stringify!($op)))
449 }}
450 }
451
452 impl<'a> VisitOperator<'a> for VisitConstOperator<'a> {
453 type Output = Result<()>;
454
455 #[cfg(feature = "simd")]
456 fn simd_visitor(
457 &mut self,
458 ) -> Option<&mut dyn crate::VisitSimdOperator<'a, Output = Self::Output>> {
459 Some(self)
460 }
461
462 crate::for_each_visit_operator!(define_visit_operator);
463 }
464
465 #[cfg(feature = "simd")]
466 impl<'a> VisitSimdOperator<'a> for VisitConstOperator<'a> {
467 crate::for_each_visit_simd_operator!(define_visit_operator);
468 }
469
470 impl<'a> FrameStack for VisitConstOperator<'a> {
471 fn current_frame(&self) -> Option<FrameKind> {
472 Some(self.ops.get_frame(0)?.kind)
473 }
474 }
475 }
476}
477
478#[derive(Debug)]
479pub(crate) struct Module {
480 pub snapshot: Option<Arc<TypeList>>,
484 pub types: Vec<CoreTypeId>,
486 pub tables: Vec<TableType>,
487 pub memories: Vec<MemoryType>,
488 pub globals: Vec<GlobalType>,
489 pub element_types: Vec<RefType>,
490 pub data_count: Option<u32>,
491 pub functions: Vec<u32>,
493 pub tags: Vec<CoreTypeId>,
494 pub function_references: Set<u32>,
495 pub imports: IndexMap<(String, String), Vec<EntityType>>,
496 pub exports: IndexMap<String, EntityType>,
497 pub type_size: u32,
498 num_imported_globals: u32,
499 num_imported_functions: u32,
500 features: WasmFeatures,
501}
502
503impl Module {
504 pub fn new(features: WasmFeatures) -> Self {
505 Self {
506 snapshot: Default::default(),
507 types: Default::default(),
508 tables: Default::default(),
509 memories: Default::default(),
510 globals: Default::default(),
511 element_types: Default::default(),
512 data_count: Default::default(),
513 functions: Default::default(),
514 tags: Default::default(),
515 function_references: Default::default(),
516 imports: Default::default(),
517 exports: Default::default(),
518 type_size: 1,
519 num_imported_globals: Default::default(),
520 num_imported_functions: Default::default(),
521 features,
522 }
523 }
524
525 pub(crate) fn add_types(
526 &mut self,
527 rec_group: RecGroup,
528 types: &mut TypeAlloc,
529 offset: usize,
530 check_limit: bool,
531 ) -> Result<()> {
532 if check_limit {
533 check_max(
534 self.types.len(),
535 rec_group.types().len() as u32,
536 MAX_WASM_TYPES,
537 "types",
538 offset,
539 )?;
540 }
541 self.canonicalize_and_intern_rec_group(types, rec_group, offset)
542 }
543
544 pub fn add_import(
545 &mut self,
546 mut import: crate::Import,
547 types: &TypeList,
548 offset: usize,
549 ) -> Result<()> {
550 let entity = self.check_type_ref(&mut import.ty, types, offset)?;
551
552 let (len, max, desc) = match import.ty {
553 TypeRef::Func(type_index) => {
554 self.functions.push(type_index);
555 self.num_imported_functions += 1;
556 (self.functions.len(), MAX_WASM_FUNCTIONS, "functions")
557 }
558 TypeRef::Table(ty) => {
559 self.tables.push(ty);
560 (self.tables.len(), self.max_tables(), "tables")
561 }
562 TypeRef::Memory(ty) => {
563 self.memories.push(ty);
564 (self.memories.len(), self.max_memories(), "memories")
565 }
566 TypeRef::Tag(ty) => {
567 self.tags.push(self.types[ty.func_type_idx as usize]);
568 (self.tags.len(), MAX_WASM_TAGS, "tags")
569 }
570 TypeRef::Global(ty) => {
571 if !self.features.mutable_global() && ty.mutable {
572 return Err(BinaryReaderError::new(
573 "mutable global support is not enabled",
574 offset,
575 ));
576 }
577 self.globals.push(ty);
578 self.num_imported_globals += 1;
579 (self.globals.len(), MAX_WASM_GLOBALS, "globals")
580 }
581 };
582
583 check_max(len, 0, max, desc, offset)?;
584
585 self.type_size = combine_type_sizes(self.type_size, entity.info(types).size(), offset)?;
586
587 self.imports
588 .entry((import.module.to_string(), import.name.to_string()))
589 .or_default()
590 .push(entity);
591
592 Ok(())
593 }
594
595 pub fn add_export(
596 &mut self,
597 name: &str,
598 ty: EntityType,
599 offset: usize,
600 check_limit: bool,
601 types: &TypeList,
602 ) -> Result<()> {
603 if !self.features.mutable_global() {
604 if let EntityType::Global(global_type) = ty {
605 if global_type.mutable {
606 return Err(BinaryReaderError::new(
607 "mutable global support is not enabled",
608 offset,
609 ));
610 }
611 }
612 }
613
614 if check_limit {
615 check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?;
616 }
617
618 self.type_size = combine_type_sizes(self.type_size, ty.info(types).size(), offset)?;
619
620 match self.exports.insert(name.to_string(), ty) {
621 Some(_) => Err(format_err!(
622 offset,
623 "duplicate export name `{name}` already defined"
624 )),
625 None => Ok(()),
626 }
627 }
628
629 pub fn add_function(&mut self, type_index: u32, types: &TypeList, offset: usize) -> Result<()> {
630 self.func_type_at(type_index, types, offset)?;
631 self.functions.push(type_index);
632 Ok(())
633 }
634
635 pub fn add_memory(&mut self, ty: MemoryType, offset: usize) -> Result<()> {
636 self.check_memory_type(&ty, offset)?;
637 self.memories.push(ty);
638 Ok(())
639 }
640
641 pub fn add_tag(&mut self, ty: TagType, types: &TypeList, offset: usize) -> Result<()> {
642 self.check_tag_type(&ty, types, offset)?;
643 self.tags.push(self.types[ty.func_type_idx as usize]);
644 Ok(())
645 }
646
647 fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> {
648 let id = self.type_id_at(idx, offset)?;
649 Ok(&types[id])
650 }
651
652 fn func_type_at<'a>(
653 &self,
654 type_index: u32,
655 types: &'a TypeList,
656 offset: usize,
657 ) -> Result<&'a FuncType> {
658 match &self
659 .sub_type_at(types, type_index, offset)?
660 .composite_type
661 .inner
662 {
663 CompositeInnerType::Func(f) => Ok(f),
664 _ => bail!(offset, "type index {type_index} is not a function type"),
665 }
666 }
667
668 pub fn check_type_ref(
669 &self,
670 type_ref: &mut TypeRef,
671 types: &TypeList,
672 offset: usize,
673 ) -> Result<EntityType> {
674 Ok(match type_ref {
675 TypeRef::Func(type_index) => {
676 self.func_type_at(*type_index, types, offset)?;
677 EntityType::Func(self.types[*type_index as usize])
678 }
679 TypeRef::Table(t) => {
680 self.check_table_type(t, types, offset)?;
681 EntityType::Table(*t)
682 }
683 TypeRef::Memory(t) => {
684 self.check_memory_type(t, offset)?;
685 EntityType::Memory(*t)
686 }
687 TypeRef::Tag(t) => {
688 self.check_tag_type(t, types, offset)?;
689 EntityType::Tag(self.types[t.func_type_idx as usize])
690 }
691 TypeRef::Global(t) => {
692 self.check_global_type(t, types, offset)?;
693 EntityType::Global(*t)
694 }
695 })
696 }
697
698 fn check_table_type(&self, ty: &mut TableType, types: &TypeList, offset: usize) -> Result<()> {
699 if ty.element_type != RefType::FUNCREF {
702 self.check_ref_type(&mut ty.element_type, offset)?
703 }
704
705 self.check_limits(ty.initial, ty.maximum, offset)?;
706 if ty.table64 && !self.features().memory64() {
707 bail!(offset, "memory64 must be enabled for 64-bit tables");
708 }
709 if ty.shared && !self.features().shared_everything_threads() {
710 bail!(
711 offset,
712 "shared tables require the shared-everything-threads proposal"
713 );
714 }
715
716 let true_maximum = if ty.table64 {
717 u64::MAX
718 } else {
719 u32::MAX as u64
720 };
721 let err = format!("table size must be at most {true_maximum:#x} entries");
722 if ty.initial > true_maximum {
723 return Err(BinaryReaderError::new(err, offset));
724 }
725 if let Some(maximum) = ty.maximum {
726 if maximum > true_maximum {
727 return Err(BinaryReaderError::new(err, offset));
728 }
729 }
730 if ty.shared && !types.reftype_is_shared(ty.element_type) {
731 return Err(BinaryReaderError::new(
732 "shared tables must have a shared element type",
733 offset,
734 ));
735 }
736 Ok(())
737 }
738
739 fn check_memory_type(&self, ty: &MemoryType, offset: usize) -> Result<()> {
740 self.check_limits(ty.initial, ty.maximum, offset)?;
741
742 if ty.memory64 && !self.features().memory64() {
743 bail!(offset, "memory64 must be enabled for 64-bit memories");
744 }
745 if ty.shared && !self.features().threads() {
746 bail!(offset, "threads must be enabled for shared memories");
747 }
748
749 let page_size = if let Some(page_size_log2) = ty.page_size_log2 {
750 if !self.features().custom_page_sizes() {
751 return Err(BinaryReaderError::new(
752 "the custom page sizes proposal must be enabled to customize a memory's page size",
753 offset,
754 ));
755 }
756 if page_size_log2 != 0 && page_size_log2 != 16 {
759 return Err(BinaryReaderError::new("invalid custom page size", offset));
760 }
761 let page_size = 1_u64 << page_size_log2;
762 debug_assert!(page_size.is_power_of_two());
763 debug_assert!(page_size == DEFAULT_WASM_PAGE_SIZE || page_size == 1);
764 page_size
765 } else {
766 let page_size_log2 = 16;
767 debug_assert_eq!(DEFAULT_WASM_PAGE_SIZE, 1 << page_size_log2);
768 DEFAULT_WASM_PAGE_SIZE
769 };
770 let true_maximum = if ty.memory64 {
771 max_wasm_memory64_pages(page_size)
772 } else {
773 max_wasm_memory32_pages(page_size)
774 };
775 let err = format!("memory size must be at most {true_maximum:#x} {page_size}-byte pages");
776 if ty.initial > true_maximum {
777 return Err(BinaryReaderError::new(err, offset));
778 }
779 if let Some(maximum) = ty.maximum {
780 if maximum > true_maximum {
781 return Err(BinaryReaderError::new(err, offset));
782 }
783 }
784 if ty.shared && ty.maximum.is_none() {
785 return Err(BinaryReaderError::new(
786 "shared memory must have maximum size",
787 offset,
788 ));
789 }
790 Ok(())
791 }
792
793 #[cfg(feature = "component-model")]
794 pub(crate) fn imports_for_module_type(
795 &self,
796 offset: usize,
797 ) -> Result<IndexMap<(String, String), EntityType>> {
798 self.imports
801 .iter()
802 .map(|((module, name), types)| {
803 if types.len() != 1 {
804 bail!(
805 offset,
806 "module has a duplicate import name `{module}:{name}` \
807 that is not allowed in components",
808 );
809 }
810 Ok(((module.clone(), name.clone()), types[0]))
811 })
812 .collect::<Result<_>>()
813 }
814
815 fn check_value_type(&self, ty: &mut ValType, offset: usize) -> Result<()> {
816 match ty {
819 ValType::Ref(rt) => self.check_ref_type(rt, offset),
820 _ => self
821 .features
822 .check_value_type(*ty)
823 .map_err(|e| BinaryReaderError::new(e, offset)),
824 }
825 }
826
827 fn check_ref_type(&self, ty: &mut RefType, offset: usize) -> Result<()> {
828 self.features
829 .check_ref_type(*ty)
830 .map_err(|e| BinaryReaderError::new(e, offset))?;
831 let mut hty = ty.heap_type();
832 self.check_heap_type(&mut hty, offset)?;
833 *ty = RefType::new(ty.is_nullable(), hty).unwrap();
834 Ok(())
835 }
836
837 fn check_heap_type(&self, ty: &mut HeapType, offset: usize) -> Result<()> {
838 let type_index = match ty {
840 HeapType::Abstract { .. } => return Ok(()),
841 HeapType::Concrete(type_index) => type_index,
842 };
843 match type_index {
844 UnpackedIndex::Module(idx) => {
845 let id = self.type_id_at(*idx, offset)?;
846 *type_index = UnpackedIndex::Id(id);
847 Ok(())
848 }
849 UnpackedIndex::RecGroup(_) | UnpackedIndex::Id(_) => unreachable!(),
853 }
854 }
855
856 fn check_tag_type(&self, ty: &TagType, types: &TypeList, offset: usize) -> Result<()> {
857 if !self.features().exceptions() {
858 bail!(offset, "exceptions proposal not enabled");
859 }
860 let ty = self.func_type_at(ty.func_type_idx, types, offset)?;
861 if !ty.results().is_empty() && !self.features.stack_switching() {
862 return Err(BinaryReaderError::new(
863 "invalid exception type: non-empty tag result type",
864 offset,
865 ));
866 }
867 Ok(())
868 }
869
870 fn check_global_type(
871 &self,
872 ty: &mut GlobalType,
873 types: &TypeList,
874 offset: usize,
875 ) -> Result<()> {
876 self.check_value_type(&mut ty.content_type, offset)?;
877 if ty.shared && !self.features.shared_everything_threads() {
878 bail!(
879 offset,
880 "shared globals require the shared-everything-threads proposal"
881 );
882 }
883 if ty.shared && !types.valtype_is_shared(ty.content_type) {
884 return Err(BinaryReaderError::new(
885 "shared globals must have a shared value type",
886 offset,
887 ));
888 }
889 Ok(())
890 }
891
892 fn check_limits<T>(&self, initial: T, maximum: Option<T>, offset: usize) -> Result<()>
893 where
894 T: Into<u64>,
895 {
896 if let Some(max) = maximum {
897 if initial.into() > max.into() {
898 return Err(BinaryReaderError::new(
899 "size minimum must not be greater than maximum",
900 offset,
901 ));
902 }
903 }
904 Ok(())
905 }
906
907 pub fn max_tables(&self) -> usize {
908 if self.features.reference_types() {
909 MAX_WASM_TABLES
910 } else {
911 1
912 }
913 }
914
915 pub fn max_memories(&self) -> usize {
916 if self.features.multi_memory() {
917 MAX_WASM_MEMORIES
918 } else {
919 1
920 }
921 }
922
923 pub fn export_to_entity_type(
924 &mut self,
925 export: &crate::Export,
926 offset: usize,
927 ) -> Result<EntityType> {
928 let check = |ty: &str, index: u32, total: usize| {
929 if index as usize >= total {
930 Err(format_err!(
931 offset,
932 "unknown {ty} {index}: exported {ty} index out of bounds",
933 ))
934 } else {
935 Ok(())
936 }
937 };
938
939 Ok(match export.kind {
940 ExternalKind::Func => {
941 check("function", export.index, self.functions.len())?;
942 self.function_references.insert(export.index);
943 EntityType::Func(self.types[self.functions[export.index as usize] as usize])
944 }
945 ExternalKind::Table => {
946 check("table", export.index, self.tables.len())?;
947 EntityType::Table(self.tables[export.index as usize])
948 }
949 ExternalKind::Memory => {
950 check("memory", export.index, self.memories.len())?;
951 EntityType::Memory(self.memories[export.index as usize])
952 }
953 ExternalKind::Global => {
954 check("global", export.index, self.globals.len())?;
955 EntityType::Global(self.globals[export.index as usize])
956 }
957 ExternalKind::Tag => {
958 check("tag", export.index, self.tags.len())?;
959 EntityType::Tag(self.tags[export.index as usize])
960 }
961 })
962 }
963
964 pub fn get_func_type<'a>(
965 &self,
966 func_idx: u32,
967 types: &'a TypeList,
968 offset: usize,
969 ) -> Result<&'a FuncType> {
970 match self.functions.get(func_idx as usize) {
971 Some(idx) => self.func_type_at(*idx, types, offset),
972 None => Err(format_err!(
973 offset,
974 "unknown function {func_idx}: func index out of bounds",
975 )),
976 }
977 }
978
979 fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> {
980 match self.globals.get(idx as usize) {
981 Some(t) => Ok(t),
982 None => Err(format_err!(
983 offset,
984 "unknown global {idx}: global index out of bounds"
985 )),
986 }
987 }
988
989 fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> {
990 match self.tables.get(idx as usize) {
991 Some(t) => Ok(t),
992 None => Err(format_err!(
993 offset,
994 "unknown table {idx}: table index out of bounds"
995 )),
996 }
997 }
998
999 fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> {
1000 match self.memories.get(idx as usize) {
1001 Some(t) => Ok(t),
1002 None => Err(format_err!(
1003 offset,
1004 "unknown memory {idx}: memory index out of bounds"
1005 )),
1006 }
1007 }
1008}
1009
1010impl InternRecGroup for Module {
1011 fn features(&self) -> &WasmFeatures {
1012 &self.features
1013 }
1014
1015 fn add_type_id(&mut self, id: CoreTypeId) {
1016 self.types.push(id);
1017 }
1018
1019 fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> {
1020 self.types
1021 .get(idx as usize)
1022 .copied()
1023 .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
1024 }
1025
1026 fn types_len(&self) -> u32 {
1027 u32::try_from(self.types.len()).unwrap()
1028 }
1029}
1030
1031struct OperatorValidatorResources<'a> {
1032 module: &'a mut MaybeOwned<Module>,
1033 types: &'a TypeList,
1034}
1035
1036impl WasmModuleResources for OperatorValidatorResources<'_> {
1037 fn table_at(&self, at: u32) -> Option<TableType> {
1038 self.module.tables.get(at as usize).cloned()
1039 }
1040
1041 fn memory_at(&self, at: u32) -> Option<MemoryType> {
1042 self.module.memories.get(at as usize).cloned()
1043 }
1044
1045 fn tag_at(&self, at: u32) -> Option<&FuncType> {
1046 let type_id = *self.module.tags.get(at as usize)?;
1047 Some(self.types[type_id].unwrap_func())
1048 }
1049
1050 fn global_at(&self, at: u32) -> Option<GlobalType> {
1051 self.module.globals.get(at as usize).cloned()
1052 }
1053
1054 fn sub_type_at(&self, at: u32) -> Option<&SubType> {
1055 let id = *self.module.types.get(at as usize)?;
1056 Some(&self.types[id])
1057 }
1058
1059 fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType {
1060 &self.types[at]
1061 }
1062
1063 fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> {
1064 let type_index = self.module.functions.get(at as usize)?;
1065 self.module.types.get(*type_index as usize).copied()
1066 }
1067
1068 fn type_index_of_function(&self, at: u32) -> Option<u32> {
1069 self.module.functions.get(at as usize).copied()
1070 }
1071
1072 fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> {
1073 self.module.check_heap_type(t, offset)
1074 }
1075
1076 fn top_type(&self, heap_type: &HeapType) -> HeapType {
1077 self.types.top_type(heap_type)
1078 }
1079
1080 fn element_type_at(&self, at: u32) -> Option<RefType> {
1081 self.module.element_types.get(at as usize).cloned()
1082 }
1083
1084 fn is_subtype(&self, a: ValType, b: ValType) -> bool {
1085 self.types.valtype_is_subtype(a, b)
1086 }
1087
1088 fn is_shared(&self, ty: RefType) -> bool {
1089 self.types.reftype_is_shared(ty)
1090 }
1091
1092 fn element_count(&self) -> u32 {
1093 self.module.element_types.len() as u32
1094 }
1095
1096 fn data_count(&self) -> Option<u32> {
1097 self.module.data_count
1098 }
1099
1100 fn is_function_referenced(&self, idx: u32) -> bool {
1101 self.module.function_references.contains(&idx)
1102 }
1103}
1104
1105#[derive(Debug)]
1108pub struct ValidatorResources(pub(crate) Arc<Module>);
1109
1110impl WasmModuleResources for ValidatorResources {
1111 fn table_at(&self, at: u32) -> Option<TableType> {
1112 self.0.tables.get(at as usize).cloned()
1113 }
1114
1115 fn memory_at(&self, at: u32) -> Option<MemoryType> {
1116 self.0.memories.get(at as usize).cloned()
1117 }
1118
1119 fn tag_at(&self, at: u32) -> Option<&FuncType> {
1120 let id = *self.0.tags.get(at as usize)?;
1121 let types = self.0.snapshot.as_ref().unwrap();
1122 match &types[id].composite_type.inner {
1123 CompositeInnerType::Func(f) => Some(f),
1124 _ => None,
1125 }
1126 }
1127
1128 fn global_at(&self, at: u32) -> Option<GlobalType> {
1129 self.0.globals.get(at as usize).cloned()
1130 }
1131
1132 fn sub_type_at(&self, at: u32) -> Option<&SubType> {
1133 let id = *self.0.types.get(at as usize)?;
1134 let types = self.0.snapshot.as_ref().unwrap();
1135 Some(&types[id])
1136 }
1137
1138 fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType {
1139 let types = self.0.snapshot.as_ref().unwrap();
1140 &types[at]
1141 }
1142
1143 fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> {
1144 let type_index = *self.0.functions.get(at as usize)?;
1145 self.0.types.get(type_index as usize).copied()
1146 }
1147
1148 fn type_index_of_function(&self, at: u32) -> Option<u32> {
1149 self.0.functions.get(at as usize).copied()
1150 }
1151
1152 fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> {
1153 self.0.check_heap_type(t, offset)
1154 }
1155
1156 fn top_type(&self, heap_type: &HeapType) -> HeapType {
1157 self.0.snapshot.as_ref().unwrap().top_type(heap_type)
1158 }
1159
1160 fn element_type_at(&self, at: u32) -> Option<RefType> {
1161 self.0.element_types.get(at as usize).cloned()
1162 }
1163
1164 fn is_subtype(&self, a: ValType, b: ValType) -> bool {
1165 self.0.snapshot.as_ref().unwrap().valtype_is_subtype(a, b)
1166 }
1167
1168 fn is_shared(&self, ty: RefType) -> bool {
1169 self.0.snapshot.as_ref().unwrap().reftype_is_shared(ty)
1170 }
1171
1172 fn element_count(&self) -> u32 {
1173 self.0.element_types.len() as u32
1174 }
1175
1176 fn data_count(&self) -> Option<u32> {
1177 self.0.data_count
1178 }
1179
1180 fn is_function_referenced(&self, idx: u32) -> bool {
1181 self.0.function_references.contains(&idx)
1182 }
1183}
1184
1185const _: () = {
1186 const fn assert_send<T: Send>() {}
1187
1188 assert_send::<ValidatorResources>();
1191};
1192
1193mod arc {
1194 use alloc::sync::Arc;
1195 use core::ops::Deref;
1196
1197 enum Inner<T> {
1198 Owned(T),
1199 Shared(Arc<T>),
1200
1201 Empty, }
1203
1204 pub struct MaybeOwned<T> {
1205 inner: Inner<T>,
1206 }
1207
1208 impl<T> MaybeOwned<T> {
1209 pub fn new(val: T) -> MaybeOwned<T> {
1210 MaybeOwned {
1211 inner: Inner::Owned(val),
1212 }
1213 }
1214
1215 #[inline]
1216 pub(crate) fn as_mut(&mut self) -> Option<&mut T> {
1217 match &mut self.inner {
1218 Inner::Owned(x) => Some(x),
1219 Inner::Shared(_) => None,
1220 Inner::Empty => Self::unreachable(),
1221 }
1222 }
1223
1224 #[inline]
1225 pub fn assert_mut(&mut self) -> &mut T {
1226 self.as_mut().unwrap()
1227 }
1228
1229 pub fn arc(&mut self) -> &Arc<T> {
1230 self.make_shared();
1231 match &self.inner {
1232 Inner::Shared(x) => x,
1233 _ => Self::unreachable(),
1234 }
1235 }
1236
1237 #[inline]
1238 fn make_shared(&mut self) {
1239 if let Inner::Shared(_) = self.inner {
1240 return;
1241 }
1242
1243 let inner = core::mem::replace(&mut self.inner, Inner::Empty);
1244 let x = match inner {
1245 Inner::Owned(x) => x,
1246 _ => Self::unreachable(),
1247 };
1248 let x = Arc::new(x);
1249 self.inner = Inner::Shared(x);
1250 }
1251
1252 #[cold]
1253 #[inline(never)]
1254 fn unreachable() -> ! {
1255 unreachable!()
1256 }
1257 }
1258
1259 impl<T: Default> Default for MaybeOwned<T> {
1260 fn default() -> MaybeOwned<T> {
1261 MaybeOwned::new(T::default())
1262 }
1263 }
1264
1265 impl<T> Deref for MaybeOwned<T> {
1266 type Target = T;
1267
1268 fn deref(&self) -> &T {
1269 match &self.inner {
1270 Inner::Owned(x) => x,
1271 Inner::Shared(x) => x,
1272 Inner::Empty => Self::unreachable(),
1273 }
1274 }
1275 }
1276}