1use alloc::{
20 borrow::Cow,
21 format,
22 string::{String, ToString},
23 vec,
24 vec::Vec,
25};
26use core::convert::Infallible;
27use wasm_encoder::{
28 reencode,
29 reencode::{Reencode, RoundtripReencoder},
30};
31use wasmparser::{
32 BinaryReaderError, Encoding, ExternalKind, FuncType, FunctionBody, GlobalType, KnownCustom,
33 MemoryType, Payload, RefType, TableType, TypeRef, ValType, WasmFeatures,
34};
35
36pub const GEAR_SUPPORTED_FEATURES: WasmFeatures = WasmFeatures::WASM1
37 .union(WasmFeatures::SIGN_EXTENSION)
38 .difference(WasmFeatures::FLOATS);
39
40macro_rules! for_each_instruction_group {
45 ($mac:ident) => {
46 $mac! {
47 @mvp {
48 Unreachable
49 Nop
50 Block { blockty: $crate::BlockType }
51 Loop { blockty: $crate::BlockType }
52 If { blockty: $crate::BlockType }
53 Else
54 End
55 Br { relative_depth: u32 }
56 BrIf { relative_depth: u32 }
57 BrTable { targets: $crate::BrTable }
58 Return
59 Call { function_index: u32 }
60 CallIndirect { type_index: u32, table_index: u32 }
61 Drop
62 Select
63 LocalGet { local_index: u32 }
64 LocalSet { local_index: u32 }
65 LocalTee { local_index: u32 }
66 GlobalGet { global_index: u32 }
67 GlobalSet { global_index: u32 }
68 I32Load { memarg: $crate::MemArg }
69 I64Load { memarg: $crate::MemArg }
70 I32Load8S { memarg: $crate::MemArg }
71 I32Load8U { memarg: $crate::MemArg }
72 I32Load16S { memarg: $crate::MemArg }
73 I32Load16U { memarg: $crate::MemArg }
74 I64Load8S { memarg: $crate::MemArg }
75 I64Load8U { memarg: $crate::MemArg }
76 I64Load16S { memarg: $crate::MemArg }
77 I64Load16U { memarg: $crate::MemArg }
78 I64Load32S { memarg: $crate::MemArg }
79 I64Load32U { memarg: $crate::MemArg }
80 I32Store { memarg: $crate::MemArg }
81 I64Store { memarg: $crate::MemArg }
82 I32Store8 { memarg: $crate::MemArg }
83 I32Store16 { memarg: $crate::MemArg }
84 I64Store8 { memarg: $crate::MemArg }
85 I64Store16 { memarg: $crate::MemArg }
86 I64Store32 { memarg: $crate::MemArg }
87 MemorySize { mem: u32 }
88 MemoryGrow { mem: u32 }
89 I32Const { value: i32 }
90 I64Const { value: i64 }
91 I32Eqz
92 I32Eq
93 I32Ne
94 I32LtS
95 I32LtU
96 I32GtS
97 I32GtU
98 I32LeS
99 I32LeU
100 I32GeS
101 I32GeU
102 I64Eqz
103 I64Eq
104 I64Ne
105 I64LtS
106 I64LtU
107 I64GtS
108 I64GtU
109 I64LeS
110 I64LeU
111 I64GeS
112 I64GeU
113 I32Clz
114 I32Ctz
115 I32Popcnt
116 I32Add
117 I32Sub
118 I32Mul
119 I32DivS
120 I32DivU
121 I32RemS
122 I32RemU
123 I32And
124 I32Or
125 I32Xor
126 I32Shl
127 I32ShrS
128 I32ShrU
129 I32Rotl
130 I32Rotr
131 I64Clz
132 I64Ctz
133 I64Popcnt
134 I64Add
135 I64Sub
136 I64Mul
137 I64DivS
138 I64DivU
139 I64RemS
140 I64RemU
141 I64And
142 I64Or
143 I64Xor
144 I64Shl
145 I64ShrS
146 I64ShrU
147 I64Rotl
148 I64Rotr
149 I32WrapI64
150 I64ExtendI32S
151 I64ExtendI32U
152 }
153
154 @sign_extension {
155 I32Extend8S
156 I32Extend16S
157 I64Extend8S
158 I64Extend16S
159 I64Extend32S
160 }
161 }
162 };
163}
164
165macro_rules! define_for_each_instruction {
167 ($(
168 @$proposal:ident {
169 $($op:ident $( { $( $arg:ident: $argty:ty ),+ } )?)+
170 }
171 )+) => {
172 macro_rules! for_each_instruction {
173 ($mac:ident) => {
174 $mac! {
175 $(
176 $(
177 $op $( { $( $arg: $argty ),+ } )?
178 )+
179 )+
180 }
181 };
182 }
183 };
184}
185
186for_each_instruction_group!(define_for_each_instruction);
188
189macro_rules! define_instruction {
190 ($( $op:ident $( { $( $arg:ident: $argty:ty ),+ } )? )+) => {
191 define_instruction!(@convert $( $op $( { $( $arg: $argty ),+ } )? )+ @accum);
192 };
193 (
197 @convert
198 CallIndirect { $type_index_arg:ident: $type_index_argty:ty, $table_index_arg:ident: $table_index_argty:ty }
199 $( $ops:ident $( { $($args:ident: $argtys:ty),+ } )? )*
200 @accum
201 $( $accum_op:ident $( { $($original_arg:ident: $original_argty:ty),+ } => { $($accum_arg:ident: $accum_argty:ty),+ } )? )*
202 ) => {
203 define_instruction!(
204 @convert
205 $( $ops $( { $($args: $argtys),+ } )? )*
206 @accum
207 CallIndirect { $type_index_arg: $type_index_argty, $table_index_arg: $table_index_argty } => { $type_index_arg: $type_index_argty }
208 $( $accum_op $( { $($original_arg: $original_argty),+ } => { $($accum_arg: $accum_argty),+ } )? )*
209 );
210 };
211 (
213 @convert
214 $op:ident $( { $($arg:ident: $argty:ty),+ } )?
215 $( $ops:ident $( { $($args:ident: $argtys:ty),+ } )? )*
216 @accum
217 $( $accum_op:ident $( { $($original_arg:ident: $original_argty:ty),+ } => { $($accum_arg:ident: $accum_argty:ty),+ } )? )*
218 ) => {
219 define_instruction!(
220 @convert
221 $( $ops $( { $($args: $argtys),+ } )? )*
222 @accum
223 $op $( { $($arg: $argty),+ } => { $($arg: $argty),+ } )?
224 $( $accum_op $( { $($original_arg: $original_argty),+ } => { $($accum_arg: $accum_argty),+ } )? )*
225 );
226 };
227 (
229 @convert
230 @accum
231 $( $op:ident $( { $( $original_arg:ident: $original_argty:ty ),+ } => { $( $arg:ident: $argty:ty ),+ } )? )+
232 ) => {
233 #[derive(Debug, Clone, Eq, PartialEq)]
234 pub enum Instruction {
235 $(
236 $op $(( $( $argty ),+ ))?,
237 )+
238 }
239
240 impl Instruction {
241 fn parse(op: wasmparser::Operator) -> Result<Self> {
242 match op {
243 $(
244 wasmparser::Operator::$op $({ $($original_arg),* })? => {
245 define_instruction!(@parse $op $(( $($original_arg $original_arg),* ))?)
246 }
247 )*
248 op => Err(ModuleError::UnsupportedInstruction(format!("{op:?}"))),
249 }
250 }
251
252 fn reencode(&self) -> Result<wasm_encoder::Instruction<'_>> {
253 Ok(match self {
254 $(
255 Self::$op $(( $($arg),+ ))? => {
256 $(
257 $(let $arg = define_instruction!(@arg $arg $arg);)*
258 )?
259 define_instruction!(@build $op $($($arg)*)?)
260 }
261 )*
262 })
263 }
264 }
265 };
266
267 (@parse CallIndirect($type_index:ident type_index, $table_index:ident table_index)) => {{
270 debug_assert_eq!($table_index, 0);
272
273 Ok(Self::CallIndirect(<_>::try_from($type_index)?))
274 }};
275 (@parse $op:ident $(( $($arg:ident $_arg:ident),* ))?) => {
276 Ok(Self::$op $(( $(<_>::try_from($arg)?),* ))?)
277 };
278
279 (@arg $arg:ident blockty) => (RoundtripReencoder.block_type(*$arg)?);
280 (@arg $arg:ident targets) => ((
281 ($arg).targets.clone().into(),
282 ($arg).default,
283 ));
284 (@arg $arg:ident memarg) => ((*$arg).reencode());
285 (@arg $arg:ident $_arg:ident) => (*$arg);
286
287 (@build $op:ident) => (wasm_encoder::Instruction::$op);
288 (@build BrTable $arg:ident) => (wasm_encoder::Instruction::BrTable($arg.0, $arg.1));
289 (@build I32Const $arg:ident) => (wasm_encoder::Instruction::I32Const($arg));
290 (@build I64Const $arg:ident) => (wasm_encoder::Instruction::I64Const($arg));
291 (@build F32Const $arg:ident) => (wasm_encoder::Instruction::F32Const(f32::from_bits($arg.bits())));
292 (@build F64Const $arg:ident) => (wasm_encoder::Instruction::F64Const(f64::from_bits($arg.bits())));
293 (@build CallIndirect $arg:ident) => (wasm_encoder::Instruction::CallIndirect { type_index: $arg, table_index: 0 });
294 (@build $op:ident $arg:ident) => (wasm_encoder::Instruction::$op($arg));
295 (@build $op:ident $($arg:ident)*) => (wasm_encoder::Instruction::$op { $($arg),* });
296}
297
298for_each_instruction!(define_instruction);
299
300impl Instruction {
301 pub fn is_user_forbidden(&self) -> bool {
304 matches!(self, Self::MemoryGrow { .. })
305 }
306}
307
308pub type Result<T, E = ModuleError> = core::result::Result<T, E>;
309
310#[derive(Debug, derive_more::Display, derive_more::From)]
311pub enum ModuleError {
312 #[display("Binary reader error: {_0}")]
313 BinaryReader(BinaryReaderError),
314 #[display("Reencode error: {_0}")]
315 Reencode(reencode::Error),
316 #[display("Int conversion error: {_0}")]
317 TryFromInt(core::num::TryFromIntError),
318 #[display("Unsupported instruction: {_0}")]
319 UnsupportedInstruction(String),
320 #[display("Multiple tables")]
321 MultipleTables,
322 #[display("Multiple memories")]
323 MultipleMemories,
324 #[from(skip)]
325 #[display("Memory index must be zero (actual: {_0})")]
326 NonZeroMemoryIdx(u32),
327 #[from(skip)]
328 #[display("Optional table index of element segment is not supported (index: {_0})")]
329 ElementTableIdx(u32),
330 #[display("Passive data is not supported")]
331 PassiveDataKind,
332 #[display("Element expressions are not supported")]
333 ElementExpressions,
334 #[display("Only active element is supported")]
335 NonActiveElementKind,
336 #[display("Table init expression is not supported")]
337 TableInitExpr,
338}
339
340impl core::error::Error for ModuleError {
341 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
342 match self {
343 ModuleError::BinaryReader(e) => Some(e),
344 ModuleError::Reencode(e) => Some(e),
345 ModuleError::TryFromInt(e) => Some(e),
346 ModuleError::UnsupportedInstruction(_) => None,
347 ModuleError::MultipleTables => None,
348 ModuleError::MultipleMemories => None,
349 ModuleError::NonZeroMemoryIdx(_) => None,
350 ModuleError::ElementTableIdx(_) => None,
351 ModuleError::PassiveDataKind => None,
352 ModuleError::ElementExpressions => None,
353 ModuleError::NonActiveElementKind => None,
354 ModuleError::TableInitExpr => None,
355 }
356 }
357}
358
359impl From<Infallible> for ModuleError {
360 fn from(value: Infallible) -> Self {
361 match value {}
362 }
363}
364
365#[derive(Debug, Clone, Copy, Eq, PartialEq)]
366pub struct MemArg {
367 pub align: u8,
370 pub offset: u32,
372}
373
374impl TryFrom<wasmparser::MemArg> for MemArg {
375 type Error = ModuleError;
376
377 fn try_from(
378 wasmparser::MemArg {
379 align,
380 max_align: _,
381 offset,
382 memory,
383 }: wasmparser::MemArg,
384 ) -> Result<Self, Self::Error> {
385 debug_assert_eq!(memory, 0);
387 Ok(Self {
388 align,
389 offset: offset.try_into()?,
390 })
391 }
392}
393
394impl MemArg {
395 pub fn zero() -> Self {
396 Self {
397 align: 0,
398 offset: 0,
399 }
400 }
401
402 pub fn i32() -> Self {
403 Self::i32_offset(0)
404 }
405
406 pub fn i64() -> Self {
407 Self::i64_offset(0)
408 }
409
410 pub fn i32_offset(offset: u32) -> Self {
411 Self { align: 2, offset }
412 }
413
414 pub fn i64_offset(offset: u32) -> Self {
415 Self { align: 3, offset }
416 }
417
418 fn reencode(self) -> wasm_encoder::MemArg {
419 wasm_encoder::MemArg {
420 offset: self.offset as u64,
421 align: self.align as u32,
422 memory_index: 0,
423 }
424 }
425}
426
427#[derive(Debug, Clone, Eq, PartialEq)]
428pub struct BrTable {
429 pub default: u32,
430 pub targets: Vec<u32>,
431}
432
433impl BrTable {
434 pub fn len(&self) -> u32 {
436 self.targets.len() as u32
437 }
438
439 pub fn is_empty(&self) -> bool {
441 self.targets.is_empty()
442 }
443}
444
445impl TryFrom<wasmparser::BrTable<'_>> for BrTable {
446 type Error = ModuleError;
447
448 fn try_from(targets: wasmparser::BrTable) -> Result<Self> {
449 Ok(Self {
450 default: targets.default(),
451 targets: targets
452 .targets()
453 .collect::<Result<Vec<_>, BinaryReaderError>>()?,
454 })
455 }
456}
457
458#[derive(Default, Clone, derive_more::Debug, Eq, PartialEq)]
459#[debug("ConstExpr {{ .. }}")]
460pub struct ConstExpr {
461 pub instructions: Vec<Instruction>,
462}
463
464impl ConstExpr {
465 pub fn empty() -> Self {
466 Self {
467 instructions: Vec::new(),
468 }
469 }
470
471 pub fn i32_value(value: i32) -> Self {
472 Self {
473 instructions: vec![Instruction::I32Const(value)],
474 }
475 }
476
477 pub fn i64_value(value: i64) -> Self {
478 Self {
479 instructions: vec![Instruction::I64Const(value)],
480 }
481 }
482
483 fn parse(expr: wasmparser::ConstExpr) -> Result<Self> {
484 let mut instructions = Vec::new();
485 let mut ops = expr.get_operators_reader();
486 while !ops.is_end_then_eof() {
487 instructions.push(Instruction::parse(ops.read()?)?);
488 }
489
490 Ok(Self { instructions })
491 }
492
493 fn reencode(&self) -> Result<wasm_encoder::ConstExpr> {
494 Ok(wasm_encoder::ConstExpr::extended(
495 self.instructions
496 .iter()
497 .map(Instruction::reencode)
498 .collect::<Result<Vec<_>>>()?,
499 ))
500 }
501}
502
503#[derive(Debug, Clone, Eq, PartialEq)]
504pub struct Import {
505 pub module: Cow<'static, str>,
506 pub name: Cow<'static, str>,
507 pub ty: TypeRef,
508}
509
510impl Import {
511 pub fn func(
512 module: impl Into<Cow<'static, str>>,
513 name: impl Into<Cow<'static, str>>,
514 index: u32,
515 ) -> Self {
516 Self {
517 module: module.into(),
518 name: name.into(),
519 ty: TypeRef::Func(index),
520 }
521 }
522
523 pub fn memory(initial: u32, maximum: Option<u32>) -> Self {
524 Self {
525 module: "env".into(),
526 name: "memory".into(),
527 ty: TypeRef::Memory(MemoryType {
528 memory64: false,
529 shared: false,
530 initial: initial as u64,
531 maximum: maximum.map(|v| v as u64),
532 page_size_log2: None,
533 }),
534 }
535 }
536
537 fn parse(import: wasmparser::Import) -> Self {
538 Self {
539 module: import.module.to_string().into(),
540 name: import.name.to_string().into(),
541 ty: import.ty,
542 }
543 }
544
545 pub fn reencode(&self, imports: &mut wasm_encoder::ImportSection) -> Result<()> {
546 imports.import(
547 &self.module,
548 &self.name,
549 RoundtripReencoder.entity_type(self.ty)?,
550 );
551 Ok(())
552 }
553}
554
555#[derive(Clone, Debug, Eq, PartialEq)]
556pub enum TableInit {
557 RefNull,
558}
559
560#[derive(Clone, Debug, Eq, PartialEq)]
561pub struct Table {
562 pub ty: TableType,
563 pub init: TableInit,
564}
565
566impl Table {
567 pub fn funcref(initial: u32, maximum: Option<u32>) -> Self {
568 Table {
569 ty: TableType {
570 element_type: RefType::FUNCREF,
571 table64: false,
572 initial: initial as u64,
573 maximum: maximum.map(|v| v as u64),
574 shared: false,
575 },
576 init: TableInit::RefNull,
577 }
578 }
579
580 fn parse(table: wasmparser::Table) -> Result<Self> {
581 Ok(Self {
582 ty: table.ty,
583 init: match table.init {
584 wasmparser::TableInit::RefNull => TableInit::RefNull,
585 wasmparser::TableInit::Expr(_expr) => return Err(ModuleError::TableInitExpr),
586 },
587 })
588 }
589
590 fn reencode(&self, tables: &mut wasm_encoder::TableSection) -> Result<()> {
591 let ty = RoundtripReencoder.table_type(self.ty)?;
592 match &self.init {
593 TableInit::RefNull => {
594 tables.table(ty);
595 }
596 }
597 Ok(())
598 }
599}
600
601#[derive(Debug, Clone)]
602pub struct Global {
603 pub ty: GlobalType,
604 pub init_expr: ConstExpr,
605}
606
607impl Global {
608 pub fn i32_value(value: i32) -> Self {
609 Self {
610 ty: GlobalType {
611 content_type: ValType::I32,
612 mutable: false,
613 shared: false,
614 },
615 init_expr: ConstExpr::i32_value(value),
616 }
617 }
618
619 pub fn i64_value(value: i64) -> Self {
620 Self {
621 ty: GlobalType {
622 content_type: ValType::I64,
623 mutable: false,
624 shared: false,
625 },
626 init_expr: ConstExpr::i64_value(value),
627 }
628 }
629
630 pub fn i64_value_mut(value: i64) -> Self {
631 Self {
632 ty: GlobalType {
633 content_type: ValType::I64,
634 mutable: true,
635 shared: false,
636 },
637 init_expr: ConstExpr::i64_value(value),
638 }
639 }
640
641 fn parse(global: wasmparser::Global) -> Result<Self> {
642 Ok(Self {
643 ty: global.ty,
644 init_expr: ConstExpr::parse(global.init_expr)?,
645 })
646 }
647}
648
649#[derive(Debug, Clone)]
650pub struct Export {
651 pub name: Cow<'static, str>,
652 pub kind: ExternalKind,
653 pub index: u32,
654}
655
656impl Export {
657 pub fn func(name: impl Into<Cow<'static, str>>, index: u32) -> Self {
658 Self {
659 name: name.into(),
660 kind: ExternalKind::Func,
661 index,
662 }
663 }
664
665 pub fn global(name: impl Into<Cow<'static, str>>, index: u32) -> Self {
666 Self {
667 name: name.into(),
668 kind: ExternalKind::Global,
669 index,
670 }
671 }
672
673 fn parse(export: wasmparser::Export) -> Self {
674 Self {
675 name: export.name.to_string().into(),
676 kind: export.kind,
677 index: export.index,
678 }
679 }
680}
681
682#[derive(Clone)]
683pub enum ElementKind {
684 Active { offset_expr: ConstExpr },
685}
686
687impl ElementKind {
688 fn parse(kind: wasmparser::ElementKind) -> Result<Self> {
689 match kind {
690 wasmparser::ElementKind::Passive => Err(ModuleError::NonActiveElementKind),
691 wasmparser::ElementKind::Active {
692 table_index,
693 offset_expr,
694 } => {
695 if let Some(table_index) = table_index {
696 return Err(ModuleError::ElementTableIdx(table_index));
697 }
698
699 Ok(Self::Active {
700 offset_expr: ConstExpr::parse(offset_expr)?,
701 })
702 }
703 wasmparser::ElementKind::Declared => Err(ModuleError::NonActiveElementKind),
704 }
705 }
706}
707
708#[derive(Clone)]
709pub enum ElementItems {
710 Functions(Vec<u32>),
711}
712
713impl ElementItems {
714 fn parse(elements: wasmparser::ElementItems) -> Result<Self> {
715 match elements {
716 wasmparser::ElementItems::Functions(f) => {
717 let mut funcs = Vec::new();
718 for func in f {
719 funcs.push(func?);
720 }
721 Ok(Self::Functions(funcs))
722 }
723 wasmparser::ElementItems::Expressions(_ty, _e) => Err(ModuleError::ElementExpressions),
724 }
725 }
726}
727
728#[derive(Clone)]
729pub struct Element {
730 pub kind: ElementKind,
731 pub items: ElementItems,
732}
733
734impl Element {
735 pub fn functions(funcs: Vec<u32>) -> Self {
736 Self {
737 kind: ElementKind::Active {
738 offset_expr: ConstExpr::i32_value(0),
739 },
740 items: ElementItems::Functions(funcs),
741 }
742 }
743
744 fn parse(element: wasmparser::Element) -> Result<Self> {
745 Ok(Self {
746 kind: ElementKind::parse(element.kind)?,
747 items: ElementItems::parse(element.items)?,
748 })
749 }
750
751 fn reencode(&self, encoder_section: &mut wasm_encoder::ElementSection) -> Result<()> {
752 let items = match &self.items {
753 ElementItems::Functions(funcs) => {
754 wasm_encoder::Elements::Functions(funcs.clone().into())
755 }
756 };
757
758 match &self.kind {
759 ElementKind::Active { offset_expr } => {
760 encoder_section.active(None, &offset_expr.reencode()?, items);
761 }
762 }
763
764 Ok(())
765 }
766}
767
768#[derive(Debug, Clone)]
769pub struct Data {
770 pub offset_expr: ConstExpr,
771 pub data: Cow<'static, [u8]>,
772}
773
774impl Data {
775 pub fn with_offset(data: impl Into<Cow<'static, [u8]>>, offset: u32) -> Self {
776 Self {
777 offset_expr: ConstExpr::i32_value(offset as i32),
778 data: data.into(),
779 }
780 }
781
782 fn parse(data: wasmparser::Data) -> Result<Self> {
783 Ok(Self {
784 offset_expr: match data.kind {
785 wasmparser::DataKind::Passive => return Err(ModuleError::PassiveDataKind),
786 wasmparser::DataKind::Active {
787 memory_index,
788 offset_expr,
789 } => {
790 if memory_index != 0 {
791 return Err(ModuleError::NonZeroMemoryIdx(memory_index));
792 }
793
794 ConstExpr::parse(offset_expr)?
795 }
796 },
797 data: data.data.to_vec().into(),
798 })
799 }
800}
801
802#[derive(Debug, Clone, Default)]
803pub struct Function {
804 pub locals: Vec<(u32, ValType)>,
805 pub instructions: Vec<Instruction>,
806}
807
808impl Function {
809 pub fn from_instructions(instructions: impl Into<Vec<Instruction>>) -> Self {
810 Self {
811 locals: Vec::new(),
812 instructions: instructions.into(),
813 }
814 }
815
816 fn from_entry(func: FunctionBody) -> Result<Self> {
817 let mut locals = Vec::new();
818 for pair in func.get_locals_reader()? {
819 let (cnt, ty) = pair?;
820 locals.push((cnt, ty));
821 }
822
823 let mut instructions = Vec::new();
824 let mut reader = func.get_operators_reader()?;
825 while !reader.eof() {
826 instructions.push(Instruction::parse(reader.read()?)?);
827 }
828
829 Ok(Self {
830 locals,
831 instructions,
832 })
833 }
834
835 fn reencode(&self) -> Result<wasm_encoder::Function> {
836 let mut encoder_func = wasm_encoder::Function::new(
837 self.locals
838 .iter()
839 .map(|&(cnt, ty)| Ok((cnt, RoundtripReencoder.val_type(ty)?)))
840 .collect::<Result<Vec<_>, reencode::Error>>()?,
841 );
842
843 for op in &self.instructions {
844 encoder_func.instruction(&op.reencode()?);
845 }
846
847 if self.instructions.is_empty() {
848 encoder_func.instruction(&wasm_encoder::Instruction::End);
849 }
850
851 Ok(encoder_func)
852 }
853}
854
855pub type NameMap = Vec<Naming>;
856
857#[derive(Debug, Clone)]
859pub struct Naming {
860 pub index: u32,
862 pub name: Cow<'static, str>,
864}
865
866pub type IndirectNameMap = Vec<IndirectNaming>;
867
868#[derive(Debug, Clone)]
870pub struct IndirectNaming {
871 pub index: u32,
873 pub names: NameMap,
875}
876
877#[derive(Debug, Clone)]
878pub enum Name {
879 Module(Cow<'static, str>),
881 Function(NameMap),
883 Local(IndirectNameMap),
885 Label(IndirectNameMap),
887 Type(NameMap),
889 Table(NameMap),
891 Memory(NameMap),
893 Global(NameMap),
895 Element(NameMap),
897 Data(NameMap),
899 Field(IndirectNameMap),
901 Tag(NameMap),
903 Unknown {
905 ty: u8,
907 data: Cow<'static, [u8]>,
909 },
910}
911
912impl Name {
913 fn parse(name: wasmparser::Name) -> Result<Self> {
914 let name_map = |map: wasmparser::NameMap| {
915 map.into_iter()
916 .map(|n| {
917 n.map(|n| Naming {
918 index: n.index,
919 name: n.name.to_string().into(),
920 })
921 })
922 .collect::<Result<Vec<_>, BinaryReaderError>>()
923 };
924
925 let indirect_name_map = |map: wasmparser::IndirectNameMap| {
926 map.into_iter()
927 .map(|n| {
928 n.and_then(|n| {
929 Ok(IndirectNaming {
930 index: n.index,
931 names: name_map(n.names)?,
932 })
933 })
934 })
935 .collect::<Result<Vec<_>, BinaryReaderError>>()
936 };
937
938 Ok(match name {
939 wasmparser::Name::Module {
940 name,
941 name_range: _,
942 } => Self::Module(name.to_string().into()),
943 wasmparser::Name::Function(map) => Self::Function(name_map(map)?),
944 wasmparser::Name::Local(map) => Self::Local(indirect_name_map(map)?),
945 wasmparser::Name::Label(map) => Self::Label(indirect_name_map(map)?),
946 wasmparser::Name::Type(map) => Self::Type(name_map(map)?),
947 wasmparser::Name::Table(map) => Self::Table(name_map(map)?),
948 wasmparser::Name::Memory(map) => Self::Memory(name_map(map)?),
949 wasmparser::Name::Global(map) => Self::Global(name_map(map)?),
950 wasmparser::Name::Element(map) => Self::Element(name_map(map)?),
951 wasmparser::Name::Data(map) => Self::Data(name_map(map)?),
952 wasmparser::Name::Field(map) => Self::Field(indirect_name_map(map)?),
953 wasmparser::Name::Tag(map) => Self::Tag(name_map(map)?),
954 wasmparser::Name::Unknown { ty, data, range: _ } => Self::Unknown {
955 ty,
956 data: data.to_vec().into(),
957 },
958 })
959 }
960
961 fn reencode(&self, section: &mut wasm_encoder::NameSection) {
962 let name_map = |map: &NameMap| {
963 map.iter()
964 .fold(wasm_encoder::NameMap::new(), |mut map, naming| {
965 map.append(naming.index, &naming.name);
966 map
967 })
968 };
969
970 let indirect_name_map = |map: &IndirectNameMap| {
971 map.iter()
972 .fold(wasm_encoder::IndirectNameMap::new(), |mut map, naming| {
973 map.append(naming.index, &name_map(&naming.names));
974 map
975 })
976 };
977
978 match self {
979 Name::Module(name) => {
980 section.module(name);
981 }
982 Name::Function(map) => section.functions(&name_map(map)),
983 Name::Local(map) => section.locals(&indirect_name_map(map)),
984 Name::Label(map) => section.labels(&indirect_name_map(map)),
985 Name::Type(map) => section.types(&name_map(map)),
986 Name::Table(map) => section.tables(&name_map(map)),
987 Name::Memory(map) => section.memories(&name_map(map)),
988 Name::Global(map) => section.globals(&name_map(map)),
989 Name::Element(map) => section.elements(&name_map(map)),
990 Name::Data(map) => section.data(&name_map(map)),
991 Name::Field(map) => section.fields(&indirect_name_map(map)),
992 Name::Tag(map) => section.tags(&name_map(map)),
993 Name::Unknown { ty, data } => section.raw(*ty, data),
994 }
995 }
996}
997
998pub struct ModuleFuncIndexShifter {
999 builder: ModuleBuilder,
1000 inserted_at: u32,
1001 code_section: bool,
1002 export_section: bool,
1003 element_section: bool,
1004 start_section: bool,
1005 name_section: bool,
1006}
1007
1008impl ModuleFuncIndexShifter {
1009 pub fn with_code_section(mut self) -> Self {
1010 self.code_section = true;
1011 self
1012 }
1013
1014 pub fn with_export_section(mut self) -> Self {
1015 self.export_section = true;
1016 self
1017 }
1018
1019 pub fn with_element_section(mut self) -> Self {
1020 self.element_section = true;
1021 self
1022 }
1023
1024 pub fn with_start_section(mut self) -> Self {
1025 self.start_section = true;
1026 self
1027 }
1028
1029 pub fn with_name_section(mut self) -> Self {
1030 self.name_section = true;
1031 self
1032 }
1033
1034 pub fn with_all(self) -> Self {
1036 self.with_code_section()
1037 .with_export_section()
1038 .with_element_section()
1039 .with_start_section()
1040 .with_name_section()
1041 }
1042
1043 pub fn shift_all(self) -> ModuleBuilder {
1044 self.with_all().shift()
1045 }
1046
1047 pub fn shift(mut self) -> ModuleBuilder {
1049 if let Some(section) = self
1050 .builder
1051 .module
1052 .code_section
1053 .as_mut()
1054 .filter(|_| self.code_section)
1055 {
1056 for func in section {
1057 for instruction in &mut func.instructions {
1058 if let Instruction::Call(function_index) = instruction
1059 && *function_index >= self.inserted_at
1060 {
1061 *function_index += 1
1062 }
1063 }
1064 }
1065 }
1066
1067 if let Some(section) = self
1068 .builder
1069 .module
1070 .export_section
1071 .as_mut()
1072 .filter(|_| self.export_section)
1073 {
1074 for export in section {
1075 if let ExternalKind::Func = export.kind
1076 && export.index >= self.inserted_at
1077 {
1078 export.index += 1
1079 }
1080 }
1081 }
1082
1083 if let Some(section) = self
1084 .builder
1085 .module
1086 .element_section
1087 .as_mut()
1088 .filter(|_| self.element_section)
1089 {
1090 for segment in section {
1091 match &mut segment.items {
1093 ElementItems::Functions(funcs) => {
1094 for func_index in funcs.iter_mut() {
1095 if *func_index >= self.inserted_at {
1096 *func_index += 1
1097 }
1098 }
1099 }
1100 }
1101 }
1102 }
1103
1104 if let Some(start_idx) = self
1105 .builder
1106 .module
1107 .start_section
1108 .as_mut()
1109 .filter(|_| self.start_section)
1110 && *start_idx >= self.inserted_at
1111 {
1112 *start_idx += 1
1113 }
1114
1115 if let Some(section) = self
1116 .builder
1117 .module
1118 .name_section
1119 .as_mut()
1120 .filter(|_| self.name_section)
1121 {
1122 for name in section {
1123 if let Name::Function(map) = name {
1124 for naming in map {
1125 if naming.index >= self.inserted_at {
1126 naming.index += 1;
1127 }
1128 }
1129 }
1130 }
1131 }
1132
1133 self.builder
1134 }
1135}
1136
1137#[derive(Debug, Default)]
1138pub struct ModuleBuilder {
1139 module: Module,
1140}
1141
1142impl ModuleBuilder {
1143 pub fn from_module(module: Module) -> Self {
1144 Self { module }
1145 }
1146
1147 pub fn shift_func_index(self, inserted_at: u32) -> ModuleFuncIndexShifter {
1148 ModuleFuncIndexShifter {
1149 builder: self,
1150 inserted_at,
1151 code_section: false,
1152 export_section: false,
1153 element_section: false,
1154 start_section: false,
1155 name_section: false,
1156 }
1157 }
1158
1159 pub fn build(self) -> Module {
1160 self.module
1161 }
1162
1163 fn type_section(&mut self) -> &mut TypeSection {
1164 self.module
1165 .type_section
1166 .get_or_insert_with(Default::default)
1167 }
1168
1169 fn import_section(&mut self) -> &mut Vec<Import> {
1170 self.module.import_section.get_or_insert_with(Vec::new)
1171 }
1172
1173 fn func_section(&mut self) -> &mut Vec<u32> {
1174 self.module.function_section.get_or_insert_with(Vec::new)
1175 }
1176
1177 fn global_section(&mut self) -> &mut Vec<Global> {
1178 self.module.global_section.get_or_insert_with(Vec::new)
1179 }
1180
1181 fn export_section(&mut self) -> &mut Vec<Export> {
1182 self.module.export_section.get_or_insert_with(Vec::new)
1183 }
1184
1185 fn element_section(&mut self) -> &mut Vec<Element> {
1186 self.module.element_section.get_or_insert_with(Vec::new)
1187 }
1188
1189 fn code_section(&mut self) -> &mut CodeSection {
1190 self.module.code_section.get_or_insert_with(Vec::new)
1191 }
1192
1193 fn data_section(&mut self) -> &mut DataSection {
1194 self.module.data_section.get_or_insert_with(Vec::new)
1195 }
1196
1197 fn custom_sections(&mut self) -> &mut Vec<CustomSection> {
1198 self.module.custom_sections.get_or_insert_with(Vec::new)
1199 }
1200
1201 pub fn push_custom_section(
1202 &mut self,
1203 name: impl Into<Cow<'static, str>>,
1204 data: impl Into<Vec<u8>>,
1205 ) {
1206 self.custom_sections().push((name.into(), data.into()));
1207 }
1208
1209 pub fn add_func(&mut self, ty: FuncType, function: Function) -> u32 {
1213 let type_idx = self.push_type(ty);
1214 self.func_section().push(type_idx);
1215 let func_idx = self.func_section().len() as u32 - 1;
1216 self.code_section().push(function);
1217 func_idx
1218 }
1219
1220 pub fn push_type(&mut self, ty: FuncType) -> u32 {
1221 let idx = self.type_section().iter().position(|vec_ty| *vec_ty == ty);
1222 idx.map(|pos| pos as u32).unwrap_or_else(|| {
1223 self.type_section().push(ty);
1224 self.type_section().len() as u32 - 1
1225 })
1226 }
1227
1228 pub fn push_import(&mut self, import: Import) -> u32 {
1229 self.import_section().push(import);
1230 self.import_section().len() as u32 - 1
1231 }
1232
1233 pub fn set_table(&mut self, table: Table) {
1234 debug_assert_eq!(self.module.table_section, None);
1235 self.module.table_section = Some(table);
1236 }
1237
1238 pub fn push_global(&mut self, global: Global) -> u32 {
1239 self.global_section().push(global);
1240 self.global_section().len() as u32 - 1
1241 }
1242
1243 pub fn push_export(&mut self, export: Export) {
1244 self.export_section().push(export);
1245 }
1246
1247 pub fn push_element(&mut self, element: Element) {
1248 self.element_section().push(element);
1249 }
1250
1251 pub fn push_data(&mut self, data: Data) {
1252 self.data_section().push(data);
1253 }
1254}
1255
1256pub type TypeSection = Vec<FuncType>;
1257pub type FuncSection = Vec<u32>;
1258pub type CodeSection = Vec<Function>;
1259pub type DataSection = Vec<Data>;
1260pub type CustomSection = (Cow<'static, str>, Vec<u8>);
1261
1262#[derive(derive_more::Debug, Clone, Default)]
1263#[debug("Module {{ .. }}")]
1264pub struct Module {
1265 pub type_section: Option<TypeSection>,
1266 pub import_section: Option<Vec<Import>>,
1267 pub function_section: Option<FuncSection>,
1268 pub table_section: Option<Table>,
1269 pub memory_section: Option<MemoryType>,
1270 pub global_section: Option<Vec<Global>>,
1271 pub export_section: Option<Vec<Export>>,
1272 pub start_section: Option<u32>,
1273 pub element_section: Option<Vec<Element>>,
1274 pub code_section: Option<CodeSection>,
1275 pub data_section: Option<DataSection>,
1276 pub name_section: Option<Vec<Name>>,
1277 pub custom_sections: Option<Vec<CustomSection>>,
1278}
1279
1280impl Module {
1281 pub fn new(code: &[u8]) -> Result<Self> {
1282 let mut type_section = None;
1283 let mut import_section = None;
1284 let mut function_section = None;
1285 let mut table_section = None;
1286 let mut memory_section = None;
1287 let mut global_section = None;
1288 let mut export_section = None;
1289 let mut start_section = None;
1290 let mut element_section = None;
1291 let mut code_section = None;
1292 let mut data_section = None;
1293 let mut name_section = None;
1294 let mut custom_sections = None;
1295
1296 let mut parser = wasmparser::Parser::new(0);
1297 parser.set_features(GEAR_SUPPORTED_FEATURES);
1298 for payload in parser.parse_all(code) {
1299 match payload? {
1300 Payload::Version {
1301 num: _,
1302 encoding,
1303 range: _,
1304 } => {
1305 debug_assert_eq!(encoding, Encoding::Module);
1306 }
1307 Payload::TypeSection(section) => {
1308 debug_assert!(type_section.is_none());
1309 type_section = Some(
1310 section
1311 .into_iter_err_on_gc_types()
1312 .collect::<Result<_, _>>()?,
1313 );
1314 }
1315 Payload::ImportSection(section) => {
1316 debug_assert!(import_section.is_none());
1317 import_section = Some(
1318 section
1319 .into_iter()
1320 .map(|import| import.map(Import::parse))
1321 .collect::<Result<_, _>>()?,
1322 );
1323 }
1324 Payload::FunctionSection(section) => {
1325 debug_assert!(function_section.is_none());
1326 function_section = Some(section.into_iter().collect::<Result<_, _>>()?);
1327 }
1328 Payload::TableSection(section) => {
1329 debug_assert!(table_section.is_none());
1330 let mut section = section.into_iter();
1331
1332 table_section = section
1333 .next()
1334 .map(|table| table.map_err(Into::into).and_then(Table::parse))
1335 .transpose()?;
1336
1337 if section.next().is_some() {
1338 return Err(ModuleError::MultipleTables);
1339 }
1340 }
1341 Payload::MemorySection(section) => {
1342 debug_assert!(memory_section.is_none());
1343 let mut section = section.into_iter();
1344
1345 memory_section = section.next().transpose()?;
1346
1347 if section.next().is_some() {
1348 return Err(ModuleError::MultipleMemories);
1349 }
1350 }
1351 Payload::TagSection(_) => {}
1352 Payload::GlobalSection(section) => {
1353 debug_assert!(global_section.is_none());
1354 global_section = Some(
1355 section
1356 .into_iter()
1357 .map(|element| element.map_err(Into::into).and_then(Global::parse))
1358 .collect::<Result<_, _>>()?,
1359 );
1360 }
1361 Payload::ExportSection(section) => {
1362 debug_assert!(export_section.is_none());
1363 export_section = Some(
1364 section
1365 .into_iter()
1366 .map(|e| e.map(Export::parse))
1367 .collect::<Result<_, _>>()?,
1368 );
1369 }
1370 Payload::StartSection { func, range: _ } => {
1371 start_section = Some(func);
1372 }
1373 Payload::ElementSection(section) => {
1374 debug_assert!(element_section.is_none());
1375 element_section = Some(
1376 section
1377 .into_iter()
1378 .map(|element| element.map_err(Into::into).and_then(Element::parse))
1379 .collect::<Result<Vec<_>>>()?,
1380 );
1381 }
1382 Payload::DataCountSection { count, range: _ } => {
1384 data_section = Some(Vec::with_capacity(count as usize));
1385 }
1386 Payload::DataSection(section) => {
1387 let data_section = data_section.get_or_insert_with(Vec::new);
1388 for data in section {
1389 let data = data?;
1390 data_section.push(Data::parse(data)?);
1391 }
1392 }
1393 Payload::CodeSectionStart {
1394 count,
1395 range: _,
1396 size: _,
1397 } => {
1398 code_section = Some(Vec::with_capacity(count as usize));
1399 }
1400 Payload::CodeSectionEntry(entry) => {
1401 code_section
1402 .as_mut()
1403 .expect("code section start missing")
1404 .push(Function::from_entry(entry)?);
1405 }
1406 Payload::CustomSection(section) => match section.as_known() {
1407 KnownCustom::Name(name_section_reader) => {
1408 name_section = Some(
1409 name_section_reader
1410 .into_iter()
1411 .map(|name| name.map_err(Into::into).and_then(Name::parse))
1412 .collect::<Result<Vec<_>>>()?,
1413 );
1414 }
1415 _ => {
1416 let custom_sections = custom_sections.get_or_insert_with(Vec::new);
1417 let name = section.name().to_string().into();
1418 let data = section.data().to_vec();
1419 custom_sections.push((name, data));
1420 }
1421 },
1422 Payload::UnknownSection { .. } => {}
1423 _ => {}
1424 }
1425 }
1426
1427 Ok(Self {
1428 type_section,
1429 import_section,
1430 function_section,
1431 table_section,
1432 memory_section,
1433 global_section,
1434 export_section,
1435 start_section,
1436 element_section,
1437 code_section,
1438 data_section,
1439 name_section,
1440 custom_sections,
1441 })
1442 }
1443
1444 pub fn serialize(&self) -> Result<Vec<u8>> {
1445 let mut module = wasm_encoder::Module::new();
1446
1447 if let Some(crate_section) = &self.type_section {
1448 let mut encoder_section = wasm_encoder::TypeSection::new();
1449 for func_type in crate_section.clone() {
1450 encoder_section
1451 .ty()
1452 .func_type(&RoundtripReencoder.func_type(func_type)?);
1453 }
1454 module.section(&encoder_section);
1455 }
1456
1457 if let Some(crate_section) = &self.import_section {
1458 let mut encoder_section = wasm_encoder::ImportSection::new();
1459 for import in crate_section.clone() {
1460 import.reencode(&mut encoder_section)?;
1461 }
1462 module.section(&encoder_section);
1463 }
1464
1465 if let Some(crate_section) = &self.function_section {
1466 let mut encoder_section = wasm_encoder::FunctionSection::new();
1467 for &function in crate_section {
1468 encoder_section.function(function);
1469 }
1470 module.section(&encoder_section);
1471 }
1472
1473 if let Some(table) = &self.table_section {
1474 let mut encoder_section = wasm_encoder::TableSection::new();
1475 table.reencode(&mut encoder_section)?;
1476 module.section(&encoder_section);
1477 }
1478
1479 if let Some(memory) = &self.memory_section {
1480 let mut encoder_section = wasm_encoder::MemorySection::new();
1481 encoder_section.memory(RoundtripReencoder.memory_type(*memory));
1482 module.section(&encoder_section);
1483 }
1484
1485 if let Some(crate_section) = &self.global_section {
1486 let mut encoder_section = wasm_encoder::GlobalSection::new();
1487 for global in crate_section {
1488 encoder_section.global(
1489 RoundtripReencoder.global_type(global.ty)?,
1490 &global.init_expr.reencode()?,
1491 );
1492 }
1493 module.section(&encoder_section);
1494 }
1495
1496 if let Some(crate_section) = &self.export_section {
1497 let mut encoder_section = wasm_encoder::ExportSection::new();
1498 for export in crate_section {
1499 encoder_section.export(
1500 &export.name,
1501 RoundtripReencoder.export_kind(export.kind),
1502 export.index,
1503 );
1504 }
1505 module.section(&encoder_section);
1506 }
1507
1508 if let Some(function_index) = self.start_section {
1509 module.section(&wasm_encoder::StartSection { function_index });
1510 }
1511
1512 if let Some(crate_section) = &self.element_section {
1513 let mut encoder_section = wasm_encoder::ElementSection::new();
1514 for element in crate_section {
1515 element.reencode(&mut encoder_section)?;
1516 }
1517 module.section(&encoder_section);
1518 }
1519
1520 if let Some(crate_section) = &self.code_section {
1521 let mut encoder_section = wasm_encoder::CodeSection::new();
1522 for function in crate_section {
1523 encoder_section.function(&function.reencode()?);
1524 }
1525 module.section(&encoder_section);
1526 }
1527
1528 if let Some(crate_section) = &self.data_section {
1529 let mut encoder_section = wasm_encoder::DataSection::new();
1530 for data in crate_section {
1531 encoder_section.active(0, &data.offset_expr.reencode()?, data.data.iter().copied());
1532 }
1533 module.section(&encoder_section);
1534 }
1535
1536 if let Some(name_section) = &self.name_section {
1537 let mut encoder_section = wasm_encoder::NameSection::new();
1538 for name in name_section {
1539 name.reencode(&mut encoder_section);
1540 }
1541 module.section(&encoder_section);
1542 }
1543
1544 if let Some(custom_sections) = &self.custom_sections {
1545 for (name, data) in custom_sections {
1546 let encoder_section = wasm_encoder::CustomSection {
1547 name: Cow::Borrowed(name),
1548 data: Cow::Borrowed(data),
1549 };
1550 module.section(&encoder_section);
1551 }
1552 }
1553
1554 Ok(module.finish())
1555 }
1556
1557 pub fn import_count(&self, pred: impl Fn(&TypeRef) -> bool) -> usize {
1558 self.import_section
1559 .as_ref()
1560 .map(|imports| imports.iter().filter(|import| pred(&import.ty)).count())
1561 .unwrap_or(0)
1562 }
1563
1564 pub fn functions_space(&self) -> usize {
1565 self.import_count(|ty| matches!(ty, TypeRef::Func(_)))
1566 + self
1567 .function_section
1568 .as_ref()
1569 .map(|section| section.len())
1570 .unwrap_or(0)
1571 }
1572
1573 pub fn globals_space(&self) -> usize {
1574 self.import_count(|ty| matches!(ty, TypeRef::Global(_)))
1575 + self
1576 .global_section
1577 .as_ref()
1578 .map(|section| section.len())
1579 .unwrap_or(0)
1580 }
1581}
1582
1583#[cfg(test)]
1584mod tests {
1585 use super::*;
1586
1587 macro_rules! test_parsing_failed {
1588 (
1589 $( $test_name:ident: $wat:literal => $err:expr; )*
1590 ) => {
1591 $(
1592 #[test]
1593 fn $test_name() {
1594 let wasm = wat::parse_str($wat).unwrap();
1595 let lhs = Module::new(&wasm).unwrap_err();
1596 let rhs: ModuleError = $err;
1597 assert_eq!(format!("{lhs:?}"), format!("{rhs:?}"));
1599 }
1600 )*
1601 };
1602 }
1603
1604 test_parsing_failed! {
1605 multiple_tables_denied: r#"
1606 (module
1607 (table 10 10 funcref)
1608 (table 20 20 funcref)
1609 )"# => ModuleError::MultipleTables;
1610
1611 multiple_memories_denied: r#"
1612 (module
1613 (memory (export "memory") 1)
1614 (memory (export "memory2") 2)
1615 )"# => ModuleError::MultipleMemories;
1616
1617 data_non_zero_memory_idx_denied: r#"
1618 (module
1619 (data (memory 123) (offset i32.const 0) "")
1620 )
1621 "# => ModuleError::NonZeroMemoryIdx(123);
1622
1623 element_table_idx_denied: r#"
1624 (module
1625 (elem 123 (offset i32.const 0) 0 0 0 0)
1626 )"# => ModuleError::ElementTableIdx(123);
1627
1628 passive_data_kind_denied: r#"
1629 (module
1630 (data "")
1631 )
1632 "# => ModuleError::PassiveDataKind;
1633
1634 passive_element_denied: r#"
1635 (module
1636 (elem funcref (item i32.const 0))
1637 )
1638 "# => ModuleError::NonActiveElementKind;
1639
1640 declared_element_denied: r#"
1641 (module
1642 (func $a)
1643 (elem declare func $a)
1644 )
1645 "# => ModuleError::NonActiveElementKind;
1646
1647 element_expressions_denied: r#"
1648 (module
1649 (elem (i32.const 1) funcref)
1650 )
1651 "# => ModuleError::ElementExpressions;
1652
1653 table_init_expr_denied: r#"
1654 (module
1655 (table 0 0 funcref (i32.const 0))
1656 )"# => ModuleError::TableInitExpr;
1657 }
1658
1659 #[test]
1660 fn call_indirect_non_zero_table_idx_denied() {
1661 let wasm = wat::parse_str(
1662 r#"
1663 (module
1664 (func
1665 call_indirect 123 (type 333)
1666 )
1667 )
1668 "#,
1669 )
1670 .unwrap();
1671 let err = Module::new(&wasm).unwrap_err();
1672 if let ModuleError::BinaryReader(err) = err {
1673 assert_eq!(err.offset(), 26);
1674 assert_eq!(err.message(), "zero byte expected");
1675 } else {
1676 panic!("{err}");
1677 }
1678 }
1679
1680 #[test]
1681 fn custom_section_kept() {
1682 let mut builder = ModuleBuilder::default();
1683 builder.push_custom_section("dummy", [1, 2, 3]);
1684 let module = builder.build();
1685 let module_bytes = module.serialize().unwrap();
1686 let wat = wasmprinter::print_bytes(&module_bytes).unwrap();
1687
1688 let parsed_module_bytes = Module::new(&module_bytes).unwrap().serialize().unwrap();
1689 let parsed_wat = wasmprinter::print_bytes(&parsed_module_bytes).unwrap();
1690 assert_eq!(wat, parsed_wat);
1691 }
1692}