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_struct_new_desc $type_index:ident) => {{
406 $self.validate_gc("struct.new_desc")?;
407 $self.validator().visit_struct_new_desc($type_index)
408 }};
409 (@visit $self:ident visit_struct_new_default_desc $type_index:ident) => {{
410 $self.validate_gc("struct.new_default_desc")?;
411 $self.validator().visit_struct_new_default_desc($type_index)
412 }};
413 (@visit $self:ident visit_array_new $type_index:ident) => {{
414 $self.validate_gc("array.new")?;
415 $self.validator().visit_array_new($type_index)
416 }};
417 (@visit $self:ident visit_array_new_default $type_index:ident) => {{
418 $self.validate_gc("array.new_default")?;
419 $self.validator().visit_array_new_default($type_index)
420 }};
421 (@visit $self:ident visit_array_new_fixed $type_index:ident $n:ident) => {{
422 $self.validate_gc("array.new_fixed")?;
423 $self.validator().visit_array_new_fixed($type_index, $n)
424 }};
425 (@visit $self:ident visit_ref_i31) => {{
426 $self.validate_gc("ref.i31")?;
427 $self.validator().visit_ref_i31()
428 }};
429 (@visit $self:ident visit_extern_convert_any) => {{
430 $self.validate_gc("extern.convert_any")?;
431 $self.validator().visit_extern_convert_any()
432 }};
433 (@visit $self:ident visit_any_convert_extern) => {{
434 $self.validate_gc("any.convert_extern")?;
435 $self.validator().visit_any_convert_extern()
436 }};
437 (@visit $self:ident visit_ref_i31_shared) => {{
438 $self.validate_shared_everything_threads("ref.i31_shared")?;
439 $self.validator().visit_ref_i31_shared()
440 }};
441
442 (@visit $self:ident visit_global_get $idx:ident) => {{
445 $self.validate_global($idx)?;
446 $self.validator().visit_global_get($idx)
447 }};
448 (@visit $self:ident visit_ref_func $idx:ident) => {{
451 $self.insert_ref_func($idx);
452 $self.validator().visit_ref_func($idx)
453 }};
454
455 (@visit $self:ident $op:ident $($args:tt)*) => {{
456 Err($self.not_const(stringify!($op)))
457 }}
458 }
459
460 impl<'a> VisitOperator<'a> for VisitConstOperator<'a> {
461 type Output = Result<()>;
462
463 #[cfg(feature = "simd")]
464 fn simd_visitor(
465 &mut self,
466 ) -> Option<&mut dyn crate::VisitSimdOperator<'a, Output = Self::Output>> {
467 Some(self)
468 }
469
470 crate::for_each_visit_operator!(define_visit_operator);
471 }
472
473 #[cfg(feature = "simd")]
474 impl<'a> VisitSimdOperator<'a> for VisitConstOperator<'a> {
475 crate::for_each_visit_simd_operator!(define_visit_operator);
476 }
477
478 impl<'a> FrameStack for VisitConstOperator<'a> {
479 fn current_frame(&self) -> Option<FrameKind> {
480 Some(self.ops.get_frame(0)?.kind)
481 }
482 }
483 }
484}
485
486#[derive(Debug)]
487pub(crate) struct Module {
488 pub snapshot: Option<Arc<TypeList>>,
492 pub types: Vec<CoreTypeId>,
494 pub tables: Vec<TableType>,
495 pub memories: Vec<MemoryType>,
496 pub globals: Vec<GlobalType>,
497 pub element_types: Vec<RefType>,
498 pub data_count: Option<u32>,
499 pub functions: Vec<u32>,
501 pub tags: Vec<CoreTypeId>,
502 pub function_references: Set<u32>,
503 pub exact_function_imports: Set<u32>,
504 pub imports: IndexMap<(String, String), Vec<EntityType>>,
505 pub exports: IndexMap<String, EntityType>,
506 pub type_size: u32,
507 num_imported_globals: u32,
508 num_imported_functions: u32,
509 features: WasmFeatures,
510}
511
512impl Module {
513 pub fn new(features: WasmFeatures) -> Self {
514 Self {
515 snapshot: Default::default(),
516 types: Default::default(),
517 tables: Default::default(),
518 memories: Default::default(),
519 globals: Default::default(),
520 element_types: Default::default(),
521 data_count: Default::default(),
522 functions: Default::default(),
523 tags: Default::default(),
524 function_references: Default::default(),
525 exact_function_imports: Default::default(),
526 imports: Default::default(),
527 exports: Default::default(),
528 type_size: 1,
529 num_imported_globals: Default::default(),
530 num_imported_functions: Default::default(),
531 features,
532 }
533 }
534
535 pub(crate) fn add_types(
536 &mut self,
537 rec_group: RecGroup,
538 types: &mut TypeAlloc,
539 offset: usize,
540 check_limit: bool,
541 ) -> Result<()> {
542 if check_limit {
543 check_max(
544 self.types.len(),
545 rec_group.types().len() as u32,
546 MAX_WASM_TYPES,
547 "types",
548 offset,
549 )?;
550 }
551 self.canonicalize_and_intern_rec_group(types, rec_group, offset)
552 }
553
554 pub fn add_import(
555 &mut self,
556 mut import: crate::Import,
557 types: &TypeList,
558 offset: usize,
559 ) -> Result<()> {
560 let entity = self.check_type_ref(&mut import.ty, types, offset)?;
561
562 let (len, max, desc) = match import.ty {
563 TypeRef::Func(type_index) => {
564 self.functions.push(type_index);
565 self.num_imported_functions += 1;
566 (self.functions.len(), MAX_WASM_FUNCTIONS, "functions")
567 }
568 TypeRef::FuncExact(type_index) => {
569 self.functions.push(type_index);
570 self.exact_function_imports
571 .insert(self.num_imported_functions);
572 self.num_imported_functions += 1;
573 (self.functions.len(), MAX_WASM_FUNCTIONS, "functions")
574 }
575 TypeRef::Table(ty) => {
576 self.tables.push(ty);
577 (self.tables.len(), self.max_tables(), "tables")
578 }
579 TypeRef::Memory(ty) => {
580 self.memories.push(ty);
581 (self.memories.len(), self.max_memories(), "memories")
582 }
583 TypeRef::Tag(ty) => {
584 self.tags.push(self.types[ty.func_type_idx as usize]);
585 (self.tags.len(), MAX_WASM_TAGS, "tags")
586 }
587 TypeRef::Global(ty) => {
588 if !self.features.mutable_global() && ty.mutable {
589 return Err(BinaryReaderError::new(
590 "mutable global support is not enabled",
591 offset,
592 ));
593 }
594 self.globals.push(ty);
595 self.num_imported_globals += 1;
596 (self.globals.len(), MAX_WASM_GLOBALS, "globals")
597 }
598 };
599
600 check_max(len, 0, max, desc, offset)?;
601
602 self.type_size = combine_type_sizes(self.type_size, entity.info(types).size(), offset)?;
603
604 self.imports
605 .entry((import.module.to_string(), import.name.to_string()))
606 .or_default()
607 .push(entity);
608
609 Ok(())
610 }
611
612 pub fn add_export(
613 &mut self,
614 name: &str,
615 ty: EntityType,
616 offset: usize,
617 check_limit: bool,
618 types: &TypeList,
619 ) -> Result<()> {
620 if !self.features.mutable_global() {
621 if let EntityType::Global(global_type) = ty {
622 if global_type.mutable {
623 return Err(BinaryReaderError::new(
624 "mutable global support is not enabled",
625 offset,
626 ));
627 }
628 }
629 }
630
631 if check_limit {
632 check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?;
633 }
634
635 self.type_size = combine_type_sizes(self.type_size, ty.info(types).size(), offset)?;
636
637 match self.exports.insert(name.to_string(), ty) {
638 Some(_) => Err(format_err!(
639 offset,
640 "duplicate export name `{name}` already defined"
641 )),
642 None => Ok(()),
643 }
644 }
645
646 pub fn add_function(&mut self, type_index: u32, types: &TypeList, offset: usize) -> Result<()> {
647 self.func_type_at(type_index, types, offset)?;
648 self.functions.push(type_index);
649 Ok(())
650 }
651
652 pub fn add_memory(&mut self, ty: MemoryType, offset: usize) -> Result<()> {
653 self.check_memory_type(&ty, offset)?;
654 self.memories.push(ty);
655 Ok(())
656 }
657
658 pub fn add_tag(&mut self, ty: TagType, types: &TypeList, offset: usize) -> Result<()> {
659 self.check_tag_type(&ty, types, offset)?;
660 self.tags.push(self.types[ty.func_type_idx as usize]);
661 Ok(())
662 }
663
664 fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> {
665 let id = self.type_id_at(idx, offset)?;
666 Ok(&types[id])
667 }
668
669 fn func_type_at<'a>(
670 &self,
671 type_index: u32,
672 types: &'a TypeList,
673 offset: usize,
674 ) -> Result<&'a FuncType> {
675 match &self
676 .sub_type_at(types, type_index, offset)?
677 .composite_type
678 .inner
679 {
680 CompositeInnerType::Func(f) => Ok(f),
681 _ => bail!(offset, "type index {type_index} is not a function type"),
682 }
683 }
684
685 pub fn check_type_ref(
686 &self,
687 type_ref: &mut TypeRef,
688 types: &TypeList,
689 offset: usize,
690 ) -> Result<EntityType> {
691 Ok(match type_ref {
692 TypeRef::Func(type_index) => {
693 self.func_type_at(*type_index, types, offset)?;
694 EntityType::Func(self.types[*type_index as usize])
695 }
696 TypeRef::FuncExact(type_index) => {
697 self.func_type_at(*type_index, types, offset)?;
698 EntityType::FuncExact(self.types[*type_index as usize])
699 }
700 TypeRef::Table(t) => {
701 self.check_table_type(t, types, offset)?;
702 EntityType::Table(*t)
703 }
704 TypeRef::Memory(t) => {
705 self.check_memory_type(t, offset)?;
706 EntityType::Memory(*t)
707 }
708 TypeRef::Tag(t) => {
709 self.check_tag_type(t, types, offset)?;
710 EntityType::Tag(self.types[t.func_type_idx as usize])
711 }
712 TypeRef::Global(t) => {
713 self.check_global_type(t, types, offset)?;
714 EntityType::Global(*t)
715 }
716 })
717 }
718
719 fn check_table_type(&self, ty: &mut TableType, types: &TypeList, offset: usize) -> Result<()> {
720 if ty.element_type != RefType::FUNCREF {
723 self.check_ref_type(&mut ty.element_type, offset)?
724 }
725
726 self.check_limits(ty.initial, ty.maximum, offset)?;
727 if ty.table64 && !self.features().memory64() {
728 bail!(offset, "memory64 must be enabled for 64-bit tables");
729 }
730 if ty.shared && !self.features().shared_everything_threads() {
731 bail!(
732 offset,
733 "shared tables require the shared-everything-threads proposal"
734 );
735 }
736
737 let true_maximum = if ty.table64 {
738 u64::MAX
739 } else {
740 u32::MAX as u64
741 };
742 let err = format!("table size must be at most {true_maximum:#x} entries");
743 if ty.initial > true_maximum {
744 return Err(BinaryReaderError::new(err, offset));
745 }
746 if let Some(maximum) = ty.maximum {
747 if maximum > true_maximum {
748 return Err(BinaryReaderError::new(err, offset));
749 }
750 }
751 if ty.shared && !types.reftype_is_shared(ty.element_type) {
752 return Err(BinaryReaderError::new(
753 "shared tables must have a shared element type",
754 offset,
755 ));
756 }
757 Ok(())
758 }
759
760 fn check_memory_type(&self, ty: &MemoryType, offset: usize) -> Result<()> {
761 self.check_limits(ty.initial, ty.maximum, offset)?;
762
763 if ty.memory64 && !self.features().memory64() {
764 bail!(offset, "memory64 must be enabled for 64-bit memories");
765 }
766 if ty.shared && !self.features().threads() {
767 bail!(offset, "threads must be enabled for shared memories");
768 }
769
770 let page_size = if let Some(page_size_log2) = ty.page_size_log2 {
771 if !self.features().custom_page_sizes() {
772 return Err(BinaryReaderError::new(
773 "the custom page sizes proposal must be enabled to customize a memory's page size",
774 offset,
775 ));
776 }
777 if page_size_log2 != 0 && page_size_log2 != 16 {
780 return Err(BinaryReaderError::new("invalid custom page size", offset));
781 }
782 let page_size = 1_u64 << page_size_log2;
783 debug_assert!(page_size.is_power_of_two());
784 debug_assert!(page_size == DEFAULT_WASM_PAGE_SIZE || page_size == 1);
785 page_size
786 } else {
787 let page_size_log2 = 16;
788 debug_assert_eq!(DEFAULT_WASM_PAGE_SIZE, 1 << page_size_log2);
789 DEFAULT_WASM_PAGE_SIZE
790 };
791 let true_maximum = if ty.memory64 {
792 max_wasm_memory64_pages(page_size)
793 } else {
794 max_wasm_memory32_pages(page_size)
795 };
796 let err = format!("memory size must be at most {true_maximum:#x} {page_size}-byte pages");
797 if ty.initial > true_maximum {
798 return Err(BinaryReaderError::new(err, offset));
799 }
800 if let Some(maximum) = ty.maximum {
801 if maximum > true_maximum {
802 return Err(BinaryReaderError::new(err, offset));
803 }
804 }
805 if ty.shared && ty.maximum.is_none() {
806 return Err(BinaryReaderError::new(
807 "shared memory must have maximum size",
808 offset,
809 ));
810 }
811 Ok(())
812 }
813
814 #[cfg(feature = "component-model")]
815 pub(crate) fn imports_for_module_type(
816 &self,
817 offset: usize,
818 ) -> Result<IndexMap<(String, String), EntityType>> {
819 self.imports
822 .iter()
823 .map(|((module, name), types)| {
824 if types.len() != 1 {
825 bail!(
826 offset,
827 "module has a duplicate import name `{module}:{name}` \
828 that is not allowed in components",
829 );
830 }
831 Ok(((module.clone(), name.clone()), types[0]))
832 })
833 .collect::<Result<_>>()
834 }
835
836 fn check_value_type(&self, ty: &mut ValType, offset: usize) -> Result<()> {
837 match ty {
840 ValType::Ref(rt) => self.check_ref_type(rt, offset),
841 _ => self
842 .features
843 .check_value_type(*ty)
844 .map_err(|e| BinaryReaderError::new(e, offset)),
845 }
846 }
847
848 fn check_ref_type(&self, ty: &mut RefType, offset: usize) -> Result<()> {
849 self.features
850 .check_ref_type(*ty)
851 .map_err(|e| BinaryReaderError::new(e, offset))?;
852 let mut hty = ty.heap_type();
853 self.check_heap_type(&mut hty, offset)?;
854 *ty = RefType::new(ty.is_nullable(), hty).unwrap();
855 Ok(())
856 }
857
858 fn check_heap_type(&self, ty: &mut HeapType, offset: usize) -> Result<()> {
859 let type_index = match ty {
861 HeapType::Abstract { .. } => return Ok(()),
862 HeapType::Concrete(type_index) | HeapType::Exact(type_index) => type_index,
863 };
864 match type_index {
865 UnpackedIndex::Module(idx) => {
866 let id = self.type_id_at(*idx, offset)?;
867 *type_index = UnpackedIndex::Id(id);
868 Ok(())
869 }
870 UnpackedIndex::RecGroup(_) | UnpackedIndex::Id(_) => unreachable!(),
874 }
875 }
876
877 fn check_tag_type(&self, ty: &TagType, types: &TypeList, offset: usize) -> Result<()> {
878 if !self.features().exceptions() {
879 bail!(offset, "exceptions proposal not enabled");
880 }
881 let ty = self.func_type_at(ty.func_type_idx, types, offset)?;
882 if !ty.results().is_empty() && !self.features.stack_switching() {
883 return Err(BinaryReaderError::new(
884 "invalid exception type: non-empty tag result type",
885 offset,
886 ));
887 }
888 Ok(())
889 }
890
891 fn check_global_type(
892 &self,
893 ty: &mut GlobalType,
894 types: &TypeList,
895 offset: usize,
896 ) -> Result<()> {
897 self.check_value_type(&mut ty.content_type, offset)?;
898 if ty.shared && !self.features.shared_everything_threads() {
899 bail!(
900 offset,
901 "shared globals require the shared-everything-threads proposal"
902 );
903 }
904 if ty.shared && !types.valtype_is_shared(ty.content_type) {
905 return Err(BinaryReaderError::new(
906 "shared globals must have a shared value type",
907 offset,
908 ));
909 }
910 Ok(())
911 }
912
913 fn check_limits<T>(&self, initial: T, maximum: Option<T>, offset: usize) -> Result<()>
914 where
915 T: Into<u64>,
916 {
917 if let Some(max) = maximum {
918 if initial.into() > max.into() {
919 return Err(BinaryReaderError::new(
920 "size minimum must not be greater than maximum",
921 offset,
922 ));
923 }
924 }
925 Ok(())
926 }
927
928 pub fn max_tables(&self) -> usize {
929 if self.features.reference_types() {
930 MAX_WASM_TABLES
931 } else {
932 1
933 }
934 }
935
936 pub fn max_memories(&self) -> usize {
937 if self.features.multi_memory() {
938 MAX_WASM_MEMORIES
939 } else {
940 1
941 }
942 }
943
944 pub fn export_to_entity_type(
945 &mut self,
946 export: &crate::Export,
947 offset: usize,
948 ) -> Result<EntityType> {
949 let check = |ty: &str, index: u32, total: usize| {
950 if index as usize >= total {
951 Err(format_err!(
952 offset,
953 "unknown {ty} {index}: exported {ty} index out of bounds",
954 ))
955 } else {
956 Ok(())
957 }
958 };
959
960 Ok(match export.kind {
961 ExternalKind::Func | ExternalKind::FuncExact => {
962 check("function", export.index, self.functions.len())?;
963 self.function_references.insert(export.index);
964 EntityType::Func(self.types[self.functions[export.index as usize] as usize])
965 }
966 ExternalKind::Table => {
967 check("table", export.index, self.tables.len())?;
968 EntityType::Table(self.tables[export.index as usize])
969 }
970 ExternalKind::Memory => {
971 check("memory", export.index, self.memories.len())?;
972 EntityType::Memory(self.memories[export.index as usize])
973 }
974 ExternalKind::Global => {
975 check("global", export.index, self.globals.len())?;
976 EntityType::Global(self.globals[export.index as usize])
977 }
978 ExternalKind::Tag => {
979 check("tag", export.index, self.tags.len())?;
980 EntityType::Tag(self.tags[export.index as usize])
981 }
982 })
983 }
984
985 pub fn get_func_type<'a>(
986 &self,
987 func_idx: u32,
988 types: &'a TypeList,
989 offset: usize,
990 ) -> Result<&'a FuncType> {
991 match self.functions.get(func_idx as usize) {
992 Some(idx) => self.func_type_at(*idx, types, offset),
993 None => Err(format_err!(
994 offset,
995 "unknown function {func_idx}: func index out of bounds",
996 )),
997 }
998 }
999
1000 fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> {
1001 match self.globals.get(idx as usize) {
1002 Some(t) => Ok(t),
1003 None => Err(format_err!(
1004 offset,
1005 "unknown global {idx}: global index out of bounds"
1006 )),
1007 }
1008 }
1009
1010 fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> {
1011 match self.tables.get(idx as usize) {
1012 Some(t) => Ok(t),
1013 None => Err(format_err!(
1014 offset,
1015 "unknown table {idx}: table index out of bounds"
1016 )),
1017 }
1018 }
1019
1020 fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> {
1021 match self.memories.get(idx as usize) {
1022 Some(t) => Ok(t),
1023 None => Err(format_err!(
1024 offset,
1025 "unknown memory {idx}: memory index out of bounds"
1026 )),
1027 }
1028 }
1029}
1030
1031impl InternRecGroup for Module {
1032 fn features(&self) -> &WasmFeatures {
1033 &self.features
1034 }
1035
1036 fn add_type_id(&mut self, id: CoreTypeId) {
1037 self.types.push(id);
1038 }
1039
1040 fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> {
1041 self.types
1042 .get(idx as usize)
1043 .copied()
1044 .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
1045 }
1046
1047 fn types_len(&self) -> u32 {
1048 u32::try_from(self.types.len()).unwrap()
1049 }
1050}
1051
1052struct OperatorValidatorResources<'a> {
1053 module: &'a mut MaybeOwned<Module>,
1054 types: &'a TypeList,
1055}
1056
1057impl WasmModuleResources for OperatorValidatorResources<'_> {
1058 fn table_at(&self, at: u32) -> Option<TableType> {
1059 self.module.tables.get(at as usize).cloned()
1060 }
1061
1062 fn memory_at(&self, at: u32) -> Option<MemoryType> {
1063 self.module.memories.get(at as usize).cloned()
1064 }
1065
1066 fn tag_at(&self, at: u32) -> Option<&FuncType> {
1067 let type_id = *self.module.tags.get(at as usize)?;
1068 Some(self.types[type_id].unwrap_func())
1069 }
1070
1071 fn global_at(&self, at: u32) -> Option<GlobalType> {
1072 self.module.globals.get(at as usize).cloned()
1073 }
1074
1075 fn sub_type_at(&self, at: u32) -> Option<&SubType> {
1076 let id = *self.module.types.get(at as usize)?;
1077 Some(&self.types[id])
1078 }
1079
1080 fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType {
1081 &self.types[at]
1082 }
1083
1084 fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> {
1085 let type_index = self.module.functions.get(at as usize)?;
1086 self.module.types.get(*type_index as usize).copied()
1087 }
1088
1089 fn type_index_of_function(&self, at: u32) -> Option<u32> {
1090 self.module.functions.get(at as usize).copied()
1091 }
1092
1093 fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> {
1094 self.module.check_heap_type(t, offset)
1095 }
1096
1097 fn top_type(&self, heap_type: &HeapType) -> HeapType {
1098 self.types.top_type(heap_type)
1099 }
1100
1101 fn element_type_at(&self, at: u32) -> Option<RefType> {
1102 self.module.element_types.get(at as usize).cloned()
1103 }
1104
1105 fn is_subtype(&self, a: ValType, b: ValType) -> bool {
1106 self.types.valtype_is_subtype(a, b)
1107 }
1108
1109 fn is_shared(&self, ty: RefType) -> bool {
1110 self.types.reftype_is_shared(ty)
1111 }
1112
1113 fn element_count(&self) -> u32 {
1114 self.module.element_types.len() as u32
1115 }
1116
1117 fn data_count(&self) -> Option<u32> {
1118 self.module.data_count
1119 }
1120
1121 fn is_function_referenced(&self, idx: u32) -> bool {
1122 self.module.function_references.contains(&idx)
1123 }
1124
1125 fn has_function_exact_type(&self, idx: u32) -> bool {
1126 if idx >= self.module.num_imported_functions {
1127 true
1128 } else {
1129 self.module.exact_function_imports.contains(&idx)
1130 }
1131 }
1132}
1133
1134#[derive(Debug)]
1137pub struct ValidatorResources(pub(crate) Arc<Module>);
1138
1139impl WasmModuleResources for ValidatorResources {
1140 fn table_at(&self, at: u32) -> Option<TableType> {
1141 self.0.tables.get(at as usize).cloned()
1142 }
1143
1144 fn memory_at(&self, at: u32) -> Option<MemoryType> {
1145 self.0.memories.get(at as usize).cloned()
1146 }
1147
1148 fn tag_at(&self, at: u32) -> Option<&FuncType> {
1149 let id = *self.0.tags.get(at as usize)?;
1150 let types = self.0.snapshot.as_ref().unwrap();
1151 match &types[id].composite_type.inner {
1152 CompositeInnerType::Func(f) => Some(f),
1153 _ => None,
1154 }
1155 }
1156
1157 fn global_at(&self, at: u32) -> Option<GlobalType> {
1158 self.0.globals.get(at as usize).cloned()
1159 }
1160
1161 fn sub_type_at(&self, at: u32) -> Option<&SubType> {
1162 let id = *self.0.types.get(at as usize)?;
1163 let types = self.0.snapshot.as_ref().unwrap();
1164 Some(&types[id])
1165 }
1166
1167 fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType {
1168 let types = self.0.snapshot.as_ref().unwrap();
1169 &types[at]
1170 }
1171
1172 fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> {
1173 let type_index = *self.0.functions.get(at as usize)?;
1174 self.0.types.get(type_index as usize).copied()
1175 }
1176
1177 fn type_index_of_function(&self, at: u32) -> Option<u32> {
1178 self.0.functions.get(at as usize).copied()
1179 }
1180
1181 fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> {
1182 self.0.check_heap_type(t, offset)
1183 }
1184
1185 fn top_type(&self, heap_type: &HeapType) -> HeapType {
1186 self.0.snapshot.as_ref().unwrap().top_type(heap_type)
1187 }
1188
1189 fn element_type_at(&self, at: u32) -> Option<RefType> {
1190 self.0.element_types.get(at as usize).cloned()
1191 }
1192
1193 fn is_subtype(&self, a: ValType, b: ValType) -> bool {
1194 self.0.snapshot.as_ref().unwrap().valtype_is_subtype(a, b)
1195 }
1196
1197 fn is_shared(&self, ty: RefType) -> bool {
1198 self.0.snapshot.as_ref().unwrap().reftype_is_shared(ty)
1199 }
1200
1201 fn element_count(&self) -> u32 {
1202 self.0.element_types.len() as u32
1203 }
1204
1205 fn data_count(&self) -> Option<u32> {
1206 self.0.data_count
1207 }
1208
1209 fn is_function_referenced(&self, idx: u32) -> bool {
1210 self.0.function_references.contains(&idx)
1211 }
1212
1213 fn has_function_exact_type(&self, idx: u32) -> bool {
1214 if idx >= self.0.num_imported_functions {
1215 true
1216 } else {
1217 self.0.exact_function_imports.contains(&idx)
1218 }
1219 }
1220}
1221
1222const _: () = {
1223 const fn assert_send<T: Send>() {}
1224
1225 assert_send::<ValidatorResources>();
1228};
1229
1230mod arc {
1231 use alloc::sync::Arc;
1232 use core::ops::Deref;
1233
1234 enum Inner<T> {
1235 Owned(T),
1236 Shared(Arc<T>),
1237
1238 Empty, }
1240
1241 pub struct MaybeOwned<T> {
1242 inner: Inner<T>,
1243 }
1244
1245 impl<T> MaybeOwned<T> {
1246 pub fn new(val: T) -> MaybeOwned<T> {
1247 MaybeOwned {
1248 inner: Inner::Owned(val),
1249 }
1250 }
1251
1252 #[inline]
1253 pub(crate) fn as_mut(&mut self) -> Option<&mut T> {
1254 match &mut self.inner {
1255 Inner::Owned(x) => Some(x),
1256 Inner::Shared(_) => None,
1257 Inner::Empty => Self::unreachable(),
1258 }
1259 }
1260
1261 #[inline]
1262 pub fn assert_mut(&mut self) -> &mut T {
1263 self.as_mut().unwrap()
1264 }
1265
1266 pub fn arc(&mut self) -> &Arc<T> {
1267 self.make_shared();
1268 match &self.inner {
1269 Inner::Shared(x) => x,
1270 _ => Self::unreachable(),
1271 }
1272 }
1273
1274 #[inline]
1275 fn make_shared(&mut self) {
1276 if let Inner::Shared(_) = self.inner {
1277 return;
1278 }
1279
1280 let inner = core::mem::replace(&mut self.inner, Inner::Empty);
1281 let x = match inner {
1282 Inner::Owned(x) => x,
1283 _ => Self::unreachable(),
1284 };
1285 let x = Arc::new(x);
1286 self.inner = Inner::Shared(x);
1287 }
1288
1289 #[cold]
1290 #[inline(never)]
1291 fn unreachable() -> ! {
1292 unreachable!()
1293 }
1294 }
1295
1296 impl<T: Default> Default for MaybeOwned<T> {
1297 fn default() -> MaybeOwned<T> {
1298 MaybeOwned::new(T::default())
1299 }
1300 }
1301
1302 impl<T> Deref for MaybeOwned<T> {
1303 type Target = T;
1304
1305 fn deref(&self) -> &T {
1306 match &self.inner {
1307 Inner::Owned(x) => x,
1308 Inner::Shared(x) => x,
1309 Inner::Empty => Self::unreachable(),
1310 }
1311 }
1312 }
1313}