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
11pub const PROGRAM_MAGIC: [u8; 8] = [66, 77, 84, 80, 67, 0, 0, 1];
13pub const PROGRAM_VERSION: u8 = 3;
15
16index_type!(
17 ModuleId
19);
20index_type!(
21 EdgeId
23);
24index_type!(
25 BindingId
27);
28
29#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
31pub enum EdgeTarget {
32 Local(ModuleId),
33 External,
34}
35
36#[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#[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#[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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
87pub struct Binding {
88 pub name: ConstantId,
89 pub kind: BindingKind,
90}
91
92#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
94pub enum ExportSource {
95 Local(BindingId),
96 Indirect { edge: EdgeId, name: ConstantId },
97}
98
99#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
101pub struct Export {
102 pub name: ConstantId,
103 pub source: ExportSource,
104}
105
106#[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#[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 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#[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 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 #[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 #[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(¤t.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(¤t.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(¤t.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#[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
682pub 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
734pub 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 = ¤t_module.edges[edge.get() as usize];
1416 match edge.target {
1417 EdgeTarget::External => None,
1418 EdgeTarget::Local(target) => {
1419 let name = string(¤t_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 = ¤t_module.edges[edge.get() as usize];
1444 match edge.target {
1445 EdgeTarget::External => None,
1446 EdgeTarget::Local(target) => {
1447 let name = string(¤t_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}