Skip to main content

bamts_bytecode/
lib.rs

1//! Production BamTS bytecode: a verified instruction set, strict codec, and
2//! definite-initialization verifier with an unforgeable `Verified` typestate.
3//!
4//! # Relation to the formal five-op core
5//!
6//! The proven structural core in `formal/lean/Bamti/Bytecode/Model.lean`
7//! (`Load`, `Add`, `Jump`, `Suspend`, `Halt`) is preserved exactly as a
8//! *subset* of this ISA:
9//!
10//! * `Load`    -> [`Instruction::LoadConst`] (now names the loaded constant).
11//! * `Add`     -> [`Instruction::Binary`] with [`BinaryOp::Add`] (generalized
12//!   to the full closed operator algebra).
13//! * `Jump`    -> [`Instruction::Jump`] (identical control transfer).
14//! * `Suspend` -> [`Instruction::Suspend`] (refined with an out register for
15//!   the resumed value and an in register for the yielded value).
16//! * `Halt`    -> [`Instruction::Halt`] (identical terminator).
17//!
18//! Every additional opcode *extends* that core. The verifier here mirrors the
19//! *structure* proven in `Bytecode/Verify.lean` -- a real entry, valid CFG
20//! targets, nested (non-partially-overlapping) handlers, and a syntactic
21//! definite-initialization witness across CFG joins -- before the `Verified`
22//! typestate can be constructed. Number constants use `Bamti.canonical_nan`,
23//! and persisted constants carry no heap or runtime identity, matching
24//! `no_serialized_runtime_identity`.
25//!
26//! # Dynamic-computation ISA
27//!
28//! Unlike a fixed-key/fixed-window shape, this ISA expresses the *dynamic*
29//! runtime kernel of the corpus without special-casing syntax:
30//!
31//! * **Property access is register-keyed.** [`Instruction::GetProperty`],
32//!   [`Instruction::SetProperty`], and [`Instruction::DeleteProperty`] take the
33//!   key in a `Register`, so computed access (`obj[e]`), string/number keys,
34//!   `Symbol` keys, and private names (via [`Instruction::CreatePrivateName`])
35//!   are one uniform operation. [`Instruction::DefineAccessor`] installs a
36//!   getter or setter descriptor under a register key.
37//! * **Calls are variadic.** [`Instruction::Call`] and
38//!   [`Instruction::Construct`] receive one *arguments-array* `Register`, so
39//!   spread (`f(...xs)`) and any arity -- far beyond 127 -- lower identically.
40//!   The runtime validates that the register holds a dynamic array.
41//! * **Closures capture explicitly.** [`Instruction::CreateClosure`] binds a
42//!   function together with a *captures-array* `Register`. On entry, a callee's
43//!   leading [`Function::capture_count`] registers are the captured cells,
44//!   followed by its [`Function::parameter_count`] parameters; both count as
45//!   definitely initialized on entry.
46//! * **Aggregate building blocks.** [`Instruction::ArrayPush`],
47//!   [`Instruction::ArrayExtend`] (iterable spread), [`Instruction::ObjectSpread`],
48//!   and [`Instruction::SetPrototype`] build non-empty arrays, objects, and
49//!   class prototype chains incrementally.
50//! * **Iteration protocol.** [`Instruction::GetIterator`] (with a closed
51//!   [`IteratorKind`]) and the two-write [`Instruction::IteratorNext`] model
52//!   `for`/`of`, `for`/`await`/`of`, `for`/`in`, destructuring, and array/call
53//!   spread against the ECMAScript iterator protocol.
54//! * **Environment access.** [`Instruction::LoadGlobal`],
55//!   [`Instruction::StoreGlobal`], [`Instruction::TypeOfGlobal`] (the last
56//!   models `typeof g` without throwing on an undeclared global),
57//!   [`Instruction::LoadThis`], [`Instruction::LoadArguments`], and
58//!   [`Instruction::LoadNewTarget`] name the ambient bindings a function body
59//!   observes.
60//! * **Modules.** [`Instruction::Import`] is dynamic and names a dependency by
61//!   string constant; static bindings and exports live in [`Program`] linkage
62//!   metadata so they identify live cells rather than activation registers.
63//! * **Regular expressions.** [`Instruction::CreateRegExp`] materializes a
64//!   `RegExp` from string-constant pattern and flags.
65//!
66//! ## Resume contract (async / generators)
67//!
68//! [`Instruction::Suspend`] `{ dst, src, resume }` is the single suspension
69//! primitive; [`FunctionFlags::is_async`] and [`FunctionFlags::is_generator`]
70//! select *how* a suspension is driven, but the wire form is identical:
71//!
72//! 1. The activation yields the value in `src` (a produced item for a
73//!    generator; an awaited operand for an async function) to its driver.
74//! 2. When the driver resumes the activation, control continues at `resume`
75//!    with the resumed value written to `dst` (the argument of `.next(v)` for a
76//!    generator; the settled result of the awaited value for `await`).
77//! 3. `resume` is a normal CFG successor and the only successor of `Suspend`, so
78//!    the definite-initialization witness treats every register live across a
79//!    suspension as it would across any join: `dst` is initialized on the
80//!    resume edge, and registers not provably initialized before the suspension
81//!    are not assumed initialized after it.
82//!
83//! A generator's completion is an ordinary [`Instruction::Return`]; an uncaught
84//! throw during drive routes to an enclosing [`ExceptionHandler`] exactly as in
85//! synchronous code.
86//!
87//! The wire format is a deliberate superset departure from the formal single
88//! seven-bit-group encoding: integer fields are canonical unsigned LEB128 `u32`
89//! (functions and modules may exceed 127 instructions, constants, registers,
90//! captures, and arguments), bounded by explicit decode and structural resource
91//! limits. Its round-trip guarantees -- totality over hostile bytes, canonical
92//! re-encoding, and decode/encode identity -- are fresh properties of this
93//! codec, proven by the tests in this module, not the Lean single-byte theorems
94//! (`decode_total`, `decode_encode_canonical`, `encode_decode_identity`), which
95//! remain scoped to the formal five-op wire.
96
97#![forbid(unsafe_code)]
98
99use std::error::Error;
100use std::fmt;
101use std::marker::PhantomData;
102
103/// `BMTBC\0\0\1`, matching `Bamti.Bytecode.magicBytes`.
104pub const MAGIC: [u8; 8] = [66, 77, 84, 66, 67, 0, 0, 1];
105/// The sole supported wire version.
106pub const FORMAT_VERSION: u8 = 3;
107
108/// Structural verify-time ceiling on a function's register count. Generous
109/// enough for real code yet bounds definite-initialization bitset allocation.
110pub const MAX_REGISTERS: u32 = 1 << 16;
111/// Structural verify-time ceiling on a function's instruction count.
112pub const MAX_INSTRUCTIONS: u32 = 1 << 20;
113/// Structural verify-time ceiling on a function's handler count.
114pub const MAX_HANDLERS: u32 = 1 << 16;
115/// Structural verify-time ceiling on a module's constant count.
116pub const MAX_CONSTANTS: u32 = 1 << 20;
117/// Structural verify-time ceiling on a module's function count.
118pub const MAX_FUNCTIONS: u32 = 1 << 20;
119
120/// Combined verify-time ceiling on the total definite-initialization fact
121/// storage a module may force the verifier to allocate, in 64-bit words. Each
122/// function needs `instructions * ceil(registers / 64)` words, so independent
123/// per-function maxima (`MAX_INSTRUCTIONS * (MAX_REGISTERS / 64)` alone is
124/// 2^30 words = 8 GiB, and modules hold many functions) would permit multi-GiB
125/// allocations from untrusted input. This bound caps that transient storage at
126/// `MAX_VERIFIER_FACTS_WORDS * 8` bytes (64 MiB) while remaining generous
127/// enough for large real modules with far more than 127 functions.
128pub const MAX_VERIFIER_FACTS_WORDS: u64 = 1 << 23;
129
130const CANONICAL_NAN_BITS: u64 = 0x7ff8_0000_0000_0000;
131const EXPONENT_MASK: u64 = 0x7ff0_0000_0000_0000;
132const FRACTION_MASK: u64 = 0x000f_ffff_ffff_ffff;
133
134macro_rules! index_type {
135    ($(#[$meta:meta])* $name:ident) => {
136        $(#[$meta])*
137        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
138        #[repr(transparent)]
139        pub struct $name(u32);
140
141        impl $name {
142            #[must_use]
143            pub const fn new(raw: u32) -> Self {
144                Self(raw)
145            }
146
147            #[must_use]
148            pub const fn get(self) -> u32 {
149                self.0
150            }
151        }
152    };
153}
154
155index_type!(
156    /// Index of a virtual register within a function's register file.
157    Register
158);
159index_type!(
160    /// Index into a module's constant pool.
161    ConstantId
162);
163index_type!(
164    /// Index into a module's function table.
165    FunctionId
166);
167index_type!(
168    /// A program counter: an instruction index within a function's code.
169    Pc
170);
171
172mod string;
173
174mod program;
175
176pub use string::{EcmaString, EcmaStringBuilder, IllFormedUtf16, InvalidCodePoint};
177
178pub use program::{
179    Binding, BindingId, BindingKind, Edge, EdgeId, EdgeKind, EdgeTarget, Export, ExportSource,
180    ModuleId, PROGRAM_MAGIC, PROGRAM_VERSION, Program, ProgramDecodeError, ProgramDecodeErrorKind,
181    ProgramDecodeLimits, ProgramLoadError, ProgramModule, ProgramVerifyError,
182    ProgramVerifyErrorKind, ResolvedExport, decode_program, decode_verified_program,
183};
184
185/// Canonical IEEE-754 bits. Every positive or negative NaN payload collapses
186/// to the unique arithmetic NaN from `Bamti.canonical_nan`.
187#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
188#[repr(transparent)]
189pub struct NumberBits(u64);
190
191impl NumberBits {
192    #[must_use]
193    pub const fn from_bits(bits: u64) -> Self {
194        if is_nan(bits) {
195            Self(CANONICAL_NAN_BITS)
196        } else {
197            Self(bits)
198        }
199    }
200
201    #[must_use]
202    pub const fn from_f64(value: f64) -> Self {
203        Self::from_bits(value.to_bits())
204    }
205
206    #[must_use]
207    pub const fn bits(self) -> u64 {
208        self.0
209    }
210
211    #[must_use]
212    pub const fn to_f64(self) -> f64 {
213        f64::from_bits(self.0)
214    }
215
216    const fn from_wire(bits: u64) -> Option<Self> {
217        if is_nan(bits) && bits != CANONICAL_NAN_BITS {
218            None
219        } else {
220            Some(Self(bits))
221        }
222    }
223}
224
225const fn is_nan(bits: u64) -> bool {
226    bits & EXPONENT_MASK == EXPONENT_MASK && bits & FRACTION_MASK != 0
227}
228
229/// A canonical BigInt literal in decimal text form. Constructed only through
230/// [`BigIntLiteral::new`], so a `BigIntLiteral` value is *always* a canonical
231/// decimal integer: optional leading `-`, no redundant leading zeros, no `-0`.
232#[derive(Clone, Debug, Eq, Hash, PartialEq)]
233pub struct BigIntLiteral(String);
234
235impl BigIntLiteral {
236    /// Parses canonical decimal text, rejecting empty text, non-digits,
237    /// redundant leading zeros, a bare sign, and negative zero.
238    #[must_use]
239    pub fn new(text: String) -> Option<Self> {
240        if is_canonical_bigint(&text) {
241            Some(Self(text))
242        } else {
243            None
244        }
245    }
246
247    #[must_use]
248    pub fn as_str(&self) -> &str {
249        &self.0
250    }
251}
252
253fn is_canonical_bigint(text: &str) -> bool {
254    let bytes = text.as_bytes();
255    if bytes.is_empty() {
256        return false;
257    }
258    let negative = bytes[0] == b'-';
259    let digits = if negative { &bytes[1..] } else { bytes };
260    if digits.is_empty() || !digits.iter().all(u8::is_ascii_digit) {
261        return false;
262    }
263    // No redundant leading zero (e.g. "007", "00").
264    if digits.len() > 1 && digits[0] == b'0' {
265        return false;
266    }
267    // No "-0".
268    !(negative && digits == b"0")
269}
270
271/// Persistable values. Heap references, holes, and uninitialized sentinels are
272/// intentionally absent because they are runtime identities/states. String
273/// constants back property keys, global names, private-name descriptions,
274/// regular-expression pattern/flags, module specifiers, and export names.
275#[derive(Clone, Debug, Eq, PartialEq)]
276pub enum Constant {
277    Number(NumberBits),
278    Int32(i32),
279    String(EcmaString),
280    Boolean(bool),
281    Null,
282    Undefined,
283    BigInt(BigIntLiteral),
284}
285
286/// Closed set of unary operators.
287#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
288pub enum UnaryOp {
289    Void,
290    TypeOf,
291    Plus,
292    Negate,
293    BitwiseNot,
294    LogicalNot,
295}
296
297impl UnaryOp {
298    const fn to_u8(self) -> u8 {
299        match self {
300            Self::Void => 0,
301            Self::TypeOf => 1,
302            Self::Plus => 2,
303            Self::Negate => 3,
304            Self::BitwiseNot => 4,
305            Self::LogicalNot => 5,
306        }
307    }
308
309    const fn from_u8(tag: u8) -> Option<Self> {
310        match tag {
311            0 => Some(Self::Void),
312            1 => Some(Self::TypeOf),
313            2 => Some(Self::Plus),
314            3 => Some(Self::Negate),
315            4 => Some(Self::BitwiseNot),
316            5 => Some(Self::LogicalNot),
317            _ => None,
318        }
319    }
320}
321
322/// Closed set of binary operators. [`BinaryOp::Add`] is the formal core's `Add`.
323#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
324pub enum BinaryOp {
325    Add,
326    Subtract,
327    Multiply,
328    Divide,
329    Remainder,
330    Exponent,
331    BitAnd,
332    BitOr,
333    BitXor,
334    ShiftLeft,
335    ShiftRight,
336    UnsignedShiftRight,
337    Equal,
338    NotEqual,
339    StrictEqual,
340    StrictNotEqual,
341    LessThan,
342    LessThanOrEqual,
343    GreaterThan,
344    GreaterThanOrEqual,
345    InstanceOf,
346    In,
347}
348
349impl BinaryOp {
350    const fn to_u8(self) -> u8 {
351        match self {
352            Self::Add => 0,
353            Self::Subtract => 1,
354            Self::Multiply => 2,
355            Self::Divide => 3,
356            Self::Remainder => 4,
357            Self::Exponent => 5,
358            Self::BitAnd => 6,
359            Self::BitOr => 7,
360            Self::BitXor => 8,
361            Self::ShiftLeft => 9,
362            Self::ShiftRight => 10,
363            Self::UnsignedShiftRight => 11,
364            Self::Equal => 12,
365            Self::NotEqual => 13,
366            Self::StrictEqual => 14,
367            Self::StrictNotEqual => 15,
368            Self::LessThan => 16,
369            Self::LessThanOrEqual => 17,
370            Self::GreaterThan => 18,
371            Self::GreaterThanOrEqual => 19,
372            Self::InstanceOf => 20,
373            Self::In => 21,
374        }
375    }
376
377    const fn from_u8(tag: u8) -> Option<Self> {
378        match tag {
379            0 => Some(Self::Add),
380            1 => Some(Self::Subtract),
381            2 => Some(Self::Multiply),
382            3 => Some(Self::Divide),
383            4 => Some(Self::Remainder),
384            5 => Some(Self::Exponent),
385            6 => Some(Self::BitAnd),
386            7 => Some(Self::BitOr),
387            8 => Some(Self::BitXor),
388            9 => Some(Self::ShiftLeft),
389            10 => Some(Self::ShiftRight),
390            11 => Some(Self::UnsignedShiftRight),
391            12 => Some(Self::Equal),
392            13 => Some(Self::NotEqual),
393            14 => Some(Self::StrictEqual),
394            15 => Some(Self::StrictNotEqual),
395            16 => Some(Self::LessThan),
396            17 => Some(Self::LessThanOrEqual),
397            18 => Some(Self::GreaterThan),
398            19 => Some(Self::GreaterThanOrEqual),
399            20 => Some(Self::InstanceOf),
400            21 => Some(Self::In),
401            _ => None,
402        }
403    }
404}
405
406/// Closed set of iterator acquisition protocols for [`Instruction::GetIterator`].
407#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
408pub enum IteratorKind {
409    /// `Symbol.iterator` (`for`/`of`, array/call spread, destructuring).
410    Sync,
411    /// `Symbol.asyncIterator` (`for`/`await`/`of`).
412    Async,
413    /// Enumerable string keys (`for`/`in`).
414    Keys,
415}
416
417impl IteratorKind {
418    const fn to_u8(self) -> u8 {
419        match self {
420            Self::Sync => 0,
421            Self::Async => 1,
422            Self::Keys => 2,
423        }
424    }
425
426    const fn from_u8(tag: u8) -> Option<Self> {
427        match tag {
428            0 => Some(Self::Sync),
429            1 => Some(Self::Async),
430            2 => Some(Self::Keys),
431            _ => None,
432        }
433    }
434}
435
436/// Which half of an accessor descriptor [`Instruction::DefineAccessor`] installs.
437/// A property with both a getter and a setter is defined by two instructions on
438/// the same key.
439#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
440pub enum AccessorKind {
441    Getter,
442    Setter,
443}
444
445impl AccessorKind {
446    const fn to_u8(self) -> u8 {
447        match self {
448            Self::Getter => 0,
449            Self::Setter => 1,
450        }
451    }
452
453    const fn from_u8(tag: u8) -> Option<Self> {
454        match tag {
455            0 => Some(Self::Getter),
456            1 => Some(Self::Setter),
457            _ => None,
458        }
459    }
460}
461
462/// The production instruction algebra. Opcodes 0..=36 are stable wire tags.
463#[derive(Clone, Copy, Debug, Eq, PartialEq)]
464pub enum Instruction {
465    /// Load a constant into `dst` (refines the formal `Load`).
466    LoadConst { dst: Register, constant: ConstantId },
467    /// Copy `src` into `dst`.
468    Move { dst: Register, src: Register },
469    /// Apply a unary operator to `operand`, writing `dst`.
470    Unary {
471        dst: Register,
472        op: UnaryOp,
473        operand: Register,
474    },
475    /// Apply a binary operator (generalizes the formal `Add`), writing `dst`.
476    Binary {
477        dst: Register,
478        op: BinaryOp,
479        left: Register,
480        right: Register,
481    },
482    /// Create a fresh empty object in `dst`.
483    CreateObject { dst: Register },
484    /// Create a fresh empty array in `dst`.
485    CreateArray { dst: Register },
486    /// Create a compiler-private one-element array cell seeded with the
487    /// runtime-only uninitialized sentinel.
488    CreateCell { dst: Register },
489    /// Materialize a closure over `function`, binding the captured cells held in
490    /// the array register `captures`, into `dst`. The captured cells initialize
491    /// the callee's leading `capture_count` registers.
492    CreateClosure {
493        dst: Register,
494        function: FunctionId,
495        captures: Register,
496    },
497    /// `dst = object[key]`, with the property key taken from a register.
498    GetProperty {
499        dst: Register,
500        object: Register,
501        key: Register,
502    },
503    /// `object[key] = value`, with the property key taken from a register.
504    SetProperty {
505        object: Register,
506        key: Register,
507        value: Register,
508    },
509    /// `dst = delete object[key]`, with the property key taken from a register.
510    DeleteProperty {
511        dst: Register,
512        object: Register,
513        key: Register,
514    },
515    /// Install a getter or setter `accessor` under `key` on `object`.
516    DefineAccessor {
517        object: Register,
518        key: Register,
519        accessor: Register,
520        kind: AccessorKind,
521    },
522    /// Call `callee` with receiver `this_value` and the dynamic argument array
523    /// in `arguments`, writing the result to `dst`. Spread and any arity lower
524    /// through the single arguments array.
525    Call {
526        dst: Register,
527        callee: Register,
528        this_value: Register,
529        arguments: Register,
530    },
531    /// Construct with `callee` and the dynamic argument array in `arguments`,
532    /// writing the instance to `dst`.
533    Construct {
534        dst: Register,
535        callee: Register,
536        arguments: Register,
537    },
538    /// `dst = globalThis[name]`, where `name` is a string constant. Throws a
539    /// `ReferenceError` at runtime for an undeclared global.
540    LoadGlobal { dst: Register, name: ConstantId },
541    /// `globalThis[name] = value`, where `name` is a string constant.
542    StoreGlobal { name: ConstantId, value: Register },
543    /// `dst = typeof globalThis[name]`, where `name` is a string constant.
544    /// Yields `"undefined"` for an undeclared global rather than throwing.
545    TypeOfGlobal { dst: Register, name: ConstantId },
546    /// Load the receiver binding `this` into `dst`.
547    LoadThis { dst: Register },
548    /// Load the `arguments` exotic object into `dst`.
549    LoadArguments { dst: Register },
550    /// Load `new.target` into `dst`.
551    LoadNewTarget { dst: Register },
552    /// Append `value` to the array in `array`.
553    ArrayPush { array: Register, value: Register },
554    /// Spread every element of `iterable` onto the end of the array in `array`.
555    ArrayExtend { array: Register, iterable: Register },
556    /// Copy the own enumerable properties of `source` onto `target`
557    /// (`{ ...source }`).
558    ObjectSpread { target: Register, source: Register },
559    /// Set the `[[Prototype]]` of `object` to `prototype`.
560    SetPrototype {
561        object: Register,
562        prototype: Register,
563    },
564    /// Create a fresh private name described by the string constant
565    /// `description`, writing it to `dst`. The result is used as a register key
566    /// for private-field access via the property instructions.
567    CreatePrivateName {
568        dst: Register,
569        description: ConstantId,
570    },
571    /// Create a `RegExp` from the string-constant `pattern` and `flags`.
572    CreateRegExp {
573        dst: Register,
574        pattern: ConstantId,
575        flags: ConstantId,
576    },
577    /// Acquire an iterator over `src` using protocol `kind`, writing it to `dst`.
578    GetIterator {
579        dst: Register,
580        src: Register,
581        kind: IteratorKind,
582    },
583    /// Advance `iterator` one step: write whether iteration is done to `done`
584    /// and the produced value to `value` (two writes).
585    IteratorNext {
586        done: Register,
587        value: Register,
588        iterator: Register,
589    },
590    /// Unconditional control transfer (identical to the formal `Jump`).
591    Jump { target: Pc },
592    /// Branch to `target` when `condition` is truthy, else fall through.
593    JumpIfTrue { condition: Register, target: Pc },
594    /// Branch to `target` when `condition` is falsy, else fall through.
595    JumpIfFalse { condition: Register, target: Pc },
596    /// Return `value` to the caller (terminator).
597    Return { value: Register },
598    /// Throw `value` (terminator; caught by an enclosing handler if any).
599    Throw { value: Register },
600    /// Yield `src` and resume at `resume`, receiving the resumed value in `dst`
601    /// (refines the formal `Suspend`). See the module-level resume contract.
602    Suspend {
603        dst: Register,
604        src: Register,
605        resume: Pc,
606    },
607    /// Import the module named by the string constant `specifier` into `dst`.
608    Import {
609        dst: Register,
610        specifier: ConstantId,
611    },
612    /// Export the local value in `src` under the string constant `name`.
613    Export { name: ConstantId, src: Register },
614    /// Terminate the current activation (identical to the formal `Halt`).
615    Halt,
616}
617
618impl Instruction {
619    /// Visits every register this instruction reads before executing.
620    fn visit_reads(self, mut visit: impl FnMut(Register)) {
621        match self {
622            Self::Move { src, .. } => visit(src),
623            Self::Unary { operand, .. } => visit(operand),
624            Self::Binary { left, right, .. } => {
625                visit(left);
626                visit(right);
627            }
628            Self::CreateClosure { captures, .. } => visit(captures),
629            Self::GetProperty { object, key, .. } | Self::DeleteProperty { object, key, .. } => {
630                visit(object);
631                visit(key);
632            }
633            Self::SetProperty { object, key, value } => {
634                visit(object);
635                visit(key);
636                visit(value);
637            }
638            Self::DefineAccessor {
639                object,
640                key,
641                accessor,
642                ..
643            } => {
644                visit(object);
645                visit(key);
646                visit(accessor);
647            }
648            Self::Call {
649                callee,
650                this_value,
651                arguments,
652                ..
653            } => {
654                visit(callee);
655                visit(this_value);
656                visit(arguments);
657            }
658            Self::Construct {
659                callee, arguments, ..
660            } => {
661                visit(callee);
662                visit(arguments);
663            }
664            Self::StoreGlobal { value, .. } => visit(value),
665            Self::ArrayPush { array, value } => {
666                visit(array);
667                visit(value);
668            }
669            Self::ArrayExtend { array, iterable } => {
670                visit(array);
671                visit(iterable);
672            }
673            Self::ObjectSpread { target, source } => {
674                visit(target);
675                visit(source);
676            }
677            Self::SetPrototype { object, prototype } => {
678                visit(object);
679                visit(prototype);
680            }
681            Self::GetIterator { src, .. } => visit(src),
682            Self::IteratorNext { iterator, .. } => visit(iterator),
683            Self::JumpIfTrue { condition, .. } | Self::JumpIfFalse { condition, .. } => {
684                visit(condition);
685            }
686            Self::Return { value } | Self::Throw { value } | Self::Export { src: value, .. } => {
687                visit(value);
688            }
689            Self::Suspend { src, .. } => visit(src),
690            Self::LoadConst { .. }
691            | Self::CreateObject { .. }
692            | Self::CreateArray { .. }
693            | Self::CreateCell { .. }
694            | Self::LoadGlobal { .. }
695            | Self::TypeOfGlobal { .. }
696            | Self::LoadThis { .. }
697            | Self::LoadArguments { .. }
698            | Self::LoadNewTarget { .. }
699            | Self::CreatePrivateName { .. }
700            | Self::CreateRegExp { .. }
701            | Self::Jump { .. }
702            | Self::Import { .. }
703            | Self::Halt => {}
704        }
705    }
706
707    /// Visits each register this instruction defines: zero, one, or two.
708    /// [`Instruction::IteratorNext`] is the sole two-write opcode.
709    fn visit_writes(self, mut visit: impl FnMut(Register)) {
710        match self {
711            Self::LoadConst { dst, .. }
712            | Self::Move { dst, .. }
713            | Self::Unary { dst, .. }
714            | Self::Binary { dst, .. }
715            | Self::CreateObject { dst }
716            | Self::CreateArray { dst }
717            | Self::CreateCell { dst }
718            | Self::CreateClosure { dst, .. }
719            | Self::GetProperty { dst, .. }
720            | Self::DeleteProperty { dst, .. }
721            | Self::Call { dst, .. }
722            | Self::Construct { dst, .. }
723            | Self::LoadGlobal { dst, .. }
724            | Self::TypeOfGlobal { dst, .. }
725            | Self::LoadThis { dst }
726            | Self::LoadArguments { dst }
727            | Self::LoadNewTarget { dst }
728            | Self::CreatePrivateName { dst, .. }
729            | Self::CreateRegExp { dst, .. }
730            | Self::GetIterator { dst, .. }
731            | Self::Suspend { dst, .. }
732            | Self::Import { dst, .. } => visit(dst),
733            Self::IteratorNext { done, value, .. } => {
734                visit(done);
735                visit(value);
736            }
737            Self::SetProperty { .. }
738            | Self::DefineAccessor { .. }
739            | Self::StoreGlobal { .. }
740            | Self::ArrayPush { .. }
741            | Self::ArrayExtend { .. }
742            | Self::ObjectSpread { .. }
743            | Self::SetPrototype { .. }
744            | Self::Export { .. }
745            | Self::Jump { .. }
746            | Self::JumpIfTrue { .. }
747            | Self::JumpIfFalse { .. }
748            | Self::Return { .. }
749            | Self::Throw { .. }
750            | Self::Halt => {}
751        }
752    }
753
754    /// Visits each normal-control successor. Terminators visit nothing, which
755    /// is exactly how reachable fall-off is forbidden: any non-terminator whose
756    /// fall-through `pc + 1` equals the code length fails target verification.
757    fn visit_successors(self, pc: u32, mut visit: impl FnMut(Pc)) {
758        match self {
759            Self::Jump { target } => visit(target),
760            Self::JumpIfTrue { target, .. } | Self::JumpIfFalse { target, .. } => {
761                visit(target);
762                visit(Pc::new(pc + 1));
763            }
764            Self::Suspend { resume, .. } => visit(resume),
765            Self::Return { .. } | Self::Throw { .. } | Self::Halt => {}
766            Self::LoadConst { .. }
767            | Self::Move { .. }
768            | Self::Unary { .. }
769            | Self::Binary { .. }
770            | Self::CreateObject { .. }
771            | Self::CreateArray { .. }
772            | Self::CreateCell { .. }
773            | Self::CreateClosure { .. }
774            | Self::GetProperty { .. }
775            | Self::SetProperty { .. }
776            | Self::DeleteProperty { .. }
777            | Self::DefineAccessor { .. }
778            | Self::Call { .. }
779            | Self::Construct { .. }
780            | Self::LoadGlobal { .. }
781            | Self::StoreGlobal { .. }
782            | Self::TypeOfGlobal { .. }
783            | Self::LoadThis { .. }
784            | Self::LoadArguments { .. }
785            | Self::LoadNewTarget { .. }
786            | Self::ArrayPush { .. }
787            | Self::ArrayExtend { .. }
788            | Self::ObjectSpread { .. }
789            | Self::SetPrototype { .. }
790            | Self::CreatePrivateName { .. }
791            | Self::CreateRegExp { .. }
792            | Self::GetIterator { .. }
793            | Self::IteratorNext { .. }
794            | Self::Import { .. }
795            | Self::Export { .. } => visit(Pc::new(pc + 1)),
796        }
797    }
798}
799
800/// A half-open protected range `[start, end)`, its handler entry PC, and the
801/// register that receives the thrown value on dispatch.
802#[derive(Clone, Copy, Debug, Eq, PartialEq)]
803pub struct ExceptionHandler {
804    pub start: Pc,
805    pub end: Pc,
806    pub handler: Pc,
807    pub catch_register: Register,
808}
809
810/// Compact function flags record.
811#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
812pub struct FunctionFlags {
813    pub is_async: bool,
814    pub is_generator: bool,
815}
816
817impl FunctionFlags {
818    const ASYNC: u8 = 0b01;
819    const GENERATOR: u8 = 0b10;
820    const KNOWN: u8 = Self::ASYNC | Self::GENERATOR;
821
822    const fn to_bits(self) -> u8 {
823        let mut bits = 0;
824        if self.is_async {
825            bits |= Self::ASYNC;
826        }
827        if self.is_generator {
828            bits |= Self::GENERATOR;
829        }
830        bits
831    }
832
833    const fn from_bits(bits: u8) -> Option<Self> {
834        if bits & !Self::KNOWN != 0 {
835            return None;
836        }
837        Some(Self {
838            is_async: bits & Self::ASYNC != 0,
839            is_generator: bits & Self::GENERATOR != 0,
840        })
841    }
842}
843
844/// An explicit function record: metadata, code, and handlers. On entry the
845/// leading `capture_count` registers hold the closure's captured cells and the
846/// next `parameter_count` registers hold the parameters; all `capture_count +
847/// parameter_count` are initialized on entry.
848#[derive(Clone, Debug, Eq, PartialEq)]
849pub struct Function {
850    name: Option<ConstantId>,
851    capture_count: u32,
852    parameter_count: u32,
853    register_count: u32,
854    flags: FunctionFlags,
855    code: Vec<Instruction>,
856    handlers: Vec<ExceptionHandler>,
857}
858
859impl Function {
860    #[must_use]
861    pub fn new(
862        name: Option<ConstantId>,
863        capture_count: u32,
864        parameter_count: u32,
865        register_count: u32,
866        flags: FunctionFlags,
867        code: Vec<Instruction>,
868        handlers: Vec<ExceptionHandler>,
869    ) -> Self {
870        Self {
871            name,
872            capture_count,
873            parameter_count,
874            register_count,
875            flags,
876            code,
877            handlers,
878        }
879    }
880
881    #[must_use]
882    pub const fn name(&self) -> Option<ConstantId> {
883        self.name
884    }
885
886    #[must_use]
887    pub const fn capture_count(&self) -> u32 {
888        self.capture_count
889    }
890
891    #[must_use]
892    pub const fn parameter_count(&self) -> u32 {
893        self.parameter_count
894    }
895
896    #[must_use]
897    pub const fn register_count(&self) -> u32 {
898        self.register_count
899    }
900
901    #[must_use]
902    pub const fn flags(&self) -> FunctionFlags {
903        self.flags
904    }
905
906    #[must_use]
907    pub fn code(&self) -> &[Instruction] {
908        &self.code
909    }
910
911    #[must_use]
912    pub fn handlers(&self) -> &[ExceptionHandler] {
913        &self.handlers
914    }
915
916    /// The count of registers initialized on entry: captures followed by
917    /// parameters. Saturating, so it never wraps for hostile metadata (the
918    /// verifier separately rejects a sum exceeding `register_count`).
919    const fn entry_initialized(&self) -> u32 {
920        self.capture_count.saturating_add(self.parameter_count)
921    }
922}
923
924/// Marker for decoded or newly assembled, untrusted bytecode.
925#[derive(Clone, Copy, Debug, Eq, PartialEq)]
926pub struct Unverified {
927    _private: (),
928}
929
930/// Unforgeable marker proving verification completed.
931#[derive(Clone, Copy, Debug, Eq, PartialEq)]
932pub struct Verified {
933    _private: (),
934}
935
936/// Explicit constant pool, function table, and entry function, with typestate.
937#[derive(Clone, Debug, Eq, PartialEq)]
938pub struct Module<State = Unverified> {
939    constants: Vec<Constant>,
940    functions: Vec<Function>,
941    entry: FunctionId,
942    certificates: Vec<Certificate>,
943    state: PhantomData<State>,
944}
945
946impl<State> Module<State> {
947    #[must_use]
948    pub fn constants(&self) -> &[Constant] {
949        &self.constants
950    }
951
952    #[must_use]
953    pub fn functions(&self) -> &[Function] {
954        &self.functions
955    }
956
957    #[must_use]
958    pub const fn entry(&self) -> FunctionId {
959        self.entry
960    }
961}
962
963impl Module<Unverified> {
964    #[must_use]
965    pub fn new(constants: Vec<Constant>, functions: Vec<Function>, entry: FunctionId) -> Self {
966        Self {
967            constants,
968            functions,
969            entry,
970            certificates: Vec::new(),
971            state: PhantomData,
972        }
973    }
974
975    /// Consumes untrusted structure and is the only route to `Module<Verified>`.
976    ///
977    /// # Errors
978    /// Returns the first structural violation found (bounds, references, CFG,
979    /// handlers, or definite initialization).
980    pub fn verify(self) -> Result<Module<Verified>, VerifyError> {
981        verify_module(self)
982    }
983}
984
985impl Module<Verified> {
986    #[must_use]
987    pub fn certificate(&self, function: FunctionId) -> Option<&Certificate> {
988        self.certificates.get(function.get() as usize)
989    }
990
991    /// Heap bytes retained by this module's verification certificates.
992    #[must_use]
993    pub fn verification_bytes(&self) -> usize {
994        self.certificates.iter().fold(0usize, |bytes, certificate| {
995            bytes.saturating_add(certificate.retained_bytes())
996        })
997    }
998
999    /// Emits one deterministic canonical representation.
1000    #[must_use]
1001    pub fn encode(&self) -> Vec<u8> {
1002        let mut output = Vec::new();
1003        output.extend_from_slice(&MAGIC);
1004        output.push(FORMAT_VERSION);
1005        write_u32(self.constants.len() as u32, &mut output);
1006        for constant in &self.constants {
1007            encode_constant(constant, &mut output);
1008        }
1009        write_u32(self.functions.len() as u32, &mut output);
1010        write_u32(self.entry.get(), &mut output);
1011        for function in &self.functions {
1012            encode_function(function, &mut output);
1013        }
1014        output
1015    }
1016}
1017
1018/// Forward-dataflow definite-initialization facts, mirroring Lean's
1019/// `Certificate.facts`. Construction is private, so certificates are unforgeable.
1020#[derive(Clone, Debug, Eq, PartialEq)]
1021pub struct Certificate {
1022    register_count: u32,
1023    facts: Vec<RegisterSet>,
1024}
1025
1026impl Certificate {
1027    fn retained_bytes(&self) -> usize {
1028        self.facts.iter().fold(
1029            std::mem::size_of::<Self>().saturating_add(
1030                self.facts
1031                    .len()
1032                    .saturating_mul(std::mem::size_of::<RegisterSet>()),
1033            ),
1034            |bytes, facts| {
1035                bytes.saturating_add(facts.words.len().saturating_mul(std::mem::size_of::<u64>()))
1036            },
1037        )
1038    }
1039
1040    #[must_use]
1041    pub fn instruction_count(&self) -> usize {
1042        self.facts.len()
1043    }
1044
1045    /// Whether `register` is definitely initialized before executing `pc`.
1046    /// Total for every wrapper value: out-of-range registers or PCs yield
1047    /// `None` rather than panicking.
1048    #[must_use]
1049    pub fn initialized_before(&self, pc: Pc, register: Register) -> Option<bool> {
1050        if register.get() >= self.register_count {
1051            return None;
1052        }
1053        self.facts
1054            .get(pc.get() as usize)
1055            .map(|facts| facts.contains(register))
1056    }
1057}
1058
1059/// A dynamically sized register bitset covering a function's register file.
1060#[derive(Clone, Debug, Eq, PartialEq)]
1061struct RegisterSet {
1062    words: Box<[u64]>,
1063}
1064
1065impl RegisterSet {
1066    fn words_for(register_count: u32) -> usize {
1067        (register_count as usize).div_ceil(64)
1068    }
1069
1070    fn empty(register_count: u32) -> Self {
1071        Self {
1072            words: vec![0; Self::words_for(register_count)].into_boxed_slice(),
1073        }
1074    }
1075
1076    fn full(register_count: u32) -> Self {
1077        let words_len = Self::words_for(register_count);
1078        let mut words = vec![u64::MAX; words_len];
1079        let remainder = register_count % 64;
1080        if remainder != 0 && words_len != 0 {
1081            words[words_len - 1] = (1_u64 << remainder) - 1;
1082        }
1083        Self {
1084            words: words.into_boxed_slice(),
1085        }
1086    }
1087
1088    fn contains(&self, register: Register) -> bool {
1089        let index = register.get() as usize;
1090        self.words
1091            .get(index / 64)
1092            .is_some_and(|word| word & (1_u64 << (index % 64)) != 0)
1093    }
1094
1095    fn insert(&mut self, register: Register) {
1096        let index = register.get() as usize;
1097        if let Some(word) = self.words.get_mut(index / 64) {
1098            *word |= 1_u64 << (index % 64);
1099        }
1100    }
1101
1102    fn insert_prefix(&mut self, count: u32) {
1103        for register in 0..count {
1104            self.insert(Register::new(register));
1105        }
1106    }
1107
1108    fn intersect(&mut self, other: &Self) -> bool {
1109        let mut changed = false;
1110        for (slot, mask) in self.words.iter_mut().zip(other.words.iter()) {
1111            let next = *slot & *mask;
1112            if next != *slot {
1113                changed = true;
1114                *slot = next;
1115            }
1116        }
1117        changed
1118    }
1119}
1120
1121/// A structural verification failure, located at a function and/or instruction.
1122#[derive(Clone, Debug, Eq, PartialEq)]
1123pub struct VerifyError {
1124    pub function: Option<FunctionId>,
1125    pub instruction: Option<Pc>,
1126    pub kind: VerifyErrorKind,
1127}
1128
1129#[derive(Clone, Debug, Eq, PartialEq)]
1130pub enum VerifyErrorKind {
1131    EmptyModule,
1132    VerifierWorkLimitExceeded {
1133        work: u64,
1134        limit: u64,
1135    },
1136    TooManyConstants {
1137        count: usize,
1138    },
1139    TooManyFunctions {
1140        count: usize,
1141    },
1142    EntryFunctionOutOfBounds {
1143        entry: u32,
1144        function_count: usize,
1145    },
1146    EmptyFunction,
1147    TooManyInstructions {
1148        count: usize,
1149    },
1150    TooManyHandlers {
1151        count: usize,
1152    },
1153    RegisterCountOutOfBounds {
1154        count: u32,
1155    },
1156    ParameterCountExceedsRegisters {
1157        parameter_count: u32,
1158        register_count: u32,
1159    },
1160    EntryRegistersExceedRegisterCount {
1161        capture_count: u32,
1162        parameter_count: u32,
1163        register_count: u32,
1164    },
1165    FunctionNameOutOfBounds {
1166        constant: u32,
1167    },
1168    FunctionNameNotString {
1169        constant: ConstantId,
1170    },
1171    RegisterOutOfBounds {
1172        register: Register,
1173        register_count: u32,
1174    },
1175    ConstantOutOfBounds {
1176        constant: ConstantId,
1177        constant_count: usize,
1178    },
1179    StringConstantExpected {
1180        constant: ConstantId,
1181    },
1182    FunctionReferenceOutOfBounds {
1183        function: FunctionId,
1184        function_count: usize,
1185    },
1186    JumpOutOfBounds {
1187        target: u32,
1188        instruction_count: usize,
1189    },
1190    InvalidHandlerBounds {
1191        handler: usize,
1192        start: u32,
1193        end: u32,
1194        target: u32,
1195    },
1196    HandlerCatchRegisterOutOfBounds {
1197        handler: usize,
1198        register: Register,
1199        register_count: u32,
1200    },
1201    HandlersPartiallyOverlap {
1202        left: usize,
1203        right: usize,
1204    },
1205    ReadBeforeWrite {
1206        register: Register,
1207    },
1208}
1209
1210impl fmt::Display for VerifyError {
1211    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1212        if let Some(function) = self.function {
1213            write!(formatter, "function {}", function.get())?;
1214            if let Some(instruction) = self.instruction {
1215                write!(formatter, " instruction {}", instruction.get())?;
1216            }
1217            formatter.write_str(": ")?;
1218        }
1219        match &self.kind {
1220            VerifyErrorKind::EmptyModule => formatter.write_str("module has no functions"),
1221            VerifyErrorKind::VerifierWorkLimitExceeded { work, limit } => write!(
1222                formatter,
1223                "verifier fact allocation {work} words exceeds the {limit}-word limit"
1224            ),
1225            VerifyErrorKind::TooManyConstants { count } => {
1226                write!(formatter, "{count} constants exceed the structural limit")
1227            }
1228            VerifyErrorKind::TooManyFunctions { count } => {
1229                write!(formatter, "{count} functions exceed the structural limit")
1230            }
1231            VerifyErrorKind::EntryFunctionOutOfBounds {
1232                entry,
1233                function_count,
1234            } => write!(
1235                formatter,
1236                "entry function {entry} is outside {function_count} functions"
1237            ),
1238            VerifyErrorKind::EmptyFunction => {
1239                formatter.write_str("function has no entry instruction")
1240            }
1241            VerifyErrorKind::TooManyInstructions { count } => {
1242                write!(
1243                    formatter,
1244                    "{count} instructions exceed the structural limit"
1245                )
1246            }
1247            VerifyErrorKind::TooManyHandlers { count } => {
1248                write!(formatter, "{count} handlers exceed the structural limit")
1249            }
1250            VerifyErrorKind::RegisterCountOutOfBounds { count } => {
1251                write!(
1252                    formatter,
1253                    "register count {count} exceeds the structural limit"
1254                )
1255            }
1256            VerifyErrorKind::ParameterCountExceedsRegisters {
1257                parameter_count,
1258                register_count,
1259            } => write!(
1260                formatter,
1261                "parameter count {parameter_count} exceeds register count {register_count}"
1262            ),
1263            VerifyErrorKind::EntryRegistersExceedRegisterCount {
1264                capture_count,
1265                parameter_count,
1266                register_count,
1267            } => write!(
1268                formatter,
1269                "capture count {capture_count} plus parameter count {parameter_count} exceeds \
1270                 register count {register_count}"
1271            ),
1272            VerifyErrorKind::FunctionNameOutOfBounds { constant } => {
1273                write!(
1274                    formatter,
1275                    "function name constant {constant} is out of bounds"
1276                )
1277            }
1278            VerifyErrorKind::FunctionNameNotString { constant } => write!(
1279                formatter,
1280                "function name constant {} is not a string",
1281                constant.get()
1282            ),
1283            VerifyErrorKind::RegisterOutOfBounds {
1284                register,
1285                register_count,
1286            } => write!(
1287                formatter,
1288                "register {} is outside register count {register_count}",
1289                register.get()
1290            ),
1291            VerifyErrorKind::ConstantOutOfBounds {
1292                constant,
1293                constant_count,
1294            } => write!(
1295                formatter,
1296                "constant {} is outside {constant_count} constants",
1297                constant.get()
1298            ),
1299            VerifyErrorKind::StringConstantExpected { constant } => {
1300                write!(formatter, "constant {} must be a string", constant.get())
1301            }
1302            VerifyErrorKind::FunctionReferenceOutOfBounds {
1303                function,
1304                function_count,
1305            } => write!(
1306                formatter,
1307                "function reference {} is outside {function_count} functions",
1308                function.get()
1309            ),
1310            VerifyErrorKind::JumpOutOfBounds {
1311                target,
1312                instruction_count,
1313            } => write!(
1314                formatter,
1315                "target {target} is not one of {instruction_count} instruction boundaries"
1316            ),
1317            VerifyErrorKind::InvalidHandlerBounds {
1318                handler,
1319                start,
1320                end,
1321                target,
1322            } => write!(
1323                formatter,
1324                "handler {handler} has invalid range {start}..{end} or target {target}"
1325            ),
1326            VerifyErrorKind::HandlerCatchRegisterOutOfBounds {
1327                handler,
1328                register,
1329                register_count,
1330            } => write!(
1331                formatter,
1332                "handler {handler} catch register {} is outside register count {register_count}",
1333                register.get()
1334            ),
1335            VerifyErrorKind::HandlersPartiallyOverlap { left, right } => write!(
1336                formatter,
1337                "handlers {left} and {right} partially overlap instead of nesting"
1338            ),
1339            VerifyErrorKind::ReadBeforeWrite { register } => write!(
1340                formatter,
1341                "register {} may be read before initialization",
1342                register.get()
1343            ),
1344        }
1345    }
1346}
1347
1348impl Error for VerifyError {}
1349
1350fn module_error(kind: VerifyErrorKind) -> VerifyError {
1351    VerifyError {
1352        function: None,
1353        instruction: None,
1354        kind,
1355    }
1356}
1357
1358fn function_error(function: usize, kind: VerifyErrorKind) -> VerifyError {
1359    VerifyError {
1360        function: Some(FunctionId::new(function as u32)),
1361        instruction: None,
1362        kind,
1363    }
1364}
1365
1366fn instruction_error(function: usize, pc: usize, kind: VerifyErrorKind) -> VerifyError {
1367    VerifyError {
1368        function: Some(FunctionId::new(function as u32)),
1369        instruction: Some(Pc::new(pc as u32)),
1370        kind,
1371    }
1372}
1373
1374fn verify_module(module: Module<Unverified>) -> Result<Module<Verified>, VerifyError> {
1375    if module.functions.is_empty() {
1376        return Err(module_error(VerifyErrorKind::EmptyModule));
1377    }
1378    if module.constants.len() as u64 > u64::from(MAX_CONSTANTS) {
1379        return Err(module_error(VerifyErrorKind::TooManyConstants {
1380            count: module.constants.len(),
1381        }));
1382    }
1383    if module.functions.len() as u64 > u64::from(MAX_FUNCTIONS) {
1384        return Err(module_error(VerifyErrorKind::TooManyFunctions {
1385            count: module.functions.len(),
1386        }));
1387    }
1388    if module.entry.get() as usize >= module.functions.len() {
1389        return Err(module_error(VerifyErrorKind::EntryFunctionOutOfBounds {
1390            entry: module.entry.get(),
1391            function_count: module.functions.len(),
1392        }));
1393    }
1394
1395    // Combined, overflow-safe cap on the definite-initialization fact storage
1396    // this module can force the verifier to allocate, checked before any
1397    // per-function allocation. Saturating arithmetic can only tighten the
1398    // bound, so it never masks an over-limit module. The comparison is
1399    // strict `>`: facts-work equal to the cap is the largest permitted
1400    // allocation. Acceptance at exactly the cap is intentionally untested
1401    // because it forces the full 64 MiB `facts` allocation; the boundary is
1402    // pinned from the rejection side only (see `verifier_rejects_facts_work_
1403    // above_cap`).
1404    let mut total_facts_words: u64 = 0;
1405    for function in &module.functions {
1406        let words = RegisterSet::words_for(function.register_count) as u64;
1407        let function_words = (function.code.len() as u64).saturating_mul(words);
1408        total_facts_words = total_facts_words.saturating_add(function_words);
1409        if total_facts_words > MAX_VERIFIER_FACTS_WORDS {
1410            return Err(module_error(VerifyErrorKind::VerifierWorkLimitExceeded {
1411                work: total_facts_words,
1412                limit: MAX_VERIFIER_FACTS_WORDS,
1413            }));
1414        }
1415    }
1416
1417    let mut certificates = Vec::with_capacity(module.functions.len());
1418    for (index, function) in module.functions.iter().enumerate() {
1419        certificates.push(verify_function(&module, index, function)?);
1420    }
1421
1422    Ok(Module {
1423        constants: module.constants,
1424        functions: module.functions,
1425        entry: module.entry,
1426        certificates,
1427        state: PhantomData,
1428    })
1429}
1430
1431fn verify_function(
1432    module: &Module<Unverified>,
1433    function_index: usize,
1434    function: &Function,
1435) -> Result<Certificate, VerifyError> {
1436    if function.code.is_empty() {
1437        return Err(function_error(
1438            function_index,
1439            VerifyErrorKind::EmptyFunction,
1440        ));
1441    }
1442    if function.code.len() as u64 > u64::from(MAX_INSTRUCTIONS) {
1443        return Err(function_error(
1444            function_index,
1445            VerifyErrorKind::TooManyInstructions {
1446                count: function.code.len(),
1447            },
1448        ));
1449    }
1450    if function.handlers.len() as u64 > u64::from(MAX_HANDLERS) {
1451        return Err(function_error(
1452            function_index,
1453            VerifyErrorKind::TooManyHandlers {
1454                count: function.handlers.len(),
1455            },
1456        ));
1457    }
1458    if function.register_count > MAX_REGISTERS {
1459        return Err(function_error(
1460            function_index,
1461            VerifyErrorKind::RegisterCountOutOfBounds {
1462                count: function.register_count,
1463            },
1464        ));
1465    }
1466    if function.parameter_count > function.register_count {
1467        return Err(function_error(
1468            function_index,
1469            VerifyErrorKind::ParameterCountExceedsRegisters {
1470                parameter_count: function.parameter_count,
1471                register_count: function.register_count,
1472            },
1473        ));
1474    }
1475    // Captures and parameters share the leading register file; their sum must
1476    // fit. Checked with u64 so hostile counts near u32::MAX cannot wrap.
1477    if u64::from(function.capture_count) + u64::from(function.parameter_count)
1478        > u64::from(function.register_count)
1479    {
1480        return Err(function_error(
1481            function_index,
1482            VerifyErrorKind::EntryRegistersExceedRegisterCount {
1483                capture_count: function.capture_count,
1484                parameter_count: function.parameter_count,
1485                register_count: function.register_count,
1486            },
1487        ));
1488    }
1489    verify_function_name(module, function_index, function)?;
1490    verify_handlers(function_index, function)?;
1491    for (pc, instruction) in function.code.iter().copied().enumerate() {
1492        verify_instruction(module, function_index, function, pc, instruction)?;
1493    }
1494    definite_initialization(function_index, function)
1495}
1496
1497fn verify_function_name(
1498    module: &Module<Unverified>,
1499    function_index: usize,
1500    function: &Function,
1501) -> Result<(), VerifyError> {
1502    let Some(name) = function.name else {
1503        return Ok(());
1504    };
1505    let Some(constant) = module.constants.get(name.get() as usize) else {
1506        return Err(function_error(
1507            function_index,
1508            VerifyErrorKind::FunctionNameOutOfBounds {
1509                constant: name.get(),
1510            },
1511        ));
1512    };
1513    if !matches!(constant, Constant::String(_)) {
1514        return Err(function_error(
1515            function_index,
1516            VerifyErrorKind::FunctionNameNotString { constant: name },
1517        ));
1518    }
1519    Ok(())
1520}
1521
1522fn verify_handlers(function_index: usize, function: &Function) -> Result<(), VerifyError> {
1523    let code_len = function.code.len();
1524    for (index, handler) in function.handlers.iter().copied().enumerate() {
1525        if handler.start.get() >= handler.end.get()
1526            || handler.end.get() as usize > code_len
1527            || handler.handler.get() as usize >= code_len
1528        {
1529            return Err(function_error(
1530                function_index,
1531                VerifyErrorKind::InvalidHandlerBounds {
1532                    handler: index,
1533                    start: handler.start.get(),
1534                    end: handler.end.get(),
1535                    target: handler.handler.get(),
1536                },
1537            ));
1538        }
1539        if handler.catch_register.get() >= function.register_count {
1540            return Err(function_error(
1541                function_index,
1542                VerifyErrorKind::HandlerCatchRegisterOutOfBounds {
1543                    handler: index,
1544                    register: handler.catch_register,
1545                    register_count: function.register_count,
1546                },
1547            ));
1548        }
1549    }
1550    // Deterministic O(n log n) laminar-family check over half-open ranges.
1551    // Sort handler indices by (start ascending, end descending) so an
1552    // enclosing range is always visited before any range it contains, then
1553    // sweep with a stack of open ancestor ends. `function.handlers` is never
1554    // mutated, so its original order (and the reported indices) is preserved.
1555    let mut order: Vec<usize> = (0..function.handlers.len()).collect();
1556    order.sort_by(|&left, &right| {
1557        let a = function.handlers[left];
1558        let b = function.handlers[right];
1559        a.start
1560            .get()
1561            .cmp(&b.start.get())
1562            .then_with(|| b.end.get().cmp(&a.end.get()))
1563    });
1564    let mut open: Vec<(u32, usize)> = Vec::new();
1565    for &index in &order {
1566        let range = function.handlers[index];
1567        let start = range.start.get();
1568        let end = range.end.get();
1569        // Disjoint and sibling ranges (including adjacency `[a, b) + [b, c)`)
1570        // close once the sweep passes their end.
1571        while open.last().is_some_and(|&(open_end, _)| open_end <= start) {
1572            open.pop();
1573        }
1574        if let Some(&(parent_end, parent_index)) = open.last() {
1575            // The current range shares an open ancestor. It must nest fully
1576            // inside it; extending past the ancestor's end is a partial overlap
1577            // (or a crossing), never proper nesting.
1578            if end > parent_end {
1579                let (left, right) = if parent_index < index {
1580                    (parent_index, index)
1581                } else {
1582                    (index, parent_index)
1583                };
1584                return Err(function_error(
1585                    function_index,
1586                    VerifyErrorKind::HandlersPartiallyOverlap { left, right },
1587                ));
1588            }
1589        }
1590        open.push((end, index));
1591    }
1592    Ok(())
1593}
1594
1595fn verify_instruction(
1596    module: &Module<Unverified>,
1597    function_index: usize,
1598    function: &Function,
1599    pc: usize,
1600    instruction: Instruction,
1601) -> Result<(), VerifyError> {
1602    let register_count = function.register_count;
1603    let constant_count = module.constants.len();
1604    let function_count = module.functions.len();
1605    let code_len = function.code.len();
1606
1607    let check_register = |register: Register| -> Result<(), VerifyError> {
1608        if register.get() >= register_count {
1609            Err(instruction_error(
1610                function_index,
1611                pc,
1612                VerifyErrorKind::RegisterOutOfBounds {
1613                    register,
1614                    register_count,
1615                },
1616            ))
1617        } else {
1618            Ok(())
1619        }
1620    };
1621    let check_constant = |constant: ConstantId| -> Result<(), VerifyError> {
1622        if constant.get() as usize >= constant_count {
1623            Err(instruction_error(
1624                function_index,
1625                pc,
1626                VerifyErrorKind::ConstantOutOfBounds {
1627                    constant,
1628                    constant_count,
1629                },
1630            ))
1631        } else {
1632            Ok(())
1633        }
1634    };
1635    // A string constant reference: property/global/private/regexp/export/import
1636    // names must all resolve to a `Constant::String`.
1637    let check_string_constant = |constant: ConstantId| -> Result<(), VerifyError> {
1638        check_constant(constant)?;
1639        if matches!(
1640            module.constants[constant.get() as usize],
1641            Constant::String(_)
1642        ) {
1643            Ok(())
1644        } else {
1645            Err(instruction_error(
1646                function_index,
1647                pc,
1648                VerifyErrorKind::StringConstantExpected { constant },
1649            ))
1650        }
1651    };
1652
1653    match instruction {
1654        Instruction::LoadConst { dst, constant } => {
1655            check_register(dst)?;
1656            check_constant(constant)?;
1657        }
1658        Instruction::Move { dst, src } => {
1659            check_register(dst)?;
1660            check_register(src)?;
1661        }
1662        Instruction::Unary { dst, operand, .. } => {
1663            check_register(dst)?;
1664            check_register(operand)?;
1665        }
1666        Instruction::Binary {
1667            dst, left, right, ..
1668        } => {
1669            check_register(dst)?;
1670            check_register(left)?;
1671            check_register(right)?;
1672        }
1673        Instruction::CreateObject { dst }
1674        | Instruction::CreateArray { dst }
1675        | Instruction::CreateCell { dst } => {
1676            check_register(dst)?;
1677        }
1678        Instruction::CreateClosure {
1679            dst,
1680            function: reference,
1681            captures,
1682        } => {
1683            check_register(dst)?;
1684            check_register(captures)?;
1685            if reference.get() as usize >= function_count {
1686                return Err(instruction_error(
1687                    function_index,
1688                    pc,
1689                    VerifyErrorKind::FunctionReferenceOutOfBounds {
1690                        function: reference,
1691                        function_count,
1692                    },
1693                ));
1694            }
1695        }
1696        Instruction::GetProperty { dst, object, key } => {
1697            check_register(dst)?;
1698            check_register(object)?;
1699            check_register(key)?;
1700        }
1701        Instruction::SetProperty { object, key, value } => {
1702            check_register(object)?;
1703            check_register(key)?;
1704            check_register(value)?;
1705        }
1706        Instruction::DeleteProperty { dst, object, key } => {
1707            check_register(dst)?;
1708            check_register(object)?;
1709            check_register(key)?;
1710        }
1711        Instruction::DefineAccessor {
1712            object,
1713            key,
1714            accessor,
1715            ..
1716        } => {
1717            check_register(object)?;
1718            check_register(key)?;
1719            check_register(accessor)?;
1720        }
1721        Instruction::Call {
1722            dst,
1723            callee,
1724            this_value,
1725            arguments,
1726        } => {
1727            check_register(dst)?;
1728            check_register(callee)?;
1729            check_register(this_value)?;
1730            check_register(arguments)?;
1731        }
1732        Instruction::Construct {
1733            dst,
1734            callee,
1735            arguments,
1736        } => {
1737            check_register(dst)?;
1738            check_register(callee)?;
1739            check_register(arguments)?;
1740        }
1741        Instruction::LoadGlobal { dst, name } | Instruction::TypeOfGlobal { dst, name } => {
1742            check_register(dst)?;
1743            check_string_constant(name)?;
1744        }
1745        Instruction::StoreGlobal { name, value } => {
1746            check_string_constant(name)?;
1747            check_register(value)?;
1748        }
1749        Instruction::LoadThis { dst }
1750        | Instruction::LoadArguments { dst }
1751        | Instruction::LoadNewTarget { dst } => {
1752            check_register(dst)?;
1753        }
1754        Instruction::ArrayPush { array, value } => {
1755            check_register(array)?;
1756            check_register(value)?;
1757        }
1758        Instruction::ArrayExtend { array, iterable } => {
1759            check_register(array)?;
1760            check_register(iterable)?;
1761        }
1762        Instruction::ObjectSpread { target, source } => {
1763            check_register(target)?;
1764            check_register(source)?;
1765        }
1766        Instruction::SetPrototype { object, prototype } => {
1767            check_register(object)?;
1768            check_register(prototype)?;
1769        }
1770        Instruction::CreatePrivateName { dst, description } => {
1771            check_register(dst)?;
1772            check_string_constant(description)?;
1773        }
1774        Instruction::CreateRegExp {
1775            dst,
1776            pattern,
1777            flags,
1778        } => {
1779            check_register(dst)?;
1780            check_string_constant(pattern)?;
1781            check_string_constant(flags)?;
1782        }
1783        Instruction::GetIterator { dst, src, .. } => {
1784            check_register(dst)?;
1785            check_register(src)?;
1786        }
1787        Instruction::IteratorNext {
1788            done,
1789            value,
1790            iterator,
1791        } => {
1792            check_register(done)?;
1793            check_register(value)?;
1794            check_register(iterator)?;
1795        }
1796        Instruction::Jump { target } => verify_target(function_index, pc, target, code_len)?,
1797        Instruction::JumpIfTrue { condition, target }
1798        | Instruction::JumpIfFalse { condition, target } => {
1799            check_register(condition)?;
1800            verify_target(function_index, pc, target, code_len)?;
1801        }
1802        Instruction::Return { value } | Instruction::Throw { value } => check_register(value)?,
1803        Instruction::Suspend { dst, src, resume } => {
1804            check_register(dst)?;
1805            check_register(src)?;
1806            verify_target(function_index, pc, resume, code_len)?;
1807        }
1808        Instruction::Import { dst, specifier } => {
1809            check_register(dst)?;
1810            check_string_constant(specifier)?;
1811        }
1812        Instruction::Export { name, src } => {
1813            check_string_constant(name)?;
1814            check_register(src)?;
1815        }
1816        Instruction::Halt => {}
1817    }
1818
1819    // Every normal successor (including fall-through) must be a real
1820    // instruction boundary: this forbids reachable fall-off past the end.
1821    let mut successor_error = None;
1822    instruction.visit_successors(pc as u32, |successor| {
1823        if successor_error.is_none() {
1824            successor_error = verify_target(function_index, pc, successor, code_len).err();
1825        }
1826    });
1827    if let Some(error) = successor_error {
1828        return Err(error);
1829    }
1830    Ok(())
1831}
1832
1833fn verify_target(
1834    function_index: usize,
1835    pc: usize,
1836    target: Pc,
1837    instruction_count: usize,
1838) -> Result<(), VerifyError> {
1839    if target.get() as usize >= instruction_count {
1840        Err(instruction_error(
1841            function_index,
1842            pc,
1843            VerifyErrorKind::JumpOutOfBounds {
1844                target: target.get(),
1845                instruction_count,
1846            },
1847        ))
1848    } else {
1849        Ok(())
1850    }
1851}
1852
1853/// Builds the greatest syntactic forward witness satisfying every transfer,
1854/// with the entry fact fixed to the capture and parameter registers. Handler
1855/// entries are reached conservatively: an exception may occur at the first
1856/// protected instruction, so a handler's fact is the intersection of the
1857/// pre-facts across its protected range plus its catch register. This does not
1858/// use semantic reachability, matching Lean's `Certificate` and
1859/// `verifier_never_skips_invariant`.
1860fn definite_initialization(
1861    function_index: usize,
1862    function: &Function,
1863) -> Result<Certificate, VerifyError> {
1864    let register_count = function.register_count;
1865    let mut facts = vec![RegisterSet::full(register_count); function.code.len()];
1866    let mut entry = RegisterSet::empty(register_count);
1867    entry.insert_prefix(function.entry_initialized());
1868    facts[0] = entry;
1869
1870    loop {
1871        let mut changed = false;
1872        for (pc, instruction) in function.code.iter().copied().enumerate() {
1873            let mut after = facts[pc].clone();
1874            instruction.visit_writes(|write| after.insert(write));
1875            instruction.visit_successors(pc as u32, |successor| {
1876                changed |= facts[successor.get() as usize].intersect(&after);
1877            });
1878        }
1879        for handler in function.handlers.iter().copied() {
1880            let start = handler.start.get() as usize;
1881            let end = handler.end.get() as usize;
1882            let mut contribution = RegisterSet::full(register_count);
1883            for protected in &facts[start..end] {
1884                contribution.intersect(protected);
1885            }
1886            contribution.insert(handler.catch_register);
1887            changed |= facts[handler.handler.get() as usize].intersect(&contribution);
1888        }
1889        if !changed {
1890            break;
1891        }
1892    }
1893
1894    for (pc, instruction) in function.code.iter().copied().enumerate() {
1895        let mut missing = None;
1896        instruction.visit_reads(|register| {
1897            if missing.is_none() && !facts[pc].contains(register) {
1898                missing = Some(register);
1899            }
1900        });
1901        if let Some(register) = missing {
1902            return Err(instruction_error(
1903                function_index,
1904                pc,
1905                VerifyErrorKind::ReadBeforeWrite { register },
1906            ));
1907        }
1908    }
1909    Ok(Certificate {
1910        register_count,
1911        facts,
1912    })
1913}
1914
1915/// Decoder allocation/input ceilings, enforced before any allocation.
1916#[derive(Clone, Debug, Eq, PartialEq)]
1917pub struct DecodeLimits {
1918    pub max_bytes: usize,
1919    pub max_constants: u32,
1920    pub max_functions: u32,
1921    pub max_capture_count: u32,
1922    pub max_parameter_count: u32,
1923    pub max_register_count: u32,
1924    pub max_instructions_per_function: u32,
1925    pub max_total_instructions: u64,
1926    pub max_handlers_per_function: u32,
1927    pub max_string_units: u32,
1928    pub max_bigint_bytes: u32,
1929}
1930
1931impl Default for DecodeLimits {
1932    fn default() -> Self {
1933        Self {
1934            max_bytes: 16 * 1024 * 1024,
1935            max_constants: MAX_CONSTANTS,
1936            max_functions: MAX_FUNCTIONS,
1937            max_capture_count: MAX_REGISTERS,
1938            max_parameter_count: MAX_REGISTERS,
1939            max_register_count: MAX_REGISTERS,
1940            max_instructions_per_function: MAX_INSTRUCTIONS,
1941            max_total_instructions: 1 << 24,
1942            max_handlers_per_function: MAX_HANDLERS,
1943            max_string_units: 1 << 20,
1944            max_bigint_bytes: 1 << 20,
1945        }
1946    }
1947}
1948
1949#[derive(Clone, Debug, Eq, PartialEq)]
1950pub struct DecodeError {
1951    pub offset: usize,
1952    pub kind: DecodeErrorKind,
1953}
1954
1955#[derive(Clone, Debug, Eq, PartialEq)]
1956pub enum DecodeErrorKind {
1957    InputLimitExceeded {
1958        limit: usize,
1959        actual: usize,
1960    },
1961    UnexpectedEof,
1962    BadMagic {
1963        expected: u8,
1964        actual: u8,
1965    },
1966    UnsupportedVersion {
1967        version: u8,
1968    },
1969    MalformedInteger,
1970    NonCanonicalInteger,
1971    IntegerOverflow,
1972    InvalidConstantTag {
1973        tag: u8,
1974    },
1975    NonCanonicalNumber {
1976        bits: u64,
1977    },
1978    InvalidUtf8,
1979    InvalidBigInt,
1980    InvalidFunctionFlags {
1981        bits: u8,
1982    },
1983    InvalidUnaryOp {
1984        tag: u8,
1985    },
1986    InvalidBinaryOp {
1987        tag: u8,
1988    },
1989    InvalidIteratorKind {
1990        tag: u8,
1991    },
1992    InvalidAccessorKind {
1993        tag: u8,
1994    },
1995    InvalidOpcode {
1996        opcode: u8,
1997    },
1998    LimitExceeded {
1999        field: &'static str,
2000        limit: u64,
2001        actual: u64,
2002    },
2003    TrailingBytes {
2004        count: usize,
2005    },
2006}
2007
2008impl fmt::Display for DecodeError {
2009    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2010        write!(formatter, "byte {}: ", self.offset)?;
2011        match self.kind {
2012            DecodeErrorKind::InputLimitExceeded { limit, actual } => {
2013                write!(formatter, "input has {actual} bytes, limit is {limit}")
2014            }
2015            DecodeErrorKind::UnexpectedEof => formatter.write_str("unexpected end of input"),
2016            DecodeErrorKind::BadMagic { expected, actual } => write!(
2017                formatter,
2018                "bad magic byte {actual:#04x}, expected {expected:#04x}"
2019            ),
2020            DecodeErrorKind::UnsupportedVersion { version } => {
2021                write!(formatter, "unsupported format version {version}")
2022            }
2023            DecodeErrorKind::MalformedInteger => formatter.write_str("malformed LEB128 integer"),
2024            DecodeErrorKind::NonCanonicalInteger => {
2025                formatter.write_str("noncanonical (overlong) LEB128 integer")
2026            }
2027            DecodeErrorKind::IntegerOverflow => {
2028                formatter.write_str("LEB128 integer exceeds 32 bits")
2029            }
2030            DecodeErrorKind::InvalidConstantTag { tag } => {
2031                write!(formatter, "invalid constant tag {tag}")
2032            }
2033            DecodeErrorKind::NonCanonicalNumber { bits } => {
2034                write!(formatter, "noncanonical NaN bits {bits:#018x}")
2035            }
2036            DecodeErrorKind::InvalidUtf8 => formatter.write_str("bigint text is not UTF-8"),
2037            DecodeErrorKind::InvalidBigInt => {
2038                formatter.write_str("bigint constant is not canonical decimal text")
2039            }
2040            DecodeErrorKind::InvalidFunctionFlags { bits } => {
2041                write!(formatter, "invalid function flags {bits:#04x}")
2042            }
2043            DecodeErrorKind::InvalidUnaryOp { tag } => {
2044                write!(formatter, "invalid unary operator {tag}")
2045            }
2046            DecodeErrorKind::InvalidBinaryOp { tag } => {
2047                write!(formatter, "invalid binary operator {tag}")
2048            }
2049            DecodeErrorKind::InvalidIteratorKind { tag } => {
2050                write!(formatter, "invalid iterator kind {tag}")
2051            }
2052            DecodeErrorKind::InvalidAccessorKind { tag } => {
2053                write!(formatter, "invalid accessor kind {tag}")
2054            }
2055            DecodeErrorKind::InvalidOpcode { opcode } => {
2056                write!(formatter, "invalid opcode {opcode}")
2057            }
2058            DecodeErrorKind::LimitExceeded {
2059                field,
2060                limit,
2061                actual,
2062            } => write!(formatter, "{field} value {actual} exceeds limit {limit}"),
2063            DecodeErrorKind::TrailingBytes { count } => write!(formatter, "{count} trailing bytes"),
2064        }
2065    }
2066}
2067
2068impl Error for DecodeError {}
2069
2070#[derive(Clone, Debug, Eq, PartialEq)]
2071pub enum LoadError {
2072    Decode(DecodeError),
2073    Verify(VerifyError),
2074}
2075
2076impl fmt::Display for LoadError {
2077    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2078        match self {
2079            Self::Decode(error) => error.fmt(formatter),
2080            Self::Verify(error) => error.fmt(formatter),
2081        }
2082    }
2083}
2084
2085impl Error for LoadError {
2086    fn source(&self) -> Option<&(dyn Error + 'static)> {
2087        match self {
2088            Self::Decode(error) => Some(error),
2089            Self::Verify(error) => Some(error),
2090        }
2091    }
2092}
2093
2094/// Strictly decodes untrusted bytes. Every length is checked before allocation;
2095/// semantic validity remains represented by the `Unverified` typestate.
2096///
2097/// # Errors
2098/// Returns the first malformed, noncanonical, or over-limit byte encountered,
2099/// or trailing bytes after a complete module.
2100pub fn decode(bytes: &[u8], limits: &DecodeLimits) -> Result<Module<Unverified>, DecodeError> {
2101    if bytes.len() > limits.max_bytes {
2102        return Err(DecodeError {
2103            offset: 0,
2104            kind: DecodeErrorKind::InputLimitExceeded {
2105                limit: limits.max_bytes,
2106                actual: bytes.len(),
2107            },
2108        });
2109    }
2110    let mut decoder = Decoder {
2111        bytes,
2112        offset: 0,
2113        limits,
2114        total_instructions: 0,
2115    };
2116    let module = decoder.module()?;
2117    if decoder.offset != bytes.len() {
2118        return Err(DecodeError {
2119            offset: decoder.offset,
2120            kind: DecodeErrorKind::TrailingBytes {
2121                count: bytes.len() - decoder.offset,
2122            },
2123        });
2124    }
2125    Ok(module)
2126}
2127
2128/// Decodes and verifies in one boundary operation.
2129///
2130/// # Errors
2131/// Returns [`LoadError::Decode`] for malformed bytes or [`LoadError::Verify`]
2132/// for a structurally invalid module.
2133pub fn decode_verified(bytes: &[u8], limits: &DecodeLimits) -> Result<Module<Verified>, LoadError> {
2134    decode(bytes, limits)
2135        .map_err(LoadError::Decode)?
2136        .verify()
2137        .map_err(LoadError::Verify)
2138}
2139
2140struct Decoder<'a> {
2141    bytes: &'a [u8],
2142    offset: usize,
2143    limits: &'a DecodeLimits,
2144    total_instructions: u64,
2145}
2146
2147impl<'a> Decoder<'a> {
2148    fn module(&mut self) -> Result<Module<Unverified>, DecodeError> {
2149        for expected in MAGIC {
2150            let at = self.offset;
2151            let actual = self.byte()?;
2152            if actual != expected {
2153                return Err(self.error(at, DecodeErrorKind::BadMagic { expected, actual }));
2154            }
2155        }
2156        let version_at = self.offset;
2157        let version = self.byte()?;
2158        if version != FORMAT_VERSION {
2159            return Err(self.error(version_at, DecodeErrorKind::UnsupportedVersion { version }));
2160        }
2161
2162        let constant_count = self.bounded("constant count", self.limits.max_constants)?;
2163        let mut constants = Vec::with_capacity(self.cap(constant_count));
2164        for _ in 0..constant_count {
2165            constants.push(self.constant()?);
2166        }
2167
2168        let function_count = self.bounded("function count", self.limits.max_functions)?;
2169        let entry = FunctionId::new(self.leb128()?);
2170        let mut functions = Vec::with_capacity(self.cap(function_count));
2171        for _ in 0..function_count {
2172            functions.push(self.function()?);
2173        }
2174        Ok(Module::new(constants, functions, entry))
2175    }
2176
2177    fn constant(&mut self) -> Result<Constant, DecodeError> {
2178        let tag_at = self.offset;
2179        match self.byte()? {
2180            0 => {
2181                let number_at = self.offset;
2182                let bits = u64::from_le_bytes(self.exact::<8>()?);
2183                NumberBits::from_wire(bits)
2184                    .map(Constant::Number)
2185                    .ok_or_else(|| {
2186                        self.error(number_at, DecodeErrorKind::NonCanonicalNumber { bits })
2187                    })
2188            }
2189            1 => Ok(Constant::Int32(i32::from_le_bytes(self.exact::<4>()?))),
2190            2 => Ok(Constant::String(self.string()?)),
2191            3 => Ok(Constant::Boolean(false)),
2192            4 => Ok(Constant::Boolean(true)),
2193            5 => Ok(Constant::Null),
2194            6 => Ok(Constant::Undefined),
2195            7 => {
2196                let start = self.offset;
2197                let text = self.text()?;
2198                BigIntLiteral::new(text)
2199                    .map(Constant::BigInt)
2200                    .ok_or_else(|| self.error(start, DecodeErrorKind::InvalidBigInt))
2201            }
2202            tag => Err(self.error(tag_at, DecodeErrorKind::InvalidConstantTag { tag })),
2203        }
2204    }
2205
2206    fn string(&mut self) -> Result<EcmaString, DecodeError> {
2207        let unit_count = self.bounded("string unit count", self.limits.max_string_units)?;
2208        let byte_count = usize::try_from(unit_count)
2209            .ok()
2210            .and_then(|units| units.checked_mul(2))
2211            .ok_or_else(|| self.error(self.offset, DecodeErrorKind::UnexpectedEof))?;
2212        let bytes = self.slice(byte_count)?;
2213        Ok(EcmaString::from_le_bytes(bytes))
2214    }
2215
2216    fn text(&mut self) -> Result<String, DecodeError> {
2217        let length = self.bounded("bigint byte length", self.limits.max_bigint_bytes)?;
2218        let start = self.offset;
2219        let bytes = self.slice(length as usize)?;
2220        let value = std::str::from_utf8(bytes)
2221            .map_err(|_| self.error(start, DecodeErrorKind::InvalidUtf8))?;
2222        Ok(value.to_owned())
2223    }
2224
2225    fn function(&mut self) -> Result<Function, DecodeError> {
2226        let name = match self.leb128()? {
2227            0 => None,
2228            encoded => Some(ConstantId::new(encoded - 1)),
2229        };
2230        let capture_count = self.bounded("capture count", self.limits.max_capture_count)?;
2231        let parameter_count = self.bounded("parameter count", self.limits.max_parameter_count)?;
2232        let register_count = self.bounded("register count", self.limits.max_register_count)?;
2233        let flags_at = self.offset;
2234        let flags_bits = self.byte()?;
2235        let flags = FunctionFlags::from_bits(flags_bits).ok_or_else(|| {
2236            self.error(
2237                flags_at,
2238                DecodeErrorKind::InvalidFunctionFlags { bits: flags_bits },
2239            )
2240        })?;
2241        let instruction_at = self.offset;
2242        let instruction_count = self.bounded(
2243            "instruction count",
2244            self.limits.max_instructions_per_function,
2245        )?;
2246        self.total_instructions += u64::from(instruction_count);
2247        if self.total_instructions > self.limits.max_total_instructions {
2248            return Err(self.error(
2249                instruction_at,
2250                DecodeErrorKind::LimitExceeded {
2251                    field: "total instruction count",
2252                    limit: self.limits.max_total_instructions,
2253                    actual: self.total_instructions,
2254                },
2255            ));
2256        }
2257        let mut code = Vec::with_capacity(self.cap(instruction_count));
2258        for _ in 0..instruction_count {
2259            code.push(self.instruction()?);
2260        }
2261
2262        let handler_count = self.bounded("handler count", self.limits.max_handlers_per_function)?;
2263        let mut handlers = Vec::with_capacity(self.cap(handler_count));
2264        for _ in 0..handler_count {
2265            handlers.push(ExceptionHandler {
2266                start: Pc::new(self.leb128()?),
2267                end: Pc::new(self.leb128()?),
2268                handler: Pc::new(self.leb128()?),
2269                catch_register: Register::new(self.leb128()?),
2270            });
2271        }
2272        Ok(Function::new(
2273            name,
2274            capture_count,
2275            parameter_count,
2276            register_count,
2277            flags,
2278            code,
2279            handlers,
2280        ))
2281    }
2282
2283    fn instruction(&mut self) -> Result<Instruction, DecodeError> {
2284        let opcode_at = self.offset;
2285        match self.byte()? {
2286            0 => Ok(Instruction::LoadConst {
2287                dst: Register::new(self.leb128()?),
2288                constant: ConstantId::new(self.leb128()?),
2289            }),
2290            1 => Ok(Instruction::Move {
2291                dst: Register::new(self.leb128()?),
2292                src: Register::new(self.leb128()?),
2293            }),
2294            2 => Ok(Instruction::Unary {
2295                dst: Register::new(self.leb128()?),
2296                op: self.unary_op()?,
2297                operand: Register::new(self.leb128()?),
2298            }),
2299            3 => Ok(Instruction::Binary {
2300                dst: Register::new(self.leb128()?),
2301                op: self.binary_op()?,
2302                left: Register::new(self.leb128()?),
2303                right: Register::new(self.leb128()?),
2304            }),
2305            4 => Ok(Instruction::CreateObject {
2306                dst: Register::new(self.leb128()?),
2307            }),
2308            5 => Ok(Instruction::CreateArray {
2309                dst: Register::new(self.leb128()?),
2310            }),
2311            6 => Ok(Instruction::CreateClosure {
2312                dst: Register::new(self.leb128()?),
2313                function: FunctionId::new(self.leb128()?),
2314                captures: Register::new(self.leb128()?),
2315            }),
2316            7 => Ok(Instruction::GetProperty {
2317                dst: Register::new(self.leb128()?),
2318                object: Register::new(self.leb128()?),
2319                key: Register::new(self.leb128()?),
2320            }),
2321            8 => Ok(Instruction::SetProperty {
2322                object: Register::new(self.leb128()?),
2323                key: Register::new(self.leb128()?),
2324                value: Register::new(self.leb128()?),
2325            }),
2326            9 => Ok(Instruction::DeleteProperty {
2327                dst: Register::new(self.leb128()?),
2328                object: Register::new(self.leb128()?),
2329                key: Register::new(self.leb128()?),
2330            }),
2331            10 => Ok(Instruction::DefineAccessor {
2332                object: Register::new(self.leb128()?),
2333                key: Register::new(self.leb128()?),
2334                accessor: Register::new(self.leb128()?),
2335                kind: self.accessor_kind()?,
2336            }),
2337            11 => Ok(Instruction::Call {
2338                dst: Register::new(self.leb128()?),
2339                callee: Register::new(self.leb128()?),
2340                this_value: Register::new(self.leb128()?),
2341                arguments: Register::new(self.leb128()?),
2342            }),
2343            12 => Ok(Instruction::Construct {
2344                dst: Register::new(self.leb128()?),
2345                callee: Register::new(self.leb128()?),
2346                arguments: Register::new(self.leb128()?),
2347            }),
2348            13 => Ok(Instruction::LoadGlobal {
2349                dst: Register::new(self.leb128()?),
2350                name: ConstantId::new(self.leb128()?),
2351            }),
2352            14 => Ok(Instruction::StoreGlobal {
2353                name: ConstantId::new(self.leb128()?),
2354                value: Register::new(self.leb128()?),
2355            }),
2356            15 => Ok(Instruction::TypeOfGlobal {
2357                dst: Register::new(self.leb128()?),
2358                name: ConstantId::new(self.leb128()?),
2359            }),
2360            16 => Ok(Instruction::LoadThis {
2361                dst: Register::new(self.leb128()?),
2362            }),
2363            17 => Ok(Instruction::LoadArguments {
2364                dst: Register::new(self.leb128()?),
2365            }),
2366            18 => Ok(Instruction::LoadNewTarget {
2367                dst: Register::new(self.leb128()?),
2368            }),
2369            19 => Ok(Instruction::ArrayPush {
2370                array: Register::new(self.leb128()?),
2371                value: Register::new(self.leb128()?),
2372            }),
2373            20 => Ok(Instruction::ArrayExtend {
2374                array: Register::new(self.leb128()?),
2375                iterable: Register::new(self.leb128()?),
2376            }),
2377            21 => Ok(Instruction::ObjectSpread {
2378                target: Register::new(self.leb128()?),
2379                source: Register::new(self.leb128()?),
2380            }),
2381            22 => Ok(Instruction::SetPrototype {
2382                object: Register::new(self.leb128()?),
2383                prototype: Register::new(self.leb128()?),
2384            }),
2385            23 => Ok(Instruction::CreatePrivateName {
2386                dst: Register::new(self.leb128()?),
2387                description: ConstantId::new(self.leb128()?),
2388            }),
2389            24 => Ok(Instruction::CreateRegExp {
2390                dst: Register::new(self.leb128()?),
2391                pattern: ConstantId::new(self.leb128()?),
2392                flags: ConstantId::new(self.leb128()?),
2393            }),
2394            25 => Ok(Instruction::GetIterator {
2395                dst: Register::new(self.leb128()?),
2396                src: Register::new(self.leb128()?),
2397                kind: self.iterator_kind()?,
2398            }),
2399            26 => Ok(Instruction::IteratorNext {
2400                done: Register::new(self.leb128()?),
2401                value: Register::new(self.leb128()?),
2402                iterator: Register::new(self.leb128()?),
2403            }),
2404            27 => Ok(Instruction::Jump {
2405                target: Pc::new(self.leb128()?),
2406            }),
2407            28 => Ok(Instruction::JumpIfTrue {
2408                condition: Register::new(self.leb128()?),
2409                target: Pc::new(self.leb128()?),
2410            }),
2411            29 => Ok(Instruction::JumpIfFalse {
2412                condition: Register::new(self.leb128()?),
2413                target: Pc::new(self.leb128()?),
2414            }),
2415            30 => Ok(Instruction::Return {
2416                value: Register::new(self.leb128()?),
2417            }),
2418            31 => Ok(Instruction::Throw {
2419                value: Register::new(self.leb128()?),
2420            }),
2421            32 => Ok(Instruction::Suspend {
2422                dst: Register::new(self.leb128()?),
2423                src: Register::new(self.leb128()?),
2424                resume: Pc::new(self.leb128()?),
2425            }),
2426            33 => Ok(Instruction::Import {
2427                dst: Register::new(self.leb128()?),
2428                specifier: ConstantId::new(self.leb128()?),
2429            }),
2430            34 => Ok(Instruction::Export {
2431                name: ConstantId::new(self.leb128()?),
2432                src: Register::new(self.leb128()?),
2433            }),
2434            35 => Ok(Instruction::Halt),
2435            36 => Ok(Instruction::CreateCell {
2436                dst: Register::new(self.leb128()?),
2437            }),
2438            opcode => Err(self.error(opcode_at, DecodeErrorKind::InvalidOpcode { opcode })),
2439        }
2440    }
2441
2442    fn unary_op(&mut self) -> Result<UnaryOp, DecodeError> {
2443        let at = self.offset;
2444        let tag = self.byte()?;
2445        UnaryOp::from_u8(tag).ok_or_else(|| self.error(at, DecodeErrorKind::InvalidUnaryOp { tag }))
2446    }
2447
2448    fn binary_op(&mut self) -> Result<BinaryOp, DecodeError> {
2449        let at = self.offset;
2450        let tag = self.byte()?;
2451        BinaryOp::from_u8(tag)
2452            .ok_or_else(|| self.error(at, DecodeErrorKind::InvalidBinaryOp { tag }))
2453    }
2454
2455    fn iterator_kind(&mut self) -> Result<IteratorKind, DecodeError> {
2456        let at = self.offset;
2457        let tag = self.byte()?;
2458        IteratorKind::from_u8(tag)
2459            .ok_or_else(|| self.error(at, DecodeErrorKind::InvalidIteratorKind { tag }))
2460    }
2461
2462    fn accessor_kind(&mut self) -> Result<AccessorKind, DecodeError> {
2463        let at = self.offset;
2464        let tag = self.byte()?;
2465        AccessorKind::from_u8(tag)
2466            .ok_or_else(|| self.error(at, DecodeErrorKind::InvalidAccessorKind { tag }))
2467    }
2468
2469    fn bounded(&mut self, field: &'static str, limit: u32) -> Result<u32, DecodeError> {
2470        let at = self.offset;
2471        let actual = self.leb128()?;
2472        if actual > limit {
2473            Err(self.error(
2474                at,
2475                DecodeErrorKind::LimitExceeded {
2476                    field,
2477                    limit: u64::from(limit),
2478                    actual: u64::from(actual),
2479                },
2480            ))
2481        } else {
2482            Ok(actual)
2483        }
2484    }
2485
2486    /// Reads one canonical unsigned LEB128 `u32`. Rejects EOF mid-integer,
2487    /// overlong (trailing-zero) encodings, and values exceeding 32 bits.
2488    fn leb128(&mut self) -> Result<u32, DecodeError> {
2489        let start = self.offset;
2490        let mut result: u32 = 0;
2491        let mut shift: u32 = 0;
2492        loop {
2493            let byte = self.byte()?;
2494            if shift == 28 {
2495                // Fifth group: only the low four bits may be set, and the
2496                // continuation bit must be clear (else overflow); a zero final
2497                // group would be overlong.
2498                if byte & 0x80 != 0 || byte > 0x0f {
2499                    return Err(self.error(start, DecodeErrorKind::IntegerOverflow));
2500                }
2501                if byte == 0 {
2502                    return Err(self.error(start, DecodeErrorKind::NonCanonicalInteger));
2503                }
2504                return Ok(result | (u32::from(byte) << 28));
2505            }
2506            result |= u32::from(byte & 0x7f) << shift;
2507            if byte & 0x80 == 0 {
2508                if byte == 0 && self.offset - start > 1 {
2509                    return Err(self.error(start, DecodeErrorKind::NonCanonicalInteger));
2510                }
2511                return Ok(result);
2512            }
2513            shift += 7;
2514        }
2515    }
2516
2517    fn cap(&self, count: u32) -> usize {
2518        // Each element consumes at least one wire byte, so the remaining input
2519        // caps how many can actually be present; never pre-allocate beyond it.
2520        (count as usize).min(self.bytes.len().saturating_sub(self.offset))
2521    }
2522
2523    fn byte(&mut self) -> Result<u8, DecodeError> {
2524        let Some(byte) = self.bytes.get(self.offset).copied() else {
2525            return Err(self.error(self.offset, DecodeErrorKind::UnexpectedEof));
2526        };
2527        self.offset += 1;
2528        Ok(byte)
2529    }
2530
2531    fn exact<const N: usize>(&mut self) -> Result<[u8; N], DecodeError> {
2532        let bytes = self.slice(N)?;
2533        let mut result = [0; N];
2534        result.copy_from_slice(bytes);
2535        Ok(result)
2536    }
2537
2538    fn slice(&mut self, length: usize) -> Result<&'a [u8], DecodeError> {
2539        let source: &'a [u8] = self.bytes;
2540        let Some(end) = self.offset.checked_add(length) else {
2541            return Err(self.error(self.offset, DecodeErrorKind::UnexpectedEof));
2542        };
2543        let Some(bytes) = source.get(self.offset..end) else {
2544            return Err(self.error(self.offset, DecodeErrorKind::UnexpectedEof));
2545        };
2546        self.offset = end;
2547        Ok(bytes)
2548    }
2549
2550    const fn error(&self, offset: usize, kind: DecodeErrorKind) -> DecodeError {
2551        DecodeError { offset, kind }
2552    }
2553}
2554
2555fn write_u32(value: u32, output: &mut Vec<u8>) {
2556    let mut remaining = value;
2557    loop {
2558        let byte = (remaining & 0x7f) as u8;
2559        remaining >>= 7;
2560        if remaining == 0 {
2561            output.push(byte);
2562            return;
2563        }
2564        output.push(byte | 0x80);
2565    }
2566}
2567
2568fn encode_constant(constant: &Constant, output: &mut Vec<u8>) {
2569    match constant {
2570        Constant::Number(bits) => {
2571            output.push(0);
2572            output.extend_from_slice(&bits.bits().to_le_bytes());
2573        }
2574        Constant::Int32(value) => {
2575            output.push(1);
2576            output.extend_from_slice(&value.to_le_bytes());
2577        }
2578        Constant::String(value) => {
2579            output.push(2);
2580            write_string(value, output);
2581        }
2582        Constant::Boolean(false) => output.push(3),
2583        Constant::Boolean(true) => output.push(4),
2584        Constant::Null => output.push(5),
2585        Constant::Undefined => output.push(6),
2586        Constant::BigInt(value) => {
2587            output.push(7);
2588            write_text(value.as_str(), output);
2589        }
2590    }
2591}
2592
2593fn write_string(value: &EcmaString, output: &mut Vec<u8>) {
2594    write_u32(value.len_units() as u32, output);
2595    for unit in value.as_units() {
2596        output.extend_from_slice(&unit.to_le_bytes());
2597    }
2598}
2599
2600fn write_text(value: &str, output: &mut Vec<u8>) {
2601    write_u32(value.len() as u32, output);
2602    output.extend_from_slice(value.as_bytes());
2603}
2604
2605fn encode_function(function: &Function, output: &mut Vec<u8>) {
2606    write_u32(function.name.map_or(0, |name| name.get() + 1), output);
2607    write_u32(function.capture_count, output);
2608    write_u32(function.parameter_count, output);
2609    write_u32(function.register_count, output);
2610    output.push(function.flags.to_bits());
2611    write_u32(function.code.len() as u32, output);
2612    for instruction in &function.code {
2613        encode_instruction(*instruction, output);
2614    }
2615    write_u32(function.handlers.len() as u32, output);
2616    for handler in &function.handlers {
2617        write_u32(handler.start.get(), output);
2618        write_u32(handler.end.get(), output);
2619        write_u32(handler.handler.get(), output);
2620        write_u32(handler.catch_register.get(), output);
2621    }
2622}
2623
2624fn encode_instruction(instruction: Instruction, output: &mut Vec<u8>) {
2625    match instruction {
2626        Instruction::LoadConst { dst, constant } => {
2627            output.push(0);
2628            write_u32(dst.get(), output);
2629            write_u32(constant.get(), output);
2630        }
2631        Instruction::Move { dst, src } => {
2632            output.push(1);
2633            write_u32(dst.get(), output);
2634            write_u32(src.get(), output);
2635        }
2636        Instruction::Unary { dst, op, operand } => {
2637            output.push(2);
2638            write_u32(dst.get(), output);
2639            output.push(op.to_u8());
2640            write_u32(operand.get(), output);
2641        }
2642        Instruction::Binary {
2643            dst,
2644            op,
2645            left,
2646            right,
2647        } => {
2648            output.push(3);
2649            write_u32(dst.get(), output);
2650            output.push(op.to_u8());
2651            write_u32(left.get(), output);
2652            write_u32(right.get(), output);
2653        }
2654        Instruction::CreateObject { dst } => {
2655            output.push(4);
2656            write_u32(dst.get(), output);
2657        }
2658        Instruction::CreateArray { dst } => {
2659            output.push(5);
2660            write_u32(dst.get(), output);
2661        }
2662        Instruction::CreateClosure {
2663            dst,
2664            function,
2665            captures,
2666        } => {
2667            output.push(6);
2668            write_u32(dst.get(), output);
2669            write_u32(function.get(), output);
2670            write_u32(captures.get(), output);
2671        }
2672        Instruction::GetProperty { dst, object, key } => {
2673            output.push(7);
2674            write_u32(dst.get(), output);
2675            write_u32(object.get(), output);
2676            write_u32(key.get(), output);
2677        }
2678        Instruction::SetProperty { object, key, value } => {
2679            output.push(8);
2680            write_u32(object.get(), output);
2681            write_u32(key.get(), output);
2682            write_u32(value.get(), output);
2683        }
2684        Instruction::DeleteProperty { dst, object, key } => {
2685            output.push(9);
2686            write_u32(dst.get(), output);
2687            write_u32(object.get(), output);
2688            write_u32(key.get(), output);
2689        }
2690        Instruction::DefineAccessor {
2691            object,
2692            key,
2693            accessor,
2694            kind,
2695        } => {
2696            output.push(10);
2697            write_u32(object.get(), output);
2698            write_u32(key.get(), output);
2699            write_u32(accessor.get(), output);
2700            output.push(kind.to_u8());
2701        }
2702        Instruction::Call {
2703            dst,
2704            callee,
2705            this_value,
2706            arguments,
2707        } => {
2708            output.push(11);
2709            write_u32(dst.get(), output);
2710            write_u32(callee.get(), output);
2711            write_u32(this_value.get(), output);
2712            write_u32(arguments.get(), output);
2713        }
2714        Instruction::Construct {
2715            dst,
2716            callee,
2717            arguments,
2718        } => {
2719            output.push(12);
2720            write_u32(dst.get(), output);
2721            write_u32(callee.get(), output);
2722            write_u32(arguments.get(), output);
2723        }
2724        Instruction::LoadGlobal { dst, name } => {
2725            output.push(13);
2726            write_u32(dst.get(), output);
2727            write_u32(name.get(), output);
2728        }
2729        Instruction::StoreGlobal { name, value } => {
2730            output.push(14);
2731            write_u32(name.get(), output);
2732            write_u32(value.get(), output);
2733        }
2734        Instruction::TypeOfGlobal { dst, name } => {
2735            output.push(15);
2736            write_u32(dst.get(), output);
2737            write_u32(name.get(), output);
2738        }
2739        Instruction::LoadThis { dst } => {
2740            output.push(16);
2741            write_u32(dst.get(), output);
2742        }
2743        Instruction::LoadArguments { dst } => {
2744            output.push(17);
2745            write_u32(dst.get(), output);
2746        }
2747        Instruction::LoadNewTarget { dst } => {
2748            output.push(18);
2749            write_u32(dst.get(), output);
2750        }
2751        Instruction::ArrayPush { array, value } => {
2752            output.push(19);
2753            write_u32(array.get(), output);
2754            write_u32(value.get(), output);
2755        }
2756        Instruction::ArrayExtend { array, iterable } => {
2757            output.push(20);
2758            write_u32(array.get(), output);
2759            write_u32(iterable.get(), output);
2760        }
2761        Instruction::ObjectSpread { target, source } => {
2762            output.push(21);
2763            write_u32(target.get(), output);
2764            write_u32(source.get(), output);
2765        }
2766        Instruction::SetPrototype { object, prototype } => {
2767            output.push(22);
2768            write_u32(object.get(), output);
2769            write_u32(prototype.get(), output);
2770        }
2771        Instruction::CreatePrivateName { dst, description } => {
2772            output.push(23);
2773            write_u32(dst.get(), output);
2774            write_u32(description.get(), output);
2775        }
2776        Instruction::CreateRegExp {
2777            dst,
2778            pattern,
2779            flags,
2780        } => {
2781            output.push(24);
2782            write_u32(dst.get(), output);
2783            write_u32(pattern.get(), output);
2784            write_u32(flags.get(), output);
2785        }
2786        Instruction::GetIterator { dst, src, kind } => {
2787            output.push(25);
2788            write_u32(dst.get(), output);
2789            write_u32(src.get(), output);
2790            output.push(kind.to_u8());
2791        }
2792        Instruction::IteratorNext {
2793            done,
2794            value,
2795            iterator,
2796        } => {
2797            output.push(26);
2798            write_u32(done.get(), output);
2799            write_u32(value.get(), output);
2800            write_u32(iterator.get(), output);
2801        }
2802        Instruction::Jump { target } => {
2803            output.push(27);
2804            write_u32(target.get(), output);
2805        }
2806        Instruction::JumpIfTrue { condition, target } => {
2807            output.push(28);
2808            write_u32(condition.get(), output);
2809            write_u32(target.get(), output);
2810        }
2811        Instruction::JumpIfFalse { condition, target } => {
2812            output.push(29);
2813            write_u32(condition.get(), output);
2814            write_u32(target.get(), output);
2815        }
2816        Instruction::Return { value } => {
2817            output.push(30);
2818            write_u32(value.get(), output);
2819        }
2820        Instruction::Throw { value } => {
2821            output.push(31);
2822            write_u32(value.get(), output);
2823        }
2824        Instruction::Suspend { dst, src, resume } => {
2825            output.push(32);
2826            write_u32(dst.get(), output);
2827            write_u32(src.get(), output);
2828            write_u32(resume.get(), output);
2829        }
2830        Instruction::Import { dst, specifier } => {
2831            output.push(33);
2832            write_u32(dst.get(), output);
2833            write_u32(specifier.get(), output);
2834        }
2835        Instruction::Export { name, src } => {
2836            output.push(34);
2837            write_u32(name.get(), output);
2838            write_u32(src.get(), output);
2839        }
2840        Instruction::Halt => output.push(35),
2841        Instruction::CreateCell { dst } => {
2842            output.push(36);
2843            write_u32(dst.get(), output);
2844        }
2845    }
2846}
2847
2848#[cfg(test)]
2849mod tests {
2850    use super::*;
2851
2852    fn flags() -> FunctionFlags {
2853        FunctionFlags::default()
2854    }
2855
2856    fn prefix() -> Vec<u8> {
2857        let mut bytes = MAGIC.to_vec();
2858        bytes.push(FORMAT_VERSION);
2859        bytes
2860    }
2861
2862    /// A single function exercising the dynamic-computation opcodes, all
2863    /// references in bounds and every read dominated by a write. Register map:
2864    ///
2865    /// * r0 = 21, r1 = 1.5, r2 = r0 + r1, r3 = -r2
2866    /// * r4 = {}, r5 = "main" (used as a register property key)
2867    /// * r6 = r4[r5]; r7 = [] then push/extend
2868    /// * r9 = {}; object-spread and set-prototype from r4
2869    /// * r10 = closure(fn0, captures=r7)
2870    /// * r11/r12/r13 = this / arguments / new.target
2871    /// * r14 = global g; store it back; r15 = typeof g
2872    /// * r16 = #p private name; r17 = /ab/gi
2873    /// * r18 = iterator(r7); (r19,r20) = next(r18)
2874    /// * r21 = call(r10, this=r4, args=r7); r22 = construct(r10, args=r7)
2875    /// * r23 = import "./dep"; export "x" = r23; suspend yields r22, resumes r24
2876    /// * handler over [0,34) catches into r25
2877    fn rich_module() -> Module<Unverified> {
2878        let constants = vec![
2879            Constant::String(EcmaString::from_utf8("main")), // 0: function name + key string
2880            Constant::Int32(21),                             // 1
2881            Constant::Number(NumberBits::from_f64(1.5)),     // 2
2882            Constant::BigInt(BigIntLiteral::new("-1234567890123".to_owned()).unwrap()), // 3
2883            Constant::Boolean(true),                         // 4
2884            Constant::Null,                                  // 5
2885            Constant::Undefined,                             // 6
2886            Constant::String(EcmaString::from_utf8("./dep")), // 7: import specifier
2887            Constant::String(EcmaString::from_utf8("g")),    // 8: global name
2888            Constant::String(EcmaString::from_utf8("#p")),   // 9: private description
2889            Constant::String(EcmaString::from_utf8("ab")),   // 10: regexp pattern
2890            Constant::String(EcmaString::from_utf8("gi")),   // 11: regexp flags
2891            Constant::String(EcmaString::from_utf8("x")),    // 12: export name
2892        ];
2893        let code = vec![
2894            Instruction::LoadConst {
2895                dst: Register::new(0),
2896                constant: ConstantId::new(1),
2897            },
2898            Instruction::LoadConst {
2899                dst: Register::new(1),
2900                constant: ConstantId::new(2),
2901            },
2902            Instruction::Binary {
2903                dst: Register::new(2),
2904                op: BinaryOp::Add,
2905                left: Register::new(0),
2906                right: Register::new(1),
2907            },
2908            Instruction::Unary {
2909                dst: Register::new(3),
2910                op: UnaryOp::Negate,
2911                operand: Register::new(2),
2912            },
2913            Instruction::CreateObject {
2914                dst: Register::new(4),
2915            },
2916            Instruction::LoadConst {
2917                dst: Register::new(5),
2918                constant: ConstantId::new(0),
2919            },
2920            Instruction::SetProperty {
2921                object: Register::new(4),
2922                key: Register::new(5),
2923                value: Register::new(3),
2924            },
2925            Instruction::GetProperty {
2926                dst: Register::new(6),
2927                object: Register::new(4),
2928                key: Register::new(5),
2929            },
2930            Instruction::CreateArray {
2931                dst: Register::new(7),
2932            },
2933            Instruction::ArrayPush {
2934                array: Register::new(7),
2935                value: Register::new(6),
2936            },
2937            Instruction::ArrayExtend {
2938                array: Register::new(7),
2939                iterable: Register::new(7),
2940            },
2941            Instruction::CreateObject {
2942                dst: Register::new(9),
2943            },
2944            Instruction::ObjectSpread {
2945                target: Register::new(9),
2946                source: Register::new(4),
2947            },
2948            Instruction::SetPrototype {
2949                object: Register::new(9),
2950                prototype: Register::new(4),
2951            },
2952            Instruction::CreateClosure {
2953                dst: Register::new(10),
2954                function: FunctionId::new(0),
2955                captures: Register::new(7),
2956            },
2957            Instruction::LoadThis {
2958                dst: Register::new(11),
2959            },
2960            Instruction::LoadArguments {
2961                dst: Register::new(12),
2962            },
2963            Instruction::LoadNewTarget {
2964                dst: Register::new(13),
2965            },
2966            Instruction::LoadGlobal {
2967                dst: Register::new(14),
2968                name: ConstantId::new(8),
2969            },
2970            Instruction::StoreGlobal {
2971                name: ConstantId::new(8),
2972                value: Register::new(14),
2973            },
2974            Instruction::TypeOfGlobal {
2975                dst: Register::new(15),
2976                name: ConstantId::new(8),
2977            },
2978            Instruction::CreatePrivateName {
2979                dst: Register::new(16),
2980                description: ConstantId::new(9),
2981            },
2982            Instruction::CreateRegExp {
2983                dst: Register::new(17),
2984                pattern: ConstantId::new(10),
2985                flags: ConstantId::new(11),
2986            },
2987            Instruction::GetIterator {
2988                dst: Register::new(18),
2989                src: Register::new(7),
2990                kind: IteratorKind::Sync,
2991            },
2992            Instruction::IteratorNext {
2993                done: Register::new(19),
2994                value: Register::new(20),
2995                iterator: Register::new(18),
2996            },
2997            Instruction::DefineAccessor {
2998                object: Register::new(9),
2999                key: Register::new(5),
3000                accessor: Register::new(10),
3001                kind: AccessorKind::Getter,
3002            },
3003            Instruction::Call {
3004                dst: Register::new(21),
3005                callee: Register::new(10),
3006                this_value: Register::new(4),
3007                arguments: Register::new(7),
3008            },
3009            Instruction::Construct {
3010                dst: Register::new(22),
3011                callee: Register::new(10),
3012                arguments: Register::new(7),
3013            },
3014            Instruction::Import {
3015                dst: Register::new(23),
3016                specifier: ConstantId::new(7),
3017            },
3018            Instruction::Export {
3019                name: ConstantId::new(12),
3020                src: Register::new(23),
3021            },
3022            Instruction::Suspend {
3023                dst: Register::new(24),
3024                src: Register::new(22),
3025                resume: Pc::new(31),
3026            },
3027            Instruction::JumpIfTrue {
3028                condition: Register::new(21),
3029                target: Pc::new(33),
3030            },
3031            Instruction::Return {
3032                value: Register::new(6),
3033            },
3034            Instruction::Return {
3035                value: Register::new(24),
3036            },
3037            Instruction::Return {
3038                value: Register::new(25),
3039            },
3040        ];
3041        let handlers = vec![ExceptionHandler {
3042            start: Pc::new(0),
3043            end: Pc::new(34),
3044            handler: Pc::new(34),
3045            catch_register: Register::new(25),
3046        }];
3047        Module::new(
3048            constants,
3049            vec![Function::new(
3050                Some(ConstantId::new(0)),
3051                0,
3052                0,
3053                26,
3054                FunctionFlags {
3055                    is_async: true,
3056                    is_generator: false,
3057                },
3058                code,
3059                handlers,
3060            )],
3061            FunctionId::new(0),
3062        )
3063    }
3064
3065    #[test]
3066    fn rich_module_verifies_round_trips_and_is_deterministic() {
3067        let verified = rich_module().verify().expect("valid production module");
3068        let encoded = verified.encode();
3069        assert_eq!(verified.encode(), encoded, "encoding is deterministic");
3070
3071        let decoded = decode(&encoded, &DecodeLimits::default()).expect("canonical wire");
3072        assert_eq!(decoded, rich_module(), "decode is the inverse of encode");
3073        let reverified = decoded.verify().expect("decoded module reverifies");
3074        assert_eq!(reverified.encode(), encoded, "round-trip is canonical");
3075    }
3076
3077    /// Every opcode variant survives an encode -> decode round-trip at the
3078    /// instruction level, independent of CFG/reference validity. This pins the
3079    /// wire tag and field order for all 37 opcodes.
3080    #[test]
3081    fn every_opcode_round_trips_on_the_wire() {
3082        let instructions = [
3083            Instruction::LoadConst {
3084                dst: Register::new(1),
3085                constant: ConstantId::new(2),
3086            },
3087            Instruction::Move {
3088                dst: Register::new(3),
3089                src: Register::new(4),
3090            },
3091            Instruction::Unary {
3092                dst: Register::new(5),
3093                op: UnaryOp::LogicalNot,
3094                operand: Register::new(6),
3095            },
3096            Instruction::Binary {
3097                dst: Register::new(7),
3098                op: BinaryOp::StrictEqual,
3099                left: Register::new(8),
3100                right: Register::new(9),
3101            },
3102            Instruction::CreateObject {
3103                dst: Register::new(10),
3104            },
3105            Instruction::CreateArray {
3106                dst: Register::new(11),
3107            },
3108            Instruction::CreateClosure {
3109                dst: Register::new(12),
3110                function: FunctionId::new(13),
3111                captures: Register::new(14),
3112            },
3113            Instruction::GetProperty {
3114                dst: Register::new(15),
3115                object: Register::new(16),
3116                key: Register::new(17),
3117            },
3118            Instruction::SetProperty {
3119                object: Register::new(18),
3120                key: Register::new(19),
3121                value: Register::new(20),
3122            },
3123            Instruction::DeleteProperty {
3124                dst: Register::new(21),
3125                object: Register::new(22),
3126                key: Register::new(23),
3127            },
3128            Instruction::DefineAccessor {
3129                object: Register::new(24),
3130                key: Register::new(25),
3131                accessor: Register::new(26),
3132                kind: AccessorKind::Setter,
3133            },
3134            Instruction::Call {
3135                dst: Register::new(27),
3136                callee: Register::new(28),
3137                this_value: Register::new(29),
3138                arguments: Register::new(30),
3139            },
3140            Instruction::Construct {
3141                dst: Register::new(31),
3142                callee: Register::new(32),
3143                arguments: Register::new(33),
3144            },
3145            Instruction::LoadGlobal {
3146                dst: Register::new(34),
3147                name: ConstantId::new(35),
3148            },
3149            Instruction::StoreGlobal {
3150                name: ConstantId::new(36),
3151                value: Register::new(37),
3152            },
3153            Instruction::TypeOfGlobal {
3154                dst: Register::new(38),
3155                name: ConstantId::new(39),
3156            },
3157            Instruction::LoadThis {
3158                dst: Register::new(40),
3159            },
3160            Instruction::LoadArguments {
3161                dst: Register::new(41),
3162            },
3163            Instruction::LoadNewTarget {
3164                dst: Register::new(42),
3165            },
3166            Instruction::ArrayPush {
3167                array: Register::new(43),
3168                value: Register::new(44),
3169            },
3170            Instruction::ArrayExtend {
3171                array: Register::new(45),
3172                iterable: Register::new(46),
3173            },
3174            Instruction::ObjectSpread {
3175                target: Register::new(47),
3176                source: Register::new(48),
3177            },
3178            Instruction::SetPrototype {
3179                object: Register::new(49),
3180                prototype: Register::new(50),
3181            },
3182            Instruction::CreatePrivateName {
3183                dst: Register::new(51),
3184                description: ConstantId::new(52),
3185            },
3186            Instruction::CreateRegExp {
3187                dst: Register::new(53),
3188                pattern: ConstantId::new(54),
3189                flags: ConstantId::new(55),
3190            },
3191            Instruction::GetIterator {
3192                dst: Register::new(56),
3193                src: Register::new(57),
3194                kind: IteratorKind::Async,
3195            },
3196            Instruction::IteratorNext {
3197                done: Register::new(58),
3198                value: Register::new(59),
3199                iterator: Register::new(60),
3200            },
3201            Instruction::Jump {
3202                target: Pc::new(61),
3203            },
3204            Instruction::JumpIfTrue {
3205                condition: Register::new(62),
3206                target: Pc::new(63),
3207            },
3208            Instruction::JumpIfFalse {
3209                condition: Register::new(64),
3210                target: Pc::new(65),
3211            },
3212            Instruction::Return {
3213                value: Register::new(66),
3214            },
3215            Instruction::Throw {
3216                value: Register::new(67),
3217            },
3218            Instruction::Suspend {
3219                dst: Register::new(68),
3220                src: Register::new(69),
3221                resume: Pc::new(70),
3222            },
3223            Instruction::Import {
3224                dst: Register::new(71),
3225                specifier: ConstantId::new(72),
3226            },
3227            Instruction::Export {
3228                name: ConstantId::new(73),
3229                src: Register::new(74),
3230            },
3231            Instruction::Halt,
3232            Instruction::CreateCell {
3233                dst: Register::new(75),
3234            },
3235        ];
3236        assert_eq!(instructions.len(), 37, "one case per opcode");
3237        let limits = DecodeLimits::default();
3238        for (opcode, instruction) in instructions.into_iter().enumerate() {
3239            let mut bytes = Vec::new();
3240            encode_instruction(instruction, &mut bytes);
3241            assert_eq!(
3242                bytes.first().copied(),
3243                Some(opcode as u8),
3244                "opcode tag is its table index"
3245            );
3246            let mut decoder = Decoder {
3247                bytes: &bytes,
3248                offset: 0,
3249                limits: &limits,
3250                total_instructions: 0,
3251            };
3252            let decoded = decoder.instruction().expect("opcode decodes");
3253            assert_eq!(decoded, instruction, "{instruction:?} round-trips");
3254            assert_eq!(decoder.offset, bytes.len(), "consumes exactly its bytes");
3255        }
3256    }
3257
3258    #[test]
3259    fn minimal_module_has_exact_canonical_wire() {
3260        let module = Module::new(
3261            vec![Constant::Int32(7)],
3262            vec![Function::new(
3263                None,
3264                0,
3265                0,
3266                1,
3267                flags(),
3268                vec![
3269                    Instruction::LoadConst {
3270                        dst: Register::new(0),
3271                        constant: ConstantId::new(0),
3272                    },
3273                    Instruction::Return {
3274                        value: Register::new(0),
3275                    },
3276                ],
3277                Vec::new(),
3278            )],
3279            FunctionId::new(0),
3280        );
3281        let encoded = module.verify().expect("valid").encode();
3282        let mut expected = prefix();
3283        expected.extend_from_slice(&[
3284            1, // constant count
3285            1, 7, 0, 0, 0, // Int32(7)
3286            1, // function count
3287            0, // entry
3288            0, // name none
3289            0, // capture count
3290            0, // parameter count
3291            1, // register count
3292            0, // flags
3293            2, // code length
3294            0, 0, 0, // LoadConst dst0 const0
3295            30, 0, // Return value0
3296            0, // handler count
3297        ]);
3298        assert_eq!(encoded, expected);
3299    }
3300
3301    #[test]
3302    fn fields_beyond_127_scale_and_round_trip() {
3303        // 200 constants, 200 registers, 200 instructions -> multi-byte LEB128.
3304        let constant_count: u32 = 200;
3305        let mut constants = Vec::new();
3306        for value in 0..constant_count {
3307            constants.push(Constant::Int32(value as i32));
3308        }
3309        let register_count: u32 = 200;
3310        let mut code = Vec::new();
3311        for register in 0..register_count {
3312            code.push(Instruction::LoadConst {
3313                dst: Register::new(register),
3314                constant: ConstantId::new(register % constant_count),
3315            });
3316        }
3317        code.push(Instruction::Return {
3318            value: Register::new(register_count - 1),
3319        });
3320        let module = Module::new(
3321            constants,
3322            vec![Function::new(
3323                None,
3324                0,
3325                0,
3326                register_count,
3327                flags(),
3328                code,
3329                Vec::new(),
3330            )],
3331            FunctionId::new(0),
3332        );
3333        let verified = module.clone().verify().expect("large module verifies");
3334        let encoded = verified.encode();
3335        // 200 needs two LEB128 bytes; confirm the multi-byte path is exercised.
3336        assert!(
3337            (0..encoded.len().saturating_sub(1))
3338                .any(|i| encoded[i] == 0xc8 && encoded[i + 1] == 0x01),
3339            "constant count 200 must use a two-byte LEB128 group"
3340        );
3341        let decoded = decode(&encoded, &DecodeLimits::default()).expect("round trip");
3342        assert_eq!(decoded, module);
3343        assert_eq!(decoded.verify().expect("reverify").encode(), encoded,);
3344    }
3345
3346    /// A call whose arguments array holds far more than the old 127/fixed-window
3347    /// ceiling: a single arguments register, no window, no arg-count field.
3348    #[test]
3349    fn calls_scale_past_fixed_window_via_arguments_array() {
3350        let mut code = vec![
3351            Instruction::CreateObject {
3352                dst: Register::new(0),
3353            }, // callee stand-in
3354            Instruction::CreateObject {
3355                dst: Register::new(1),
3356            }, // this
3357            Instruction::CreateArray {
3358                dst: Register::new(2),
3359            }, // arguments array
3360        ];
3361        // Push 500 elements into the arguments array: arity is unbounded by the
3362        // ISA shape, limited only by structural register/instruction ceilings.
3363        for _ in 0..500 {
3364            code.push(Instruction::ArrayPush {
3365                array: Register::new(2),
3366                value: Register::new(1),
3367            });
3368        }
3369        code.push(Instruction::Call {
3370            dst: Register::new(3),
3371            callee: Register::new(0),
3372            this_value: Register::new(1),
3373            arguments: Register::new(2),
3374        });
3375        code.push(Instruction::Return {
3376            value: Register::new(3),
3377        });
3378        let module = Module::new(
3379            vec![],
3380            vec![Function::new(None, 0, 0, 4, flags(), code, vec![])],
3381            FunctionId::new(0),
3382        );
3383        let verified = module.clone().verify().expect("variadic call verifies");
3384        assert_eq!(
3385            decode(&verified.encode(), &DecodeLimits::default())
3386                .expect("round trip")
3387                .verify()
3388                .expect("reverify")
3389                .encode(),
3390            verified.encode()
3391        );
3392    }
3393
3394    fn decode_leb(bytes: &[u8]) -> Result<u32, DecodeError> {
3395        let limits = DecodeLimits::default();
3396        let mut decoder = Decoder {
3397            bytes,
3398            offset: 0,
3399            limits: &limits,
3400            total_instructions: 0,
3401        };
3402        decoder.leb128()
3403    }
3404
3405    #[test]
3406    fn leb128_accepts_only_canonical_encodings() {
3407        assert_eq!(decode_leb(&[0]), Ok(0));
3408        assert_eq!(decode_leb(&[1]), Ok(1));
3409        assert_eq!(decode_leb(&[127]), Ok(127));
3410        assert_eq!(decode_leb(&[0xc8, 0x01]), Ok(200));
3411        assert_eq!(decode_leb(&[0xff, 0xff, 0xff, 0xff, 0x0f]), Ok(u32::MAX));
3412
3413        // Overlong single zero group.
3414        assert_eq!(
3415            decode_leb(&[0x80, 0x00]),
3416            Err(DecodeError {
3417                offset: 0,
3418                kind: DecodeErrorKind::NonCanonicalInteger,
3419            })
3420        );
3421        // Overlong nonzero value.
3422        assert_eq!(
3423            decode_leb(&[0x81, 0x00]),
3424            Err(DecodeError {
3425                offset: 0,
3426                kind: DecodeErrorKind::NonCanonicalInteger,
3427            })
3428        );
3429        // Truncated mid-integer: EOF is reported where the missing
3430        // continuation byte belongs (offset 1, after consuming 0x80).
3431        assert_eq!(
3432            decode_leb(&[0x80]),
3433            Err(DecodeError {
3434                offset: 1,
3435                kind: DecodeErrorKind::UnexpectedEof,
3436            })
3437        );
3438        // Overflow beyond 32 bits (sixth group / high bits on fifth group).
3439        assert_eq!(
3440            decode_leb(&[0xff, 0xff, 0xff, 0xff, 0x1f]),
3441            Err(DecodeError {
3442                offset: 0,
3443                kind: DecodeErrorKind::IntegerOverflow,
3444            })
3445        );
3446        assert_eq!(
3447            decode_leb(&[0x80, 0x80, 0x80, 0x80, 0x80]),
3448            Err(DecodeError {
3449                offset: 0,
3450                kind: DecodeErrorKind::IntegerOverflow,
3451            })
3452        );
3453    }
3454
3455    #[test]
3456    fn decode_errors_report_the_first_bad_byte() {
3457        let mut bytes = prefix();
3458        bytes[3] ^= 1;
3459        assert_eq!(
3460            decode(&bytes, &DecodeLimits::default()),
3461            Err(DecodeError {
3462                offset: 3,
3463                kind: DecodeErrorKind::BadMagic {
3464                    expected: MAGIC[3],
3465                    actual: bytes[3],
3466                },
3467            })
3468        );
3469        assert_eq!(
3470            decode(&MAGIC[..4], &DecodeLimits::default()),
3471            Err(DecodeError {
3472                offset: 4,
3473                kind: DecodeErrorKind::UnexpectedEof,
3474            })
3475        );
3476        let mut bad_version = MAGIC.to_vec();
3477        bad_version.push(1);
3478        assert_eq!(
3479            decode(&bad_version, &DecodeLimits::default()),
3480            Err(DecodeError {
3481                offset: 8,
3482                kind: DecodeErrorKind::UnsupportedVersion { version: 1 },
3483            })
3484        );
3485    }
3486
3487    #[test]
3488    fn truncated_string_reports_payload_start() {
3489        let mut bytes = prefix();
3490        bytes.extend_from_slice(&[1, 2, 2, b'a', 0]);
3491
3492        assert_eq!(
3493            decode(&bytes, &DecodeLimits::default()),
3494            Err(DecodeError {
3495                offset: 12,
3496                kind: DecodeErrorKind::UnexpectedEof,
3497            })
3498        );
3499    }
3500
3501    #[test]
3502    fn decoder_checks_limits_before_allocation() {
3503        let mut bytes = prefix();
3504        write_u32(200, &mut bytes); // constant count, two-byte LEB128 [0xc8, 0x01]
3505        let limits = DecodeLimits {
3506            max_constants: 128,
3507            ..DecodeLimits::default()
3508        };
3509        assert_eq!(
3510            decode(&bytes, &limits),
3511            Err(DecodeError {
3512                offset: prefix().len(),
3513                kind: DecodeErrorKind::LimitExceeded {
3514                    field: "constant count",
3515                    limit: 128,
3516                    actual: 200,
3517                },
3518            })
3519        );
3520    }
3521
3522    #[test]
3523    fn decoder_rejects_invalid_bigint_utf8_without_recovery() {
3524        let mut bytes = prefix();
3525        write_u32(1, &mut bytes); // constant count
3526        bytes.push(7); // bigint tag
3527        write_u32(1, &mut bytes); // length
3528        bytes.push(0xff); // invalid UTF-8
3529        assert_eq!(
3530            decode(&bytes, &DecodeLimits::default()),
3531            Err(DecodeError {
3532                offset: prefix().len() + 3,
3533                kind: DecodeErrorKind::InvalidUtf8,
3534            })
3535        );
3536    }
3537
3538    #[test]
3539    fn decoder_rejects_truncated_string_units() {
3540        let mut bytes = prefix();
3541        write_u32(1, &mut bytes);
3542        bytes.push(2);
3543        write_u32(2, &mut bytes);
3544        bytes.extend_from_slice(&0xD800_u16.to_le_bytes());
3545        assert!(matches!(
3546            decode(&bytes, &DecodeLimits::default()),
3547            Err(DecodeError {
3548                kind: DecodeErrorKind::UnexpectedEof,
3549                ..
3550            })
3551        ));
3552    }
3553
3554    #[test]
3555    fn decoder_rejects_string_over_unit_limit() {
3556        let mut bytes = prefix();
3557        write_u32(1, &mut bytes);
3558        bytes.push(2);
3559        write_u32(3, &mut bytes);
3560        let limits = DecodeLimits {
3561            max_string_units: 2,
3562            ..DecodeLimits::default()
3563        };
3564        assert!(matches!(
3565            decode(&bytes, &limits),
3566            Err(DecodeError {
3567                kind: DecodeErrorKind::LimitExceeded {
3568                    field: "string unit count",
3569                    limit: 2,
3570                    actual: 3,
3571                },
3572                ..
3573            })
3574        ));
3575    }
3576
3577    #[test]
3578    fn string_constants_round_trip_lone_surrogates() {
3579        let encoded = Module::new(
3580            vec![Constant::String(EcmaString::from_units(&[
3581                0xD800, 0xDC00, 0xDFFF,
3582            ]))],
3583            vec![Function::new(
3584                None,
3585                0,
3586                0,
3587                1,
3588                flags(),
3589                vec![Instruction::Halt],
3590                Vec::new(),
3591            )],
3592            FunctionId::new(0),
3593        )
3594        .verify()
3595        .expect("lone surrogates are valid literal constants")
3596        .encode();
3597        let module = decode(&encoded, &DecodeLimits::default())
3598            .expect("valid UTF-16 units")
3599            .verify()
3600            .expect("the decoded module verifies");
3601        assert!(matches!(
3602            module.constants(),
3603            [Constant::String(value)] if value.as_units() == [0xD800, 0xDC00, 0xDFFF]
3604        ));
3605        assert_eq!(module.encode(), encoded);
3606    }
3607
3608    #[test]
3609    fn decoder_rejects_noncanonical_nan_bits() {
3610        let mut bytes = prefix();
3611        write_u32(1, &mut bytes);
3612        bytes.push(0); // number tag
3613        bytes.extend_from_slice(&0x7ff0_0000_0000_0001_u64.to_le_bytes());
3614        assert_eq!(
3615            decode(&bytes, &DecodeLimits::default()),
3616            Err(DecodeError {
3617                offset: prefix().len() + 2,
3618                kind: DecodeErrorKind::NonCanonicalNumber {
3619                    bits: 0x7ff0_0000_0000_0001,
3620                },
3621            })
3622        );
3623        assert_eq!(
3624            NumberBits::from_bits(0xfff0_0000_0000_0001).bits(),
3625            CANONICAL_NAN_BITS
3626        );
3627    }
3628
3629    #[test]
3630    fn decoder_rejects_invalid_bigint_text() {
3631        for text in ["007", "-0", "", "-", "1a", "+1", "00"] {
3632            let mut bytes = prefix();
3633            write_u32(1, &mut bytes);
3634            bytes.push(7); // bigint tag
3635            write_text(text, &mut bytes);
3636            assert!(
3637                matches!(
3638                    decode(&bytes, &DecodeLimits::default()),
3639                    Err(DecodeError {
3640                        kind: DecodeErrorKind::InvalidBigInt,
3641                        ..
3642                    })
3643                ),
3644                "expected {text:?} to be rejected"
3645            );
3646        }
3647        // Canonical forms accepted.
3648        for text in ["0", "7", "-7", "1234567890123456789012345"] {
3649            assert!(BigIntLiteral::new(text.to_owned()).is_some(), "{text}");
3650        }
3651    }
3652
3653    /// A module builder for the flags/opcode/operator hostile tests: one
3654    /// function with `body` as its raw code bytes.
3655    fn one_function_bytes(body: &[u8]) -> Vec<u8> {
3656        let mut bytes = prefix();
3657        write_u32(0, &mut bytes); // constants
3658        write_u32(1, &mut bytes); // functions
3659        write_u32(0, &mut bytes); // entry
3660        write_u32(0, &mut bytes); // name none
3661        write_u32(0, &mut bytes); // capture count
3662        write_u32(0, &mut bytes); // parameter count
3663        write_u32(1, &mut bytes); // register count
3664        bytes.push(0); // flags
3665        write_u32(1, &mut bytes); // code length
3666        bytes.extend_from_slice(body);
3667        bytes
3668    }
3669
3670    #[test]
3671    fn decoder_rejects_unknown_tags_flags_and_operators() {
3672        // Invalid constant tag.
3673        let mut bad_constant = prefix();
3674        write_u32(1, &mut bad_constant);
3675        bad_constant.push(200);
3676        assert!(matches!(
3677            decode(&bad_constant, &DecodeLimits::default()),
3678            Err(DecodeError {
3679                kind: DecodeErrorKind::InvalidConstantTag { tag: 200 },
3680                ..
3681            })
3682        ));
3683
3684        // Invalid function flags (unknown bit).
3685        let mut bad_flags = prefix();
3686        write_u32(0, &mut bad_flags); // constants
3687        write_u32(1, &mut bad_flags); // functions
3688        write_u32(0, &mut bad_flags); // entry
3689        write_u32(0, &mut bad_flags); // name none
3690        write_u32(0, &mut bad_flags); // capture count
3691        write_u32(0, &mut bad_flags); // params
3692        write_u32(1, &mut bad_flags); // registers
3693        bad_flags.push(0b100); // unknown flag bit
3694        assert!(matches!(
3695            decode(&bad_flags, &DecodeLimits::default()),
3696            Err(DecodeError {
3697                kind: DecodeErrorKind::InvalidFunctionFlags { bits: 0b100 },
3698                ..
3699            })
3700        ));
3701
3702        // Invalid opcode.
3703        assert!(matches!(
3704            decode(&one_function_bytes(&[250]), &DecodeLimits::default()),
3705            Err(DecodeError {
3706                kind: DecodeErrorKind::InvalidOpcode { opcode: 250 },
3707                ..
3708            })
3709        ));
3710        // Unary with a bad operator tag (opcode 2, dst 0, op 99, operand 0).
3711        assert!(matches!(
3712            decode(
3713                &one_function_bytes(&[2, 0, 99, 0]),
3714                &DecodeLimits::default()
3715            ),
3716            Err(DecodeError {
3717                kind: DecodeErrorKind::InvalidUnaryOp { tag: 99 },
3718                ..
3719            })
3720        ));
3721        // Binary with a bad operator tag (opcode 3, dst 0, op 99, l 0, r 0).
3722        assert!(matches!(
3723            decode(
3724                &one_function_bytes(&[3, 0, 99, 0, 0]),
3725                &DecodeLimits::default()
3726            ),
3727            Err(DecodeError {
3728                kind: DecodeErrorKind::InvalidBinaryOp { tag: 99 },
3729                ..
3730            })
3731        ));
3732        // GetIterator with a bad kind tag (opcode 25, dst 0, src 0, kind 9).
3733        assert!(matches!(
3734            decode(
3735                &one_function_bytes(&[25, 0, 0, 9]),
3736                &DecodeLimits::default()
3737            ),
3738            Err(DecodeError {
3739                kind: DecodeErrorKind::InvalidIteratorKind { tag: 9 },
3740                ..
3741            })
3742        ));
3743        // DefineAccessor with a bad kind tag (opcode 10, obj 0, key 0, acc 0, kind 9).
3744        assert!(matches!(
3745            decode(
3746                &one_function_bytes(&[10, 0, 0, 0, 9]),
3747                &DecodeLimits::default()
3748            ),
3749            Err(DecodeError {
3750                kind: DecodeErrorKind::InvalidAccessorKind { tag: 9 },
3751                ..
3752            })
3753        ));
3754    }
3755
3756    #[test]
3757    fn decoder_rejects_trailing_bytes() {
3758        let mut trailing = rich_module().verify().expect("valid").encode();
3759        trailing.push(0);
3760        assert!(matches!(
3761            decode(&trailing, &DecodeLimits::default()),
3762            Err(DecodeError {
3763                kind: DecodeErrorKind::TrailingBytes { count: 1 },
3764                ..
3765            })
3766        ));
3767    }
3768
3769    #[test]
3770    fn decode_is_total_over_hostile_bytes() {
3771        // Every truncation of a valid module either decodes or errors, never
3772        // panics; and adversarial byte soup never panics.
3773        let valid = rich_module().verify().expect("valid").encode();
3774        for len in 0..=valid.len() {
3775            let _ = decode(&valid[..len], &DecodeLimits::default());
3776        }
3777        for seed in 0u16..=255 {
3778            let soup: Vec<u8> = (0..64).map(|i| (seed as u8).wrapping_mul(i + 1)).collect();
3779            let _ = decode(&soup, &DecodeLimits::default());
3780        }
3781    }
3782
3783    #[test]
3784    fn single_byte_mutations_change_decode_result() {
3785        let encoded = rich_module().verify().expect("valid").encode();
3786        let baseline = decode(&encoded, &DecodeLimits::default());
3787        let mut observed_difference = false;
3788        for index in 0..encoded.len() {
3789            let mut mutated = encoded.clone();
3790            mutated[index] = mutated[index].wrapping_add(1);
3791            if decode(&mutated, &DecodeLimits::default()) != baseline {
3792                observed_difference = true;
3793            }
3794        }
3795        assert!(
3796            observed_difference,
3797            "flipping bytes must be observable at the decode boundary"
3798        );
3799    }
3800
3801    #[test]
3802    fn verifier_rejects_empty_module_and_bad_entry() {
3803        assert_eq!(
3804            Module::new(vec![], vec![], FunctionId::new(0)).verify(),
3805            Err(module_error(VerifyErrorKind::EmptyModule))
3806        );
3807        let bad_entry = Module::new(
3808            vec![],
3809            vec![Function::new(
3810                None,
3811                0,
3812                0,
3813                0,
3814                flags(),
3815                vec![Instruction::Halt],
3816                vec![],
3817            )],
3818            FunctionId::new(1),
3819        );
3820        assert!(matches!(
3821            bad_entry.verify(),
3822            Err(VerifyError {
3823                kind: VerifyErrorKind::EntryFunctionOutOfBounds { .. },
3824                ..
3825            })
3826        ));
3827    }
3828
3829    #[test]
3830    fn verifier_rejects_bad_function_metadata() {
3831        let empty_function = Module::new(
3832            vec![],
3833            vec![Function::new(None, 0, 0, 0, flags(), vec![], vec![])],
3834            FunctionId::new(0),
3835        );
3836        assert!(matches!(
3837            empty_function.verify(),
3838            Err(VerifyError {
3839                kind: VerifyErrorKind::EmptyFunction,
3840                ..
3841            })
3842        ));
3843
3844        let too_many_params = Module::new(
3845            vec![],
3846            vec![Function::new(
3847                None,
3848                0,
3849                2,
3850                1,
3851                flags(),
3852                vec![Instruction::Halt],
3853                vec![],
3854            )],
3855            FunctionId::new(0),
3856        );
3857        assert!(matches!(
3858            too_many_params.verify(),
3859            Err(VerifyError {
3860                kind: VerifyErrorKind::ParameterCountExceedsRegisters { .. },
3861                ..
3862            })
3863        ));
3864
3865        // Captures plus parameters overflow the register file even though each
3866        // alone fits: 1 capture + 1 parameter = 2 > 1 register.
3867        let captures_and_params_overflow = Module::new(
3868            vec![],
3869            vec![Function::new(
3870                None,
3871                1,
3872                1,
3873                1,
3874                flags(),
3875                vec![Instruction::Halt],
3876                vec![],
3877            )],
3878            FunctionId::new(0),
3879        );
3880        assert!(matches!(
3881            captures_and_params_overflow.verify(),
3882            Err(VerifyError {
3883                kind: VerifyErrorKind::EntryRegistersExceedRegisterCount { .. },
3884                ..
3885            })
3886        ));
3887
3888        let bad_name = Module::new(
3889            vec![Constant::Int32(0)],
3890            vec![Function::new(
3891                Some(ConstantId::new(0)),
3892                0,
3893                0,
3894                0,
3895                flags(),
3896                vec![Instruction::Halt],
3897                vec![],
3898            )],
3899            FunctionId::new(0),
3900        );
3901        assert!(matches!(
3902            bad_name.verify(),
3903            Err(VerifyError {
3904                kind: VerifyErrorKind::FunctionNameNotString { .. },
3905                ..
3906            })
3907        ));
3908    }
3909
3910    #[test]
3911    fn verifier_rejects_out_of_bounds_references() {
3912        let bad_register = Module::new(
3913            vec![Constant::Int32(0)],
3914            vec![Function::new(
3915                None,
3916                0,
3917                0,
3918                1,
3919                flags(),
3920                vec![Instruction::LoadConst {
3921                    dst: Register::new(1),
3922                    constant: ConstantId::new(0),
3923                }],
3924                vec![],
3925            )],
3926            FunctionId::new(0),
3927        );
3928        assert!(matches!(
3929            bad_register.verify(),
3930            Err(VerifyError {
3931                kind: VerifyErrorKind::RegisterOutOfBounds { .. },
3932                ..
3933            })
3934        ));
3935
3936        let bad_constant = Module::new(
3937            vec![],
3938            vec![Function::new(
3939                None,
3940                0,
3941                0,
3942                1,
3943                flags(),
3944                vec![Instruction::LoadConst {
3945                    dst: Register::new(0),
3946                    constant: ConstantId::new(0),
3947                }],
3948                vec![],
3949            )],
3950            FunctionId::new(0),
3951        );
3952        assert!(matches!(
3953            bad_constant.verify(),
3954            Err(VerifyError {
3955                kind: VerifyErrorKind::ConstantOutOfBounds { .. },
3956                ..
3957            })
3958        ));
3959
3960        let bad_function_ref = Module::new(
3961            vec![],
3962            vec![Function::new(
3963                None,
3964                0,
3965                0,
3966                1,
3967                flags(),
3968                vec![
3969                    Instruction::CreateArray {
3970                        dst: Register::new(0),
3971                    },
3972                    Instruction::CreateClosure {
3973                        dst: Register::new(0),
3974                        function: FunctionId::new(5),
3975                        captures: Register::new(0),
3976                    },
3977                    Instruction::Return {
3978                        value: Register::new(0),
3979                    },
3980                ],
3981                vec![],
3982            )],
3983            FunctionId::new(0),
3984        );
3985        assert!(matches!(
3986            bad_function_ref.verify(),
3987            Err(VerifyError {
3988                kind: VerifyErrorKind::FunctionReferenceOutOfBounds { .. },
3989                ..
3990            })
3991        ));
3992    }
3993
3994    /// Global/private/regexp/export/import names must resolve to string
3995    /// constants; a non-string constant is rejected.
3996    #[test]
3997    fn verifier_requires_string_constants_for_named_refs() {
3998        let cases: Vec<(&str, Instruction)> = vec![
3999            (
4000                "LoadGlobal",
4001                Instruction::LoadGlobal {
4002                    dst: Register::new(0),
4003                    name: ConstantId::new(0),
4004                },
4005            ),
4006            (
4007                "TypeOfGlobal",
4008                Instruction::TypeOfGlobal {
4009                    dst: Register::new(0),
4010                    name: ConstantId::new(0),
4011                },
4012            ),
4013            (
4014                "CreatePrivateName",
4015                Instruction::CreatePrivateName {
4016                    dst: Register::new(0),
4017                    description: ConstantId::new(0),
4018                },
4019            ),
4020            (
4021                "CreateRegExp",
4022                Instruction::CreateRegExp {
4023                    dst: Register::new(0),
4024                    pattern: ConstantId::new(0),
4025                    flags: ConstantId::new(0),
4026                },
4027            ),
4028            (
4029                "Import",
4030                Instruction::Import {
4031                    dst: Register::new(0),
4032                    specifier: ConstantId::new(0),
4033                },
4034            ),
4035        ];
4036        for (label, instruction) in cases {
4037            let module = Module::new(
4038                vec![Constant::Int32(0)], // constant 0 is NOT a string
4039                vec![Function::new(
4040                    None,
4041                    0,
4042                    0,
4043                    1,
4044                    flags(),
4045                    vec![
4046                        instruction,
4047                        Instruction::Return {
4048                            value: Register::new(0),
4049                        },
4050                    ],
4051                    vec![],
4052                )],
4053                FunctionId::new(0),
4054            );
4055            assert!(
4056                matches!(
4057                    module.verify(),
4058                    Err(VerifyError {
4059                        kind: VerifyErrorKind::StringConstantExpected { .. },
4060                        ..
4061                    })
4062                ),
4063                "{label} must require a string constant"
4064            );
4065        }
4066
4067        // Export with a non-string name is likewise rejected.
4068        let export = Module::new(
4069            vec![Constant::Int32(0)],
4070            vec![Function::new(
4071                None,
4072                0,
4073                0,
4074                1,
4075                flags(),
4076                vec![
4077                    Instruction::CreateObject {
4078                        dst: Register::new(0),
4079                    },
4080                    Instruction::Export {
4081                        name: ConstantId::new(0),
4082                        src: Register::new(0),
4083                    },
4084                    Instruction::Return {
4085                        value: Register::new(0),
4086                    },
4087                ],
4088                vec![],
4089            )],
4090            FunctionId::new(0),
4091        );
4092        assert!(matches!(
4093            export.verify(),
4094            Err(VerifyError {
4095                kind: VerifyErrorKind::StringConstantExpected { .. },
4096                ..
4097            })
4098        ));
4099    }
4100
4101    /// A `CreateClosure` capture-array register and a `Call`/`Construct`
4102    /// arguments register must be definitely initialized before use.
4103    #[test]
4104    fn verifier_requires_capture_and_argument_registers_initialized() {
4105        // captures register (r0) read before any write.
4106        let uninit_captures = Module::new(
4107            vec![],
4108            vec![Function::new(
4109                None,
4110                0,
4111                0,
4112                2,
4113                flags(),
4114                vec![
4115                    Instruction::CreateClosure {
4116                        dst: Register::new(1),
4117                        function: FunctionId::new(0),
4118                        captures: Register::new(0),
4119                    },
4120                    Instruction::Return {
4121                        value: Register::new(1),
4122                    },
4123                ],
4124                vec![],
4125            )],
4126            FunctionId::new(0),
4127        );
4128        assert_eq!(
4129            uninit_captures.verify(),
4130            Err(VerifyError {
4131                function: Some(FunctionId::new(0)),
4132                instruction: Some(Pc::new(0)),
4133                kind: VerifyErrorKind::ReadBeforeWrite {
4134                    register: Register::new(0),
4135                },
4136            })
4137        );
4138
4139        // arguments register (r2) read before any write in a Call.
4140        let uninit_args = Module::new(
4141            vec![],
4142            vec![Function::new(
4143                None,
4144                0,
4145                0,
4146                4,
4147                flags(),
4148                vec![
4149                    Instruction::CreateObject {
4150                        dst: Register::new(0),
4151                    },
4152                    Instruction::CreateObject {
4153                        dst: Register::new(1),
4154                    },
4155                    Instruction::Call {
4156                        dst: Register::new(3),
4157                        callee: Register::new(0),
4158                        this_value: Register::new(1),
4159                        arguments: Register::new(2),
4160                    },
4161                    Instruction::Return {
4162                        value: Register::new(3),
4163                    },
4164                ],
4165                vec![],
4166            )],
4167            FunctionId::new(0),
4168        );
4169        assert_eq!(
4170            uninit_args.verify(),
4171            Err(VerifyError {
4172                function: Some(FunctionId::new(0)),
4173                instruction: Some(Pc::new(2)),
4174                kind: VerifyErrorKind::ReadBeforeWrite {
4175                    register: Register::new(2),
4176                },
4177            })
4178        );
4179    }
4180
4181    #[test]
4182    fn verifier_rejects_reachable_falloff() {
4183        let module = Module::new(
4184            vec![],
4185            vec![Function::new(
4186                None,
4187                0,
4188                0,
4189                1,
4190                flags(),
4191                // Last instruction is a non-terminator: falls off the end.
4192                vec![Instruction::CreateObject {
4193                    dst: Register::new(0),
4194                }],
4195                vec![],
4196            )],
4197            FunctionId::new(0),
4198        );
4199        assert!(matches!(
4200            module.verify(),
4201            Err(VerifyError {
4202                kind: VerifyErrorKind::JumpOutOfBounds { target: 1, .. },
4203                ..
4204            })
4205        ));
4206    }
4207
4208    #[test]
4209    fn verifier_rejects_bad_jump_and_conditional_targets() {
4210        for instruction in [
4211            Instruction::Jump { target: Pc::new(9) },
4212            Instruction::JumpIfFalse {
4213                condition: Register::new(0),
4214                target: Pc::new(9),
4215            },
4216        ] {
4217            let module = Module::new(
4218                vec![],
4219                vec![Function::new(
4220                    None,
4221                    0,
4222                    1, // register 0 is a parameter so the condition read is valid
4223                    1,
4224                    flags(),
4225                    vec![instruction],
4226                    vec![],
4227                )],
4228                FunctionId::new(0),
4229            );
4230            assert!(matches!(
4231                module.verify(),
4232                Err(VerifyError {
4233                    kind: VerifyErrorKind::JumpOutOfBounds { .. },
4234                    ..
4235                })
4236            ));
4237        }
4238    }
4239
4240    #[test]
4241    fn verifier_rejects_read_before_write() {
4242        let module = Module::new(
4243            vec![],
4244            vec![Function::new(
4245                None,
4246                0,
4247                0,
4248                3,
4249                flags(),
4250                vec![
4251                    Instruction::Binary {
4252                        dst: Register::new(2),
4253                        op: BinaryOp::Add,
4254                        left: Register::new(0),
4255                        right: Register::new(1),
4256                    },
4257                    Instruction::Return {
4258                        value: Register::new(2),
4259                    },
4260                ],
4261                vec![],
4262            )],
4263            FunctionId::new(0),
4264        );
4265        assert_eq!(
4266            module.verify(),
4267            Err(VerifyError {
4268                function: Some(FunctionId::new(0)),
4269                instruction: Some(Pc::new(0)),
4270                kind: VerifyErrorKind::ReadBeforeWrite {
4271                    register: Register::new(0),
4272                },
4273            })
4274        );
4275    }
4276
4277    #[test]
4278    fn captures_and_parameters_are_initialized_on_entry() {
4279        // One capture (r0) and two parameters (r1, r2) read immediately with no
4280        // preceding write must verify; captures precede parameters.
4281        let module = Module::new(
4282            vec![],
4283            vec![Function::new(
4284                None,
4285                1,
4286                2,
4287                4,
4288                flags(),
4289                vec![
4290                    Instruction::Binary {
4291                        dst: Register::new(3),
4292                        op: BinaryOp::Add,
4293                        left: Register::new(0),  // capture
4294                        right: Register::new(2), // parameter
4295                    },
4296                    Instruction::Return {
4297                        value: Register::new(1), // parameter
4298                    },
4299                ],
4300                vec![],
4301            )],
4302            FunctionId::new(0),
4303        );
4304        let verified = module.verify().expect("captures and params initialized");
4305        let certificate = verified.certificate(FunctionId::new(0)).unwrap();
4306        assert_eq!(
4307            certificate.initialized_before(Pc::new(0), Register::new(0)),
4308            Some(true),
4309            "capture is initialized on entry"
4310        );
4311        assert_eq!(
4312            certificate.initialized_before(Pc::new(0), Register::new(2)),
4313            Some(true),
4314            "parameter is initialized on entry"
4315        );
4316        assert_eq!(
4317            certificate.initialized_before(Pc::new(0), Register::new(3)),
4318            Some(false),
4319            "a non-entry register is not initialized on entry"
4320        );
4321    }
4322
4323    /// `IteratorNext` writes both `done` and `value`; both are initialized after
4324    /// it, letting a subsequent instruction read them without a separate write.
4325    #[test]
4326    fn iterator_next_initializes_both_written_registers() {
4327        let module = Module::new(
4328            vec![],
4329            vec![Function::new(
4330                None,
4331                0,
4332                0,
4333                4,
4334                flags(),
4335                vec![
4336                    Instruction::CreateArray {
4337                        dst: Register::new(0),
4338                    },
4339                    Instruction::GetIterator {
4340                        dst: Register::new(1),
4341                        src: Register::new(0),
4342                        kind: IteratorKind::Sync,
4343                    },
4344                    Instruction::IteratorNext {
4345                        done: Register::new(2),
4346                        value: Register::new(3),
4347                        iterator: Register::new(1),
4348                    },
4349                    // Read BOTH results: sound only because IteratorNext defines
4350                    // two registers.
4351                    Instruction::Binary {
4352                        dst: Register::new(2),
4353                        op: BinaryOp::StrictEqual,
4354                        left: Register::new(2),
4355                        right: Register::new(3),
4356                    },
4357                    Instruction::Return {
4358                        value: Register::new(2),
4359                    },
4360                ],
4361                vec![],
4362            )],
4363            FunctionId::new(0),
4364        );
4365        let verified = module.verify().expect("two-write dataflow verifies");
4366        let certificate = verified.certificate(FunctionId::new(0)).unwrap();
4367        assert_eq!(
4368            certificate.initialized_before(Pc::new(3), Register::new(2)),
4369            Some(true)
4370        );
4371        assert_eq!(
4372            certificate.initialized_before(Pc::new(3), Register::new(3)),
4373            Some(true)
4374        );
4375    }
4376
4377    #[test]
4378    fn catch_register_is_observable_in_handler() {
4379        // The handler reads its catch register with no in-block write; this is
4380        // sound only because handler dispatch initializes the catch register.
4381        let module = Module::new(
4382            vec![],
4383            vec![Function::new(
4384                None,
4385                0,
4386                0,
4387                1,
4388                flags(),
4389                vec![
4390                    Instruction::CreateObject {
4391                        dst: Register::new(0),
4392                    },
4393                    Instruction::Throw {
4394                        value: Register::new(0),
4395                    },
4396                    Instruction::Return {
4397                        value: Register::new(0),
4398                    }, // handler
4399                ],
4400                vec![ExceptionHandler {
4401                    start: Pc::new(0),
4402                    end: Pc::new(2),
4403                    handler: Pc::new(2),
4404                    catch_register: Register::new(0),
4405                }],
4406            )],
4407            FunctionId::new(0),
4408        );
4409        let verified = module
4410            .verify()
4411            .expect("catch register initialized on dispatch");
4412        let certificate = verified.certificate(FunctionId::new(0)).unwrap();
4413        assert_eq!(
4414            certificate.initialized_before(Pc::new(2), Register::new(0)),
4415            Some(true)
4416        );
4417    }
4418
4419    #[test]
4420    fn verifier_rejects_bad_handler_bounds_and_catch_register() {
4421        let bad_range = Module::new(
4422            vec![],
4423            vec![Function::new(
4424                None,
4425                0,
4426                0,
4427                1,
4428                flags(),
4429                vec![Instruction::Halt, Instruction::Halt],
4430                vec![ExceptionHandler {
4431                    start: Pc::new(1),
4432                    end: Pc::new(1), // empty range
4433                    handler: Pc::new(0),
4434                    catch_register: Register::new(0),
4435                }],
4436            )],
4437            FunctionId::new(0),
4438        );
4439        assert!(matches!(
4440            bad_range.verify(),
4441            Err(VerifyError {
4442                kind: VerifyErrorKind::InvalidHandlerBounds { .. },
4443                ..
4444            })
4445        ));
4446
4447        let bad_catch = Module::new(
4448            vec![],
4449            vec![Function::new(
4450                None,
4451                0,
4452                0,
4453                1,
4454                flags(),
4455                vec![Instruction::Halt, Instruction::Halt],
4456                vec![ExceptionHandler {
4457                    start: Pc::new(0),
4458                    end: Pc::new(2),
4459                    handler: Pc::new(1),
4460                    catch_register: Register::new(5), // >= register_count
4461                }],
4462            )],
4463            FunctionId::new(0),
4464        );
4465        assert!(matches!(
4466            bad_catch.verify(),
4467            Err(VerifyError {
4468                kind: VerifyErrorKind::HandlerCatchRegisterOutOfBounds { .. },
4469                ..
4470            })
4471        ));
4472    }
4473
4474    #[test]
4475    fn verifier_rejects_partially_overlapping_handlers() {
4476        let module = Module::new(
4477            vec![],
4478            vec![Function::new(
4479                None,
4480                0,
4481                0,
4482                1,
4483                flags(),
4484                vec![Instruction::Halt, Instruction::Halt, Instruction::Halt],
4485                vec![
4486                    ExceptionHandler {
4487                        start: Pc::new(0),
4488                        end: Pc::new(2),
4489                        handler: Pc::new(0),
4490                        catch_register: Register::new(0),
4491                    },
4492                    ExceptionHandler {
4493                        start: Pc::new(1),
4494                        end: Pc::new(3),
4495                        handler: Pc::new(2),
4496                        catch_register: Register::new(0),
4497                    },
4498                ],
4499            )],
4500            FunctionId::new(0),
4501        );
4502        assert!(matches!(
4503            module.verify(),
4504            Err(VerifyError {
4505                kind: VerifyErrorKind::HandlersPartiallyOverlap { left: 0, right: 1 },
4506                ..
4507            })
4508        ));
4509    }
4510
4511    #[test]
4512    fn verifier_accepts_disjoint_adjacent_and_nested_handlers() {
4513        // An outer range, an identical duplicate of it (degenerate nesting), a
4514        // nested child, and two adjacent disjoint siblings (`[1,4)`, `[4,7)`,
4515        // `[7,10)` touch at boundaries): all laminar, none partially overlapping.
4516        let handler = |start, end, target| ExceptionHandler {
4517            start: Pc::new(start),
4518            end: Pc::new(end),
4519            handler: Pc::new(target),
4520            catch_register: Register::new(0),
4521        };
4522        let module = Module::new(
4523            vec![],
4524            vec![Function::new(
4525                None,
4526                0,
4527                0,
4528                1,
4529                flags(),
4530                vec![Instruction::Halt; 10],
4531                vec![
4532                    handler(0, 10, 0),
4533                    handler(0, 10, 0),
4534                    handler(1, 4, 1),
4535                    handler(4, 7, 4),
4536                    handler(7, 10, 7),
4537                ],
4538            )],
4539            FunctionId::new(0),
4540        );
4541        assert!(module.verify().is_ok());
4542    }
4543
4544    #[test]
4545    fn verifier_rejects_crossing_handlers_with_original_indices() {
4546        // `[0,10)` and `[5,15)` cross: neither disjoint nor nested. The reported
4547        // indices are the original `Function::handlers` positions.
4548        let module = Module::new(
4549            vec![],
4550            vec![Function::new(
4551                None,
4552                0,
4553                0,
4554                1,
4555                flags(),
4556                vec![Instruction::Halt; 15],
4557                vec![
4558                    ExceptionHandler {
4559                        start: Pc::new(0),
4560                        end: Pc::new(10),
4561                        handler: Pc::new(0),
4562                        catch_register: Register::new(0),
4563                    },
4564                    ExceptionHandler {
4565                        start: Pc::new(5),
4566                        end: Pc::new(15),
4567                        handler: Pc::new(5),
4568                        catch_register: Register::new(0),
4569                    },
4570                ],
4571            )],
4572            FunctionId::new(0),
4573        );
4574        assert!(matches!(
4575            module.verify(),
4576            Err(VerifyError {
4577                kind: VerifyErrorKind::HandlersPartiallyOverlap { left: 0, right: 1 },
4578                ..
4579            })
4580        ));
4581    }
4582
4583    #[test]
4584    fn verifier_accepts_and_preserves_order_of_deeply_nested_handlers() {
4585        // Deeply nested ranges supplied innermost-first (the reverse of the
4586        // sweep's internal sort order): verification accepts them and leaves
4587        // `Function::handlers` in its original wire order.
4588        const DEPTH: u32 = 64;
4589        let mut handlers: Vec<ExceptionHandler> = (0..DEPTH)
4590            .map(|level| ExceptionHandler {
4591                start: Pc::new(level),
4592                end: Pc::new(DEPTH * 2 - level),
4593                handler: Pc::new(level),
4594                catch_register: Register::new(0),
4595            })
4596            .collect();
4597        handlers.reverse();
4598        let module = Module::new(
4599            vec![],
4600            vec![Function::new(
4601                None,
4602                0,
4603                0,
4604                1,
4605                flags(),
4606                vec![Instruction::Halt; (DEPTH * 2) as usize],
4607                handlers.clone(),
4608            )],
4609            FunctionId::new(0),
4610        );
4611        let verified = module.verify().expect("laminar nesting verifies");
4612        assert_eq!(verified.functions()[0].handlers(), handlers.as_slice());
4613    }
4614
4615    #[test]
4616    fn verifier_rejects_facts_work_above_cap() {
4617        // The work-limit uses a strict `>`, so facts-work equal to the cap is
4618        // accepted and cap+1 is rejected. The at-cap acceptance case would force
4619        // the full `MAX_VERIFIER_FACTS_WORDS * 8` = 64 MiB allocation inside
4620        // `definite_initialization` (there is no register_count / code split
4621        // that hits the exact cap cheaply), so the boundary is pinned from the
4622        // rejection side only: one instruction over `MAX_REGISTERS` adds another
4623        // `MAX_REGISTERS / 64` fact words and tips the total past the cap.
4624        let words_per_instruction = u64::from(MAX_REGISTERS) / 64;
4625        let over_cap = (MAX_VERIFIER_FACTS_WORDS / words_per_instruction) as usize + 1;
4626        let module = Module::new(
4627            vec![],
4628            vec![Function::new(
4629                None,
4630                0,
4631                0,
4632                MAX_REGISTERS,
4633                flags(),
4634                vec![Instruction::Halt; over_cap],
4635                vec![],
4636            )],
4637            FunctionId::new(0),
4638        );
4639        assert!(matches!(
4640            module.verify(),
4641            Err(VerifyError {
4642                kind: VerifyErrorKind::VerifierWorkLimitExceeded { .. },
4643                ..
4644            })
4645        ));
4646    }
4647
4648    #[test]
4649    fn verifier_rejects_hostile_near_max_work_before_allocating() {
4650        // A single function whose facts would need 64 M words (512 MiB, 8x the
4651        // cap) is rejected in O(functions) time before any bitset is allocated:
4652        // the module carries only `MAX_REGISTERS` cheap `Halt` instructions.
4653        let module = Module::new(
4654            vec![],
4655            vec![Function::new(
4656                None,
4657                0,
4658                0,
4659                MAX_REGISTERS,
4660                flags(),
4661                vec![Instruction::Halt; MAX_REGISTERS as usize],
4662                vec![],
4663            )],
4664            FunctionId::new(0),
4665        );
4666        assert!(matches!(
4667            module.verify(),
4668            Err(VerifyError {
4669                kind: VerifyErrorKind::VerifierWorkLimitExceeded { .. },
4670                ..
4671            })
4672        ));
4673    }
4674
4675    #[test]
4676    fn verifier_accepts_loop_carried_facts_and_exposes_certificate() {
4677        let module = Module::new(
4678            vec![Constant::Int32(0)],
4679            vec![Function::new(
4680                None,
4681                0,
4682                0,
4683                2,
4684                flags(),
4685                vec![
4686                    Instruction::LoadConst {
4687                        dst: Register::new(0),
4688                        constant: ConstantId::new(0),
4689                    },
4690                    Instruction::Binary {
4691                        dst: Register::new(1),
4692                        op: BinaryOp::Add,
4693                        left: Register::new(0),
4694                        right: Register::new(0),
4695                    },
4696                    Instruction::Jump { target: Pc::new(1) },
4697                ],
4698                vec![],
4699            )],
4700            FunctionId::new(0),
4701        );
4702        let verified = module.verify().expect("loop has a sound witness");
4703        let certificate = verified.certificate(FunctionId::new(0)).unwrap();
4704        assert_eq!(certificate.instruction_count(), 3);
4705        assert_eq!(
4706            certificate.initialized_before(Pc::new(1), Register::new(0)),
4707            Some(true)
4708        );
4709    }
4710
4711    #[test]
4712    fn certificate_queries_are_total_for_all_wrappers() {
4713        let verified = rich_module().verify().expect("valid");
4714        let certificate = verified.certificate(FunctionId::new(0)).unwrap();
4715        // Out-of-range register -> None.
4716        assert_eq!(
4717            certificate.initialized_before(Pc::new(0), Register::new(u32::MAX)),
4718            None
4719        );
4720        // Out-of-range PC -> None.
4721        assert_eq!(
4722            certificate.initialized_before(Pc::new(u32::MAX), Register::new(0)),
4723            None
4724        );
4725        // Missing certificate for an out-of-range function -> None.
4726        assert!(verified.certificate(FunctionId::new(9)).is_none());
4727    }
4728
4729    #[test]
4730    fn verification_bytes_counts_certificate_storage() {
4731        let instruction_count = 2usize;
4732        let register_count = 130u32;
4733        let module = Module::new(
4734            vec![],
4735            vec![Function::new(
4736                None,
4737                0,
4738                1,
4739                register_count,
4740                flags(),
4741                vec![
4742                    Instruction::Move {
4743                        dst: Register::new(129),
4744                        src: Register::new(0),
4745                    },
4746                    Instruction::Return {
4747                        value: Register::new(129),
4748                    },
4749                ],
4750                vec![],
4751            )],
4752            FunctionId::new(0),
4753        )
4754        .verify()
4755        .expect("test module verifies");
4756        let words = RegisterSet::words_for(register_count);
4757        let expected = std::mem::size_of::<Certificate>()
4758            + instruction_count * std::mem::size_of::<RegisterSet>()
4759            + instruction_count * words * std::mem::size_of::<u64>();
4760
4761        assert_eq!(module.verification_bytes(), expected);
4762    }
4763
4764    #[test]
4765    fn compact_scalar_layouts_are_preserved() {
4766        assert_eq!(std::mem::size_of::<Register>(), 4);
4767        assert_eq!(std::mem::size_of::<Pc>(), 4);
4768        assert_eq!(std::mem::size_of::<NumberBits>(), 8);
4769        assert_eq!(std::mem::size_of::<FunctionFlags>(), 2);
4770    }
4771}