Skip to main content

bamts_bytecode/
program.rs

1use std::collections::HashMap;
2use std::error::Error;
3use std::fmt;
4use std::marker::PhantomData;
5
6use crate::{
7    Constant, ConstantId, DecodeError, DecodeLimits, EcmaString, Instruction, Module, Unverified,
8    Verified, VerifyError, decode,
9};
10
11/// `BMTPC\0\0\1`: the canonical whole-program container, distinct from module magic.
12pub const PROGRAM_MAGIC: [u8; 8] = [66, 77, 84, 80, 67, 0, 0, 1];
13/// The sole supported program-envelope version.
14pub const PROGRAM_VERSION: u8 = 3;
15
16index_type!(
17    /// Index of a module within a program.
18    ModuleId
19);
20index_type!(
21    /// Index of an edge within a module's linkage table.
22    EdgeId
23);
24index_type!(
25    /// Index of a binding within a module's binding table.
26    BindingId
27);
28
29/// A module dependency. External dependencies deliberately have no path or host identity.
30#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
31pub enum EdgeTarget {
32    Local(ModuleId),
33    External,
34}
35
36/// The runtime roles represented by one canonicalized module dependency.
37#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
38pub enum EdgeKind {
39    Static,
40    Dynamic,
41    StaticAndDynamic,
42}
43
44impl EdgeKind {
45    #[must_use]
46    pub const fn has_static(self) -> bool {
47        matches!(self, Self::Static | Self::StaticAndDynamic)
48    }
49
50    #[must_use]
51    pub const fn has_dynamic(self) -> bool {
52        matches!(self, Self::Dynamic | Self::StaticAndDynamic)
53    }
54
55    #[must_use]
56    pub const fn union(self, other: Self) -> Self {
57        match (self, other) {
58            (Self::Static, Self::Static) => Self::Static,
59            (Self::Dynamic, Self::Dynamic) => Self::Dynamic,
60            (Self::StaticAndDynamic, _)
61            | (_, Self::StaticAndDynamic)
62            | (Self::Static, Self::Dynamic)
63            | (Self::Dynamic, Self::Static) => Self::StaticAndDynamic,
64        }
65    }
66}
67
68/// One canonicalized module dependency.
69#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
70pub struct Edge {
71    pub specifier: ConstantId,
72    pub target: EdgeTarget,
73    pub kind: EdgeKind,
74}
75
76/// The initialization and linkage role of a module binding.
77#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
78pub enum BindingKind {
79    Hoisted,
80    Lexical,
81    Imported { edge: EdgeId, name: ConstantId },
82    Namespace { edge: EdgeId },
83}
84
85/// One named module binding. A binding identifies a live cell, never an activation register.
86#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
87pub struct Binding {
88    pub name: ConstantId,
89    pub kind: BindingKind,
90}
91
92/// The source of an exported name.
93#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
94pub enum ExportSource {
95    Local(BindingId),
96    Indirect { edge: EdgeId, name: ConstantId },
97}
98
99/// One named export.
100#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
101pub struct Export {
102    pub name: ConstantId,
103    pub source: ExportSource,
104}
105
106/// A canonical module blob and its program-only identity/linkage metadata.
107#[derive(Clone, Debug, Eq, PartialEq)]
108pub struct ProgramModule<State = Unverified> {
109    pub name: ConstantId,
110    pub code: Module<State>,
111    pub edges: Vec<Edge>,
112    pub bindings: Vec<Binding>,
113    pub exports: Vec<Export>,
114}
115
116impl<State> ProgramModule<State> {
117    #[must_use]
118    pub fn name(&self) -> ConstantId {
119        self.name
120    }
121
122    #[must_use]
123    pub fn code(&self) -> &Module<State> {
124        &self.code
125    }
126
127    #[must_use]
128    pub fn edges(&self) -> &[Edge] {
129        &self.edges
130    }
131
132    #[must_use]
133    pub fn bindings(&self) -> &[Binding] {
134        &self.bindings
135    }
136
137    #[must_use]
138    pub fn exports(&self) -> &[Export] {
139        &self.exports
140    }
141}
142
143/// The sole self-contained executable wire value.
144#[derive(Clone, Debug, Eq, PartialEq)]
145pub struct Program<State = Unverified> {
146    modules: Vec<ProgramModule<State>>,
147    entry: ModuleId,
148    state: PhantomData<State>,
149}
150
151impl<State> Program<State> {
152    #[must_use]
153    pub fn modules(&self) -> &[ProgramModule<State>] {
154        &self.modules
155    }
156
157    #[must_use]
158    pub const fn entry(&self) -> ModuleId {
159        self.entry
160    }
161
162    #[must_use]
163    pub fn module(&self, id: ModuleId) -> Option<&ProgramModule<State>> {
164        self.modules.get(id.get() as usize)
165    }
166}
167
168impl Program<Unverified> {
169    #[must_use]
170    pub fn new(modules: Vec<ProgramModule<Unverified>>, entry: ModuleId) -> Self {
171        Self {
172            modules,
173            entry,
174            state: PhantomData,
175        }
176    }
177
178    /// Verifies every embedded module, then all program linkage invariants.
179    pub fn verify(self) -> Result<Program<Verified>, ProgramVerifyError> {
180        let mut modules = Vec::with_capacity(self.modules.len());
181        for (index, module) in self.modules.into_iter().enumerate() {
182            let ProgramModule {
183                name,
184                code,
185                edges,
186                bindings,
187                exports,
188            } = module;
189            let code = code.verify().map_err(|error| ProgramVerifyError {
190                module: Some(ModuleId::new(index as u32)),
191                kind: ProgramVerifyErrorKind::Module(error),
192            })?;
193            modules.push(ProgramModule {
194                name,
195                code,
196                edges,
197                bindings,
198                exports,
199            });
200        }
201        Program::link(modules, self.entry)
202    }
203}
204
205/// A verified export resolution with no copied names or paths.
206#[derive(Clone, Copy, Debug, Eq, PartialEq)]
207pub enum ResolvedExport {
208    Local {
209        module: ModuleId,
210        binding: BindingId,
211    },
212    External {
213        module: ModuleId,
214        edge: EdgeId,
215        name: ConstantId,
216    },
217}
218
219impl Program<Verified> {
220    /// Links modules that have already passed the canonical module verifier.
221    pub fn link(
222        modules: Vec<ProgramModule<Verified>>,
223        entry: ModuleId,
224    ) -> Result<Self, ProgramVerifyError> {
225        verify_program_metadata(&modules, entry)?;
226        Ok(Self {
227            modules,
228            entry,
229            state: PhantomData,
230        })
231    }
232
233    /// Emits the deterministic program envelope. Each module payload is exactly
234    /// `Module::encode()` and is length-delimited without another module codec.
235    #[must_use]
236    pub fn encode(&self) -> Vec<u8> {
237        let mut output = Vec::new();
238        output.extend_from_slice(&PROGRAM_MAGIC);
239        output.push(PROGRAM_VERSION);
240        write_u32(self.entry.get(), &mut output);
241        write_u32(self.modules.len() as u32, &mut output);
242        for module in &self.modules {
243            let blob = module.code.encode();
244            write_u32(module.name.get(), &mut output);
245            write_u32(blob.len() as u32, &mut output);
246            output.extend_from_slice(&blob);
247            write_u32(module.edges.len() as u32, &mut output);
248            for edge in &module.edges {
249                write_u32(edge.specifier.get(), &mut output);
250                match edge.target {
251                    EdgeTarget::Local(target) => {
252                        output.push(0);
253                        write_u32(target.get(), &mut output);
254                    }
255                    EdgeTarget::External => output.push(1),
256                }
257                output.push(match edge.kind {
258                    EdgeKind::Static => 0,
259                    EdgeKind::Dynamic => 1,
260                    EdgeKind::StaticAndDynamic => 2,
261                });
262            }
263            write_u32(module.bindings.len() as u32, &mut output);
264            for binding in &module.bindings {
265                write_u32(binding.name.get(), &mut output);
266                match binding.kind {
267                    BindingKind::Hoisted => output.push(0),
268                    BindingKind::Lexical => output.push(1),
269                    BindingKind::Imported { edge, name } => {
270                        output.push(2);
271                        write_u32(edge.get(), &mut output);
272                        write_u32(name.get(), &mut output);
273                    }
274                    BindingKind::Namespace { edge } => {
275                        output.push(3);
276                        write_u32(edge.get(), &mut output);
277                    }
278                }
279            }
280            write_u32(module.exports.len() as u32, &mut output);
281            for export in &module.exports {
282                write_u32(export.name.get(), &mut output);
283                match export.source {
284                    ExportSource::Local(binding) => {
285                        output.push(0);
286                        write_u32(binding.get(), &mut output);
287                    }
288                    ExportSource::Indirect { edge, name } => {
289                        output.push(1);
290                        write_u32(edge.get(), &mut output);
291                        write_u32(name.get(), &mut output);
292                    }
293                }
294            }
295        }
296        output
297    }
298
299    /// Resolves a verified export by its exact ECMAScript name.
300    #[must_use]
301    pub fn resolve_export(&self, module: ModuleId, name: &EcmaString) -> Option<ResolvedExport> {
302        let mut module_id = module;
303        let mut linked_name = None;
304        loop {
305            let current = self.module(module_id)?;
306            let export_name = linked_name.unwrap_or(name);
307            let export = current
308                .exports
309                .iter()
310                .find(|export| string(&current.code, export.name) == Some(export_name))?;
311            match export.source {
312                ExportSource::Local(binding) => {
313                    match current.bindings.get(binding.get() as usize)?.kind {
314                        BindingKind::Imported { edge, name } => {
315                            let edge_id = edge;
316                            let edge = current.edges.get(edge.get() as usize)?;
317                            match edge.target {
318                                EdgeTarget::Local(target) => {
319                                    module_id = target;
320                                    linked_name = Some(string(&current.code, name)?);
321                                }
322                                EdgeTarget::External => {
323                                    return Some(ResolvedExport::External {
324                                        module: module_id,
325                                        edge: edge_id,
326                                        name,
327                                    });
328                                }
329                            }
330                        }
331                        BindingKind::Hoisted
332                        | BindingKind::Lexical
333                        | BindingKind::Namespace { .. } => {
334                            return Some(ResolvedExport::Local {
335                                module: module_id,
336                                binding,
337                            });
338                        }
339                    }
340                }
341                ExportSource::Indirect { edge, name } => {
342                    let edge_id = edge;
343                    let edge = current.edges.get(edge.get() as usize)?;
344                    match edge.target {
345                        EdgeTarget::Local(target) => {
346                            module_id = target;
347                            linked_name = Some(string(&current.code, name)?);
348                        }
349                        EdgeTarget::External => {
350                            return Some(ResolvedExport::External {
351                                module: module_id,
352                                edge: edge_id,
353                                name,
354                            });
355                        }
356                    }
357                }
358            }
359        }
360    }
361}
362
363/// Strict program-level resource ceilings, applied before allocation.
364#[derive(Clone, Debug, Eq, PartialEq)]
365pub struct ProgramDecodeLimits {
366    pub max_bytes: usize,
367    pub max_modules: u32,
368    pub max_module_bytes: usize,
369    pub max_total_module_bytes: usize,
370    pub max_edges_per_module: u32,
371    pub max_bindings_per_module: u32,
372    pub max_exports_per_module: u32,
373    pub max_total_edges: u32,
374    pub max_total_bindings: u32,
375    pub max_total_exports: u32,
376    pub module: DecodeLimits,
377}
378
379impl Default for ProgramDecodeLimits {
380    fn default() -> Self {
381        Self {
382            max_bytes: 64 * 1024 * 1024,
383            max_modules: 1 << 16,
384            max_module_bytes: 16 * 1024 * 1024,
385            max_total_module_bytes: 48 * 1024 * 1024,
386            max_edges_per_module: 1 << 20,
387            max_bindings_per_module: 1 << 20,
388            max_exports_per_module: 1 << 20,
389            max_total_edges: 1 << 22,
390            max_total_bindings: 1 << 22,
391            max_total_exports: 1 << 22,
392            module: DecodeLimits::default(),
393        }
394    }
395}
396
397#[derive(Clone, Debug, Eq, PartialEq)]
398pub struct ProgramDecodeError {
399    pub offset: usize,
400    pub kind: ProgramDecodeErrorKind,
401}
402
403#[derive(Clone, Debug, Eq, PartialEq)]
404pub enum ProgramDecodeErrorKind {
405    InputLimitExceeded {
406        limit: usize,
407        actual: usize,
408    },
409    UnexpectedEof,
410    BadMagic {
411        expected: u8,
412        actual: u8,
413    },
414    UnsupportedVersion {
415        version: u8,
416    },
417    MalformedInteger,
418    NonCanonicalInteger,
419    IntegerOverflow,
420    InvalidEdgeTarget {
421        tag: u8,
422    },
423    InvalidEdgeKind {
424        tag: u8,
425    },
426    InvalidBindingKind {
427        tag: u8,
428    },
429    InvalidExportSource {
430        tag: u8,
431    },
432    LimitExceeded {
433        field: &'static str,
434        limit: u64,
435        actual: u64,
436    },
437    Module {
438        module: ModuleId,
439        error: DecodeError,
440    },
441    TrailingBytes {
442        count: usize,
443    },
444}
445
446impl fmt::Display for ProgramDecodeError {
447    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
448        write!(formatter, "program byte {}: ", self.offset)?;
449        match &self.kind {
450            ProgramDecodeErrorKind::InputLimitExceeded { limit, actual } => {
451                write!(formatter, "input has {actual} bytes, limit is {limit}")
452            }
453            ProgramDecodeErrorKind::UnexpectedEof => formatter.write_str("unexpected end of input"),
454            ProgramDecodeErrorKind::BadMagic { expected, actual } => write!(
455                formatter,
456                "bad magic byte {actual:#04x}, expected {expected:#04x}"
457            ),
458            ProgramDecodeErrorKind::UnsupportedVersion { version } => {
459                write!(formatter, "unsupported program version {version}")
460            }
461            ProgramDecodeErrorKind::MalformedInteger => {
462                formatter.write_str("malformed LEB128 integer")
463            }
464            ProgramDecodeErrorKind::NonCanonicalInteger => {
465                formatter.write_str("noncanonical LEB128 integer")
466            }
467            ProgramDecodeErrorKind::IntegerOverflow => {
468                formatter.write_str("LEB128 integer exceeds 32 bits")
469            }
470            ProgramDecodeErrorKind::InvalidEdgeTarget { tag } => {
471                write!(formatter, "invalid edge target tag {tag}")
472            }
473            ProgramDecodeErrorKind::InvalidEdgeKind { tag } => {
474                write!(formatter, "invalid edge kind tag {tag}")
475            }
476            ProgramDecodeErrorKind::InvalidBindingKind { tag } => {
477                write!(formatter, "invalid binding kind tag {tag}")
478            }
479            ProgramDecodeErrorKind::InvalidExportSource { tag } => {
480                write!(formatter, "invalid export source tag {tag}")
481            }
482            ProgramDecodeErrorKind::LimitExceeded {
483                field,
484                limit,
485                actual,
486            } => {
487                write!(formatter, "{field} value {actual} exceeds limit {limit}")
488            }
489            ProgramDecodeErrorKind::Module { module, error } => {
490                write!(formatter, "module {}: {error}", module.get())
491            }
492            ProgramDecodeErrorKind::TrailingBytes { count } => {
493                write!(formatter, "{count} trailing bytes")
494            }
495        }
496    }
497}
498
499impl Error for ProgramDecodeError {
500    fn source(&self) -> Option<&(dyn Error + 'static)> {
501        match &self.kind {
502            ProgramDecodeErrorKind::Module { error, .. } => Some(error),
503            _ => None,
504        }
505    }
506}
507
508#[derive(Clone, Debug, Eq, PartialEq)]
509pub struct ProgramVerifyError {
510    pub module: Option<ModuleId>,
511    pub kind: ProgramVerifyErrorKind,
512}
513
514#[derive(Clone, Debug, Eq, PartialEq)]
515pub enum ProgramVerifyErrorKind {
516    EmptyProgram,
517    TooManyModules {
518        count: usize,
519    },
520    EntryModuleOutOfBounds {
521        entry: u32,
522        module_count: usize,
523    },
524    Module(VerifyError),
525    ModuleNameOutOfBounds {
526        constant: ConstantId,
527    },
528    ModuleNameNotString {
529        constant: ConstantId,
530    },
531    InvalidModuleName,
532    MetadataStringIllFormed {
533        constant: ConstantId,
534    },
535    DuplicateModuleName {
536        first: ModuleId,
537    },
538    TooManyEdges {
539        count: usize,
540    },
541    TooManyBindings {
542        count: usize,
543    },
544    TooManyExports {
545        count: usize,
546    },
547    SpecifierOutOfBounds {
548        edge: EdgeId,
549        constant: ConstantId,
550    },
551    SpecifierNotString {
552        edge: EdgeId,
553        constant: ConstantId,
554    },
555    AbsoluteSpecifier {
556        edge: EdgeId,
557    },
558    DuplicateSpecifier {
559        first: EdgeId,
560        second: EdgeId,
561    },
562    LocalTargetOutOfBounds {
563        edge: EdgeId,
564        target: ModuleId,
565    },
566    BindingNameOutOfBounds {
567        binding: BindingId,
568        constant: ConstantId,
569    },
570    BindingNameNotString {
571        binding: BindingId,
572        constant: ConstantId,
573    },
574    BindingEdgeOutOfBounds {
575        binding: BindingId,
576        edge: EdgeId,
577    },
578    ImportedNameOutOfBounds {
579        binding: BindingId,
580        constant: ConstantId,
581    },
582    ImportedNameNotString {
583        binding: BindingId,
584        constant: ConstantId,
585    },
586    DuplicateBinding {
587        first: BindingId,
588        second: BindingId,
589    },
590    StaticBindingRequiresStaticEdge {
591        binding: BindingId,
592        edge: EdgeId,
593    },
594    MissingImportedExport {
595        binding: BindingId,
596    },
597    ExportNameOutOfBounds {
598        export: u32,
599        constant: ConstantId,
600    },
601    ExportNameNotString {
602        export: u32,
603        constant: ConstantId,
604    },
605    DuplicateExport {
606        first: u32,
607        second: u32,
608    },
609    ExportBindingOutOfBounds {
610        export: u32,
611        binding: BindingId,
612    },
613    ExportEdgeOutOfBounds {
614        export: u32,
615        edge: EdgeId,
616    },
617    IndirectNameOutOfBounds {
618        export: u32,
619        constant: ConstantId,
620    },
621    IndirectNameNotString {
622        export: u32,
623        constant: ConstantId,
624    },
625    DynamicImportMissingEdge {
626        specifier: ConstantId,
627    },
628    SnapshotExportInstruction {
629        function: u32,
630        pc: u32,
631    },
632    MissingIndirectExport {
633        export: u32,
634    },
635    IndirectExportCycle {
636        export: u32,
637    },
638}
639
640impl fmt::Display for ProgramVerifyError {
641    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
642        if let Some(module) = self.module {
643            write!(formatter, "module {}: ", module.get())?;
644        }
645        write!(formatter, "{:?}", self.kind)
646    }
647}
648
649impl Error for ProgramVerifyError {
650    fn source(&self) -> Option<&(dyn Error + 'static)> {
651        match &self.kind {
652            ProgramVerifyErrorKind::Module(error) => Some(error),
653            _ => None,
654        }
655    }
656}
657
658#[derive(Clone, Debug, Eq, PartialEq)]
659pub enum ProgramLoadError {
660    Decode(ProgramDecodeError),
661    Verify(ProgramVerifyError),
662}
663
664impl fmt::Display for ProgramLoadError {
665    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
666        match self {
667            Self::Decode(error) => error.fmt(formatter),
668            Self::Verify(error) => error.fmt(formatter),
669        }
670    }
671}
672
673impl Error for ProgramLoadError {
674    fn source(&self) -> Option<&(dyn Error + 'static)> {
675        match self {
676            Self::Decode(error) => Some(error),
677            Self::Verify(error) => Some(error),
678        }
679    }
680}
681
682/// Strictly decodes a program envelope while retaining unverified typestate.
683pub fn decode_program(
684    bytes: &[u8],
685    limits: &ProgramDecodeLimits,
686) -> Result<Program<Unverified>, ProgramDecodeError> {
687    if bytes.len() > limits.max_bytes {
688        return Err(ProgramDecodeError {
689            offset: 0,
690            kind: ProgramDecodeErrorKind::InputLimitExceeded {
691                limit: limits.max_bytes,
692                actual: bytes.len(),
693            },
694        });
695    }
696    let mut decoder = ProgramDecoder {
697        bytes,
698        offset: 0,
699        limits,
700        total_module_bytes: 0,
701        total_edges: 0,
702        total_bindings: 0,
703        total_exports: 0,
704    };
705    for expected in PROGRAM_MAGIC {
706        let at = decoder.offset;
707        let actual = decoder.byte()?;
708        if actual != expected {
709            return Err(decoder.error_at(at, ProgramDecodeErrorKind::BadMagic { expected, actual }));
710        }
711    }
712    let version_at = decoder.offset;
713    let version = decoder.byte()?;
714    if version != PROGRAM_VERSION {
715        return Err(decoder.error_at(
716            version_at,
717            ProgramDecodeErrorKind::UnsupportedVersion { version },
718        ));
719    }
720    let entry = ModuleId::new(decoder.u32()?);
721    let module_count = decoder.count("modules", limits.max_modules)?;
722    let mut modules = Vec::with_capacity(module_count);
723    for index in 0..module_count {
724        modules.push(decoder.module(ModuleId::new(index as u32))?);
725    }
726    if decoder.offset != bytes.len() {
727        return Err(decoder.error(ProgramDecodeErrorKind::TrailingBytes {
728            count: bytes.len() - decoder.offset,
729        }));
730    }
731    Ok(Program::new(modules, entry))
732}
733
734/// Decodes and verifies a whole program in one boundary operation.
735pub fn decode_verified_program(
736    bytes: &[u8],
737    limits: &ProgramDecodeLimits,
738) -> Result<Program<Verified>, ProgramLoadError> {
739    decode_program(bytes, limits)
740        .map_err(ProgramLoadError::Decode)?
741        .verify()
742        .map_err(ProgramLoadError::Verify)
743}
744
745struct ProgramDecoder<'a> {
746    bytes: &'a [u8],
747    offset: usize,
748    limits: &'a ProgramDecodeLimits,
749    total_module_bytes: usize,
750    total_edges: u64,
751    total_bindings: u64,
752    total_exports: u64,
753}
754
755impl<'a> ProgramDecoder<'a> {
756    fn module(
757        &mut self,
758        module_id: ModuleId,
759    ) -> Result<ProgramModule<Unverified>, ProgramDecodeError> {
760        let name = ConstantId::new(self.u32()?);
761        let length = self.length("module bytes", self.limits.max_module_bytes)?;
762        self.total_module_bytes = self
763            .total_module_bytes
764            .checked_add(length)
765            .ok_or_else(|| self.error(ProgramDecodeErrorKind::IntegerOverflow))?;
766        if self.total_module_bytes > self.limits.max_total_module_bytes {
767            return Err(self.error(ProgramDecodeErrorKind::LimitExceeded {
768                field: "total module bytes",
769                limit: self.limits.max_total_module_bytes as u64,
770                actual: self.total_module_bytes as u64,
771            }));
772        }
773        let blob_offset = self.offset;
774        let blob = self.slice(length)?;
775        let mut module_limits = self.limits.module.clone();
776        module_limits.max_bytes = module_limits.max_bytes.min(self.limits.max_module_bytes);
777        let code = decode(blob, &module_limits).map_err(|error| {
778            self.error_at(
779                blob_offset + error.offset,
780                ProgramDecodeErrorKind::Module {
781                    module: module_id,
782                    error,
783                },
784            )
785        })?;
786
787        let edge_count = self.count("edges", self.limits.max_edges_per_module)?;
788        self.add_total(
789            "total edges",
790            edge_count,
791            self.limits.max_total_edges,
792            Total::Edges,
793        )?;
794        let mut edges = Vec::with_capacity(edge_count);
795        for _ in 0..edge_count {
796            let specifier = ConstantId::new(self.u32()?);
797            let tag_at = self.offset;
798            let target = match self.byte()? {
799                0 => EdgeTarget::Local(ModuleId::new(self.u32()?)),
800                1 => EdgeTarget::External,
801                tag => {
802                    return Err(
803                        self.error_at(tag_at, ProgramDecodeErrorKind::InvalidEdgeTarget { tag })
804                    );
805                }
806            };
807            let kind_at = self.offset;
808            let kind = match self.byte()? {
809                0 => EdgeKind::Static,
810                1 => EdgeKind::Dynamic,
811                2 => EdgeKind::StaticAndDynamic,
812                tag => {
813                    return Err(
814                        self.error_at(kind_at, ProgramDecodeErrorKind::InvalidEdgeKind { tag })
815                    );
816                }
817            };
818            edges.push(Edge {
819                specifier,
820                target,
821                kind,
822            });
823        }
824
825        let binding_count = self.count("bindings", self.limits.max_bindings_per_module)?;
826        self.add_total(
827            "total bindings",
828            binding_count,
829            self.limits.max_total_bindings,
830            Total::Bindings,
831        )?;
832        let mut bindings = Vec::with_capacity(binding_count);
833        for _ in 0..binding_count {
834            let name = ConstantId::new(self.u32()?);
835            let tag_at = self.offset;
836            let kind = match self.byte()? {
837                0 => BindingKind::Hoisted,
838                1 => BindingKind::Lexical,
839                2 => BindingKind::Imported {
840                    edge: EdgeId::new(self.u32()?),
841                    name: ConstantId::new(self.u32()?),
842                },
843                3 => BindingKind::Namespace {
844                    edge: EdgeId::new(self.u32()?),
845                },
846                tag => {
847                    return Err(
848                        self.error_at(tag_at, ProgramDecodeErrorKind::InvalidBindingKind { tag })
849                    );
850                }
851            };
852            bindings.push(Binding { name, kind });
853        }
854
855        let export_count = self.count("exports", self.limits.max_exports_per_module)?;
856        self.add_total(
857            "total exports",
858            export_count,
859            self.limits.max_total_exports,
860            Total::Exports,
861        )?;
862        let mut exports = Vec::with_capacity(export_count);
863        for _ in 0..export_count {
864            let name = ConstantId::new(self.u32()?);
865            let tag_at = self.offset;
866            let source = match self.byte()? {
867                0 => ExportSource::Local(BindingId::new(self.u32()?)),
868                1 => ExportSource::Indirect {
869                    edge: EdgeId::new(self.u32()?),
870                    name: ConstantId::new(self.u32()?),
871                },
872                tag => {
873                    return Err(
874                        self.error_at(tag_at, ProgramDecodeErrorKind::InvalidExportSource { tag })
875                    );
876                }
877            };
878            exports.push(Export { name, source });
879        }
880        Ok(ProgramModule {
881            name,
882            code,
883            edges,
884            bindings,
885            exports,
886        })
887    }
888
889    fn add_total(
890        &mut self,
891        field: &'static str,
892        count: usize,
893        limit: u32,
894        total: Total,
895    ) -> Result<(), ProgramDecodeError> {
896        let slot = match total {
897            Total::Edges => &mut self.total_edges,
898            Total::Bindings => &mut self.total_bindings,
899            Total::Exports => &mut self.total_exports,
900        };
901        *slot += count as u64;
902        if *slot > u64::from(limit) {
903            return Err(ProgramDecodeError {
904                offset: self.offset,
905                kind: ProgramDecodeErrorKind::LimitExceeded {
906                    field,
907                    limit: u64::from(limit),
908                    actual: *slot,
909                },
910            });
911        }
912        Ok(())
913    }
914
915    fn count(&mut self, field: &'static str, limit: u32) -> Result<usize, ProgramDecodeError> {
916        let actual = self.u32()?;
917        if actual > limit {
918            return Err(self.error(ProgramDecodeErrorKind::LimitExceeded {
919                field,
920                limit: u64::from(limit),
921                actual: u64::from(actual),
922            }));
923        }
924        Ok(actual as usize)
925    }
926
927    fn length(&mut self, field: &'static str, limit: usize) -> Result<usize, ProgramDecodeError> {
928        let actual = self.u32()? as usize;
929        if actual > limit {
930            return Err(self.error(ProgramDecodeErrorKind::LimitExceeded {
931                field,
932                limit: limit as u64,
933                actual: actual as u64,
934            }));
935        }
936        Ok(actual)
937    }
938
939    fn byte(&mut self) -> Result<u8, ProgramDecodeError> {
940        let byte = self
941            .bytes
942            .get(self.offset)
943            .copied()
944            .ok_or_else(|| self.error(ProgramDecodeErrorKind::UnexpectedEof))?;
945        self.offset += 1;
946        Ok(byte)
947    }
948
949    fn slice(&mut self, length: usize) -> Result<&'a [u8], ProgramDecodeError> {
950        let end = self
951            .offset
952            .checked_add(length)
953            .filter(|end| *end <= self.bytes.len())
954            .ok_or_else(|| self.error(ProgramDecodeErrorKind::UnexpectedEof))?;
955        let result = &self.bytes[self.offset..end];
956        self.offset = end;
957        Ok(result)
958    }
959
960    fn u32(&mut self) -> Result<u32, ProgramDecodeError> {
961        let start = self.offset;
962        let mut value = 0_u32;
963        for group in 0..5 {
964            let byte = self.byte()?;
965            if group == 4 && byte & 0xf0 != 0 {
966                return Err(self.error_at(start, ProgramDecodeErrorKind::IntegerOverflow));
967            }
968            value |= u32::from(byte & 0x7f) << (group * 7);
969            if byte & 0x80 == 0 {
970                if group != 0 && byte == 0 {
971                    return Err(self.error_at(start, ProgramDecodeErrorKind::NonCanonicalInteger));
972                }
973                return Ok(value);
974            }
975        }
976        Err(self.error_at(start, ProgramDecodeErrorKind::MalformedInteger))
977    }
978
979    const fn error(&self, kind: ProgramDecodeErrorKind) -> ProgramDecodeError {
980        self.error_at(self.offset, kind)
981    }
982
983    const fn error_at(&self, offset: usize, kind: ProgramDecodeErrorKind) -> ProgramDecodeError {
984        ProgramDecodeError { offset, kind }
985    }
986}
987
988enum Total {
989    Edges,
990    Bindings,
991    Exports,
992}
993
994fn verify_program_metadata(
995    modules: &[ProgramModule<Verified>],
996    entry: ModuleId,
997) -> Result<(), ProgramVerifyError> {
998    if modules.is_empty() {
999        return Err(program_error(ProgramVerifyErrorKind::EmptyProgram));
1000    }
1001    if modules.len() > u32::MAX as usize {
1002        return Err(program_error(ProgramVerifyErrorKind::TooManyModules {
1003            count: modules.len(),
1004        }));
1005    }
1006    if entry.get() as usize >= modules.len() {
1007        return Err(program_error(
1008            ProgramVerifyErrorKind::EntryModuleOutOfBounds {
1009                entry: entry.get(),
1010                module_count: modules.len(),
1011            },
1012        ));
1013    }
1014
1015    let mut module_names = HashMap::with_capacity(modules.len());
1016    for (index, module) in modules.iter().enumerate() {
1017        let module_id = ModuleId::new(index as u32);
1018        let name = required_string(
1019            module_id,
1020            module,
1021            module.name,
1022            ProgramVerifyErrorKind::ModuleNameOutOfBounds {
1023                constant: module.name,
1024            },
1025            ProgramVerifyErrorKind::ModuleNameNotString {
1026                constant: module.name,
1027            },
1028        )?;
1029        if !is_normalized_module_name(name) {
1030            return Err(module_error(
1031                module_id,
1032                ProgramVerifyErrorKind::InvalidModuleName,
1033            ));
1034        }
1035        if let Some(first) = module_names.insert(name, module_id) {
1036            return Err(module_error(
1037                module_id,
1038                ProgramVerifyErrorKind::DuplicateModuleName { first },
1039            ));
1040        }
1041        verify_module_metadata(modules, module_id, module)?;
1042    }
1043    verify_export_resolutions(modules)?;
1044    verify_imported_bindings(modules)
1045}
1046
1047fn verify_module_metadata(
1048    modules: &[ProgramModule<Verified>],
1049    module_id: ModuleId,
1050    module: &ProgramModule<Verified>,
1051) -> Result<(), ProgramVerifyError> {
1052    if module.edges.len() > u32::MAX as usize {
1053        return Err(module_error(
1054            module_id,
1055            ProgramVerifyErrorKind::TooManyEdges {
1056                count: module.edges.len(),
1057            },
1058        ));
1059    }
1060    if module.bindings.len() > u32::MAX as usize {
1061        return Err(module_error(
1062            module_id,
1063            ProgramVerifyErrorKind::TooManyBindings {
1064                count: module.bindings.len(),
1065            },
1066        ));
1067    }
1068    if module.exports.len() > u32::MAX as usize {
1069        return Err(module_error(
1070            module_id,
1071            ProgramVerifyErrorKind::TooManyExports {
1072                count: module.exports.len(),
1073            },
1074        ));
1075    }
1076
1077    let mut specifiers = HashMap::with_capacity(module.edges.len());
1078    for (index, edge) in module.edges.iter().enumerate() {
1079        let edge_id = EdgeId::new(index as u32);
1080        let specifier = required_string(
1081            module_id,
1082            module,
1083            edge.specifier,
1084            ProgramVerifyErrorKind::SpecifierOutOfBounds {
1085                edge: edge_id,
1086                constant: edge.specifier,
1087            },
1088            ProgramVerifyErrorKind::SpecifierNotString {
1089                edge: edge_id,
1090                constant: edge.specifier,
1091            },
1092        )?;
1093        if is_absolute_specifier(specifier) {
1094            return Err(module_error(
1095                module_id,
1096                ProgramVerifyErrorKind::AbsoluteSpecifier { edge: edge_id },
1097            ));
1098        }
1099        if let Some(first) = specifiers.insert(specifier, edge_id) {
1100            return Err(module_error(
1101                module_id,
1102                ProgramVerifyErrorKind::DuplicateSpecifier {
1103                    first,
1104                    second: edge_id,
1105                },
1106            ));
1107        }
1108        if let EdgeTarget::Local(target) = edge.target
1109            && target.get() as usize >= modules.len()
1110        {
1111            return Err(module_error(
1112                module_id,
1113                ProgramVerifyErrorKind::LocalTargetOutOfBounds {
1114                    edge: edge_id,
1115                    target,
1116                },
1117            ));
1118        }
1119    }
1120
1121    let mut binding_names = HashMap::with_capacity(module.bindings.len());
1122    for (index, binding) in module.bindings.iter().enumerate() {
1123        let binding_id = BindingId::new(index as u32);
1124        let binding_name = required_string(
1125            module_id,
1126            module,
1127            binding.name,
1128            ProgramVerifyErrorKind::BindingNameOutOfBounds {
1129                binding: binding_id,
1130                constant: binding.name,
1131            },
1132            ProgramVerifyErrorKind::BindingNameNotString {
1133                binding: binding_id,
1134                constant: binding.name,
1135            },
1136        )?;
1137        if let Some(first) = binding_names.insert(binding_name, binding_id) {
1138            return Err(module_error(
1139                module_id,
1140                ProgramVerifyErrorKind::DuplicateBinding {
1141                    first,
1142                    second: binding_id,
1143                },
1144            ));
1145        }
1146        match binding.kind {
1147            BindingKind::Imported { edge, name } => {
1148                let dependency = require_edge(module_id, module, binding_id, edge)?;
1149                required_string(
1150                    module_id,
1151                    module,
1152                    name,
1153                    ProgramVerifyErrorKind::ImportedNameOutOfBounds {
1154                        binding: binding_id,
1155                        constant: name,
1156                    },
1157                    ProgramVerifyErrorKind::ImportedNameNotString {
1158                        binding: binding_id,
1159                        constant: name,
1160                    },
1161                )?;
1162                if !dependency.kind.has_static() {
1163                    return Err(module_error(
1164                        module_id,
1165                        ProgramVerifyErrorKind::StaticBindingRequiresStaticEdge {
1166                            binding: binding_id,
1167                            edge,
1168                        },
1169                    ));
1170                }
1171            }
1172            BindingKind::Namespace { edge } => {
1173                let dependency = require_edge(module_id, module, binding_id, edge)?;
1174                if !dependency.kind.has_static() {
1175                    return Err(module_error(
1176                        module_id,
1177                        ProgramVerifyErrorKind::StaticBindingRequiresStaticEdge {
1178                            binding: binding_id,
1179                            edge,
1180                        },
1181                    ));
1182                }
1183            }
1184            BindingKind::Hoisted | BindingKind::Lexical => {}
1185        }
1186    }
1187
1188    let mut export_names = HashMap::with_capacity(module.exports.len());
1189    for (index, export) in module.exports.iter().enumerate() {
1190        let export_id = index as u32;
1191        let export_name = required_string(
1192            module_id,
1193            module,
1194            export.name,
1195            ProgramVerifyErrorKind::ExportNameOutOfBounds {
1196                export: export_id,
1197                constant: export.name,
1198            },
1199            ProgramVerifyErrorKind::ExportNameNotString {
1200                export: export_id,
1201                constant: export.name,
1202            },
1203        )?;
1204        if let Some(first) = export_names.insert(export_name, export_id) {
1205            return Err(module_error(
1206                module_id,
1207                ProgramVerifyErrorKind::DuplicateExport {
1208                    first,
1209                    second: export_id,
1210                },
1211            ));
1212        }
1213        match export.source {
1214            ExportSource::Local(binding) => {
1215                if binding.get() as usize >= module.bindings.len() {
1216                    return Err(module_error(
1217                        module_id,
1218                        ProgramVerifyErrorKind::ExportBindingOutOfBounds {
1219                            export: export_id,
1220                            binding,
1221                        },
1222                    ));
1223                }
1224            }
1225            ExportSource::Indirect { edge, name } => {
1226                if edge.get() as usize >= module.edges.len() {
1227                    return Err(module_error(
1228                        module_id,
1229                        ProgramVerifyErrorKind::ExportEdgeOutOfBounds {
1230                            export: export_id,
1231                            edge,
1232                        },
1233                    ));
1234                }
1235                required_string(
1236                    module_id,
1237                    module,
1238                    name,
1239                    ProgramVerifyErrorKind::IndirectNameOutOfBounds {
1240                        export: export_id,
1241                        constant: name,
1242                    },
1243                    ProgramVerifyErrorKind::IndirectNameNotString {
1244                        export: export_id,
1245                        constant: name,
1246                    },
1247                )?;
1248            }
1249        }
1250    }
1251
1252    for (function_index, function) in module.code.functions().iter().enumerate() {
1253        if let Some(name) = function.name()
1254            && !string(&module.code, name)
1255                .expect("module verifier checked function-name string")
1256                .is_well_formed()
1257        {
1258            return Err(module_error(
1259                module_id,
1260                ProgramVerifyErrorKind::MetadataStringIllFormed { constant: name },
1261            ));
1262        }
1263        for (pc, instruction) in function.code().iter().copied().enumerate() {
1264            match instruction {
1265                Instruction::Import { specifier, .. } => {
1266                    let import_name =
1267                        string(&module.code, specifier).expect("module verifier checked string");
1268                    if !import_name.is_well_formed() {
1269                        return Err(module_error(
1270                            module_id,
1271                            ProgramVerifyErrorKind::MetadataStringIllFormed {
1272                                constant: specifier,
1273                            },
1274                        ));
1275                    }
1276                    if !specifiers
1277                        .get(import_name)
1278                        .is_some_and(|edge| module.edges[edge.get() as usize].kind.has_dynamic())
1279                    {
1280                        return Err(module_error(
1281                            module_id,
1282                            ProgramVerifyErrorKind::DynamicImportMissingEdge { specifier },
1283                        ));
1284                    }
1285                }
1286                Instruction::Export { .. } => {
1287                    return Err(module_error(
1288                        module_id,
1289                        ProgramVerifyErrorKind::SnapshotExportInstruction {
1290                            function: function_index as u32,
1291                            pc: pc as u32,
1292                        },
1293                    ));
1294                }
1295                _ => {}
1296            }
1297        }
1298    }
1299    Ok(())
1300}
1301
1302fn required_string(
1303    module_id: ModuleId,
1304    module: &ProgramModule<Verified>,
1305    id: ConstantId,
1306    bounds: ProgramVerifyErrorKind,
1307    kind: ProgramVerifyErrorKind,
1308) -> Result<&EcmaString, ProgramVerifyError> {
1309    match module.code.constants().get(id.get() as usize) {
1310        None => Err(module_error(module_id, bounds)),
1311        Some(Constant::String(value)) if value.is_well_formed() => Ok(value),
1312        Some(Constant::String(_)) => Err(module_error(
1313            module_id,
1314            ProgramVerifyErrorKind::MetadataStringIllFormed { constant: id },
1315        )),
1316        Some(_) => Err(module_error(module_id, kind)),
1317    }
1318}
1319
1320fn require_edge(
1321    module_id: ModuleId,
1322    module: &ProgramModule<Verified>,
1323    binding: BindingId,
1324    edge: EdgeId,
1325) -> Result<&Edge, ProgramVerifyError> {
1326    module.edges.get(edge.get() as usize).ok_or_else(|| {
1327        module_error(
1328            module_id,
1329            ProgramVerifyErrorKind::BindingEdgeOutOfBounds { binding, edge },
1330        )
1331    })
1332}
1333
1334fn verify_imported_bindings(modules: &[ProgramModule<Verified>]) -> Result<(), ProgramVerifyError> {
1335    for (module_index, module) in modules.iter().enumerate() {
1336        let module_id = ModuleId::new(module_index as u32);
1337        for (binding_index, binding) in module.bindings.iter().enumerate() {
1338            let BindingKind::Imported { edge, name } = binding.kind else {
1339                continue;
1340            };
1341            let EdgeTarget::Local(target) = module.edges[edge.get() as usize].target else {
1342                continue;
1343            };
1344            let imported_name =
1345                string(&module.code, name).expect("metadata verifier checked imported name");
1346            let target = &modules[target.get() as usize];
1347            if !target.exports.iter().any(|export| {
1348                string(&target.code, export.name).expect("metadata verifier checked export name")
1349                    == imported_name
1350            }) {
1351                return Err(module_error(
1352                    module_id,
1353                    ProgramVerifyErrorKind::MissingImportedExport {
1354                        binding: BindingId::new(binding_index as u32),
1355                    },
1356                ));
1357            }
1358        }
1359    }
1360    Ok(())
1361}
1362
1363fn verify_export_resolutions(
1364    modules: &[ProgramModule<Verified>],
1365) -> Result<(), ProgramVerifyError> {
1366    let export_indices: Vec<HashMap<&EcmaString, usize>> = modules
1367        .iter()
1368        .map(|module| {
1369            module
1370                .exports
1371                .iter()
1372                .enumerate()
1373                .map(|(index, export)| {
1374                    (
1375                        string(&module.code, export.name)
1376                            .expect("metadata verifier checked export name"),
1377                        index,
1378                    )
1379                })
1380                .collect()
1381        })
1382        .collect();
1383    let mut states: Vec<Vec<u8>> = modules
1384        .iter()
1385        .map(|module| vec![0; module.exports.len()])
1386        .collect();
1387    let mut stack = Vec::new();
1388    for (module_index, module) in modules.iter().enumerate() {
1389        for export_index in 0..module.exports.len() {
1390            if states[module_index][export_index] == 2 {
1391                continue;
1392            }
1393            stack.clear();
1394            let mut current = (module_index, export_index);
1395            loop {
1396                match states[current.0][current.1] {
1397                    2 => break,
1398                    1 => {
1399                        return Err(module_error(
1400                            ModuleId::new(current.0 as u32),
1401                            ProgramVerifyErrorKind::IndirectExportCycle {
1402                                export: current.1 as u32,
1403                            },
1404                        ));
1405                    }
1406                    _ => {}
1407                }
1408                states[current.0][current.1] = 1;
1409                stack.push(current);
1410                let current_module = &modules[current.0];
1411                let next = match current_module.exports[current.1].source {
1412                    ExportSource::Local(binding) => {
1413                        match current_module.bindings[binding.get() as usize].kind {
1414                            BindingKind::Imported { edge, name } => {
1415                                let edge = &current_module.edges[edge.get() as usize];
1416                                match edge.target {
1417                                    EdgeTarget::External => None,
1418                                    EdgeTarget::Local(target) => {
1419                                        let name = string(&current_module.code, name)
1420                                            .expect("metadata verifier checked imported name");
1421                                        let target_index = target.get() as usize;
1422                                        let target_export = export_indices[target_index]
1423                                            .get(name)
1424                                            .copied()
1425                                            .ok_or_else(|| {
1426                                                module_error(
1427                                                    ModuleId::new(current.0 as u32),
1428                                                    ProgramVerifyErrorKind::MissingImportedExport {
1429                                                        binding,
1430                                                    },
1431                                                )
1432                                            })?;
1433                                        Some((target_index, target_export))
1434                                    }
1435                                }
1436                            }
1437                            BindingKind::Hoisted
1438                            | BindingKind::Lexical
1439                            | BindingKind::Namespace { .. } => None,
1440                        }
1441                    }
1442                    ExportSource::Indirect { edge, name } => {
1443                        let edge = &current_module.edges[edge.get() as usize];
1444                        match edge.target {
1445                            EdgeTarget::External => None,
1446                            EdgeTarget::Local(target) => {
1447                                let name = string(&current_module.code, name)
1448                                    .expect("metadata verifier checked indirect name");
1449                                let target_index = target.get() as usize;
1450                                let target_export = export_indices[target_index]
1451                                    .get(name)
1452                                    .copied()
1453                                    .ok_or_else(|| {
1454                                        module_error(
1455                                            ModuleId::new(current.0 as u32),
1456                                            ProgramVerifyErrorKind::MissingIndirectExport {
1457                                                export: current.1 as u32,
1458                                            },
1459                                        )
1460                                    })?;
1461                                Some((target_index, target_export))
1462                            }
1463                        }
1464                    }
1465                };
1466                let Some(next) = next else {
1467                    break;
1468                };
1469                current = next;
1470            }
1471            for &(resolved_module, resolved_export) in &stack {
1472                states[resolved_module][resolved_export] = 2;
1473            }
1474        }
1475    }
1476    Ok(())
1477}
1478
1479fn string(module: &Module<Verified>, id: ConstantId) -> Option<&EcmaString> {
1480    match module.constants().get(id.get() as usize) {
1481        Some(Constant::String(value)) => Some(value),
1482        _ => None,
1483    }
1484}
1485
1486fn is_normalized_module_name(name: &EcmaString) -> bool {
1487    let units = name.as_units();
1488    if units.is_empty()
1489        || units.first() == Some(&u16::from(b'/'))
1490        || units.contains(&u16::from(b'\\'))
1491        || units.contains(&0)
1492        || units.split(|&unit| unit == u16::from(b'/')).any(|part| {
1493            part.is_empty()
1494                || part == [u16::from(b'.')]
1495                || part == [u16::from(b'.'), u16::from(b'.')]
1496        })
1497    {
1498        return false;
1499    }
1500    !units
1501        .split(|&unit| unit == u16::from(b'/'))
1502        .next()
1503        .unwrap_or_default()
1504        .contains(&u16::from(b':'))
1505}
1506
1507fn is_absolute_specifier(specifier: &EcmaString) -> bool {
1508    let units = specifier.as_units();
1509    units.starts_with(&[u16::from(b'/')])
1510        || units.starts_with(&[u16::from(b'\\')])
1511        || units.get(..5).is_some_and(|prefix| {
1512            prefix.iter().copied().zip(*b"file:").all(|(unit, ascii)| {
1513                unit == u16::from(ascii)
1514                    || (ascii.is_ascii_alphabetic()
1515                        && unit == u16::from(ascii.to_ascii_uppercase()))
1516            })
1517        })
1518        || matches!(units, [drive, colon, ..] if (*drive >= u16::from(b'a') && *drive <= u16::from(b'z') || *drive >= u16::from(b'A') && *drive <= u16::from(b'Z')) && *colon == u16::from(b':'))
1519}
1520
1521const fn program_error(kind: ProgramVerifyErrorKind) -> ProgramVerifyError {
1522    ProgramVerifyError { module: None, kind }
1523}
1524
1525const fn module_error(module: ModuleId, kind: ProgramVerifyErrorKind) -> ProgramVerifyError {
1526    ProgramVerifyError {
1527        module: Some(module),
1528        kind,
1529    }
1530}
1531
1532fn write_u32(value: u32, output: &mut Vec<u8>) {
1533    let mut remaining = value;
1534    loop {
1535        let byte = (remaining & 0x7f) as u8;
1536        remaining >>= 7;
1537        if remaining == 0 {
1538            output.push(byte);
1539            return;
1540        }
1541        output.push(byte | 0x80);
1542    }
1543}
1544
1545#[cfg(test)]
1546mod tests {
1547    use super::*;
1548    use crate::{Function, FunctionFlags, FunctionId, Register};
1549
1550    fn verified_module(name: &str, extra: &[&str]) -> Module<Verified> {
1551        let mut constants = vec![Constant::String(EcmaString::from_utf8(name))];
1552        constants.extend(
1553            extra
1554                .iter()
1555                .map(|value| Constant::String(EcmaString::from_utf8(value))),
1556        );
1557        Module::new(
1558            constants,
1559            vec![Function::new(
1560                None,
1561                0,
1562                0,
1563                1,
1564                FunctionFlags::default(),
1565                vec![Instruction::Halt],
1566                Vec::new(),
1567            )],
1568            FunctionId::new(0),
1569        )
1570        .verify()
1571        .unwrap()
1572    }
1573
1574    fn program_module(name: &str) -> ProgramModule<Verified> {
1575        ProgramModule {
1576            name: ConstantId::new(0),
1577            code: verified_module(name, &["x", "./dep", "remote"]),
1578            edges: Vec::new(),
1579            bindings: vec![Binding {
1580                name: ConstantId::new(1),
1581                kind: BindingKind::Hoisted,
1582            }],
1583            exports: vec![Export {
1584                name: ConstantId::new(1),
1585                source: ExportSource::Local(BindingId::new(0)),
1586            }],
1587        }
1588    }
1589
1590    fn valid_program() -> Program<Verified> {
1591        Program::link(vec![program_module("main")], ModuleId::new(0)).unwrap()
1592    }
1593
1594    fn read_u32(bytes: &[u8], offset: &mut usize) -> u32 {
1595        let mut value = 0;
1596        let mut shift = 0;
1597        loop {
1598            let byte = bytes[*offset];
1599            *offset += 1;
1600            value |= u32::from(byte & 0x7f) << shift;
1601            if byte & 0x80 == 0 {
1602                return value;
1603            }
1604            shift += 7;
1605        }
1606    }
1607
1608    fn raw_header(entry: u32, modules: u32) -> Vec<u8> {
1609        let mut bytes = PROGRAM_MAGIC.to_vec();
1610        bytes.push(PROGRAM_VERSION);
1611        write_u32(entry, &mut bytes);
1612        write_u32(modules, &mut bytes);
1613        bytes
1614    }
1615
1616    fn raw_module_prefix(code: &Module<Verified>) -> Vec<u8> {
1617        let blob = code.encode();
1618        let mut bytes = raw_header(0, 1);
1619        write_u32(0, &mut bytes);
1620        write_u32(blob.len() as u32, &mut bytes);
1621        bytes.extend_from_slice(&blob);
1622        bytes
1623    }
1624
1625    #[test]
1626    fn metadata_strings_must_be_well_formed_utf16() {
1627        let code = Module::new(
1628            vec![Constant::String(EcmaString::from_units(&[0xD800]))],
1629            vec![Function::new(
1630                None,
1631                0,
1632                0,
1633                1,
1634                FunctionFlags::default(),
1635                vec![Instruction::Halt],
1636                Vec::new(),
1637            )],
1638            FunctionId::new(0),
1639        )
1640        .verify()
1641        .expect("a literal string may contain an unpaired surrogate");
1642        let error = Program::link(
1643            vec![ProgramModule {
1644                name: ConstantId::new(0),
1645                code,
1646                edges: Vec::new(),
1647                bindings: Vec::new(),
1648                exports: Vec::new(),
1649            }],
1650            ModuleId::new(0),
1651        )
1652        .expect_err("module metadata cannot contain an unpaired surrogate");
1653        assert_eq!(
1654            error.kind,
1655            ProgramVerifyErrorKind::MetadataStringIllFormed {
1656                constant: ConstantId::new(0),
1657            }
1658        );
1659    }
1660
1661    #[test]
1662    fn round_trip_reencode_is_identical() {
1663        let encoded = valid_program().encode();
1664        let decoded = decode_verified_program(&encoded, &ProgramDecodeLimits::default()).unwrap();
1665        assert_eq!(decoded.encode(), encoded);
1666        assert_eq!(
1667            decoded.resolve_export(ModuleId::new(0), &EcmaString::from_utf8("x")),
1668            Some(ResolvedExport::Local {
1669                module: ModuleId::new(0),
1670                binding: BindingId::new(0),
1671            })
1672        );
1673    }
1674
1675    #[test]
1676    fn every_truncation_and_trailing_bytes_are_rejected() {
1677        let encoded = valid_program().encode();
1678        for length in 0..encoded.len() {
1679            assert!(
1680                decode_program(&encoded[..length], &ProgramDecodeLimits::default()).is_err(),
1681                "accepted truncation at {length}"
1682            );
1683        }
1684        let mut trailing = encoded;
1685        trailing.push(0);
1686        assert!(matches!(
1687            decode_program(&trailing, &ProgramDecodeLimits::default()),
1688            Err(ProgramDecodeError {
1689                kind: ProgramDecodeErrorKind::TrailingBytes { count: 1 },
1690                ..
1691            })
1692        ));
1693    }
1694
1695    #[test]
1696    fn embedded_module_blob_is_byte_identical() {
1697        let program = valid_program();
1698        let blob = program.modules()[0].code.encode();
1699        let encoded = program.encode();
1700        let mut offset = PROGRAM_MAGIC.len() + 1;
1701        assert_eq!(read_u32(&encoded, &mut offset), 0);
1702        assert_eq!(read_u32(&encoded, &mut offset), 1);
1703        assert_eq!(read_u32(&encoded, &mut offset), 0);
1704        assert_eq!(read_u32(&encoded, &mut offset) as usize, blob.len());
1705        assert_eq!(&encoded[offset..offset + blob.len()], blob);
1706    }
1707
1708    #[test]
1709    fn canonical_edge_kind_tags_and_version_round_trip() {
1710        let mut module = program_module("main");
1711        module.edges = vec![
1712            Edge {
1713                specifier: ConstantId::new(1),
1714                target: EdgeTarget::External,
1715                kind: EdgeKind::Static,
1716            },
1717            Edge {
1718                specifier: ConstantId::new(2),
1719                target: EdgeTarget::External,
1720                kind: EdgeKind::Dynamic,
1721            },
1722            Edge {
1723                specifier: ConstantId::new(3),
1724                target: EdgeTarget::External,
1725                kind: EdgeKind::StaticAndDynamic,
1726            },
1727        ];
1728        let program = Program::link(vec![module], ModuleId::new(0)).unwrap();
1729        let encoded = program.encode();
1730        assert_eq!(encoded[PROGRAM_MAGIC.len()], PROGRAM_VERSION);
1731
1732        let mut offset = PROGRAM_MAGIC.len() + 1;
1733        assert_eq!(read_u32(&encoded, &mut offset), 0);
1734        assert_eq!(read_u32(&encoded, &mut offset), 1);
1735        assert_eq!(read_u32(&encoded, &mut offset), 0);
1736        offset += read_u32(&encoded, &mut offset) as usize;
1737        assert_eq!(read_u32(&encoded, &mut offset), 3);
1738        for (specifier, kind) in [(1, 0), (2, 1), (3, 2)] {
1739            assert_eq!(read_u32(&encoded, &mut offset), specifier);
1740            assert_eq!(encoded[offset], 1);
1741            offset += 1;
1742            assert_eq!(encoded[offset], kind);
1743            offset += 1;
1744        }
1745        assert_eq!(
1746            decode_verified_program(&encoded, &ProgramDecodeLimits::default())
1747                .unwrap()
1748                .encode(),
1749            encoded
1750        );
1751    }
1752
1753    #[test]
1754    fn oversized_counts_lengths_and_input_are_rejected() {
1755        let mut limits = ProgramDecodeLimits {
1756            max_modules: 0,
1757            ..ProgramDecodeLimits::default()
1758        };
1759        let modules = raw_header(0, 1);
1760        assert!(matches!(
1761            decode_program(&modules, &limits),
1762            Err(ProgramDecodeError {
1763                kind: ProgramDecodeErrorKind::LimitExceeded {
1764                    field: "modules",
1765                    ..
1766                },
1767                ..
1768            })
1769        ));
1770
1771        let mut length = raw_header(0, 1);
1772        write_u32(0, &mut length);
1773        write_u32(2, &mut length);
1774        limits.max_modules = 1;
1775        limits.max_module_bytes = 1;
1776        assert!(matches!(
1777            decode_program(&length, &limits),
1778            Err(ProgramDecodeError {
1779                kind: ProgramDecodeErrorKind::LimitExceeded {
1780                    field: "module bytes",
1781                    ..
1782                },
1783                ..
1784            })
1785        ));
1786
1787        limits.max_bytes = 1;
1788        assert!(matches!(
1789            decode_program(&valid_program().encode(), &limits),
1790            Err(ProgramDecodeError {
1791                kind: ProgramDecodeErrorKind::InputLimitExceeded { .. },
1792                ..
1793            })
1794        ));
1795    }
1796
1797    #[test]
1798    fn every_metadata_count_and_total_limit_is_enforced() {
1799        let mut with_edge = program_module("main");
1800        with_edge.edges.push(Edge {
1801            specifier: ConstantId::new(2),
1802            target: EdgeTarget::External,
1803            kind: EdgeKind::Static,
1804        });
1805        let edge_bytes = Program::link(vec![with_edge], ModuleId::new(0))
1806            .unwrap()
1807            .encode();
1808        for limits in [
1809            ProgramDecodeLimits {
1810                max_edges_per_module: 0,
1811                ..ProgramDecodeLimits::default()
1812            },
1813            ProgramDecodeLimits {
1814                max_total_edges: 0,
1815                ..ProgramDecodeLimits::default()
1816            },
1817        ] {
1818            assert!(matches!(
1819                decode_program(&edge_bytes, &limits),
1820                Err(ProgramDecodeError {
1821                    kind: ProgramDecodeErrorKind::LimitExceeded { .. },
1822                    ..
1823                })
1824            ));
1825        }
1826
1827        let encoded = valid_program().encode();
1828        for limits in [
1829            ProgramDecodeLimits {
1830                max_bindings_per_module: 0,
1831                ..ProgramDecodeLimits::default()
1832            },
1833            ProgramDecodeLimits {
1834                max_total_bindings: 0,
1835                ..ProgramDecodeLimits::default()
1836            },
1837            ProgramDecodeLimits {
1838                max_exports_per_module: 0,
1839                ..ProgramDecodeLimits::default()
1840            },
1841            ProgramDecodeLimits {
1842                max_total_exports: 0,
1843                ..ProgramDecodeLimits::default()
1844            },
1845        ] {
1846            assert!(matches!(
1847                decode_program(&encoded, &limits),
1848                Err(ProgramDecodeError {
1849                    kind: ProgramDecodeErrorKind::LimitExceeded { .. },
1850                    ..
1851                })
1852            ));
1853        }
1854    }
1855
1856    #[test]
1857    fn invalid_envelope_tags_and_integers_are_rejected() {
1858        let code = verified_module("main", &["x"]);
1859
1860        let mut edge_tag = raw_module_prefix(&code);
1861        write_u32(1, &mut edge_tag);
1862        write_u32(1, &mut edge_tag);
1863        edge_tag.push(9);
1864        assert!(matches!(
1865            decode_program(&edge_tag, &ProgramDecodeLimits::default()),
1866            Err(ProgramDecodeError {
1867                kind: ProgramDecodeErrorKind::InvalidEdgeTarget { tag: 9 },
1868                ..
1869            })
1870        ));
1871
1872        let mut edge_kind = raw_module_prefix(&code);
1873        write_u32(1, &mut edge_kind);
1874        write_u32(1, &mut edge_kind);
1875        edge_kind.extend_from_slice(&[1, 9]);
1876        assert!(matches!(
1877            decode_program(&edge_kind, &ProgramDecodeLimits::default()),
1878            Err(ProgramDecodeError {
1879                kind: ProgramDecodeErrorKind::InvalidEdgeKind { tag: 9 },
1880                ..
1881            })
1882        ));
1883
1884        let mut binding_tag = raw_module_prefix(&code);
1885        write_u32(0, &mut binding_tag);
1886        write_u32(1, &mut binding_tag);
1887        binding_tag.extend_from_slice(&[1, 9]);
1888        assert!(matches!(
1889            decode_program(&binding_tag, &ProgramDecodeLimits::default()),
1890            Err(ProgramDecodeError {
1891                kind: ProgramDecodeErrorKind::InvalidBindingKind { tag: 9 },
1892                ..
1893            })
1894        ));
1895
1896        let mut export_tag = raw_module_prefix(&code);
1897        write_u32(0, &mut export_tag);
1898        write_u32(0, &mut export_tag);
1899        write_u32(1, &mut export_tag);
1900        export_tag.extend_from_slice(&[1, 9]);
1901        assert!(matches!(
1902            decode_program(&export_tag, &ProgramDecodeLimits::default()),
1903            Err(ProgramDecodeError {
1904                kind: ProgramDecodeErrorKind::InvalidExportSource { tag: 9 },
1905                ..
1906            })
1907        ));
1908
1909        let mut noncanonical = PROGRAM_MAGIC.to_vec();
1910        noncanonical.push(PROGRAM_VERSION);
1911        noncanonical.extend_from_slice(&[0x80, 0]);
1912        assert!(matches!(
1913            decode_program(&noncanonical, &ProgramDecodeLimits::default()),
1914            Err(ProgramDecodeError {
1915                kind: ProgramDecodeErrorKind::NonCanonicalInteger,
1916                ..
1917            })
1918        ));
1919    }
1920
1921    #[test]
1922    fn malformed_utf16_metadata_inside_module_blob_is_typed_verify_error() {
1923        let mut encoded = valid_program().encode();
1924        let needle = [2, 4, b'm', 0, b'a', 0, b'i', 0, b'n', 0];
1925        let at = encoded
1926            .windows(needle.len())
1927            .position(|window| window == needle)
1928            .unwrap();
1929        encoded[at + 2..at + 4].copy_from_slice(&0xD800_u16.to_le_bytes());
1930        assert!(matches!(
1931            decode_verified_program(&encoded, &ProgramDecodeLimits::default()),
1932            Err(ProgramLoadError::Verify(ProgramVerifyError {
1933                kind: ProgramVerifyErrorKind::MetadataStringIllFormed { .. },
1934                ..
1935            }))
1936        ));
1937    }
1938
1939    #[test]
1940    fn non_normalized_and_duplicate_module_names_are_rejected() {
1941        for name in ["", "/abs", "../escape", "a/../b", "a//b", "a\\b", "C:/abs"] {
1942            let error = Program::link(vec![program_module(name)], ModuleId::new(0)).unwrap_err();
1943            assert!(matches!(
1944                error.kind,
1945                ProgramVerifyErrorKind::InvalidModuleName
1946            ));
1947        }
1948        let error = Program::link(
1949            vec![program_module("same"), program_module("same")],
1950            ModuleId::new(0),
1951        )
1952        .unwrap_err();
1953        assert!(matches!(
1954            error.kind,
1955            ProgramVerifyErrorKind::DuplicateModuleName { .. }
1956        ));
1957    }
1958
1959    #[test]
1960    fn absolute_path_specifiers_are_rejected_without_banning_external_schemes() {
1961        for specifier in [
1962            "/tmp/module",
1963            "\\\\server\\share",
1964            "C:/module",
1965            "file:///tmp/module",
1966        ] {
1967            let mut module = ProgramModule {
1968                name: ConstantId::new(0),
1969                code: verified_module("main", &[specifier]),
1970                edges: Vec::new(),
1971                bindings: Vec::new(),
1972                exports: Vec::new(),
1973            };
1974            module.edges.push(Edge {
1975                specifier: ConstantId::new(1),
1976                target: EdgeTarget::External,
1977                kind: EdgeKind::Static,
1978            });
1979            assert!(matches!(
1980                Program::link(vec![module], ModuleId::new(0))
1981                    .unwrap_err()
1982                    .kind,
1983                ProgramVerifyErrorKind::AbsoluteSpecifier { .. }
1984            ));
1985        }
1986
1987        let mut module = ProgramModule {
1988            name: ConstantId::new(0),
1989            code: verified_module("main", &["node:fs"]),
1990            edges: Vec::new(),
1991            bindings: Vec::new(),
1992            exports: Vec::new(),
1993        };
1994        module.edges.push(Edge {
1995            specifier: ConstantId::new(1),
1996            target: EdgeTarget::External,
1997            kind: EdgeKind::Static,
1998        });
1999        Program::link(vec![module], ModuleId::new(0)).unwrap();
2000    }
2001
2002    #[test]
2003    fn duplicate_linkage_tables_are_rejected() {
2004        let mut module = program_module("main");
2005        module.edges = vec![
2006            Edge {
2007                specifier: ConstantId::new(2),
2008                target: EdgeTarget::External,
2009                kind: EdgeKind::Static,
2010            },
2011            Edge {
2012                specifier: ConstantId::new(2),
2013                target: EdgeTarget::External,
2014                kind: EdgeKind::Static,
2015            },
2016        ];
2017        assert!(matches!(
2018            Program::link(vec![module], ModuleId::new(0))
2019                .unwrap_err()
2020                .kind,
2021            ProgramVerifyErrorKind::DuplicateSpecifier { .. }
2022        ));
2023
2024        let mut module = program_module("main");
2025        module.bindings.push(module.bindings[0]);
2026        assert!(matches!(
2027            Program::link(vec![module], ModuleId::new(0))
2028                .unwrap_err()
2029                .kind,
2030            ProgramVerifyErrorKind::DuplicateBinding { .. }
2031        ));
2032
2033        let mut module = program_module("main");
2034        module.exports.push(module.exports[0]);
2035        assert!(matches!(
2036            Program::link(vec![module], ModuleId::new(0))
2037                .unwrap_err()
2038                .kind,
2039            ProgramVerifyErrorKind::DuplicateExport { .. }
2040        ));
2041    }
2042
2043    #[test]
2044    fn bad_local_edge_binding_and_export_indices_are_rejected() {
2045        let mut module = program_module("main");
2046        module.edges.push(Edge {
2047            specifier: ConstantId::new(2),
2048            target: EdgeTarget::Local(ModuleId::new(1)),
2049            kind: EdgeKind::Static,
2050        });
2051        assert!(matches!(
2052            Program::link(vec![module], ModuleId::new(0))
2053                .unwrap_err()
2054                .kind,
2055            ProgramVerifyErrorKind::LocalTargetOutOfBounds { .. }
2056        ));
2057
2058        let mut module = program_module("main");
2059        module.bindings[0].kind = BindingKind::Namespace {
2060            edge: EdgeId::new(0),
2061        };
2062        assert!(matches!(
2063            Program::link(vec![module], ModuleId::new(0))
2064                .unwrap_err()
2065                .kind,
2066            ProgramVerifyErrorKind::BindingEdgeOutOfBounds { .. }
2067        ));
2068
2069        let mut module = program_module("main");
2070        module.exports[0].source = ExportSource::Local(BindingId::new(1));
2071        assert!(matches!(
2072            Program::link(vec![module], ModuleId::new(0))
2073                .unwrap_err()
2074                .kind,
2075            ProgramVerifyErrorKind::ExportBindingOutOfBounds { .. }
2076        ));
2077
2078        let mut module = program_module("main");
2079        module.exports[0].source = ExportSource::Indirect {
2080            edge: EdgeId::new(0),
2081            name: ConstantId::new(1),
2082        };
2083        assert!(matches!(
2084            Program::link(vec![module], ModuleId::new(0))
2085                .unwrap_err()
2086                .kind,
2087            ProgramVerifyErrorKind::ExportEdgeOutOfBounds { .. }
2088        ));
2089    }
2090
2091    #[test]
2092    fn every_metadata_string_reference_checks_bounds_and_kind() {
2093        let code = Module::new(
2094            vec![
2095                Constant::String(EcmaString::from_utf8("main")),
2096                Constant::Int32(7),
2097            ],
2098            vec![Function::new(
2099                None,
2100                0,
2101                0,
2102                1,
2103                FunctionFlags::default(),
2104                vec![Instruction::Halt],
2105                Vec::new(),
2106            )],
2107            FunctionId::new(0),
2108        )
2109        .verify()
2110        .unwrap();
2111        let empty = |name| ProgramModule {
2112            name,
2113            code: code.clone(),
2114            edges: Vec::new(),
2115            bindings: Vec::new(),
2116            exports: Vec::new(),
2117        };
2118
2119        for (name, expected_bounds) in [(ConstantId::new(2), true), (ConstantId::new(1), false)] {
2120            let kind = Program::link(vec![empty(name)], ModuleId::new(0))
2121                .unwrap_err()
2122                .kind;
2123            assert!(
2124                matches!(kind, ProgramVerifyErrorKind::ModuleNameOutOfBounds { .. })
2125                    == expected_bounds
2126            );
2127            assert!(
2128                matches!(kind, ProgramVerifyErrorKind::ModuleNameNotString { .. })
2129                    != expected_bounds
2130            );
2131        }
2132
2133        for (specifier, expected_bounds) in
2134            [(ConstantId::new(2), true), (ConstantId::new(1), false)]
2135        {
2136            let mut module = empty(ConstantId::new(0));
2137            module.edges.push(Edge {
2138                specifier,
2139                target: EdgeTarget::External,
2140                kind: EdgeKind::Static,
2141            });
2142            let kind = Program::link(vec![module], ModuleId::new(0))
2143                .unwrap_err()
2144                .kind;
2145            assert!(
2146                matches!(kind, ProgramVerifyErrorKind::SpecifierOutOfBounds { .. })
2147                    == expected_bounds
2148            );
2149            assert!(
2150                matches!(kind, ProgramVerifyErrorKind::SpecifierNotString { .. })
2151                    != expected_bounds
2152            );
2153        }
2154
2155        for (name, expected_bounds) in [(ConstantId::new(2), true), (ConstantId::new(1), false)] {
2156            let mut module = empty(ConstantId::new(0));
2157            module.bindings.push(Binding {
2158                name,
2159                kind: BindingKind::Lexical,
2160            });
2161            let kind = Program::link(vec![module], ModuleId::new(0))
2162                .unwrap_err()
2163                .kind;
2164            assert!(
2165                matches!(kind, ProgramVerifyErrorKind::BindingNameOutOfBounds { .. })
2166                    == expected_bounds
2167            );
2168            assert!(
2169                matches!(kind, ProgramVerifyErrorKind::BindingNameNotString { .. })
2170                    != expected_bounds
2171            );
2172        }
2173
2174        for (name, expected_bounds) in [(ConstantId::new(2), true), (ConstantId::new(1), false)] {
2175            let mut module = empty(ConstantId::new(0));
2176            module.exports.push(Export {
2177                name,
2178                source: ExportSource::Local(BindingId::new(0)),
2179            });
2180            let kind = Program::link(vec![module], ModuleId::new(0))
2181                .unwrap_err()
2182                .kind;
2183            assert!(
2184                matches!(kind, ProgramVerifyErrorKind::ExportNameOutOfBounds { .. })
2185                    == expected_bounds
2186            );
2187            assert!(
2188                matches!(kind, ProgramVerifyErrorKind::ExportNameNotString { .. })
2189                    != expected_bounds
2190            );
2191        }
2192
2193        let mut module = empty(ConstantId::new(0));
2194        module.edges.push(Edge {
2195            specifier: ConstantId::new(0),
2196            target: EdgeTarget::External,
2197            kind: EdgeKind::Static,
2198        });
2199        module.bindings.push(Binding {
2200            name: ConstantId::new(0),
2201            kind: BindingKind::Imported {
2202                edge: EdgeId::new(0),
2203                name: ConstantId::new(1),
2204            },
2205        });
2206        assert!(matches!(
2207            Program::link(vec![module], ModuleId::new(0))
2208                .unwrap_err()
2209                .kind,
2210            ProgramVerifyErrorKind::ImportedNameNotString { .. }
2211        ));
2212
2213        let mut module = empty(ConstantId::new(0));
2214        module.edges.push(Edge {
2215            specifier: ConstantId::new(0),
2216            target: EdgeTarget::External,
2217            kind: EdgeKind::Static,
2218        });
2219        module.exports.push(Export {
2220            name: ConstantId::new(0),
2221            source: ExportSource::Indirect {
2222                edge: EdgeId::new(0),
2223                name: ConstantId::new(1),
2224            },
2225        });
2226        assert!(matches!(
2227            Program::link(vec![module], ModuleId::new(0))
2228                .unwrap_err()
2229                .kind,
2230            ProgramVerifyErrorKind::IndirectNameNotString { .. }
2231        ));
2232    }
2233
2234    #[test]
2235    fn export_cycles_and_missing_targets_are_rejected() {
2236        let mut left = program_module("left");
2237        let mut right = program_module("right");
2238        left.edges.push(Edge {
2239            specifier: ConstantId::new(2),
2240            target: EdgeTarget::Local(ModuleId::new(1)),
2241            kind: EdgeKind::Static,
2242        });
2243        right.edges.push(Edge {
2244            specifier: ConstantId::new(2),
2245            target: EdgeTarget::Local(ModuleId::new(0)),
2246            kind: EdgeKind::Static,
2247        });
2248        left.exports[0].source = ExportSource::Indirect {
2249            edge: EdgeId::new(0),
2250            name: ConstantId::new(1),
2251        };
2252        right.exports[0].source = ExportSource::Indirect {
2253            edge: EdgeId::new(0),
2254            name: ConstantId::new(1),
2255        };
2256        assert!(matches!(
2257            Program::link(vec![left, right], ModuleId::new(0))
2258                .unwrap_err()
2259                .kind,
2260            ProgramVerifyErrorKind::IndirectExportCycle { .. }
2261        ));
2262
2263        let mut left = program_module("left");
2264        let right = program_module("right");
2265        left.edges.push(Edge {
2266            specifier: ConstantId::new(2),
2267            target: EdgeTarget::Local(ModuleId::new(1)),
2268            kind: EdgeKind::Static,
2269        });
2270        left.exports[0].source = ExportSource::Indirect {
2271            edge: EdgeId::new(0),
2272            name: ConstantId::new(3),
2273        };
2274        assert!(matches!(
2275            Program::link(vec![left, right], ModuleId::new(0))
2276                .unwrap_err()
2277                .kind,
2278            ProgramVerifyErrorKind::MissingIndirectExport { .. }
2279        ));
2280    }
2281
2282    #[test]
2283    fn external_indirect_exports_resolve_totally() {
2284        let mut module = program_module("main");
2285        module.edges.push(Edge {
2286            specifier: ConstantId::new(2),
2287            target: EdgeTarget::External,
2288            kind: EdgeKind::Static,
2289        });
2290        module.exports[0].source = ExportSource::Indirect {
2291            edge: EdgeId::new(0),
2292            name: ConstantId::new(3),
2293        };
2294        let program = Program::link(vec![module], ModuleId::new(0)).unwrap();
2295        assert_eq!(
2296            program.resolve_export(ModuleId::new(0), &EcmaString::from_utf8("x")),
2297            Some(ResolvedExport::External {
2298                module: ModuleId::new(0),
2299                edge: EdgeId::new(0),
2300                name: ConstantId::new(3),
2301            })
2302        );
2303    }
2304
2305    #[test]
2306    fn entry_bounds_are_checked_after_decode_or_link() {
2307        let error = Program::link(vec![program_module("main")], ModuleId::new(1)).unwrap_err();
2308        assert!(matches!(
2309            error.kind,
2310            ProgramVerifyErrorKind::EntryModuleOutOfBounds { .. }
2311        ));
2312
2313        let encoded = Program {
2314            modules: valid_program().modules,
2315            entry: ModuleId::new(1),
2316            state: PhantomData,
2317        }
2318        .encode();
2319        assert!(matches!(
2320            decode_verified_program(&encoded, &ProgramDecodeLimits::default()),
2321            Err(ProgramLoadError::Verify(ProgramVerifyError {
2322                kind: ProgramVerifyErrorKind::EntryModuleOutOfBounds { .. },
2323                ..
2324            }))
2325        ));
2326    }
2327
2328    #[test]
2329    fn dynamic_imports_require_dynamic_capability() {
2330        let code = Module::new(
2331            vec![
2332                Constant::String(EcmaString::from_utf8("main")),
2333                Constant::String(EcmaString::from_utf8("./dep")),
2334            ],
2335            vec![Function::new(
2336                None,
2337                0,
2338                0,
2339                1,
2340                FunctionFlags::default(),
2341                vec![
2342                    Instruction::Import {
2343                        dst: Register::new(0),
2344                        specifier: ConstantId::new(1),
2345                    },
2346                    Instruction::Halt,
2347                ],
2348                Vec::new(),
2349            )],
2350            FunctionId::new(0),
2351        )
2352        .verify()
2353        .unwrap();
2354        let dynamic_module = |kind| ProgramModule {
2355            name: ConstantId::new(0),
2356            code: code.clone(),
2357            edges: vec![Edge {
2358                specifier: ConstantId::new(1),
2359                target: EdgeTarget::External,
2360                kind,
2361            }],
2362            bindings: Vec::new(),
2363            exports: Vec::new(),
2364        };
2365
2366        Program::link(vec![dynamic_module(EdgeKind::Dynamic)], ModuleId::new(0)).unwrap();
2367        Program::link(
2368            vec![dynamic_module(EdgeKind::StaticAndDynamic)],
2369            ModuleId::new(0),
2370        )
2371        .unwrap();
2372        assert!(matches!(
2373            Program::link(vec![dynamic_module(EdgeKind::Static)], ModuleId::new(0))
2374                .unwrap_err()
2375                .kind,
2376            ProgramVerifyErrorKind::DynamicImportMissingEdge { .. }
2377        ));
2378    }
2379
2380    #[test]
2381    fn static_bindings_require_static_capability() {
2382        for kind in [
2383            BindingKind::Imported {
2384                edge: EdgeId::new(0),
2385                name: ConstantId::new(1),
2386            },
2387            BindingKind::Namespace {
2388                edge: EdgeId::new(0),
2389            },
2390        ] {
2391            let mut module = program_module("main");
2392            module.edges.push(Edge {
2393                specifier: ConstantId::new(2),
2394                target: EdgeTarget::External,
2395                kind: EdgeKind::Dynamic,
2396            });
2397            module.bindings[0].kind = kind;
2398            assert!(matches!(
2399                Program::link(vec![module], ModuleId::new(0))
2400                    .unwrap_err()
2401                    .kind,
2402                ProgramVerifyErrorKind::StaticBindingRequiresStaticEdge { .. }
2403            ));
2404        }
2405    }
2406
2407    #[test]
2408    fn static_and_dynamic_edge_satisfies_both_linkage_capabilities() {
2409        let code = Module::new(
2410            vec![
2411                Constant::String(EcmaString::from_utf8("main")),
2412                Constant::String(EcmaString::from_utf8("./dep")),
2413                Constant::String(EcmaString::from_utf8("value")),
2414            ],
2415            vec![Function::new(
2416                None,
2417                0,
2418                0,
2419                1,
2420                FunctionFlags::default(),
2421                vec![
2422                    Instruction::Import {
2423                        dst: Register::new(0),
2424                        specifier: ConstantId::new(1),
2425                    },
2426                    Instruction::Halt,
2427                ],
2428                Vec::new(),
2429            )],
2430            FunctionId::new(0),
2431        )
2432        .verify()
2433        .unwrap();
2434        Program::link(
2435            vec![ProgramModule {
2436                name: ConstantId::new(0),
2437                code,
2438                edges: vec![Edge {
2439                    specifier: ConstantId::new(1),
2440                    target: EdgeTarget::External,
2441                    kind: EdgeKind::StaticAndDynamic,
2442                }],
2443                bindings: vec![Binding {
2444                    name: ConstantId::new(2),
2445                    kind: BindingKind::Imported {
2446                        edge: EdgeId::new(0),
2447                        name: ConstantId::new(2),
2448                    },
2449                }],
2450                exports: Vec::new(),
2451            }],
2452            ModuleId::new(0),
2453        )
2454        .unwrap();
2455    }
2456
2457    #[test]
2458    fn executable_program_rejects_snapshot_exports() {
2459        let module = Module::new(
2460            vec![Constant::String(EcmaString::from_utf8("main"))],
2461            vec![Function::new(
2462                None,
2463                0,
2464                0,
2465                1,
2466                FunctionFlags::default(),
2467                vec![
2468                    Instruction::LoadConst {
2469                        dst: Register::new(0),
2470                        constant: ConstantId::new(0),
2471                    },
2472                    Instruction::Export {
2473                        name: ConstantId::new(0),
2474                        src: Register::new(0),
2475                    },
2476                    Instruction::Halt,
2477                ],
2478                Vec::new(),
2479            )],
2480            FunctionId::new(0),
2481        )
2482        .verify()
2483        .unwrap();
2484        assert!(matches!(
2485            Program::link(
2486                vec![ProgramModule {
2487                    name: ConstantId::new(0),
2488                    code: module,
2489                    edges: Vec::new(),
2490                    bindings: Vec::new(),
2491                    exports: Vec::new(),
2492                }],
2493                ModuleId::new(0),
2494            )
2495            .unwrap_err()
2496            .kind,
2497            ProgramVerifyErrorKind::SnapshotExportInstruction { .. }
2498        ));
2499    }
2500
2501    #[test]
2502    fn post_import_mutation_keeps_external_binding_as_live_identity() {
2503        let code = Module::new(
2504            vec![
2505                Constant::String(EcmaString::from_utf8("main")),
2506                Constant::String(EcmaString::from_utf8("x")),
2507                Constant::String(EcmaString::from_utf8("builtin:live")),
2508            ],
2509            vec![Function::new(
2510                None,
2511                0,
2512                0,
2513                1,
2514                FunctionFlags::default(),
2515                vec![
2516                    Instruction::LoadConst {
2517                        dst: Register::new(0),
2518                        constant: ConstantId::new(1),
2519                    },
2520                    Instruction::Halt,
2521                ],
2522                Vec::new(),
2523            )],
2524            FunctionId::new(0),
2525        )
2526        .verify()
2527        .unwrap();
2528        let program = Program::link(
2529            vec![ProgramModule {
2530                name: ConstantId::new(0),
2531                code,
2532                edges: vec![Edge {
2533                    specifier: ConstantId::new(2),
2534                    target: EdgeTarget::External,
2535                    kind: EdgeKind::Static,
2536                }],
2537                bindings: vec![Binding {
2538                    name: ConstantId::new(1),
2539                    kind: BindingKind::Imported {
2540                        edge: EdgeId::new(0),
2541                        name: ConstantId::new(1),
2542                    },
2543                }],
2544                exports: Vec::new(),
2545            }],
2546            ModuleId::new(0),
2547        )
2548        .unwrap();
2549        assert!(matches!(
2550            program.modules()[0].bindings()[0].kind,
2551            BindingKind::Imported { .. }
2552        ));
2553    }
2554
2555    #[test]
2556    fn external_imported_reexports_remain_available_to_providers() {
2557        let mut module = program_module("main");
2558        module.edges.push(Edge {
2559            specifier: ConstantId::new(2),
2560            target: EdgeTarget::External,
2561            kind: EdgeKind::Static,
2562        });
2563        module.bindings[0].kind = BindingKind::Imported {
2564            edge: EdgeId::new(0),
2565            name: ConstantId::new(1),
2566        };
2567        let program = Program::link(vec![module], ModuleId::new(0)).unwrap();
2568        assert_eq!(
2569            program.resolve_export(ModuleId::new(0), &EcmaString::from_utf8("x")),
2570            Some(ResolvedExport::External {
2571                module: ModuleId::new(0),
2572                edge: EdgeId::new(0),
2573                name: ConstantId::new(1),
2574            })
2575        );
2576    }
2577
2578    #[test]
2579    fn imported_export_cycles_are_rejected() {
2580        let mut left = program_module("left");
2581        left.edges.push(Edge {
2582            specifier: ConstantId::new(2),
2583            target: EdgeTarget::Local(ModuleId::new(1)),
2584            kind: EdgeKind::Static,
2585        });
2586        left.bindings[0].kind = BindingKind::Imported {
2587            edge: EdgeId::new(0),
2588            name: ConstantId::new(1),
2589        };
2590
2591        let mut right = program_module("right");
2592        right.edges.push(Edge {
2593            specifier: ConstantId::new(2),
2594            target: EdgeTarget::Local(ModuleId::new(0)),
2595            kind: EdgeKind::Static,
2596        });
2597        right.bindings[0].kind = BindingKind::Imported {
2598            edge: EdgeId::new(0),
2599            name: ConstantId::new(1),
2600        };
2601
2602        assert!(matches!(
2603            Program::link(vec![left, right], ModuleId::new(0))
2604                .unwrap_err()
2605                .kind,
2606            ProgramVerifyErrorKind::IndirectExportCycle { .. }
2607        ));
2608    }
2609
2610    #[test]
2611    fn local_imports_require_exports_and_follow_indirect_exports() {
2612        let mut importer = program_module("importer");
2613        importer.edges.push(Edge {
2614            specifier: ConstantId::new(2),
2615            target: EdgeTarget::Local(ModuleId::new(1)),
2616            kind: EdgeKind::Static,
2617        });
2618        importer.bindings[0].kind = BindingKind::Imported {
2619            edge: EdgeId::new(0),
2620            name: ConstantId::new(1),
2621        };
2622
2623        let mut relay = program_module("relay");
2624        relay.edges.push(Edge {
2625            specifier: ConstantId::new(2),
2626            target: EdgeTarget::Local(ModuleId::new(2)),
2627            kind: EdgeKind::Static,
2628        });
2629        relay.exports[0].source = ExportSource::Indirect {
2630            edge: EdgeId::new(0),
2631            name: ConstantId::new(1),
2632        };
2633        let program = Program::link(
2634            vec![importer, relay, program_module("leaf")],
2635            ModuleId::new(0),
2636        )
2637        .unwrap();
2638        let leaf = Some(ResolvedExport::Local {
2639            module: ModuleId::new(2),
2640            binding: BindingId::new(0),
2641        });
2642        assert_eq!(
2643            program.resolve_export(ModuleId::new(0), &EcmaString::from_utf8("x")),
2644            leaf
2645        );
2646        assert_eq!(
2647            program.resolve_export(ModuleId::new(1), &EcmaString::from_utf8("x")),
2648            leaf
2649        );
2650
2651        let mut importer = program_module("importer");
2652        importer.edges.push(Edge {
2653            specifier: ConstantId::new(2),
2654            target: EdgeTarget::Local(ModuleId::new(1)),
2655            kind: EdgeKind::Static,
2656        });
2657        importer.bindings[0].kind = BindingKind::Imported {
2658            edge: EdgeId::new(0),
2659            name: ConstantId::new(3),
2660        };
2661        importer.exports.clear();
2662        assert!(matches!(
2663            Program::link(vec![importer, program_module("target")], ModuleId::new(0),)
2664                .unwrap_err()
2665                .kind,
2666            ProgramVerifyErrorKind::MissingImportedExport { .. }
2667        ));
2668    }
2669}