1use crate::builtins::{Blob, Lazy, UnparsedBytes, WasmbinCountable};
18#[cfg(feature = "extended-name-section")]
19use crate::indices::{DataId, ElemId, LabelId};
20use crate::indices::{ExceptionId, FuncId, GlobalId, LocalId, MemId, TableId, TypeId};
21use crate::instructions::Expression;
22use crate::io::{Decode, DecodeError, DecodeWithDiscriminant, Encode, PathItem, Wasmbin};
23use crate::types::{
24 ExceptionType, GlobalType, MemType, RecursiveType, RefType, TableType, ValueType,
25};
26use crate::visit::{Visit, VisitError};
27use custom_debug::Debug as CustomDebug;
28use std::convert::TryFrom;
29use thiserror::Error;
30
31#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
35pub struct NameAssoc<I, V = String> {
36 pub index: I,
37 pub value: V,
38}
39
40impl<I, V> WasmbinCountable for NameAssoc<I, V> {}
41
42#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
44pub struct NameMap<I, V = String> {
45 pub items: Vec<NameAssoc<I, V>>,
46}
47
48pub type IndirectNameMap<I1, I2> = NameMap<I1, NameMap<I2>>;
50
51#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
53#[repr(u8)]
54pub enum NameSubSection {
55 Module(Blob<String>) = 0,
57 Func(Blob<NameMap<FuncId>>) = 1,
59 Local(Blob<IndirectNameMap<FuncId, LocalId>>) = 2,
61 #[cfg(feature = "extended-name-section")]
62 Label(Blob<IndirectNameMap<FuncId, LabelId>>) = 3,
64 #[cfg(feature = "extended-name-section")]
65 Type(Blob<NameMap<TypeId>>) = 4,
67 #[cfg(feature = "extended-name-section")]
68 Table(Blob<NameMap<TableId>>) = 5,
70 #[cfg(feature = "extended-name-section")]
71 Memory(Blob<NameMap<MemId>>) = 6,
73 #[cfg(feature = "extended-name-section")]
74 Global(Blob<NameMap<GlobalId>>) = 7,
76 #[cfg(feature = "extended-name-section")]
77 Elem(Blob<NameMap<ElemId>>) = 8,
79 #[cfg(feature = "extended-name-section")]
80 Data(Blob<NameMap<DataId>>) = 9,
82}
83
84impl Encode for [NameSubSection] {
85 fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
86 for sub in self {
87 sub.encode(w)?;
88 }
89 Ok(())
90 }
91}
92
93impl Decode for Vec<NameSubSection> {
94 fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
95 let mut sub = Vec::new();
96 while let Some(disc) = Option::decode(r)? {
97 let i = sub.len();
98 sub.push(
99 NameSubSection::decode_with_discriminant(disc, r)
100 .map_err(move |err| err.in_path(PathItem::Index(i)))?,
101 );
102 }
103 Ok(sub)
104 }
105}
106
107#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
109pub struct ProducerField {
110 pub name: String,
111 pub values: Vec<ProducerVersionedName>,
112}
113
114#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
116pub struct ProducerVersionedName {
117 pub name: String,
118 pub version: String,
119}
120
121#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
125pub struct RawCustomSection {
126 pub name: String,
127 pub data: UnparsedBytes,
128}
129
130macro_rules! define_custom_sections {
131 ($(#[doc = $url:literal] $name:ident($ty:ty) = $disc:literal,)*) => {
132 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
138 #[non_exhaustive]
139 pub enum CustomSection {
140 $(
141 #[doc = "[`"]
142 #[doc = $disc]
143 #[doc = "`]("]
144 #[doc = $url]
145 #[doc = ") custom section."]
146 $name($ty),
147 )*
148 Other(RawCustomSection),
150 }
151
152 impl CustomSection {
153 pub fn name(&self) -> &str {
155 match self {
156 $(Self::$name(_) => $disc,)*
157 Self::Other(raw) => raw.name.as_str(),
158 }
159 }
160 }
161
162 impl Encode for CustomSection {
163 fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
164 match self {
165 $(CustomSection::$name(data) => {
166 $disc.encode(w)?;
167 data.encode(w)
168 })*
169 CustomSection::Other(raw) => raw.encode(w)
170 }
171 }
172 }
173
174 impl Decode for CustomSection {
175 fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
176 let name = String::decode(r)?;
177 Ok(match name.as_str() {
178 $($disc => CustomSection::$name(<$ty>::decode(r)?),)*
179 _ => CustomSection::Other(RawCustomSection {
180 name,
181 data: UnparsedBytes::decode(r)?
182 })
183 })
184 }
185 }
186
187 impl Visit for CustomSection {
188 fn visit_children<'a, VisitT: 'static, E, F: FnMut(&'a VisitT) -> Result<(), E>>(
189 &'a self,
190 f: &mut F,
191 ) -> Result<(), VisitError<E>> {
192 drop(match self {
194 $(CustomSection::$name(data) => Visit::visit_child(data, f),)*
195 CustomSection::Other(raw) => Visit::visit_child(raw, f),
196 });
197 Ok(())
198 }
199
200 fn visit_children_mut<VisitT: 'static, E, F: FnMut(&mut VisitT) -> Result<(), E>>(
201 &mut self,
202 f: &mut F,
203 ) -> Result<(), VisitError<E>> {
204 drop(match self {
206 $(CustomSection::$name(data) => Visit::visit_child_mut(data, f),)*
207 CustomSection::Other(raw) => Visit::visit_child_mut(raw, f),
208 });
209 Ok(())
210 }
211 }
212 };
213}
214
215define_custom_sections! {
216 Name(Lazy<Vec<NameSubSection>>) = "name",
218 Producers(Lazy<Vec<ProducerField>>) = "producers",
220 ExternalDebugInfo(Lazy<String>) = "external_debug_info",
222 SourceMappingUrl(Lazy<String>) = "sourceMappingURL",
224 BuildId(Vec<u8>) = "build_id",
226}
227
228#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
230#[repr(u8)]
231pub enum ImportDesc {
232 Func(TypeId) = 0x00,
233 Table(TableType) = 0x01,
234 Mem(MemType) = 0x02,
235 Global(GlobalType) = 0x03,
236 Exception(ExceptionType) = 0x04,
237}
238
239#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
241pub struct ImportPath {
242 pub module: String,
243 pub name: String,
244}
245
246#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
248pub struct Import {
249 pub path: ImportPath,
250 pub desc: ImportDesc,
251}
252
253#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
255pub struct Global {
256 pub ty: GlobalType,
257 pub init: Expression,
258}
259
260#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
262#[repr(u8)]
263pub enum ExportDesc {
264 Func(FuncId) = 0x00,
265 Table(TableId) = 0x01,
266 Mem(MemId) = 0x02,
267 Global(GlobalId) = 0x03,
268 Exception(ExceptionId) = 0x04,
269}
270
271#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
273pub struct Export {
274 pub name: String,
275 pub desc: ExportDesc,
276}
277
278#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
280#[repr(u8)]
281pub enum ElemKind {
282 FuncRef = 0x00,
283}
284
285#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
287#[repr(u32)]
288pub enum Element {
289 ActiveWithFuncs {
290 offset: Expression,
291 funcs: Vec<FuncId>,
292 } = 0,
293 PassiveWithFuncs {
294 kind: ElemKind,
295 funcs: Vec<FuncId>,
296 } = 1,
297 ActiveWithTableAndFuncs {
298 table: TableId,
299 offset: Expression,
300 kind: ElemKind,
301 funcs: Vec<FuncId>,
302 } = 2,
303 DeclarativeWithFuncs {
304 kind: ElemKind,
305 funcs: Vec<FuncId>,
306 } = 3,
307 ActiveWithExprs {
308 offset: Expression,
309 exprs: Vec<Expression>,
310 } = 4,
311 PassiveWithExprs {
312 ty: RefType,
313 exprs: Vec<Expression>,
314 } = 5,
315 ActiveWithTableAndExprs {
316 table: TableId,
317 offset: Expression,
318 ty: RefType,
319 exprs: Vec<Expression>,
320 } = 6,
321 DeclarativeWithExprs {
322 ty: RefType,
323 exprs: Vec<Expression>,
324 } = 7,
325}
326
327#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
329pub struct Locals {
330 pub repeat: u32,
331 pub ty: ValueType,
332}
333
334#[derive(Wasmbin, WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
336#[wasmbin(discriminant = 0x00)]
337pub struct Exception {
338 pub ty: TypeId,
339}
340
341#[derive(WasmbinCountable, Debug, PartialEq, Eq, Hash, Clone, Visit)]
343pub struct Table {
344 pub table_type: TableType,
345 pub expr: Option<Expression>,
346}
347
348impl Encode for Table {
349 fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
350 if let Some(expr) = &self.expr {
351 0x40u8.encode(w)?; 0x00u8.encode(w)?;
353 self.table_type.encode(w)?;
354 expr.encode(w)?;
355 } else {
356 self.table_type.encode(w)?;
357 }
358 Ok(())
359 }
360}
361
362impl Decode for Table {
363 fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
364 let discriminant1 = u8::decode(r)?;
366 if discriminant1 == 0x40 {
367 let discriminant2 = u8::decode(r)?;
369 if discriminant2 == 0x00 {
370 return Ok(Self {
371 table_type: TableType::decode(r)?,
372 expr: Some(Expression::decode(r)?),
373 });
374 }
375 return Err(DecodeError::unsupported_discriminant::<Self>(discriminant2));
376 }
377 let buf = [discriminant1];
379 let mut r = std::io::Read::chain(&buf[..], r);
380 Ok(Self {
381 table_type: TableType::decode(&mut r)?,
382 expr: None,
383 })
384 }
385}
386
387#[derive(Wasmbin, WasmbinCountable, Debug, Default, PartialEq, Eq, Hash, Clone, Visit)]
389pub struct FuncBody {
390 pub locals: Vec<Locals>,
391 pub expr: Expression,
392}
393
394#[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
396#[repr(u32)]
397pub enum DataInit {
398 Active { offset: Expression } = 0,
399 Passive = 1,
400 ActiveWithMemory { memory: MemId, offset: Expression } = 2,
401}
402
403#[derive(Wasmbin, WasmbinCountable, CustomDebug, PartialEq, Eq, Hash, Clone, Visit)]
405pub struct Data {
406 pub init: DataInit,
407 #[debug(with = "custom_debug::hexbuf_str")]
408 pub blob: Vec<u8>,
409}
410
411pub(crate) trait Payload: Encode + Decode + Into<Section> {
412 const KIND: Kind;
413
414 fn try_from_ref(section: &Section) -> Option<&Blob<Self>>;
415 fn try_from_mut(section: &mut Section) -> Option<&mut Blob<Self>>;
416}
417
418#[expect(private_bounds)]
420pub trait StdPayload: Payload {}
421
422macro_rules! define_sections {
423 ($($(# $attr:tt)* $name:ident($(# $ty_attr:tt)* $ty:ty) = $disc:literal,)*) => {
424 pub mod payload {
426 $($(# $attr)* pub type $name = $ty;)*
427 }
428
429 #[derive(Wasmbin, Debug, PartialEq, Eq, Hash, Clone, Visit)]
431 #[repr(u8)]
432 pub enum Section {
433 $($(# $attr)* $name($(# $ty_attr)* Blob<payload::$name>) = $disc,)*
434 }
435
436 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
438 #[repr(u8)]
439 pub enum Kind {
440 $($(# $attr)* $name = $disc,)*
441 }
442
443 impl TryFrom<u8> for Kind {
444 type Error = u8;
445
446 fn try_from(discriminant: u8) -> Result<Kind, u8> {
447 #[expect(unused_doc_comments)]
448 Ok(match discriminant {
449 $($(# $attr)* $disc => Kind::$name,)*
450 _ => return Err(discriminant),
451 })
452 }
453 }
454
455 impl Ord for Kind {
456 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
457 #[derive(PartialEq, Eq, PartialOrd, Ord)]
464 #[repr(u8)]
465 enum OrderedRepr {
466 $($(# $attr)* $name,)*
467 }
468
469 impl From<Kind> for OrderedRepr {
470 fn from(kind: Kind) -> Self {
471 #[expect(unused_doc_comments)]
472 match kind {
473 $($(# $attr)* Kind::$name => Self::$name,)*
474 }
475 }
476 }
477
478 OrderedRepr::from(*self).cmp(&OrderedRepr::from(*other))
479 }
480 }
481
482 impl PartialOrd for Kind {
483 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
484 Some(self.cmp(other))
485 }
486 }
487
488 $($(# $attr)* const _: () = {
489 impl From<Blob<payload::$name>> for Section {
490 fn from(value: Blob<payload::$name>) -> Self {
491 Section::$name(value)
492 }
493 }
494
495 impl From<payload::$name> for Section {
496 fn from(value: payload::$name) -> Self {
497 Section::$name(Blob::from(value))
498 }
499 }
500
501 impl Payload for payload::$name {
502 const KIND: Kind = Kind::$name;
503
504 fn try_from_ref(section: &Section) -> Option<&Blob<Self>> {
505 match section {
506 Section::$name(res) => Some(res),
507 _ => None,
508 }
509 }
510
511 fn try_from_mut(section: &mut Section) -> Option<&mut Blob<Self>> {
512 match section {
513 Section::$name(res) => Some(res),
514 _ => None,
515 }
516 }
517 }
518 };)*
519
520 impl Section {
521 pub fn kind(&self) -> Kind {
523 #[expect(unused_doc_comments)]
524 match self {
525 $($(# $attr)* Section::$name(_) => Kind::$name,)*
526 }
527 }
528
529 pub fn try_as<T: StdPayload>(&self) -> Option<&Blob<T>> {
531 T::try_from_ref(self)
532 }
533
534 pub fn try_as_mut<T: StdPayload>(&mut self) -> Option<&mut Blob<T>> {
536 T::try_from_mut(self)
537 }
538 }
539
540 define_sections!(@std $($(# $attr)* $name)*);
541 };
542
543 (@std $(# $ignore_custom_attr:tt)* $ignore_custom:ident $($(# $attr:tt)* $name:ident)*) => {
544 $($(# $attr)* impl StdPayload for payload::$name {})*
545 };
546}
547
548define_sections! {
549 Custom(super::CustomSection) = 0,
551 Type(Vec<super::RecursiveType>) = 1,
553 Import(Vec<super::Import>) = 2,
555 Function(Vec<super::TypeId>) = 3,
557 Table(Vec<super::Table>) = 4,
559 Memory(Vec<super::MemType>) = 5,
561 Exception(Vec<super::Exception>) = 13,
563 Global(Vec<super::Global>) = 6,
565 Export(Vec<super::Export>) = 7,
567 Start(
569 super::FuncId
571 ) = 8,
572 Element(Vec<super::Element>) = 9,
574 DataCount(
576 u32
578 ) = 12,
579 Code(Vec<super::Blob<super::FuncBody>>) = 10,
581 Data(Vec<super::Data>) = 11,
583}
584
585#[derive(Debug, Error)]
587#[error("Section out of order: {current:?} after {prev:?}")]
588pub struct SectionOrderError {
589 pub current: Kind,
590 pub prev: Kind,
591}
592
593impl From<SectionOrderError> for std::io::Error {
594 fn from(err: SectionOrderError) -> Self {
595 Self::new(std::io::ErrorKind::InvalidData, err)
596 }
597}
598
599struct SectionOrderTracker {
600 last_kind: Kind,
601}
602
603impl Default for SectionOrderTracker {
604 fn default() -> Self {
605 Self {
606 last_kind: Kind::Custom,
607 }
608 }
609}
610
611impl SectionOrderTracker {
612 fn try_add(&mut self, section: &Section) -> Result<(), SectionOrderError> {
613 match section.kind() {
614 Kind::Custom => {}
615 kind if kind > self.last_kind => {
616 self.last_kind = kind;
617 }
618 kind => {
619 return Err(SectionOrderError {
620 prev: self.last_kind,
621 current: kind,
622 });
623 }
624 }
625 Ok(())
626 }
627}
628
629impl Encode for [Section] {
630 fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
631 let mut section_order_tracker = SectionOrderTracker::default();
632 for section in self {
633 section_order_tracker.try_add(section)?;
634 section.encode(w)?;
635 }
636 Ok(())
637 }
638}
639
640impl Decode for Vec<Section> {
641 fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
642 let mut sections = Vec::new();
643 let mut section_order_tracker = SectionOrderTracker::default();
644 while let Some(disc) = Option::decode(r)? {
645 let i = sections.len();
646 (|| -> Result<(), DecodeError> {
647 let section = Section::decode_with_discriminant(disc, r)?;
648 section_order_tracker.try_add(§ion)?;
649 sections.push(section);
650 Ok(())
651 })()
652 .map_err(move |err| err.in_path(PathItem::Index(i)))?;
653 }
654 Ok(sections)
655 }
656}