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