Skip to main content

bamts_runtime/
native.rs

1//! The native semantic engine.
2//!
3//! [`NativeEngine`] is the runtime side of the native-execution ABI. It
4//! implements [`bamts_native::NativeOps`] — the panic- and nesting-safe seam the
5//! generated `bamts_*` helpers dispatch into — by reusing the *exact* heap,
6//! global, host, and value-semantic methods of the interpreter [`Machine`]. It
7//! adds no second copy of any JavaScript semantic: `dispatch` funnels every one
8//! of the 30 helpers (which cover all 36 opcodes) into the shared [`Machine`]
9//! methods, so the interpreter and the native engine agree by construction.
10//!
11//! What the engine owns beyond the shared semantics is exactly the layer the
12//! interpreter's dispatch loop cannot be reused for: its own **activation
13//! records** (`this`/`arguments`/`new.target`, resume state), **register
14//! lifetime** (each frame's register file is a driver-stack local, never
15//! engine state, so a validated [`NativeFrame`] view borrows it disjointly),
16//! **completions/handlers**, **suspend/resume**, and **stdout/exit**.
17//!
18//! # `&self` dispatch and interior mutability
19//!
20//! [`NativeOps::dispatch`] takes `&self`: a re-entrant helper (a nested `Call`
21//! or accessor invocation) dispatches back into the *same* engine on the same
22//! thread while an outer `dispatch` is still on the stack. A `&mut self`
23//! receiver would make those nested reborrows aliasing UB, so all mutable
24//! engine state lives behind [`RefCell`]/[`Cell`]. The invariant that keeps this
25//! sound and panic-free: **no borrow guard is ever held across a nested call**
26//! ([`NativeEntryTable::invoke`], [`NativeEngine::execute`], or
27//! [`NativeEngine::invoke_callee`]). Every `machine`/`activations` borrow is
28//! taken, used, and dropped before the nested call begins.
29//!
30//! # Two backends, one semantic core
31//!
32//! * **Reference** ([`NativeEngine::run`]) — the compiler-free driver. It walks
33//!   verified bytecode, and for every value/heap/host operation constructs the
34//!   [`HelperCall`] the code generator would emit and routes it through
35//!   [`NativeOps::dispatch`]. Control flow (branches, calls, handler search,
36//!   suspend/resume) is the driver's, standing in for the CLIF the AOT/JIT
37//!   backend generates. It never invokes [`Machine::run`] or its loop.
38//! * **Linked** ([`run_linked_program`]) — the real AOT/JIT path. It installs
39//!   the engine as the thread's [`NativeOps`] with
40//!   [`bamts_native::with_native_ops`] and invokes compiled entries through a
41//!   borrowed [`bamts_native::NativeEntryTable`]; the compiled code calls the
42//!   `bamts_*` helpers, which dispatch back into the same engine.
43//!
44//! Both backends share the identical `dispatch`/`truthy` implementation.
45//!
46//! No `unsafe` lives here: [`bamts_native::ShadowFrame::new`] and
47//! [`bamts_native::NativeFrame::new`] are safe constructors, and all raw-pointer
48//! dereferencing stays inside `bamts-native`.
49
50use std::borrow::Cow;
51use std::cell::{Cell, RefCell};
52use std::error::Error;
53use std::fmt;
54use std::sync::Arc;
55
56use bamts_bytecode::{
57    ConstantId, EcmaString, FunctionId, Instruction, Module, ModuleId, Program, Verified,
58};
59pub use bamts_native::AbiError;
60use bamts_native::{
61    Completion, CompletionTag, HelperCall, HelperResult, NativeEntryTable, NativeFrame, NativeOps,
62    ShadowFrame, Value, with_native_ops,
63};
64
65use crate::intrinsics::BuiltinOutcome;
66use crate::{
67    CalleeKind, EvalFailure, Execution, ExecutionOutcome, GeneratorResume, GeneratorStart,
68    GeneratorState, GetOutcome, HeapEntry, Host, IteratorNextPrepared, Limits, Machine,
69    PropertyMap, RuntimeError, RuntimeErrorKind, SetOutcome, SuspendedActivation, ThrowOrigin,
70    accessor_from_selector, binary_from_selector, iterator_kind_from_selector, unary_from_selector,
71};
72
73// -- ABI selector encoders (inverse of the shared `*_from_selector` decoders) --
74//
75// The reference driver holds a `bamts_bytecode` operator enum and must produce
76// the raw `u32` selector the code generator would pass, so the value round-trips
77// through the same `HelperCall`/`dispatch` seam generated code uses. The mapping
78// is byte-identical to `bamts_codegen`'s `*_op_selector` functions.
79
80fn unary_to_selector(op: bamts_bytecode::UnaryOp) -> u32 {
81    use bamts_bytecode::UnaryOp;
82    match op {
83        UnaryOp::Void => 0,
84        UnaryOp::TypeOf => 1,
85        UnaryOp::Plus => 2,
86        UnaryOp::Negate => 3,
87        UnaryOp::BitwiseNot => 4,
88        UnaryOp::LogicalNot => 5,
89    }
90}
91
92fn binary_to_selector(op: bamts_bytecode::BinaryOp) -> u32 {
93    use bamts_bytecode::BinaryOp;
94    match op {
95        BinaryOp::Add => 0,
96        BinaryOp::Subtract => 1,
97        BinaryOp::Multiply => 2,
98        BinaryOp::Divide => 3,
99        BinaryOp::Remainder => 4,
100        BinaryOp::Exponent => 5,
101        BinaryOp::BitAnd => 6,
102        BinaryOp::BitOr => 7,
103        BinaryOp::BitXor => 8,
104        BinaryOp::ShiftLeft => 9,
105        BinaryOp::ShiftRight => 10,
106        BinaryOp::UnsignedShiftRight => 11,
107        BinaryOp::Equal => 12,
108        BinaryOp::NotEqual => 13,
109        BinaryOp::StrictEqual => 14,
110        BinaryOp::StrictNotEqual => 15,
111        BinaryOp::LessThan => 16,
112        BinaryOp::LessThanOrEqual => 17,
113        BinaryOp::GreaterThan => 18,
114        BinaryOp::GreaterThanOrEqual => 19,
115        BinaryOp::InstanceOf => 20,
116        BinaryOp::In => 21,
117    }
118}
119
120fn iterator_kind_to_selector(kind: bamts_bytecode::IteratorKind) -> u32 {
121    use bamts_bytecode::IteratorKind;
122    match kind {
123        IteratorKind::Sync => 0,
124        IteratorKind::Async => 1,
125        IteratorKind::Keys => 2,
126    }
127}
128
129fn accessor_to_selector(kind: bamts_bytecode::AccessorKind) -> u32 {
130    use bamts_bytecode::AccessorKind;
131    match kind {
132        AccessorKind::Getter => 0,
133        AccessorKind::Setter => 1,
134    }
135}
136
137// -- Errors ------------------------------------------------------------------
138
139/// A native-execution failure: a runtime error, an AOT/entry linkage error, or
140/// an unrecoverable native trap whose completion value carries a trap id.
141#[derive(Clone, Debug, Eq, PartialEq)]
142pub enum NativeError {
143    /// A deterministic runtime error (throw, limit, or malformed value).
144    Runtime(RuntimeError),
145    /// An AOT image or entry-table linkage failure.
146    Abi(AbiError),
147    /// The entry table was not compiled from the supplied program's canonical bytes.
148    ProgramMismatch,
149    /// A native entry returned `FatalTrap`; `value` is the raw trap id word.
150    FatalTrap { value: Value },
151}
152
153impl fmt::Display for NativeError {
154    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
155        match self {
156            NativeError::Runtime(error) => write!(formatter, "{error}"),
157            NativeError::Abi(error) => write!(formatter, "{error}"),
158            NativeError::ProgramMismatch => {
159                write!(
160                    formatter,
161                    "native entries were compiled from different program bytes"
162                )
163            }
164            NativeError::FatalTrap { value } => {
165                write!(
166                    formatter,
167                    "native fatal trap (value {:#018x})",
168                    value.to_bits()
169                )
170            }
171        }
172    }
173}
174
175impl Error for NativeError {}
176
177impl From<RuntimeError> for NativeError {
178    fn from(error: RuntimeError) -> Self {
179        NativeError::Runtime(error)
180    }
181}
182
183impl From<AbiError> for NativeError {
184    fn from(error: AbiError) -> Self {
185        NativeError::Abi(error)
186    }
187}
188
189// -- Engine state ------------------------------------------------------------
190
191/// Which entry-invocation seam a nested runtime call re-enters.
192#[derive(Clone, Copy, Debug, Eq, PartialEq)]
193enum Backend {
194    /// The compiler-free driver; nested calls recurse into [`NativeEngine::execute`].
195    Reference,
196    /// Compiled entries; nested calls go through the [`NativeEntryTable`].
197    Linked,
198}
199
200/// A resolved bytecode owner that outlives a temporary machine borrow.
201enum CodeRef<'m> {
202    Root(&'m Program<Verified>),
203    Dynamic(Arc<Program<Verified>>),
204}
205
206impl CodeRef<'_> {
207    fn code(&self, module: ModuleId) -> &Module<Verified> {
208        match self {
209            Self::Root(program) => {
210                &program
211                    .module(module)
212                    .expect("verified native module id remains in bounds")
213                    .code
214            }
215            Self::Dynamic(program) => &program.modules()[0].code,
216        }
217    }
218}
219
220/// A native activation record. It holds only per-call *metadata*; the register
221/// file lives as a driver-stack-local `Vec<Value>` so a [`NativeFrame`] can
222/// borrow it disjointly from the engine.
223struct Activation {
224    this_value: Value,
225    new_target: Value,
226    args: Vec<Value>,
227    arguments_object: Option<Value>,
228    /// The resumed value delivered to a pending `ResumeValue` (linked backend).
229    pending_resume: Option<Value>,
230}
231
232/// A thrown value together with its origin, threaded out of `dispatch` through
233/// the engine (the native completion ABI carries only a value).
234#[derive(Clone, Copy)]
235struct PendingThrow {
236    value: Value,
237    origin: ThrowOrigin,
238}
239
240/// How a single driven instruction resolves.
241enum Flow {
242    /// Advance to the next instruction.
243    Next,
244    /// Jump to a program counter.
245    Goto(usize),
246    /// No covering handler; unwind this frame with the thrown value.
247    Unwind(Value, ThrowOrigin),
248}
249
250/// The observable result of driving one activation to termination.
251enum FrameCompletion {
252    Normal(Value),
253    Unwind(Value, ThrowOrigin, usize),
254    Suspend(Value, u32),
255}
256
257#[derive(Clone, Copy)]
258enum FrameDrive {
259    Ordinary,
260    GeneratorStart,
261    GeneratorResume { token: u32, sent: Value },
262}
263
264/// The result of invoking a callee (runtime function, host entity, or foreign
265/// value).
266enum InvokeOutcome {
267    Value(Value),
268    Threw(Value, ThrowOrigin),
269    /// An unrecoverable error; the engine's pending trap state is set.
270    Fatal,
271}
272
273enum ImportFailure {
274    Threw(RuntimeError),
275    Fatal,
276}
277
278/// The native semantic engine over a verified program.
279///
280/// `'m` bounds the module and entry table; `'h` bounds the host borrow. The
281/// embedded [`Machine`] owns the shared heap/globals/host/limits and every
282/// value-semantic method; the engine never calls [`Machine::run`]. All mutable
283/// state is behind [`RefCell`]/[`Cell`] because [`NativeOps`] dispatches on
284/// `&self` (see the module docs on re-entrancy).
285pub struct NativeEngine<'m, 'h, H: Host> {
286    /// The shared semantic core: heap, globals, host, limits, and the one live
287    /// module registry used by interpreter and native execution alike.
288    machine: RefCell<Machine<'h, H>>,
289    /// The verified program whose module-local bytecode drives reference calls.
290    program: &'m Program<Verified>,
291    /// The compiled entry table, used by the linked backend and nested calls.
292    entries: &'m dyn NativeEntryTable,
293    backend: Backend,
294    /// Metadata for each live activation; register files are driver-stack locals.
295    activations: RefCell<Vec<Activation>>,
296    /// Buffered process stdout.
297    stdout: RefCell<Vec<u8>>,
298    /// The process exit code (`0` for normal termination).
299    exit_code: Cell<i32>,
300    /// A throw's value+origin, set by `dispatch`, consumed by the driver.
301    pending_throw: Cell<Option<PendingThrow>>,
302    /// A shallow fatal kind, set by `dispatch`; the driver attaches source.
303    pending_fatal_kind: Cell<Option<RuntimeErrorKind>>,
304    /// A fully-sourced error from a nested activation, propagated verbatim.
305    pending_error: Cell<Option<RuntimeError>>,
306    /// A nested entry-table linkage failure, propagated through `FatalTrap`.
307    pending_abi_error: Cell<Option<AbiError>>,
308}
309
310impl<'m, 'h, H: Host> NativeEngine<'m, 'h, H> {
311    fn build(
312        program: &'m Program<Verified>,
313        entries: &'m dyn NativeEntryTable,
314        host: &'h mut H,
315        limits: Limits,
316        backend: Backend,
317    ) -> Self
318    where
319        'm: 'h,
320    {
321        let mut machine = Machine::new(program, host, limits);
322        machine.frames.clear();
323        machine.live_registers = 0;
324        NativeEngine {
325            machine: RefCell::new(machine),
326            program,
327            entries,
328            backend,
329            activations: RefCell::new(Vec::new()),
330            stdout: RefCell::new(Vec::new()),
331            exit_code: Cell::new(0),
332            pending_throw: Cell::new(None),
333            pending_fatal_kind: Cell::new(None),
334            pending_error: Cell::new(None),
335            pending_abi_error: Cell::new(None),
336        }
337    }
338
339    /// A native engine that drives control flow itself (the compiler-free
340    /// reference backend). `entries` is used only for nested calls under the
341    /// linked backend; the reference backend recurses internally.
342    #[must_use]
343    pub fn new(
344        program: &'m Program<Verified>,
345        entries: &'m dyn NativeEntryTable,
346        host: &'h mut H,
347        limits: Limits,
348    ) -> Self
349    where
350        'm: 'h,
351    {
352        Self::build(program, entries, host, limits, Backend::Reference)
353    }
354
355    /// Buffered process stdout produced during execution.
356    #[must_use]
357    pub fn stdout(&self) -> Vec<u8> {
358        self.stdout.borrow().clone()
359    }
360
361    /// The process exit code.
362    #[must_use]
363    pub fn exit_code(&self) -> i32 {
364        self.exit_code.get()
365    }
366
367    fn max_call_depth(&self) -> usize {
368        self.machine.borrow().limits.max_call_depth
369    }
370
371    fn max_total_registers(&self) -> usize {
372        self.machine.borrow().limits.max_total_registers
373    }
374
375    fn error_at(
376        &self,
377        module: ModuleId,
378        kind: RuntimeErrorKind,
379        function: usize,
380        pc: usize,
381    ) -> RuntimeError {
382        self.machine
383            .borrow()
384            .error_at_in_module(kind, module, function, pc)
385    }
386
387    fn module(&self, module: ModuleId) -> &Module<Verified> {
388        debug_assert!((module.get() as usize) < self.machine.borrow().dynamic_base);
389        &self
390            .program
391            .module(module)
392            .expect("verified native module id remains in bounds")
393            .code
394    }
395
396    fn code_ref(&self, module: ModuleId) -> CodeRef<'m> {
397        let dynamic = {
398            let machine = self.machine.borrow();
399            let index = module.get() as usize;
400            (index >= machine.dynamic_base).then(|| {
401                machine.dynamic[index - machine.dynamic_base]
402                    .program
403                    .clone()
404            })
405        };
406        dynamic.map_or(CodeRef::Root(self.program), CodeRef::Dynamic)
407    }
408
409    fn is_dynamic_module(&self, module: ModuleId) -> bool {
410        module.get() as usize >= self.machine.borrow().dynamic_base
411    }
412
413    // -- Reference backend: control-flow driver ------------------------------
414
415    /// Executes the program entry with the reference driver. Never invokes
416    /// [`Machine::run`].
417    pub fn run(self) -> Result<Execution, RuntimeError> {
418        self.machine.borrow_mut().instantiate_modules()?;
419        self.evaluate_reference_module(self.program.entry())?
420            .ok_or_else(|| {
421                let module = self.program.entry();
422                let function = self.module(module).entry().get() as usize;
423                self.error_at(
424                    module,
425                    RuntimeErrorKind::InvalidVerifiedProgram {
426                        module,
427                        instruction: Instruction::Halt,
428                    },
429                    function,
430                    0,
431                )
432            })
433    }
434
435    fn evaluate_reference_module(
436        &self,
437        module: ModuleId,
438    ) -> Result<Option<Execution>, RuntimeError> {
439        let dependencies = match self.machine.borrow_mut().begin_module_evaluation(module)? {
440            crate::ModuleEvaluation::Cycle => return Ok(None),
441            crate::ModuleEvaluation::Evaluated(result) => return result.map(|()| None),
442            crate::ModuleEvaluation::Ready(dependencies) => dependencies,
443        };
444        for dependency in dependencies {
445            if let Err(error) = self.evaluate_reference_module(dependency) {
446                return self
447                    .machine
448                    .borrow_mut()
449                    .finish_module_evaluation(module, Err(error))
450                    .map(Some);
451            }
452        }
453
454        let code = self.module(module);
455        let function = code.entry().get() as usize;
456        let register_count = code.functions()[function].register_count() as usize;
457        let result = if self.max_call_depth() < 1 {
458            Err(self.error_at(
459                module,
460                RuntimeErrorKind::CallDepthExceeded {
461                    limit: self.max_call_depth(),
462                },
463                function,
464                0,
465            ))
466        } else if register_count > self.max_total_registers() {
467            Err(self.error_at(
468                module,
469                RuntimeErrorKind::RegisterLimitExceeded {
470                    limit: self.max_total_registers(),
471                },
472                function,
473                0,
474            ))
475        } else {
476            self.execute(
477                module,
478                function,
479                Value::UNDEFINED,
480                Value::UNDEFINED,
481                Vec::new(),
482                &[],
483            )
484            .and_then(|(completion, registers)| match completion {
485                FrameCompletion::Normal(value) => Ok(Execution {
486                    outcome: ExecutionOutcome {
487                        stdout: self.stdout.borrow().clone(),
488                        exit_code: self.exit_code.get(),
489                    },
490                    value,
491                    link: value,
492                    entry_registers: registers,
493                }),
494                FrameCompletion::Unwind(value, origin, pc) => Err(self.error_at(
495                    module,
496                    RuntimeErrorKind::UncaughtThrow { value, origin },
497                    function,
498                    pc,
499                )),
500                FrameCompletion::Suspend(value, _) => Err(self.error_at(
501                    module,
502                    RuntimeErrorKind::InvalidValue { value },
503                    function,
504                    0,
505                )),
506            })
507        };
508        self.machine
509            .borrow_mut()
510            .finish_module_evaluation(module, result)
511            .map(Some)
512    }
513
514    /// Seeds a fresh register file: leading captures, then parameters, the rest
515    /// uninitialized — identical to the interpreter's frame prologue.
516    fn seed_registers(
517        &self,
518        code: &Module<Verified>,
519        function: usize,
520        captures: &[Value],
521        args: &[Value],
522    ) -> Vec<Value> {
523        let metadata = &code.functions()[function];
524        let register_count = metadata.register_count() as usize;
525        let capture_count = metadata.capture_count() as usize;
526        let parameter_count = metadata.parameter_count() as usize;
527        let mut registers = vec![Value::UNINITIALIZED; register_count];
528        for index in 0..capture_count {
529            if let Some(slot) = registers.get_mut(index) {
530                *slot = captures.get(index).copied().unwrap_or(Value::UNDEFINED);
531            }
532        }
533        for index in 0..parameter_count {
534            if let Some(slot) = registers.get_mut(capture_count + index) {
535                *slot = args.get(index).copied().unwrap_or(Value::UNDEFINED);
536            }
537        }
538        registers
539    }
540
541    /// Drives one activation to termination. Pushes the activation metadata,
542    /// runs the instruction loop over a driver-local register file, and returns
543    /// both the completion and the final register file (the caller ignores the
544    /// registers except for the entry frame).
545    fn execute(
546        &self,
547        module: ModuleId,
548        function: usize,
549        this_value: Value,
550        new_target: Value,
551        args: Vec<Value>,
552        captures: &[Value],
553    ) -> Result<(FrameCompletion, Vec<Value>), RuntimeError> {
554        let handle = self.code_ref(module);
555        let code = handle.code(module);
556        let register_count = code.functions()[function].register_count() as usize;
557        let mut registers = self.seed_registers(code, function, captures, &args);
558        let reserved = self
559            .machine
560            .borrow_mut()
561            .reserve_native_activation(register_count);
562        reserved.map_err(|kind| self.error_at(module, kind, function, 0))?;
563        self.activations.borrow_mut().push(Activation {
564            this_value,
565            new_target,
566            args,
567            arguments_object: None,
568            pending_resume: None,
569        });
570        let completion =
571            self.run_frame(module, function, code, &mut registers, FrameDrive::Ordinary);
572        self.activations.borrow_mut().pop();
573        self.machine
574            .borrow_mut()
575            .release_native_activation(register_count);
576        completion.map(|completion| (completion, registers))
577    }
578
579    /// The instruction loop for one activation. `registers` is the caller-owned
580    /// register file; a [`NativeFrame`] borrows it disjointly from `self`.
581    fn run_frame(
582        &self,
583        module: ModuleId,
584        function: usize,
585        code: &Module<Verified>,
586        registers: &mut Vec<Value>,
587        drive: FrameDrive,
588    ) -> Result<FrameCompletion, RuntimeError> {
589        let length = u16::try_from(registers.len()).map_err(|_| {
590            let limit = self.max_total_registers();
591            self.error_at(
592                module,
593                RuntimeErrorKind::RegisterLimitExceeded { limit },
594                function,
595                0,
596            )
597        })?;
598        let handles = registers.as_mut_ptr();
599        let mut shadow = ShadowFrame::new(std::ptr::null_mut(), 0, module.get(), handles, length);
600        let mut frame =
601            NativeFrame::new(&mut shadow, registers.as_mut_slice()).ok_or_else(|| {
602                self.error_at(
603                    module,
604                    RuntimeErrorKind::InvalidValue {
605                        value: Value::UNDEFINED,
606                    },
607                    function,
608                    0,
609                )
610            })?;
611
612        let target = crate::RuntimeFunction {
613            module,
614            function: FunctionId::new(function as u32),
615        };
616        let mut pc = match drive {
617            FrameDrive::Ordinary | FrameDrive::GeneratorStart => 0,
618            FrameDrive::GeneratorResume { token, sent } => {
619                let suspend_pc = token.checked_sub(1).map(|pc| pc as usize).ok_or_else(|| {
620                    self.error_at(
621                        module,
622                        RuntimeErrorKind::InvalidValue {
623                            value: Value::UNDEFINED,
624                        },
625                        function,
626                        0,
627                    )
628                })?;
629                let Instruction::Suspend { dst, resume, .. } =
630                    code.functions()[function].code()[suspend_pc]
631                else {
632                    return Err(self.error_at(
633                        module,
634                        RuntimeErrorKind::InvalidValue {
635                            value: Value::UNDEFINED,
636                        },
637                        function,
638                        suspend_pc,
639                    ));
640                };
641                frame.set_register(dst.get(), sent);
642                resume.get() as usize
643            }
644        };
645        loop {
646            let instruction = code.functions()[function].code()[pc];
647            if is_inline_instruction(instruction) {
648                let consumed = self.machine.borrow_mut().consume_fuel(1);
649                if let Err(kind) = consumed {
650                    return Err(self.error_at(module, kind, function, pc));
651                }
652            }
653
654            // Control-flow opcodes are the driver's own responsibility (the CLIF
655            // the AOT/JIT backend generates); every value/heap/host opcode is
656            // routed through `dispatch` into the shared semantics.
657            match instruction {
658                Instruction::Move { dst, src } => {
659                    let value = frame.register(src.get());
660                    frame.set_register(dst.get(), value);
661                    pc += 1;
662                }
663                Instruction::Jump { target } => pc = target.get() as usize,
664                Instruction::JumpIfTrue { condition, target } => {
665                    let value = frame.register(condition.get());
666                    pc = if self.truthy(&mut frame, value) {
667                        target.get() as usize
668                    } else {
669                        pc + 1
670                    };
671                }
672                Instruction::JumpIfFalse { condition, target } => {
673                    let value = frame.register(condition.get());
674                    pc = if self.truthy(&mut frame, value) {
675                        pc + 1
676                    } else {
677                        target.get() as usize
678                    };
679                }
680                Instruction::Return { value } => {
681                    return Ok(FrameCompletion::Normal(frame.register(value.get())));
682                }
683                Instruction::Halt => return Ok(FrameCompletion::Normal(Value::UNDEFINED)),
684                Instruction::Throw { value } => {
685                    let thrown = frame.register(value.get());
686                    match self.raise(
687                        &mut frame,
688                        code,
689                        function,
690                        pc,
691                        thrown,
692                        ThrowOrigin::Bytecode,
693                    ) {
694                        Flow::Next => pc += 1,
695                        Flow::Goto(target) => pc = target,
696                        Flow::Unwind(value, origin) => {
697                            return Ok(FrameCompletion::Unwind(value, origin, pc));
698                        }
699                    }
700                }
701                Instruction::Suspend { src, .. }
702                    if matches!(
703                        drive,
704                        FrameDrive::GeneratorStart | FrameDrive::GeneratorResume { .. }
705                    ) =>
706                {
707                    let token = u32::try_from(pc + 1).map_err(|_| {
708                        self.error_at(
709                            module,
710                            RuntimeErrorKind::InvalidValue {
711                                value: Value::UNDEFINED,
712                            },
713                            function,
714                            pc,
715                        )
716                    })?;
717                    return Ok(FrameCompletion::Suspend(frame.register(src.get()), token));
718                }
719                Instruction::Suspend { .. } => {
720                    match self.raise(
721                        &mut frame,
722                        code,
723                        function,
724                        pc,
725                        Value::UNDEFINED,
726                        ThrowOrigin::TypeError {
727                            operation: "suspend outside an engine-owned event loop",
728                        },
729                    ) {
730                        Flow::Next => pc += 1,
731                        Flow::Goto(target) => pc = target,
732                        Flow::Unwind(value, origin) => {
733                            return Ok(FrameCompletion::Unwind(value, origin, pc));
734                        }
735                    }
736                }
737                other => {
738                    let (call, dst) = self.lower(other, &frame);
739                    let result = self.dispatch(&mut frame, call);
740                    match self.apply(&mut frame, target, code, pc, dst, result)? {
741                        Flow::Next => pc += 1,
742                        Flow::Goto(target) => pc = target,
743                        Flow::Unwind(value, origin) => {
744                            return Ok(FrameCompletion::Unwind(value, origin, pc));
745                        }
746                    }
747                }
748            }
749        }
750    }
751
752    /// Lowers a value/heap/host opcode into the [`HelperCall`] the code
753    /// generator would emit plus the destination register (if any). Reading
754    /// operand registers here mirrors codegen's register loads.
755    fn lower(
756        &self,
757        instruction: Instruction,
758        frame: &NativeFrame<'_>,
759    ) -> (HelperCall, Option<u32>) {
760        let register = |r: bamts_bytecode::Register| frame.register(r.get());
761        match instruction {
762            Instruction::LoadConst { dst, constant } => (
763                HelperCall::LoadConstant {
764                    const_id: constant.get(),
765                },
766                Some(dst.get()),
767            ),
768            Instruction::Unary { dst, op, operand } => (
769                HelperCall::Unary {
770                    op: unary_to_selector(op),
771                    operand: register(operand),
772                },
773                Some(dst.get()),
774            ),
775            Instruction::Binary {
776                dst,
777                op,
778                left,
779                right,
780            } => (
781                HelperCall::Binary {
782                    op: binary_to_selector(op),
783                    left: register(left),
784                    right: register(right),
785                },
786                Some(dst.get()),
787            ),
788            Instruction::CreateObject { dst } => (HelperCall::CreateObject, Some(dst.get())),
789            Instruction::CreateArray { dst } => (HelperCall::CreateArray, Some(dst.get())),
790            Instruction::CreateCell { dst } => (HelperCall::CreateCell, Some(dst.get())),
791            Instruction::CreateClosure {
792                dst,
793                function,
794                captures,
795            } => (
796                HelperCall::CreateClosure {
797                    function_id: function.get(),
798                    captures: register(captures),
799                },
800                Some(dst.get()),
801            ),
802            Instruction::GetProperty { dst, object, key } => (
803                HelperCall::GetProperty {
804                    object: register(object),
805                    key: register(key),
806                },
807                Some(dst.get()),
808            ),
809            Instruction::SetProperty { object, key, value } => (
810                HelperCall::SetProperty {
811                    object: register(object),
812                    key: register(key),
813                    value: register(value),
814                },
815                None,
816            ),
817            Instruction::DeleteProperty { dst, object, key } => (
818                HelperCall::DeleteProperty {
819                    object: register(object),
820                    key: register(key),
821                },
822                Some(dst.get()),
823            ),
824            Instruction::DefineAccessor {
825                object,
826                key,
827                accessor,
828                kind,
829            } => (
830                HelperCall::DefineAccessor {
831                    object: register(object),
832                    key: register(key),
833                    accessor: register(accessor),
834                    kind: accessor_to_selector(kind),
835                },
836                None,
837            ),
838            Instruction::Call {
839                dst,
840                callee,
841                this_value,
842                arguments,
843            } => (
844                HelperCall::Call {
845                    callee: register(callee),
846                    this_value: register(this_value),
847                    arguments: register(arguments),
848                },
849                Some(dst.get()),
850            ),
851            Instruction::Construct {
852                dst,
853                callee,
854                arguments,
855            } => (
856                HelperCall::Construct {
857                    callee: register(callee),
858                    arguments: register(arguments),
859                },
860                Some(dst.get()),
861            ),
862            Instruction::LoadGlobal { dst, name } => {
863                (HelperCall::LoadGlobal { name: name.get() }, Some(dst.get()))
864            }
865            Instruction::StoreGlobal { name, value } => (
866                HelperCall::StoreGlobal {
867                    name: name.get(),
868                    value: register(value),
869                },
870                None,
871            ),
872            Instruction::TypeOfGlobal { dst, name } => (
873                HelperCall::TypeOfGlobal { name: name.get() },
874                Some(dst.get()),
875            ),
876            Instruction::LoadThis { dst } => (HelperCall::LoadThis, Some(dst.get())),
877            Instruction::LoadArguments { dst } => (HelperCall::LoadArguments, Some(dst.get())),
878            Instruction::LoadNewTarget { dst } => (HelperCall::LoadNewTarget, Some(dst.get())),
879            Instruction::ArrayPush { array, value } => (
880                HelperCall::ArrayPush {
881                    array: register(array),
882                    value: register(value),
883                },
884                None,
885            ),
886            Instruction::ArrayExtend { array, iterable } => (
887                HelperCall::ArrayExtend {
888                    array: register(array),
889                    iterable: register(iterable),
890                },
891                None,
892            ),
893            Instruction::ObjectSpread { target, source } => (
894                HelperCall::ObjectSpread {
895                    target: register(target),
896                    source: register(source),
897                },
898                None,
899            ),
900            Instruction::SetPrototype { object, prototype } => (
901                HelperCall::SetPrototype {
902                    object: register(object),
903                    prototype: register(prototype),
904                },
905                None,
906            ),
907            Instruction::CreatePrivateName { dst, description } => (
908                HelperCall::CreatePrivateName {
909                    description: description.get(),
910                },
911                Some(dst.get()),
912            ),
913            Instruction::CreateRegExp {
914                dst,
915                pattern,
916                flags,
917            } => (
918                HelperCall::CreateRegExp {
919                    pattern: pattern.get(),
920                    flags: flags.get(),
921                },
922                Some(dst.get()),
923            ),
924            Instruction::GetIterator { dst, src, kind } => (
925                HelperCall::GetIterator {
926                    src: register(src),
927                    kind: iterator_kind_to_selector(kind),
928                },
929                Some(dst.get()),
930            ),
931            Instruction::IteratorNext {
932                done,
933                value,
934                iterator,
935            } => (
936                HelperCall::IteratorNext {
937                    iterator: register(iterator),
938                    done_reg: done.get(),
939                    value_reg: value.get(),
940                },
941                None,
942            ),
943            Instruction::Import { dst, specifier } => (
944                HelperCall::Import {
945                    specifier: specifier.get(),
946                },
947                Some(dst.get()),
948            ),
949            Instruction::Export { name, src } => (
950                HelperCall::Export {
951                    name: name.get(),
952                    src: register(src),
953                },
954                None,
955            ),
956            // Control-flow opcodes are handled by the driver and never lowered.
957            Instruction::Move { .. }
958            | Instruction::Jump { .. }
959            | Instruction::JumpIfTrue { .. }
960            | Instruction::JumpIfFalse { .. }
961            | Instruction::Return { .. }
962            | Instruction::Throw { .. }
963            | Instruction::Suspend { .. }
964            | Instruction::Halt => {
965                unreachable!("control-flow opcode is not lowered to a helper call")
966            }
967        }
968    }
969
970    /// Interprets a [`HelperResult`] against the frame: stores a normal result
971    /// into `dst`, searches handlers for a throw, or turns a trap into an error.
972    fn take_matching_throw(&self, value: Value) -> (Value, ThrowOrigin) {
973        match self.pending_throw.take() {
974            Some(pending) if pending.value == value => (pending.value, pending.origin),
975            Some(_) | None => (value, ThrowOrigin::Bytecode),
976        }
977    }
978
979    fn apply(
980        &self,
981        frame: &mut NativeFrame<'_>,
982        target: crate::RuntimeFunction,
983        code: &Module<Verified>,
984        pc: usize,
985        dst: Option<u32>,
986        result: HelperResult,
987    ) -> Result<Flow, RuntimeError> {
988        let function = target.function.get() as usize;
989        match result.tag {
990            CompletionTag::Normal => {
991                self.pending_throw.take();
992                if let Some(register) = dst {
993                    frame.set_register(register, result.value);
994                }
995                Ok(Flow::Next)
996            }
997            CompletionTag::Throw => {
998                let (value, origin) = self.take_matching_throw(result.value);
999                Ok(self.raise(frame, code, function, pc, value, origin))
1000            }
1001            CompletionTag::Suspend => {
1002                // The reference driver drives `Suspend` inline; a helper never
1003                // returns it. Treat as a malformed completion.
1004                Err(self.error_at(
1005                    target.module,
1006                    RuntimeErrorKind::InvalidValue {
1007                        value: result.value,
1008                    },
1009                    function,
1010                    pc,
1011                ))
1012            }
1013            CompletionTag::FatalTrap => {
1014                if let Some(error) = self.pending_error.take() {
1015                    return Err(error);
1016                }
1017                let kind = self.pending_fatal_kind.take().unwrap_or({
1018                    RuntimeErrorKind::InvalidValue {
1019                        value: result.value,
1020                    }
1021                });
1022                Err(self.error_at(target.module, kind, function, pc))
1023            }
1024        }
1025    }
1026
1027    /// Searches the current function's handlers covering `pc`. Binds the thrown
1028    /// value into the handler's catch register and jumps, or signals an unwind.
1029    fn raise(
1030        &self,
1031        frame: &mut NativeFrame<'_>,
1032        code: &Module<Verified>,
1033        function: usize,
1034        pc: usize,
1035        value: Value,
1036        origin: ThrowOrigin,
1037    ) -> Flow {
1038        match crate::innermost_handler(&code.functions()[function], pc) {
1039            Some(handler) => {
1040                frame.set_register(handler.catch_register.get(), value);
1041                Flow::Goto(handler.handler.get() as usize)
1042            }
1043            None => Flow::Unwind(value, origin),
1044        }
1045    }
1046
1047    // -- Callee invocation ---------------------------------------------------
1048
1049    /// Invokes a callee value: a runtime closure re-enters native execution; a
1050    /// host function/foreign value routes to the host. Shared with the
1051    /// interpreter's classification via [`Machine::callee_kind`].
1052    fn invoke_callee(
1053        &self,
1054        callee: Value,
1055        this: Value,
1056        args: &[Value],
1057        new_target: Value,
1058    ) -> InvokeOutcome {
1059        let mut callee = callee;
1060        let mut this = this;
1061        let mut args = Cow::Borrowed(args);
1062        let mut new_target = new_target;
1063        loop {
1064            let kind = self.machine.borrow().callee_kind(callee);
1065            match kind {
1066                Ok(CalleeKind::Runtime { target, captures }) => {
1067                    return self.invoke_runtime(target, &captures, this, new_target, args.as_ref());
1068                }
1069                Ok(CalleeKind::Builtin { id }) => {
1070                    let result = {
1071                        self.machine
1072                            .borrow_mut()
1073                            .call_builtin(id, this, args.as_ref(), false)
1074                    };
1075                    match result {
1076                        Ok(BuiltinOutcome::Value(value)) => {
1077                            return InvokeOutcome::Value(value);
1078                        }
1079                        Ok(BuiltinOutcome::Call {
1080                            callee: next,
1081                            this_value: next_this,
1082                            arguments: next_arguments,
1083                        }) => {
1084                            callee = next;
1085                            this = next_this;
1086                            args = Cow::Owned(next_arguments);
1087                            new_target = Value::UNDEFINED;
1088                        }
1089                        Ok(BuiltinOutcome::ConstructCall { .. }) => {
1090                            return InvokeOutcome::Threw(
1091                                Value::UNDEFINED,
1092                                ThrowOrigin::TypeError { operation: "call" },
1093                            );
1094                        }
1095                        Ok(BuiltinOutcome::GeneratorNext {
1096                            generator,
1097                            resume_value,
1098                        }) => {
1099                            return self.resume_generator(generator, resume_value);
1100                        }
1101                        Err(EvalFailure::Throw(origin)) => {
1102                            return InvokeOutcome::Threw(Value::UNDEFINED, origin);
1103                        }
1104                        Err(EvalFailure::ThrowValue(value)) => {
1105                            return InvokeOutcome::Threw(value, ThrowOrigin::Bytecode);
1106                        }
1107                        Err(EvalFailure::ThrowValueOrigin { value, origin }) => {
1108                            return InvokeOutcome::Threw(value, origin);
1109                        }
1110                        Err(EvalFailure::Runtime(kind)) => {
1111                            self.pending_fatal_kind.set(Some(kind));
1112                            return InvokeOutcome::Fatal;
1113                        }
1114                    }
1115                }
1116                Ok(CalleeKind::Bound) => {
1117                    let bound = self
1118                        .machine
1119                        .borrow()
1120                        .flatten_bound(callee, this, args.as_ref());
1121                    match bound {
1122                        Ok(bound) => {
1123                            callee = bound.target;
1124                            if new_target == Value::UNDEFINED {
1125                                this = bound.this_value;
1126                            }
1127                            args = Cow::Owned(bound.arguments);
1128                        }
1129                        Err(kind) => {
1130                            self.pending_fatal_kind.set(Some(kind));
1131                            return InvokeOutcome::Fatal;
1132                        }
1133                    }
1134                }
1135                Ok(CalleeKind::NotCallable) => {
1136                    return InvokeOutcome::Threw(
1137                        Value::UNDEFINED,
1138                        ThrowOrigin::TypeError { operation: "call" },
1139                    );
1140                }
1141                Err(kind) => {
1142                    self.pending_fatal_kind.set(Some(kind));
1143                    return InvokeOutcome::Fatal;
1144                }
1145            }
1146        }
1147    }
1148
1149    /// Re-enters a runtime function with the active recursion and register
1150    /// ceilings, dispatching to the reference driver or the linked entry table.
1151    fn invoke_runtime(
1152        &self,
1153        target: crate::RuntimeFunction,
1154        captures: &[Value],
1155        this: Value,
1156        new_target: Value,
1157        args: &[Value],
1158    ) -> InvokeOutcome {
1159        let index = target.function.get() as usize;
1160        let handle = self.code_ref(target.module);
1161        let flags = handle.code(target.module).functions()[index].flags();
1162        if flags.is_generator && !flags.is_async {
1163            let created = self.machine.borrow_mut().create_generator(GeneratorStart {
1164                target,
1165                captures: captures.to_vec(),
1166                this_value: this,
1167                new_target,
1168                args: args.to_vec(),
1169            });
1170            return match created {
1171                Ok(generator) => InvokeOutcome::Value(generator),
1172                Err(kind) => {
1173                    self.pending_fatal_kind.set(Some(kind));
1174                    InvokeOutcome::Fatal
1175                }
1176            };
1177        }
1178        if flags.is_async && !flags.is_generator {
1179            // Async calls route through the shared reference Machine state
1180            // machine, which drives the body on the interpreter to its first
1181            // await or completion and returns the implicit Promise. No
1182            // bytecode, codegen, or ABI change is involved.
1183            let outcome = self
1184                .machine
1185                .borrow_mut()
1186                .start_async_call(target, captures, this, new_target, args);
1187            return match outcome {
1188                Ok(promise) => InvokeOutcome::Value(promise),
1189                Err(failure) => self.failure_outcome(failure),
1190            };
1191        }
1192        if self.backend == Backend::Reference || self.is_dynamic_module(target.module) {
1193            return match self.execute(
1194                target.module,
1195                index,
1196                this,
1197                new_target,
1198                args.to_vec(),
1199                captures,
1200            ) {
1201                Ok((FrameCompletion::Normal(value), _)) => InvokeOutcome::Value(value),
1202                Ok((FrameCompletion::Unwind(value, origin, _), _)) => {
1203                    InvokeOutcome::Threw(value, origin)
1204                }
1205                Ok((FrameCompletion::Suspend(value, _), _)) => {
1206                    self.pending_fatal_kind
1207                        .set(Some(RuntimeErrorKind::InvalidValue { value }));
1208                    InvokeOutcome::Fatal
1209                }
1210                Err(error) => {
1211                    self.pending_error.set(Some(error));
1212                    InvokeOutcome::Fatal
1213                }
1214            };
1215        }
1216        self.invoke_linked(target, captures, this, new_target, args)
1217    }
1218
1219    fn resume_generator(&self, generator: Value, resume_value: Value) -> InvokeOutcome {
1220        let state = self.machine.borrow_mut().take_generator_state(generator);
1221        let resumed = match state {
1222            Ok(GeneratorState::Completed) => {
1223                return self.generator_result(Value::UNDEFINED, true);
1224            }
1225            Ok(GeneratorState::SuspendedStart(start)) => {
1226                if self.backend == Backend::Reference || self.is_dynamic_module(start.target.module)
1227                {
1228                    self.start_reference_generator(start)
1229                } else {
1230                    self.start_linked_generator(start)
1231                }
1232            }
1233            Ok(GeneratorState::Suspended(activation)) => {
1234                if self.backend == Backend::Reference
1235                    || self.is_dynamic_module(activation.target.module)
1236                {
1237                    self.resume_reference_generator(activation, resume_value)
1238                } else {
1239                    self.resume_linked_generator(activation, resume_value)
1240                }
1241            }
1242            Ok(GeneratorState::Executing) => {
1243                unreachable!("executing state is rejected by take_generator_state")
1244            }
1245            Err(failure) => return self.failure_outcome(failure),
1246        };
1247
1248        match resumed {
1249            Some(GeneratorResume::Yield { value, activation }) => {
1250                let result = self
1251                    .machine
1252                    .borrow_mut()
1253                    .settle_generator_yield(generator, value, activation);
1254                self.eval_outcome(result)
1255            }
1256            Some(GeneratorResume::Return(value)) => {
1257                let settled = self
1258                    .machine
1259                    .borrow_mut()
1260                    .settle_generator_completed(generator);
1261                if let Err(failure) = settled {
1262                    return self.failure_outcome(failure);
1263                }
1264                self.generator_result(value, true)
1265            }
1266            Some(GeneratorResume::Throw { value, origin, .. }) => {
1267                let settled = self
1268                    .machine
1269                    .borrow_mut()
1270                    .settle_generator_completed(generator);
1271                if let Err(failure) = settled {
1272                    return self.failure_outcome(failure);
1273                }
1274                InvokeOutcome::Threw(value, origin)
1275            }
1276            None => {
1277                if let Err(failure) = self
1278                    .machine
1279                    .borrow_mut()
1280                    .settle_generator_completed(generator)
1281                {
1282                    return self.failure_outcome(failure);
1283                }
1284                InvokeOutcome::Fatal
1285            }
1286        }
1287    }
1288
1289    fn generator_result(&self, value: Value, done: bool) -> InvokeOutcome {
1290        let result = self.machine.borrow_mut().iterator_result(value, done);
1291        self.eval_outcome(result)
1292    }
1293
1294    fn eval_outcome(&self, result: Result<Value, EvalFailure>) -> InvokeOutcome {
1295        match result {
1296            Ok(value) => InvokeOutcome::Value(value),
1297            Err(failure) => self.failure_outcome(failure),
1298        }
1299    }
1300
1301    fn failure_outcome(&self, failure: EvalFailure) -> InvokeOutcome {
1302        match failure {
1303            EvalFailure::Throw(origin) => InvokeOutcome::Threw(Value::UNDEFINED, origin),
1304            EvalFailure::ThrowValue(value) => InvokeOutcome::Threw(value, ThrowOrigin::Bytecode),
1305            EvalFailure::ThrowValueOrigin { value, origin } => InvokeOutcome::Threw(value, origin),
1306            EvalFailure::Runtime(kind) => {
1307                self.pending_fatal_kind.set(Some(kind));
1308                InvokeOutcome::Fatal
1309            }
1310        }
1311    }
1312
1313    fn get_outcome(
1314        &self,
1315        outcome: Result<GetOutcome, EvalFailure>,
1316        receiver: Value,
1317    ) -> InvokeOutcome {
1318        match outcome {
1319            Ok(GetOutcome::Value(value)) => InvokeOutcome::Value(value),
1320            Ok(GetOutcome::Text(text)) => {
1321                match self.machine.borrow_mut().allocate(HeapEntry::String(text)) {
1322                    Ok(value) => InvokeOutcome::Value(value),
1323                    Err(kind) => {
1324                        self.pending_fatal_kind.set(Some(kind));
1325                        InvokeOutcome::Fatal
1326                    }
1327                }
1328            }
1329            Ok(GetOutcome::Getter(getter)) => {
1330                self.invoke_callee(getter, receiver, &[], Value::UNDEFINED)
1331            }
1332            Err(failure) => self.failure_outcome(failure),
1333        }
1334    }
1335
1336    fn get_ascii(&self, object: Value, name: &str) -> InvokeOutcome {
1337        let outcome = self.machine.borrow_mut().resolve_get_ascii(object, name);
1338        self.get_outcome(outcome, object)
1339    }
1340
1341    fn get_iterator_active(
1342        &self,
1343        source: Value,
1344        kind: bamts_bytecode::IteratorKind,
1345    ) -> InvokeOutcome {
1346        if kind == bamts_bytecode::IteratorKind::Keys {
1347            let created = self.machine.borrow_mut().create_iterator(source, kind);
1348            return self.eval_outcome(created);
1349        }
1350        let method = {
1351            let mut machine = self.machine.borrow_mut();
1352            let symbol = machine.intrinsics.builtins.symbol_iterator();
1353            let key = match machine.to_property_key(symbol) {
1354                Ok(key) => key,
1355                Err(failure) => return self.failure_outcome(failure),
1356            };
1357            machine.resolve_get(source, &key)
1358        };
1359        let method = match self.get_outcome(method, source) {
1360            InvokeOutcome::Value(method) => method,
1361            other => return other,
1362        };
1363        match self.machine.borrow().is_callable(method) {
1364            Ok(true) => {}
1365            Ok(false) => {
1366                return InvokeOutcome::Threw(
1367                    Value::UNDEFINED,
1368                    ThrowOrigin::TypeError {
1369                        operation: "value is not iterable",
1370                    },
1371                );
1372            }
1373            Err(failure) => return self.failure_outcome(failure),
1374        }
1375        let iterator = match self.invoke_callee(method, source, &[], Value::UNDEFINED) {
1376            InvokeOutcome::Value(iterator) => iterator,
1377            other => return other,
1378        };
1379        if !self.machine.borrow().is_object(iterator) {
1380            return InvokeOutcome::Threw(
1381                Value::UNDEFINED,
1382                ThrowOrigin::TypeError {
1383                    operation: "iterator method returned a non-object",
1384                },
1385            );
1386        }
1387        let next = match self.get_ascii(iterator, "next") {
1388            InvokeOutcome::Value(next) => next,
1389            other => return other,
1390        };
1391        let created = self
1392            .machine
1393            .borrow_mut()
1394            .create_protocol_iterator(iterator, next);
1395        self.eval_outcome(created)
1396    }
1397
1398    fn iterator_next_active(&self, iterator: Value) -> Result<(bool, Value), InvokeOutcome> {
1399        let prepared = self.machine.borrow_mut().prepare_iterator_next(iterator);
1400        let result = match prepared {
1401            Ok(IteratorNextPrepared::Ready { done, value }) => return Ok((done, value)),
1402            Ok(IteratorNextPrepared::Call { callee, this_value }) => {
1403                match self.invoke_callee(callee, this_value, &[], Value::UNDEFINED) {
1404                    InvokeOutcome::Value(result) => result,
1405                    other => return Err(other),
1406                }
1407            }
1408            Err(failure) => return Err(self.failure_outcome(failure)),
1409        };
1410        if !self.machine.borrow().is_object(result) {
1411            return Err(InvokeOutcome::Threw(
1412                Value::UNDEFINED,
1413                ThrowOrigin::TypeError {
1414                    operation: "iterator next returned a non-object",
1415                },
1416            ));
1417        }
1418        let done = match self.get_ascii(result, "done") {
1419            InvokeOutcome::Value(done) => done,
1420            other => return Err(other),
1421        };
1422        if self.machine.borrow().truthy(done) {
1423            return Ok((true, Value::UNDEFINED));
1424        }
1425        let value = match self.get_ascii(result, "value") {
1426            InvokeOutcome::Value(value) => value,
1427            other => return Err(other),
1428        };
1429        Ok((false, value))
1430    }
1431
1432    fn array_extend_active(&self, array: Value, iterable: Value) -> InvokeOutcome {
1433        let iterator = match self.get_iterator_active(iterable, bamts_bytecode::IteratorKind::Sync)
1434        {
1435            InvokeOutcome::Value(iterator) => iterator,
1436            other => return other,
1437        };
1438        loop {
1439            let (done, value) = match self.iterator_next_active(iterator) {
1440                Ok(step) => step,
1441                Err(outcome) => return outcome,
1442            };
1443            if done {
1444                return InvokeOutcome::Value(Value::UNDEFINED);
1445            }
1446            if let Err(failure) = self.machine.borrow_mut().array_push(array, value) {
1447                return self.failure_outcome(failure);
1448            }
1449        }
1450    }
1451
1452    fn start_reference_generator(&self, start: GeneratorStart) -> Option<GeneratorResume> {
1453        let target = start.target;
1454        let index = target.function.get() as usize;
1455        let handle = self.code_ref(target.module);
1456        let code = handle.code(target.module);
1457        let register_count = code.functions()[index].register_count() as usize;
1458        let mut registers = self.seed_registers(code, index, &start.captures, &start.args);
1459        if let Err(kind) = self
1460            .machine
1461            .borrow_mut()
1462            .reserve_suspended_activation_registers(register_count)
1463        {
1464            self.pending_fatal_kind.set(Some(kind));
1465            return None;
1466        }
1467        if let Err(kind) = self.machine.borrow_mut().enter_native_generator() {
1468            self.machine
1469                .borrow_mut()
1470                .release_suspended_activation_registers(register_count);
1471            self.pending_fatal_kind.set(Some(kind));
1472            return None;
1473        }
1474        self.activations.borrow_mut().push(Activation {
1475            this_value: start.this_value,
1476            new_target: start.new_target,
1477            args: start.args.clone(),
1478            arguments_object: None,
1479            pending_resume: None,
1480        });
1481        let completion = self.run_frame(
1482            target.module,
1483            index,
1484            code,
1485            &mut registers,
1486            FrameDrive::GeneratorStart,
1487        );
1488        let activation = self
1489            .activations
1490            .borrow_mut()
1491            .pop()
1492            .expect("generator activation exists");
1493        self.machine.borrow_mut().leave_native_generator();
1494        self.finish_reference_generator(target, registers, activation, completion)
1495    }
1496
1497    fn resume_reference_generator(
1498        &self,
1499        mut suspended: SuspendedActivation,
1500        sent: Value,
1501    ) -> Option<GeneratorResume> {
1502        let target = suspended.target;
1503        let index = target.function.get() as usize;
1504        let handle = self.code_ref(target.module);
1505        let code = handle.code(target.module);
1506        let register_count = suspended.registers.len();
1507        if let Err(kind) = self.machine.borrow_mut().enter_native_generator() {
1508            self.machine
1509                .borrow_mut()
1510                .release_suspended_activation_registers(register_count);
1511            self.pending_fatal_kind.set(Some(kind));
1512            return None;
1513        }
1514        self.activations.borrow_mut().push(Activation {
1515            this_value: suspended.this_value,
1516            new_target: suspended.new_target,
1517            args: suspended.args.clone(),
1518            arguments_object: suspended.arguments_object,
1519            pending_resume: None,
1520        });
1521        let completion = self.run_frame(
1522            target.module,
1523            index,
1524            code,
1525            &mut suspended.registers,
1526            FrameDrive::GeneratorResume {
1527                token: suspended.resume_token,
1528                sent,
1529            },
1530        );
1531        let activation = self
1532            .activations
1533            .borrow_mut()
1534            .pop()
1535            .expect("generator activation exists");
1536        self.machine.borrow_mut().leave_native_generator();
1537        self.finish_reference_generator(target, suspended.registers, activation, completion)
1538    }
1539
1540    fn finish_reference_generator(
1541        &self,
1542        target: crate::RuntimeFunction,
1543        registers: Vec<Value>,
1544        activation: Activation,
1545        completion: Result<FrameCompletion, RuntimeError>,
1546    ) -> Option<GeneratorResume> {
1547        let register_count = registers.len();
1548        match completion {
1549            Ok(FrameCompletion::Suspend(value, resume_token)) => Some(GeneratorResume::Yield {
1550                value,
1551                activation: SuspendedActivation {
1552                    target,
1553                    registers,
1554                    this_value: activation.this_value,
1555                    new_target: activation.new_target,
1556                    args: activation.args,
1557                    arguments_object: activation.arguments_object,
1558                    resume_token,
1559                },
1560            }),
1561            Ok(FrameCompletion::Normal(value)) => {
1562                self.machine
1563                    .borrow_mut()
1564                    .release_suspended_activation_registers(register_count);
1565                Some(GeneratorResume::Return(value))
1566            }
1567            Ok(FrameCompletion::Unwind(value, origin, _)) => {
1568                self.machine
1569                    .borrow_mut()
1570                    .release_suspended_activation_registers(register_count);
1571                Some(GeneratorResume::Throw { value, origin })
1572            }
1573            Err(error) => {
1574                self.machine
1575                    .borrow_mut()
1576                    .release_suspended_activation_registers(register_count);
1577                self.pending_error.set(Some(error));
1578                None
1579            }
1580        }
1581    }
1582
1583    fn start_linked_generator(&self, start: GeneratorStart) -> Option<GeneratorResume> {
1584        let index = start.target.function.get() as usize;
1585        let handle = self.code_ref(start.target.module);
1586        let code = handle.code(start.target.module);
1587        let registers = self.seed_registers(code, index, &start.captures, &start.args);
1588        if let Err(kind) = self
1589            .machine
1590            .borrow_mut()
1591            .reserve_suspended_activation_registers(registers.len())
1592        {
1593            self.pending_fatal_kind.set(Some(kind));
1594            return None;
1595        }
1596        self.drive_linked_generator(
1597            SuspendedActivation {
1598                target: start.target,
1599                registers,
1600                this_value: start.this_value,
1601                new_target: start.new_target,
1602                args: start.args,
1603                arguments_object: None,
1604                resume_token: 0,
1605            },
1606            None,
1607        )
1608    }
1609
1610    fn resume_linked_generator(
1611        &self,
1612        activation: SuspendedActivation,
1613        sent: Value,
1614    ) -> Option<GeneratorResume> {
1615        self.drive_linked_generator(activation, Some(sent))
1616    }
1617
1618    fn drive_linked_generator(
1619        &self,
1620        mut suspended: SuspendedActivation,
1621        pending_resume: Option<Value>,
1622    ) -> Option<GeneratorResume> {
1623        let register_count = suspended.registers.len();
1624        if let Err(kind) = self.machine.borrow_mut().enter_native_generator() {
1625            self.machine
1626                .borrow_mut()
1627                .release_suspended_activation_registers(register_count);
1628            self.pending_fatal_kind.set(Some(kind));
1629            return None;
1630        }
1631        let length = match u16::try_from(register_count) {
1632            Ok(length) => length,
1633            Err(_) => {
1634                self.machine.borrow_mut().leave_native_generator();
1635                self.machine
1636                    .borrow_mut()
1637                    .release_suspended_activation_registers(register_count);
1638                self.pending_fatal_kind
1639                    .set(Some(RuntimeErrorKind::RegisterLimitExceeded {
1640                        limit: self.max_total_registers(),
1641                    }));
1642                return None;
1643            }
1644        };
1645        self.activations.borrow_mut().push(Activation {
1646            this_value: suspended.this_value,
1647            new_target: suspended.new_target,
1648            args: suspended.args.clone(),
1649            arguments_object: suspended.arguments_object,
1650            pending_resume,
1651        });
1652        let handles = suspended.registers.as_mut_ptr();
1653        let (invoked, next_token, out) = {
1654            let mut shadow = ShadowFrame::new(
1655                std::ptr::null_mut(),
1656                suspended.resume_token,
1657                suspended.target.module.get(),
1658                handles,
1659                length,
1660            );
1661            let mut out = Completion::new(Value::UNDEFINED);
1662            let invoked = self.entries.invoke(
1663                suspended.target.module.get(),
1664                suspended.target.function.get(),
1665                &mut shadow,
1666                &mut out,
1667            );
1668            (invoked, shadow.bytecode_pc, out)
1669        };
1670        let activation = self
1671            .activations
1672            .borrow_mut()
1673            .pop()
1674            .expect("generator activation exists");
1675        self.machine.borrow_mut().leave_native_generator();
1676        suspended.arguments_object = activation.arguments_object;
1677
1678        match invoked {
1679            Ok(CompletionTag::Suspend) if next_token != 0 => {
1680                suspended.resume_token = next_token;
1681                Some(GeneratorResume::Yield {
1682                    value: out.value,
1683                    activation: suspended,
1684                })
1685            }
1686            Ok(CompletionTag::Normal) => {
1687                self.pending_throw.take();
1688                self.machine
1689                    .borrow_mut()
1690                    .release_suspended_activation_registers(register_count);
1691                Some(GeneratorResume::Return(out.value))
1692            }
1693            Ok(CompletionTag::Throw) => {
1694                self.machine
1695                    .borrow_mut()
1696                    .release_suspended_activation_registers(register_count);
1697                let (value, origin) = self.take_matching_throw(out.value);
1698                Some(GeneratorResume::Throw { value, origin })
1699            }
1700            Ok(CompletionTag::Suspend | CompletionTag::FatalTrap) => {
1701                self.machine
1702                    .borrow_mut()
1703                    .release_suspended_activation_registers(register_count);
1704                None
1705            }
1706            Err(error) => {
1707                self.machine
1708                    .borrow_mut()
1709                    .release_suspended_activation_registers(register_count);
1710                self.pending_abi_error.set(Some(error));
1711                None
1712            }
1713        }
1714    }
1715
1716    /// Invokes a compiled entry through the borrowed [`NativeEntryTable`],
1717    /// building a fresh child [`ShadowFrame`] over a driver-local register file.
1718    fn invoke_linked(
1719        &self,
1720        target: crate::RuntimeFunction,
1721        captures: &[Value],
1722        this: Value,
1723        new_target: Value,
1724        args: &[Value],
1725    ) -> InvokeOutcome {
1726        debug_assert!(!self.is_dynamic_module(target.module));
1727        let index = target.function.get() as usize;
1728        let handle = self.code_ref(target.module);
1729        let code = handle.code(target.module);
1730        let register_count = code.functions()[index].register_count() as usize;
1731        let mut registers = self.seed_registers(code, index, captures, args);
1732        let length = match u16::try_from(registers.len()) {
1733            Ok(length) => length,
1734            Err(_) => {
1735                let limit = self.max_total_registers();
1736                self.pending_fatal_kind
1737                    .set(Some(RuntimeErrorKind::RegisterLimitExceeded { limit }));
1738                return InvokeOutcome::Fatal;
1739            }
1740        };
1741        if let Err(kind) = self
1742            .machine
1743            .borrow_mut()
1744            .reserve_native_activation(register_count)
1745        {
1746            self.pending_fatal_kind.set(Some(kind));
1747            return InvokeOutcome::Fatal;
1748        }
1749        self.activations.borrow_mut().push(Activation {
1750            this_value: this,
1751            new_target,
1752            args: args.to_vec(),
1753            arguments_object: None,
1754            pending_resume: None,
1755        });
1756        let handles = registers.as_mut_ptr();
1757        let (tag, out) = {
1758            let mut shadow = ShadowFrame::new(
1759                std::ptr::null_mut(),
1760                0,
1761                target.module.get(),
1762                handles,
1763                length,
1764            );
1765            let mut out = Completion::new(Value::UNDEFINED);
1766            let tag = self.entries.invoke(
1767                target.module.get(),
1768                target.function.get(),
1769                &mut shadow,
1770                &mut out,
1771            );
1772            (tag, out)
1773        };
1774        drop(registers);
1775        self.activations.borrow_mut().pop();
1776        self.machine
1777            .borrow_mut()
1778            .release_native_activation(register_count);
1779        match tag {
1780            Ok(CompletionTag::Normal) => {
1781                self.pending_throw.take();
1782                InvokeOutcome::Value(out.value)
1783            }
1784            Ok(CompletionTag::Throw) => {
1785                let (value, origin) = self.take_matching_throw(out.value);
1786                InvokeOutcome::Threw(value, origin)
1787            }
1788            Ok(CompletionTag::Suspend | CompletionTag::FatalTrap) => InvokeOutcome::Fatal,
1789            Err(error) => {
1790                self.pending_abi_error.set(Some(error));
1791                InvokeOutcome::Fatal
1792            }
1793        }
1794    }
1795
1796    fn evaluate_import(&self, module: ModuleId) -> Result<(), ImportFailure> {
1797        let begun = self.machine.borrow_mut().begin_module_evaluation(module);
1798        let dependencies = match begun {
1799            Err(error) => {
1800                self.pending_error.set(Some(error));
1801                return Err(ImportFailure::Fatal);
1802            }
1803            Ok(crate::ModuleEvaluation::Cycle) => return Ok(()),
1804            Ok(crate::ModuleEvaluation::Evaluated(Ok(()))) => return Ok(()),
1805            Ok(crate::ModuleEvaluation::Evaluated(Err(error))) => {
1806                if matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }) {
1807                    return Err(ImportFailure::Threw(error));
1808                }
1809                self.pending_error.set(Some(error));
1810                return Err(ImportFailure::Fatal);
1811            }
1812            Ok(crate::ModuleEvaluation::Ready(dependencies)) => dependencies,
1813        };
1814        for dependency in dependencies {
1815            if let Err(failure) = self.evaluate_import(dependency) {
1816                let mut machine = self.machine.borrow_mut();
1817                match &failure {
1818                    ImportFailure::Threw(error) => {
1819                        machine.settle_module_evaluation(module, Err(error.clone()));
1820                    }
1821                    ImportFailure::Fatal => machine.abort_module_evaluation(module),
1822                }
1823                return Err(failure);
1824            }
1825        }
1826
1827        let function = self.module(module).entry();
1828        let outcome = self.invoke_runtime(
1829            crate::RuntimeFunction { module, function },
1830            &[],
1831            Value::UNDEFINED,
1832            Value::UNDEFINED,
1833            &[],
1834        );
1835        match outcome {
1836            InvokeOutcome::Value(_) => {
1837                self.machine
1838                    .borrow_mut()
1839                    .settle_module_evaluation(module, Ok(()));
1840                Ok(())
1841            }
1842            InvokeOutcome::Threw(value, origin) => {
1843                let error = self.error_at(
1844                    module,
1845                    RuntimeErrorKind::UncaughtThrow { value, origin },
1846                    function.get() as usize,
1847                    0,
1848                );
1849                self.machine
1850                    .borrow_mut()
1851                    .settle_module_evaluation(module, Err(error.clone()));
1852                Err(ImportFailure::Threw(error))
1853            }
1854            InvokeOutcome::Fatal => {
1855                self.machine.borrow_mut().abort_module_evaluation(module);
1856                Err(ImportFailure::Fatal)
1857            }
1858        }
1859    }
1860
1861    fn import_namespace(&self, requester: ModuleId, specifier: u32) -> HelperResult {
1862        let target = self
1863            .machine
1864            .borrow()
1865            .resolve_import(requester, ConstantId::new(specifier));
1866        let target = match target {
1867            Ok(target) => target,
1868            Err(kind) => return self.fatal(kind),
1869        };
1870        if let crate::ImportTarget::Local(module) = target
1871            && let Err(failure) = self.evaluate_import(module)
1872        {
1873            return match failure {
1874                ImportFailure::Threw(error) => self.fail(crate::import_failure(&error)),
1875                ImportFailure::Fatal => HelperResult {
1876                    tag: CompletionTag::FatalTrap,
1877                    value: Value::UNDEFINED,
1878                },
1879            };
1880        }
1881        let namespace = self
1882            .machine
1883            .borrow_mut()
1884            .imported_namespace(requester, target);
1885        match namespace {
1886            Ok(value) => HelperResult::normal(value),
1887            Err(kind) => self.fatal(kind),
1888        }
1889    }
1890
1891    // -- HelperResult constructors -------------------------------------------
1892
1893    fn fatal(&self, kind: RuntimeErrorKind) -> HelperResult {
1894        self.pending_fatal_kind.set(Some(kind));
1895        HelperResult {
1896            tag: CompletionTag::FatalTrap,
1897            value: Value::UNDEFINED,
1898        }
1899    }
1900
1901    fn fail(&self, failure: EvalFailure) -> HelperResult {
1902        match failure {
1903            EvalFailure::Throw(origin) => {
1904                self.pending_throw.set(Some(PendingThrow {
1905                    value: Value::UNDEFINED,
1906                    origin,
1907                }));
1908                HelperResult::throw(Value::UNDEFINED)
1909            }
1910            EvalFailure::ThrowValue(value) => {
1911                self.pending_throw.set(Some(PendingThrow {
1912                    value,
1913                    origin: ThrowOrigin::Bytecode,
1914                }));
1915                HelperResult::throw(value)
1916            }
1917            EvalFailure::ThrowValueOrigin { value, origin } => {
1918                self.pending_throw.set(Some(PendingThrow { value, origin }));
1919                HelperResult::throw(value)
1920            }
1921            EvalFailure::Runtime(kind) => self.fatal(kind),
1922        }
1923    }
1924
1925    fn eval_result(&self, result: Result<Value, EvalFailure>) -> HelperResult {
1926        match result {
1927            Ok(value) => HelperResult::normal(value),
1928            Err(failure) => self.fail(failure),
1929        }
1930    }
1931
1932    fn outcome_result(&self, outcome: InvokeOutcome) -> HelperResult {
1933        match outcome {
1934            InvokeOutcome::Value(value) => HelperResult::normal(value),
1935            InvokeOutcome::Threw(value, origin) => {
1936                self.pending_throw.set(Some(PendingThrow { value, origin }));
1937                HelperResult::throw(value)
1938            }
1939            InvokeOutcome::Fatal => HelperResult {
1940                tag: CompletionTag::FatalTrap,
1941                value: Value::UNDEFINED,
1942            },
1943        }
1944    }
1945
1946    fn validated(&self, value: Value) -> HelperResult {
1947        HelperResult::normal(value)
1948    }
1949
1950    fn allocated(&self, entry: HeapEntry) -> HelperResult {
1951        let result = self.machine.borrow_mut().allocate(entry);
1952        match result {
1953            Ok(value) => HelperResult::normal(value),
1954            Err(kind) => self.fatal(kind),
1955        }
1956    }
1957
1958    fn constant_text(&self, module: ModuleId, id: u32) -> EcmaString {
1959        self.machine
1960            .borrow()
1961            .constant_text(module, ConstantId::new(id))
1962            .clone()
1963    }
1964
1965    /// Materializes the `arguments` object for the current activation, caching it.
1966    fn load_arguments(&self) -> HelperResult {
1967        let args = match self.activations.borrow().last() {
1968            Some(activation) => activation.args.clone(),
1969            None => return HelperResult::normal(Value::UNDEFINED),
1970        };
1971        if let Some(existing) = self
1972            .activations
1973            .borrow()
1974            .last()
1975            .and_then(|activation| activation.arguments_object)
1976        {
1977            return HelperResult::normal(existing);
1978        }
1979        let prototype = self.machine.borrow().intrinsics.array_prototype;
1980        let allocated = self.machine.borrow_mut().allocate(HeapEntry::Array {
1981            elements: args,
1982            properties: PropertyMap::default(),
1983            prototype: Some(prototype),
1984            extensible: true,
1985            length_writable: true,
1986        });
1987        let value = match allocated {
1988            Ok(value) => value,
1989            Err(kind) => return self.fatal(kind),
1990        };
1991        if let Some(activation) = self.activations.borrow_mut().last_mut() {
1992            activation.arguments_object = Some(value);
1993        }
1994        HelperResult::normal(value)
1995    }
1996
1997    /// `Construct`: allocate the instance with the constructor's `prototype`,
1998    /// invoke with `new.target`, and override a non-object return with the
1999    /// instance — the shared construct semantics, over native activations.
2000    fn construct(&self, callee: Value, arguments: &[Value]) -> HelperResult {
2001        let mut callee = callee;
2002        let mut arguments = Cow::Borrowed(arguments);
2003        if matches!(
2004            self.machine.borrow().callee_kind(callee),
2005            Ok(CalleeKind::Bound)
2006        ) {
2007            let bound =
2008                self.machine
2009                    .borrow()
2010                    .flatten_bound(callee, Value::UNDEFINED, arguments.as_ref());
2011            match bound {
2012                Ok(bound) => {
2013                    callee = bound.target;
2014                    arguments = Cow::Owned(bound.arguments);
2015                }
2016                Err(kind) => return self.fatal(kind),
2017            }
2018        }
2019        let kind = self.machine.borrow().callee_kind(callee);
2020        match kind {
2021            Ok(CalleeKind::Builtin { id }) => {
2022                let result = self.machine.borrow_mut().call_builtin(
2023                    id,
2024                    Value::UNDEFINED,
2025                    arguments.as_ref(),
2026                    true,
2027                );
2028                match result {
2029                    Ok(BuiltinOutcome::Value(value)) => HelperResult::normal(value),
2030                    Ok(BuiltinOutcome::Call { .. } | BuiltinOutcome::GeneratorNext { .. }) => {
2031                        self.pending_throw.set(Some(PendingThrow {
2032                            value: Value::UNDEFINED,
2033                            origin: ThrowOrigin::TypeError {
2034                                operation: "construct",
2035                            },
2036                        }));
2037                        HelperResult::throw(Value::UNDEFINED)
2038                    }
2039                    Ok(BuiltinOutcome::ConstructCall {
2040                        callee: continuation,
2041                        this_value,
2042                        arguments: continuation_arguments,
2043                        prototype,
2044                    }) => {
2045                        let instance = match self
2046                            .machine
2047                            .borrow_mut()
2048                            .allocate_constructed_receiver_with(prototype)
2049                        {
2050                            Ok(value) => value,
2051                            Err(kind) => return self.fatal(kind),
2052                        };
2053                        let outcome = self.invoke_callee(
2054                            continuation,
2055                            this_value,
2056                            &continuation_arguments,
2057                            callee,
2058                        );
2059                        match outcome {
2060                            InvokeOutcome::Value(returned) => {
2061                                let is_object = self.machine.borrow().is_object(returned);
2062                                HelperResult::normal(if is_object { returned } else { instance })
2063                            }
2064                            other => self.outcome_result(other),
2065                        }
2066                    }
2067                    Err(failure) => self.fail(failure),
2068                }
2069            }
2070            Ok(CalleeKind::Runtime { target, captures }) => {
2071                let flags = {
2072                    let handle = self.code_ref(target.module);
2073                    handle.code(target.module).functions()[target.function.get() as usize].flags()
2074                };
2075                if flags.is_async && !flags.is_generator {
2076                    self.pending_throw.set(Some(PendingThrow {
2077                        value: Value::UNDEFINED,
2078                        origin: ThrowOrigin::TypeError {
2079                            operation: "construct",
2080                        },
2081                    }));
2082                    return HelperResult::throw(Value::UNDEFINED);
2083                }
2084                let instance = {
2085                    let allocated = self
2086                        .machine
2087                        .borrow_mut()
2088                        .allocate_constructed_receiver(callee);
2089                    match allocated {
2090                        Ok(value) => value,
2091                        Err(kind) => return self.fatal(kind),
2092                    }
2093                };
2094                let outcome =
2095                    self.invoke_runtime(target, &captures, instance, callee, arguments.as_ref());
2096                match outcome {
2097                    InvokeOutcome::Value(returned) => {
2098                        let is_object = self.machine.borrow().is_object(returned);
2099                        HelperResult::normal(if is_object { returned } else { instance })
2100                    }
2101                    other => self.outcome_result(other),
2102                }
2103            }
2104            Ok(CalleeKind::Bound) => self.fatal(RuntimeErrorKind::InvalidValue { value: callee }),
2105            Ok(CalleeKind::NotCallable) => {
2106                self.pending_throw.set(Some(PendingThrow {
2107                    value: Value::UNDEFINED,
2108                    origin: ThrowOrigin::TypeError {
2109                        operation: "construct",
2110                    },
2111                }));
2112                HelperResult::throw(Value::UNDEFINED)
2113            }
2114            Err(kind) => self.fatal(kind),
2115        }
2116    }
2117
2118    // -- Linked backend ------------------------------------------------------
2119
2120    fn run_linked(&mut self) -> Result<ExecutionOutcome, NativeError> {
2121        self.machine.borrow_mut().instantiate_modules()?;
2122        let module = self.program.entry();
2123        let execution = self.evaluate_linked_module(module)?.ok_or_else(|| {
2124            let function = self.module(module).entry().get() as usize;
2125            NativeError::Runtime(self.error_at(
2126                module,
2127                RuntimeErrorKind::InvalidVerifiedProgram {
2128                    module,
2129                    instruction: Instruction::Halt,
2130                },
2131                function,
2132                0,
2133            ))
2134        })?;
2135        Ok(execution.outcome)
2136    }
2137
2138    fn evaluate_linked_module(
2139        &mut self,
2140        module: ModuleId,
2141    ) -> Result<Option<Execution>, NativeError> {
2142        let dependencies = match self.machine.borrow_mut().begin_module_evaluation(module)? {
2143            crate::ModuleEvaluation::Cycle => return Ok(None),
2144            crate::ModuleEvaluation::Evaluated(result) => {
2145                return result.map(|()| None).map_err(Into::into);
2146            }
2147            crate::ModuleEvaluation::Ready(dependencies) => dependencies,
2148        };
2149        for dependency in dependencies {
2150            match self.evaluate_linked_module(dependency) {
2151                Ok(_) => {}
2152                Err(NativeError::Runtime(error)) => {
2153                    let error = self
2154                        .machine
2155                        .borrow_mut()
2156                        .finish_module_evaluation(module, Err(error))
2157                        .expect_err("dependency failure remains an error");
2158                    return Err(NativeError::Runtime(error));
2159                }
2160                Err(error) => {
2161                    self.machine.borrow_mut().abort_module_evaluation(module);
2162                    return Err(error);
2163                }
2164            }
2165        }
2166
2167        match self.invoke_linked_entry(module) {
2168            Ok(execution) => self
2169                .machine
2170                .borrow_mut()
2171                .finish_module_evaluation(module, Ok(execution))
2172                .map(Some)
2173                .map_err(Into::into),
2174            Err(NativeError::Runtime(error)) => {
2175                let error = self
2176                    .machine
2177                    .borrow_mut()
2178                    .finish_module_evaluation(module, Err(error))
2179                    .expect_err("module failure remains an error");
2180                Err(NativeError::Runtime(error))
2181            }
2182            Err(error) => {
2183                self.machine.borrow_mut().abort_module_evaluation(module);
2184                Err(error)
2185            }
2186        }
2187    }
2188
2189    fn invoke_linked_entry(&mut self, module: ModuleId) -> Result<Execution, NativeError> {
2190        let code = self.module(module);
2191        let function_id = code.entry();
2192        let function = function_id.get() as usize;
2193        let register_count = code.functions()[function].register_count() as usize;
2194        if self.max_call_depth() < 1 {
2195            return Err(NativeError::Runtime(self.error_at(
2196                module,
2197                RuntimeErrorKind::CallDepthExceeded {
2198                    limit: self.max_call_depth(),
2199                },
2200                function,
2201                0,
2202            )));
2203        }
2204        if register_count > self.max_total_registers() {
2205            return Err(NativeError::Runtime(self.error_at(
2206                module,
2207                RuntimeErrorKind::RegisterLimitExceeded {
2208                    limit: self.max_total_registers(),
2209                },
2210                function,
2211                0,
2212            )));
2213        }
2214        let mut registers = self.seed_registers(code, function, &[], &[]);
2215        let length = u16::try_from(register_count).map_err(|_| {
2216            NativeError::Runtime(self.error_at(
2217                module,
2218                RuntimeErrorKind::RegisterLimitExceeded {
2219                    limit: self.max_total_registers(),
2220                },
2221                function,
2222                0,
2223            ))
2224        })?;
2225        let reserved = self
2226            .machine
2227            .borrow_mut()
2228            .reserve_native_activation(register_count);
2229        reserved.map_err(|kind| NativeError::Runtime(self.error_at(module, kind, function, 0)))?;
2230        self.activations.borrow_mut().push(Activation {
2231            this_value: Value::UNDEFINED,
2232            new_target: Value::UNDEFINED,
2233            args: Vec::new(),
2234            arguments_object: None,
2235            pending_resume: None,
2236        });
2237        let handles = registers.as_mut_ptr();
2238        let entries = self.entries;
2239        let (tag, out, fault_pc) = {
2240            let mut shadow =
2241                ShadowFrame::new(std::ptr::null_mut(), 0, module.get(), handles, length);
2242            let mut out = Completion::new(Value::UNDEFINED);
2243            let tag = with_native_ops(self, || {
2244                entries.invoke(module.get(), function_id.get(), &mut shadow, &mut out)
2245            });
2246            (tag, out, shadow.bytecode_pc as usize)
2247        };
2248        self.activations.borrow_mut().pop();
2249        self.machine
2250            .borrow_mut()
2251            .release_native_activation(register_count);
2252        match tag {
2253            Ok(CompletionTag::Normal) => {
2254                self.pending_throw.take();
2255                Ok(Execution {
2256                    outcome: ExecutionOutcome {
2257                        stdout: self.stdout.borrow().clone(),
2258                        exit_code: self.exit_code.get(),
2259                    },
2260                    value: out.value,
2261                    link: out.value,
2262                    entry_registers: registers,
2263                })
2264            }
2265            Ok(CompletionTag::Throw) => {
2266                let (value, origin) = self.take_matching_throw(out.value);
2267                Err(NativeError::Runtime(self.error_at(
2268                    module,
2269                    RuntimeErrorKind::UncaughtThrow { value, origin },
2270                    function,
2271                    fault_pc,
2272                )))
2273            }
2274            Ok(CompletionTag::Suspend | CompletionTag::FatalTrap) => {
2275                if let Some(error) = self.pending_abi_error.take() {
2276                    Err(NativeError::Abi(error))
2277                } else if let Some(error) = self.pending_error.take() {
2278                    Err(NativeError::Runtime(error))
2279                } else if let Some(kind) = self.pending_fatal_kind.take() {
2280                    Err(NativeError::Runtime(
2281                        self.error_at(module, kind, function, fault_pc),
2282                    ))
2283                } else {
2284                    Err(NativeError::FatalTrap { value: out.value })
2285                }
2286            }
2287            Err(error) => Err(NativeError::Abi(error)),
2288        }
2289    }
2290}
2291
2292// -- The NativeOps seam ------------------------------------------------------
2293
2294impl<'m, 'h, H: Host> NativeOps for NativeEngine<'m, 'h, H> {
2295    fn truthy(&self, _frame: &mut NativeFrame<'_>, value: Value) -> bool {
2296        self.machine.borrow().truthy(value)
2297    }
2298
2299    fn dispatch(&self, frame: &mut NativeFrame<'_>, call: HelperCall) -> HelperResult {
2300        let module = ModuleId::new(frame.module_id());
2301        let amount = match call {
2302            HelperCall::ResumeValue => None,
2303            HelperCall::ConsumeFuel { amount } => Some(u64::from(amount)),
2304            _ => Some(1),
2305        };
2306        if let Some(amount) = amount
2307            && let Err(kind) = self.machine.borrow_mut().consume_fuel(amount)
2308        {
2309            return self.fatal(kind);
2310        }
2311        match call {
2312            HelperCall::LoadConstant { const_id } => {
2313                let result = self
2314                    .machine
2315                    .borrow_mut()
2316                    .load_constant_value(module, ConstantId::new(const_id));
2317                match result {
2318                    Ok(value) => HelperResult::normal(value),
2319                    Err(kind) => self.fatal(kind),
2320                }
2321            }
2322            HelperCall::Unary { op, operand } => match unary_from_selector(op) {
2323                Some(op) => {
2324                    let result = self.machine.borrow_mut().eval_unary(op, operand);
2325                    self.eval_result(result)
2326                }
2327                None => self.fatal(RuntimeErrorKind::InvalidValue {
2328                    value: Value::UNDEFINED,
2329                }),
2330            },
2331            HelperCall::Binary { op, left, right } => match binary_from_selector(op) {
2332                Some(op) => {
2333                    let result = self.machine.borrow_mut().eval_binary(op, left, right);
2334                    self.eval_result(result)
2335                }
2336                None => self.fatal(RuntimeErrorKind::InvalidValue {
2337                    value: Value::UNDEFINED,
2338                }),
2339            },
2340            HelperCall::CreateObject => {
2341                let prototype = self.machine.borrow().intrinsics.object_prototype;
2342                self.allocated(HeapEntry::Object {
2343                    properties: PropertyMap::default(),
2344                    prototype: Some(prototype),
2345                    boxed_primitive: None,
2346                    extensible: true,
2347                })
2348            }
2349            HelperCall::CreateArray => {
2350                let prototype = self.machine.borrow().intrinsics.array_prototype;
2351                self.allocated(HeapEntry::Array {
2352                    elements: Vec::new(),
2353                    properties: PropertyMap::default(),
2354                    prototype: Some(prototype),
2355                    extensible: true,
2356                    length_writable: true,
2357                })
2358            }
2359            HelperCall::CreateCell => {
2360                let prototype = self.machine.borrow().intrinsics.array_prototype;
2361                self.allocated(HeapEntry::Array {
2362                    elements: vec![Value::UNINITIALIZED],
2363                    properties: PropertyMap::default(),
2364                    prototype: Some(prototype),
2365                    extensible: true,
2366                    length_writable: true,
2367                })
2368            }
2369            HelperCall::CreateClosure {
2370                function_id,
2371                captures,
2372            } => {
2373                let function = FunctionId::new(function_id);
2374                let materialized = self
2375                    .machine
2376                    .borrow()
2377                    .captures_from_array(module, captures, function);
2378                match materialized {
2379                    Ok(captures) => {
2380                        let prototype = self.machine.borrow().intrinsics.function_prototype;
2381                        self.allocated(HeapEntry::Function {
2382                            module,
2383                            function,
2384                            captures,
2385                            properties: PropertyMap::default(),
2386                            prototype: Some(prototype),
2387                            extensible: true,
2388                        })
2389                    }
2390                    Err(failure) => self.fail(failure),
2391                }
2392            }
2393            HelperCall::GetProperty { object, key } => {
2394                let key = {
2395                    let coerced = self.machine.borrow().to_property_key(key);
2396                    match coerced {
2397                        Ok(key) => key,
2398                        Err(failure) => return self.fail(failure),
2399                    }
2400                };
2401                let outcome = self.machine.borrow_mut().resolve_get(object, &key);
2402                match outcome {
2403                    Ok(GetOutcome::Value(value)) => self.validated(value),
2404                    Ok(GetOutcome::Text(text)) => self.allocated(HeapEntry::String(text)),
2405                    Ok(GetOutcome::Getter(getter)) => {
2406                        let outcome = self.invoke_callee(getter, object, &[], Value::UNDEFINED);
2407                        self.outcome_result(outcome)
2408                    }
2409                    Err(failure) => self.fail(failure),
2410                }
2411            }
2412            HelperCall::SetProperty { object, key, value } => {
2413                let key = {
2414                    let coerced = self.machine.borrow().to_property_key(key);
2415                    match coerced {
2416                        Ok(key) => key,
2417                        Err(failure) => return self.fail(failure),
2418                    }
2419                };
2420                let outcome = self.machine.borrow_mut().resolve_set(object, key, value);
2421                match outcome {
2422                    Ok(SetOutcome::Done) => HelperResult::normal(Value::UNDEFINED),
2423                    Ok(SetOutcome::Setter(setter)) => {
2424                        let outcome =
2425                            self.invoke_callee(setter, object, &[value], Value::UNDEFINED);
2426                        match outcome {
2427                            InvokeOutcome::Value(_) => HelperResult::normal(Value::UNDEFINED),
2428                            other => self.outcome_result(other),
2429                        }
2430                    }
2431                    Err(failure) => self.fail(failure),
2432                }
2433            }
2434            HelperCall::DeleteProperty { object, key } => {
2435                let key = {
2436                    let coerced = self.machine.borrow().to_property_key(key);
2437                    match coerced {
2438                        Ok(key) => key,
2439                        Err(failure) => return self.fail(failure),
2440                    }
2441                };
2442                let deleted = self.machine.borrow_mut().delete_property(object, &key);
2443                match deleted {
2444                    Ok(deleted) => HelperResult::normal(Value::boolean(deleted)),
2445                    Err(failure) => self.fail(failure),
2446                }
2447            }
2448            HelperCall::DefineAccessor {
2449                object,
2450                key,
2451                accessor,
2452                kind,
2453            } => {
2454                let kind = match accessor_from_selector(kind) {
2455                    Some(kind) => kind,
2456                    None => {
2457                        return self.fatal(RuntimeErrorKind::InvalidValue {
2458                            value: Value::UNDEFINED,
2459                        });
2460                    }
2461                };
2462                let key = {
2463                    let coerced = self.machine.borrow().to_property_key(key);
2464                    match coerced {
2465                        Ok(key) => key,
2466                        Err(failure) => return self.fail(failure),
2467                    }
2468                };
2469                let defined = self
2470                    .machine
2471                    .borrow_mut()
2472                    .define_accessor(object, key, accessor, kind);
2473                match defined {
2474                    Ok(()) => HelperResult::normal(Value::UNDEFINED),
2475                    Err(failure) => self.fail(failure),
2476                }
2477            }
2478            HelperCall::Call {
2479                callee,
2480                this_value,
2481                arguments,
2482            } => {
2483                let arguments = {
2484                    let read = self.machine.borrow().arguments_from_array(arguments);
2485                    match read {
2486                        Ok(arguments) => arguments,
2487                        Err(failure) => return self.fail(failure),
2488                    }
2489                };
2490                let outcome = self.invoke_callee(callee, this_value, &arguments, Value::UNDEFINED);
2491                self.outcome_result(outcome)
2492            }
2493            HelperCall::Construct { callee, arguments } => {
2494                let arguments = {
2495                    let read = self.machine.borrow().arguments_from_array(arguments);
2496                    match read {
2497                        Ok(arguments) => arguments,
2498                        Err(failure) => return self.fail(failure),
2499                    }
2500                };
2501                self.construct(callee, &arguments)
2502            }
2503            HelperCall::Import { specifier } => self.import_namespace(module, specifier),
2504            HelperCall::Truthy { value } => {
2505                HelperResult::normal(Value::boolean(self.machine.borrow().truthy(value)))
2506            }
2507            HelperCall::ResumeValue => {
2508                let resumed = self
2509                    .activations
2510                    .borrow_mut()
2511                    .last_mut()
2512                    .and_then(|activation| activation.pending_resume.take());
2513                match resumed {
2514                    Some(value) => self.validated(value),
2515                    None => self.fatal(RuntimeErrorKind::InvalidValue {
2516                        value: Value::UNDEFINED,
2517                    }),
2518                }
2519            }
2520            HelperCall::LoadGlobal { name } => {
2521                let resolved = self
2522                    .machine
2523                    .borrow()
2524                    .load_global(module, ConstantId::new(name));
2525                match resolved {
2526                    Ok(Some(value)) => self.validated(value),
2527                    Ok(None) => {
2528                        self.pending_throw.set(Some(PendingThrow {
2529                            value: Value::UNDEFINED,
2530                            origin: ThrowOrigin::ReferenceError {
2531                                operation: "global is not defined",
2532                            },
2533                        }));
2534                        HelperResult::throw(Value::UNDEFINED)
2535                    }
2536                    Err(kind) => self.fatal(kind),
2537                }
2538            }
2539            HelperCall::StoreGlobal { name, value } => {
2540                let stored =
2541                    self.machine
2542                        .borrow_mut()
2543                        .store_global(module, ConstantId::new(name), value);
2544                match stored {
2545                    Ok(()) => HelperResult::normal(Value::UNDEFINED),
2546                    Err(failure) => self.fail(failure),
2547                }
2548            }
2549            HelperCall::TypeOfGlobal { name } => {
2550                let resolved = self
2551                    .machine
2552                    .borrow()
2553                    .load_global(module, ConstantId::new(name));
2554                let text = match resolved {
2555                    Ok(Some(value)) => EcmaString::from_utf8(self.machine.borrow().type_of(value)),
2556                    Ok(None) => EcmaString::from_utf8("undefined"),
2557                    Err(kind) => return self.fatal(kind),
2558                };
2559                self.allocated(HeapEntry::String(text))
2560            }
2561            HelperCall::LoadThis => HelperResult::normal(
2562                self.activations
2563                    .borrow()
2564                    .last()
2565                    .map_or(Value::UNDEFINED, |activation| activation.this_value),
2566            ),
2567            HelperCall::LoadArguments => self.load_arguments(),
2568            HelperCall::LoadNewTarget => HelperResult::normal(
2569                self.activations
2570                    .borrow()
2571                    .last()
2572                    .map_or(Value::UNDEFINED, |activation| activation.new_target),
2573            ),
2574            HelperCall::ArrayPush { array, value } => {
2575                let result = self.machine.borrow_mut().array_push(array, value);
2576                match result {
2577                    Ok(()) => HelperResult::normal(Value::UNDEFINED),
2578                    Err(failure) => self.fail(failure),
2579                }
2580            }
2581            HelperCall::ArrayExtend { array, iterable } => {
2582                let outcome = self.array_extend_active(array, iterable);
2583                self.outcome_result(outcome)
2584            }
2585            HelperCall::ObjectSpread { target, source } => {
2586                let result = self.machine.borrow_mut().object_spread(target, source);
2587                match result {
2588                    Ok(()) => HelperResult::normal(Value::UNDEFINED),
2589                    Err(failure) => self.fail(failure),
2590                }
2591            }
2592            HelperCall::SetPrototype { object, prototype } => {
2593                let result = self.machine.borrow_mut().set_prototype(object, prototype);
2594                match result {
2595                    Ok(()) => HelperResult::normal(Value::UNDEFINED),
2596                    Err(failure) => self.fail(failure),
2597                }
2598            }
2599            HelperCall::CreatePrivateName { description } => {
2600                let description = self.constant_text(module, description);
2601                self.allocated(HeapEntry::PrivateName { description })
2602            }
2603            HelperCall::CreateRegExp { pattern, flags } => {
2604                let pattern = self.constant_text(module, pattern);
2605                let flags = self.constant_text(module, flags);
2606                let prototype = self.machine.borrow().intrinsics.regexp_prototype();
2607                self.allocated(HeapEntry::RegExp {
2608                    pattern,
2609                    flags,
2610                    properties: PropertyMap::default(),
2611                    prototype: Some(prototype),
2612                    extensible: true,
2613                })
2614            }
2615            HelperCall::GetIterator { src, kind } => match iterator_kind_from_selector(kind) {
2616                Some(kind) => self.outcome_result(self.get_iterator_active(src, kind)),
2617                None => self.fatal(RuntimeErrorKind::InvalidValue {
2618                    value: Value::UNDEFINED,
2619                }),
2620            },
2621            HelperCall::IteratorNext {
2622                iterator,
2623                done_reg,
2624                value_reg,
2625            } => match self.iterator_next_active(iterator) {
2626                Ok((done, value)) => {
2627                    let wrote_done = frame.try_set_register(done_reg, Value::boolean(done));
2628                    let wrote_value = frame.try_set_register(value_reg, value);
2629                    if wrote_done && wrote_value {
2630                        HelperResult::normal(Value::UNDEFINED)
2631                    } else {
2632                        self.fatal(RuntimeErrorKind::InvalidValue {
2633                            value: Value::UNDEFINED,
2634                        })
2635                    }
2636                }
2637                Err(outcome) => self.outcome_result(outcome),
2638            },
2639            HelperCall::Export { .. } => self.fail(EvalFailure::Throw(ThrowOrigin::TypeError {
2640                operation: "export outside an engine-owned module registry",
2641            })),
2642            HelperCall::ConsumeFuel { .. } => HelperResult::normal(Value::UNDEFINED),
2643        }
2644    }
2645}
2646
2647fn is_inline_instruction(instruction: Instruction) -> bool {
2648    match instruction {
2649        Instruction::Move { .. }
2650        | Instruction::Jump { .. }
2651        | Instruction::JumpIfTrue { .. }
2652        | Instruction::JumpIfFalse { .. }
2653        | Instruction::Return { .. }
2654        | Instruction::Halt
2655        | Instruction::Throw { .. }
2656        | Instruction::Suspend { .. } => true,
2657        Instruction::LoadConst { .. }
2658        | Instruction::Unary { .. }
2659        | Instruction::Binary { .. }
2660        | Instruction::CreateObject { .. }
2661        | Instruction::CreateArray { .. }
2662        | Instruction::CreateCell { .. }
2663        | Instruction::CreateClosure { .. }
2664        | Instruction::GetProperty { .. }
2665        | Instruction::SetProperty { .. }
2666        | Instruction::DeleteProperty { .. }
2667        | Instruction::DefineAccessor { .. }
2668        | Instruction::Call { .. }
2669        | Instruction::Construct { .. }
2670        | Instruction::LoadGlobal { .. }
2671        | Instruction::StoreGlobal { .. }
2672        | Instruction::TypeOfGlobal { .. }
2673        | Instruction::LoadThis { .. }
2674        | Instruction::LoadArguments { .. }
2675        | Instruction::LoadNewTarget { .. }
2676        | Instruction::ArrayPush { .. }
2677        | Instruction::ArrayExtend { .. }
2678        | Instruction::ObjectSpread { .. }
2679        | Instruction::SetPrototype { .. }
2680        | Instruction::CreatePrivateName { .. }
2681        | Instruction::CreateRegExp { .. }
2682        | Instruction::GetIterator { .. }
2683        | Instruction::IteratorNext { .. }
2684        | Instruction::Import { .. }
2685        | Instruction::Export { .. } => false,
2686    }
2687}
2688
2689/// Runs a linked program's entry through the native engine and returns its
2690/// observable outcome. This is the runtime side of the AOT `main`: `entries` is
2691/// a compiled [`NativeEntryTable`] (an AOT `LinkedProgram` or a JIT program),
2692/// and the returned [`ExecutionOutcome`] carries buffered stdout and the exit
2693/// code. It is always available and never feature-gated, so a downstream host
2694/// crate can link against it without enabling the runtime's `aot-main` feature.
2695///
2696/// Returns [`NativeError::ProgramMismatch`] before constructing the engine when
2697/// `entries` was not compiled from this program's exact canonical encoding.
2698pub fn run_linked_program<H: Host>(
2699    program: &Program<Verified>,
2700    entries: &dyn NativeEntryTable,
2701    host: &mut H,
2702    limits: &Limits,
2703) -> Result<ExecutionOutcome, NativeError> {
2704    let program_bytes = program.encode();
2705    if program_bytes != entries.program_bytes() {
2706        return Err(NativeError::ProgramMismatch);
2707    }
2708    let mut engine = NativeEngine::build(program, entries, host, limits.clone(), Backend::Linked);
2709    engine.run_linked()
2710}
2711
2712#[cfg(test)]
2713mod tests {
2714    use std::cell::{Cell, RefCell};
2715    use std::sync::Arc;
2716
2717    use bamts_bytecode::{
2718        BinaryOp, Binding, BindingId, BindingKind, Constant, ConstantId, Edge, EdgeId, EdgeKind,
2719        EdgeTarget, ExceptionHandler, Export, ExportSource, Function, FunctionFlags, FunctionId,
2720        Instruction, IteratorKind, Module, ModuleId, Pc, Program, ProgramModule, Register,
2721        Verified,
2722    };
2723    use bamts_native::{
2724        AbiError, Completion, CompletionTag, HelperCall, HelperResult, NativeEntryTable,
2725        NativeFrame, NativeOps, ShadowFrame, Value,
2726    };
2727
2728    use crate::{
2729        GeneratorState, HeapEntry, Host, Limits, Machine, PropertyMap, RuntimeError,
2730        RuntimeErrorKind, ThrowOrigin,
2731    };
2732
2733    use super::EcmaString;
2734    use super::{
2735        Activation, Backend, InvokeOutcome, NativeEngine, NativeError, PendingThrow,
2736        run_linked_program,
2737    };
2738
2739    fn reg(raw: u32) -> Register {
2740        Register::new(raw)
2741    }
2742
2743    fn pc(raw: u32) -> Pc {
2744        Pc::new(raw)
2745    }
2746
2747    fn cid(raw: u32) -> bamts_bytecode::ConstantId {
2748        bamts_bytecode::ConstantId::new(raw)
2749    }
2750
2751    fn entry_function(register_count: u32, code: Vec<Instruction>) -> Function {
2752        Function::new(
2753            None,
2754            0,
2755            0,
2756            register_count,
2757            FunctionFlags::default(),
2758            code,
2759            Vec::new(),
2760        )
2761    }
2762
2763    fn receiver_sum_function() -> Function {
2764        module_function(
2765            0,
2766            5,
2767            vec![
2768                Instruction::LoadThis { dst: reg(0) },
2769                Instruction::LoadConst {
2770                    dst: reg(1),
2771                    constant: cid(1),
2772                },
2773                Instruction::GetProperty {
2774                    dst: reg(2),
2775                    object: reg(0),
2776                    key: reg(1),
2777                },
2778                Instruction::LoadArguments { dst: reg(3) },
2779                Instruction::LoadConst {
2780                    dst: reg(1),
2781                    constant: cid(2),
2782                },
2783                Instruction::GetProperty {
2784                    dst: reg(4),
2785                    object: reg(3),
2786                    key: reg(1),
2787                },
2788                Instruction::Binary {
2789                    dst: reg(2),
2790                    op: BinaryOp::Add,
2791                    left: reg(2),
2792                    right: reg(4),
2793                },
2794                Instruction::LoadConst {
2795                    dst: reg(1),
2796                    constant: cid(3),
2797                },
2798                Instruction::GetProperty {
2799                    dst: reg(4),
2800                    object: reg(3),
2801                    key: reg(1),
2802                },
2803                Instruction::Binary {
2804                    dst: reg(2),
2805                    op: BinaryOp::Add,
2806                    left: reg(2),
2807                    right: reg(4),
2808                },
2809                Instruction::Return { value: reg(2) },
2810            ],
2811        )
2812    }
2813
2814    fn verified(constants: Vec<Constant>, functions: Vec<Function>) -> Module<Verified> {
2815        Module::new(constants, functions, FunctionId::new(0))
2816            .verify()
2817            .expect("module verifies")
2818    }
2819
2820    fn module_function(captures: u32, registers: u32, code: Vec<Instruction>) -> Function {
2821        Function::new(
2822            None,
2823            captures,
2824            0,
2825            registers,
2826            FunctionFlags::default(),
2827            code,
2828            Vec::new(),
2829        )
2830    }
2831
2832    fn program_module(
2833        name: &str,
2834        mut constants: Vec<Constant>,
2835        functions: Vec<Function>,
2836        edges: Vec<Edge>,
2837        bindings: Vec<Binding>,
2838        exports: Vec<Export>,
2839    ) -> ProgramModule<Verified> {
2840        constants.insert(0, Constant::String(EcmaString::from_utf8(name)));
2841        ProgramModule {
2842            name: cid(0),
2843            code: Module::new(constants, functions, FunctionId::new(0))
2844                .verify()
2845                .expect("module fixture verifies"),
2846            edges,
2847            bindings,
2848            exports,
2849        }
2850    }
2851
2852    fn linked(modules: Vec<ProgramModule<Verified>>, entry: u32) -> Program<Verified> {
2853        Program::link(modules, ModuleId::new(entry)).expect("module fixture links")
2854    }
2855
2856    fn dynamic_cycle_program() -> Program<Verified> {
2857        let root = program_module(
2858            "root",
2859            vec![Constant::String(EcmaString::from_utf8("./target"))],
2860            vec![entry_function(
2861                3,
2862                vec![
2863                    Instruction::Import {
2864                        dst: reg(0),
2865                        specifier: cid(1),
2866                    },
2867                    Instruction::Import {
2868                        dst: reg(1),
2869                        specifier: cid(1),
2870                    },
2871                    Instruction::Binary {
2872                        dst: reg(2),
2873                        op: BinaryOp::StrictEqual,
2874                        left: reg(0),
2875                        right: reg(1),
2876                    },
2877                    Instruction::Return { value: reg(2) },
2878                ],
2879            )],
2880            vec![Edge {
2881                specifier: cid(1),
2882                target: EdgeTarget::Local(ModuleId::new(1)),
2883                kind: EdgeKind::Dynamic,
2884            }],
2885            Vec::new(),
2886            Vec::new(),
2887        );
2888        let target = program_module(
2889            "target",
2890            vec![Constant::String(EcmaString::from_utf8("./root"))],
2891            vec![entry_function(1, vec![Instruction::Halt])],
2892            vec![Edge {
2893                specifier: cid(1),
2894                target: EdgeTarget::Local(ModuleId::new(0)),
2895                kind: EdgeKind::Static,
2896            }],
2897            Vec::new(),
2898            Vec::new(),
2899        );
2900        linked(vec![root, target], 0)
2901    }
2902
2903    fn assert_program_parity(
2904        program: &Program<Verified>,
2905    ) -> Result<crate::Execution, RuntimeError> {
2906        let limits = Limits::default();
2907        let mut interpreter_host = SilentHost;
2908        let interpreter = Machine::new(program, &mut interpreter_host, limits.clone()).run();
2909        let mut native_host = SilentHost;
2910        let entries = NoEntries;
2911        let native = NativeEngine::new(program, &entries, &mut native_host, limits).run();
2912        assert_eq!(interpreter, native);
2913        native
2914    }
2915
2916    /// A dummy entry table for the reference backend, which never invokes it.
2917    struct NoEntries;
2918
2919    impl NativeEntryTable for NoEntries {
2920        fn program_bytes(&self) -> &[u8] {
2921            &[]
2922        }
2923
2924        fn invoke(
2925            &self,
2926            module_id: u32,
2927            function_id: u32,
2928            _frame: &mut ShadowFrame,
2929            _out: &mut Completion,
2930        ) -> Result<CompletionTag, AbiError> {
2931            Err(AbiError::UnknownFunction {
2932                module_id,
2933                function_id,
2934            })
2935        }
2936    }
2937
2938    #[test]
2939    fn microtask_checkpoint_matches_interpreter_and_native_engine() {
2940        let program = linked(
2941            vec![program_module(
2942                "root",
2943                vec![
2944                    Constant::String(EcmaString::from_utf8("queueMicrotask")),
2945                    Constant::String(EcmaString::from_utf8("observed")),
2946                    Constant::Int32(7),
2947                    Constant::Undefined,
2948                ],
2949                vec![
2950                    entry_function(
2951                        6,
2952                        vec![
2953                            Instruction::CreateArray { dst: reg(0) },
2954                            Instruction::CreateClosure {
2955                                dst: reg(1),
2956                                function: FunctionId::new(1),
2957                                captures: reg(0),
2958                            },
2959                            Instruction::LoadGlobal {
2960                                dst: reg(2),
2961                                name: cid(1),
2962                            },
2963                            Instruction::CreateArray { dst: reg(3) },
2964                            Instruction::ArrayPush {
2965                                array: reg(3),
2966                                value: reg(1),
2967                            },
2968                            Instruction::LoadConst {
2969                                dst: reg(4),
2970                                constant: cid(4),
2971                            },
2972                            Instruction::Call {
2973                                dst: reg(5),
2974                                callee: reg(2),
2975                                this_value: reg(4),
2976                                arguments: reg(3),
2977                            },
2978                            Instruction::Return { value: reg(4) },
2979                        ],
2980                    ),
2981                    module_function(
2982                        0,
2983                        1,
2984                        vec![
2985                            Instruction::LoadConst {
2986                                dst: reg(0),
2987                                constant: cid(3),
2988                            },
2989                            Instruction::StoreGlobal {
2990                                name: cid(2),
2991                                value: reg(0),
2992                            },
2993                            Instruction::Return { value: reg(0) },
2994                        ],
2995                    ),
2996                ],
2997                Vec::new(),
2998                Vec::new(),
2999                Vec::new(),
3000            )],
3001            0,
3002        );
3003        let observed = EcmaString::from_utf8("observed");
3004
3005        let mut interpreter_host = SilentHost;
3006        let mut interpreter = Machine::new(&program, &mut interpreter_host, Limits::default());
3007        interpreter.evaluate().unwrap();
3008        assert!(!interpreter.globals.contains_key(&observed));
3009        let interpreter_drain = interpreter.drain_microtasks().unwrap();
3010        let interpreter_value = interpreter.globals.get(&observed).copied();
3011
3012        let mut native_host = SilentHost;
3013        let engine = NativeEngine::new(&program, &NoEntries, &mut native_host, Limits::default());
3014        engine.machine.borrow_mut().instantiate_modules().unwrap();
3015        engine
3016            .evaluate_reference_module(program.entry())
3017            .unwrap()
3018            .expect("native entry completes");
3019        assert!(!engine.machine.borrow().globals.contains_key(&observed));
3020        let native_drain = engine.machine.borrow_mut().drain_microtasks().unwrap();
3021        let native_value = engine.machine.borrow().globals.get(&observed).copied();
3022
3023        assert_eq!(interpreter_drain, native_drain);
3024        assert_eq!(interpreter_value, Some(Value::int32(7)));
3025        assert_eq!(native_value, interpreter_value);
3026    }
3027
3028    fn async_module_function(registers: u32, code: Vec<Instruction>) -> Function {
3029        Function::new(
3030            None,
3031            0,
3032            0,
3033            registers,
3034            FunctionFlags {
3035                is_async: true,
3036                is_generator: false,
3037            },
3038            code,
3039            Vec::new(),
3040        )
3041    }
3042
3043    /// An ordinary async call routed through the native reference engine must
3044    /// produce the same Promise settlement, globals, and drain counts as the
3045    /// interpreter, because native async execution reuses the shared Machine
3046    /// reference state machine.
3047    #[test]
3048    fn async_call_matches_interpreter_and_native_engine() {
3049        let program = linked(
3050            vec![program_module(
3051                "root",
3052                vec![
3053                    Constant::String(EcmaString::from_utf8("observed")),
3054                    Constant::Int32(7),
3055                    Constant::Undefined,
3056                ],
3057                vec![
3058                    entry_function(
3059                        5,
3060                        vec![
3061                            Instruction::CreateArray { dst: reg(0) },
3062                            Instruction::CreateClosure {
3063                                dst: reg(1),
3064                                function: FunctionId::new(1),
3065                                captures: reg(0),
3066                            },
3067                            Instruction::CreateArray { dst: reg(2) },
3068                            Instruction::LoadConst {
3069                                dst: reg(3),
3070                                constant: cid(3),
3071                            },
3072                            Instruction::Call {
3073                                dst: reg(4),
3074                                callee: reg(1),
3075                                this_value: reg(3),
3076                                arguments: reg(2),
3077                            },
3078                            Instruction::Return { value: reg(3) },
3079                        ],
3080                    ),
3081                    async_module_function(
3082                        2,
3083                        vec![
3084                            Instruction::LoadConst {
3085                                dst: reg(0),
3086                                constant: cid(2),
3087                            },
3088                            Instruction::Suspend {
3089                                dst: reg(1),
3090                                src: reg(0),
3091                                resume: pc(2),
3092                            },
3093                            Instruction::StoreGlobal {
3094                                name: cid(1),
3095                                value: reg(1),
3096                            },
3097                            Instruction::Return { value: reg(1) },
3098                        ],
3099                    ),
3100                ],
3101                Vec::new(),
3102                Vec::new(),
3103                Vec::new(),
3104            )],
3105            0,
3106        );
3107        let observed = EcmaString::from_utf8("observed");
3108
3109        let mut interpreter_host = SilentHost;
3110        let mut interpreter = Machine::new(&program, &mut interpreter_host, Limits::default());
3111        interpreter.evaluate().unwrap();
3112        assert!(!interpreter.globals.contains_key(&observed));
3113        let interpreter_drain = interpreter.drain_microtasks().unwrap();
3114        let interpreter_value = interpreter.globals.get(&observed).copied();
3115
3116        let mut native_host = SilentHost;
3117        let engine = NativeEngine::new(&program, &NoEntries, &mut native_host, Limits::default());
3118        engine.machine.borrow_mut().instantiate_modules().unwrap();
3119        engine
3120            .evaluate_reference_module(program.entry())
3121            .unwrap()
3122            .expect("native entry completes");
3123        assert!(!engine.machine.borrow().globals.contains_key(&observed));
3124        let native_drain = engine.machine.borrow_mut().drain_microtasks().unwrap();
3125        let native_value = engine.machine.borrow().globals.get(&observed).copied();
3126
3127        let mut linked_host = SilentHost;
3128        let linked = NativeEngine::build(
3129            &program,
3130            &NoEntries,
3131            &mut linked_host,
3132            Limits::default(),
3133            Backend::Linked,
3134        );
3135        linked.machine.borrow_mut().instantiate_modules().unwrap();
3136        assert!(matches!(
3137            linked.invoke_runtime(
3138                crate::RuntimeFunction {
3139                    module: ModuleId::new(0),
3140                    function: FunctionId::new(1),
3141                },
3142                &[],
3143                Value::UNDEFINED,
3144                Value::UNDEFINED,
3145                &[],
3146            ),
3147            InvokeOutcome::Value(_)
3148        ));
3149        assert!(!linked.machine.borrow().globals.contains_key(&observed));
3150        let linked_drain = linked.machine.borrow_mut().drain_microtasks().unwrap();
3151        let linked_value = linked.machine.borrow().globals.get(&observed).copied();
3152
3153        assert_eq!(interpreter_drain, native_drain);
3154        assert_eq!(interpreter_value, Some(Value::int32(7)));
3155        assert_eq!(native_value, interpreter_value);
3156        assert_eq!(linked_drain, interpreter_drain);
3157        assert_eq!(linked_value, interpreter_value);
3158    }
3159
3160    /// Constructing an async function through the native engine is a TypeError,
3161    /// matching the interpreter's construct guard, with no bytecode/ABI change.
3162    #[test]
3163    fn native_construct_of_async_function_is_a_type_error() {
3164        let program = linked(
3165            vec![program_module(
3166                "root",
3167                Vec::new(),
3168                vec![
3169                    entry_function(
3170                        4,
3171                        vec![
3172                            Instruction::CreateArray { dst: reg(0) },
3173                            Instruction::CreateClosure {
3174                                dst: reg(1),
3175                                function: FunctionId::new(1),
3176                                captures: reg(0),
3177                            },
3178                            Instruction::CreateArray { dst: reg(2) },
3179                            Instruction::Construct {
3180                                dst: reg(3),
3181                                callee: reg(1),
3182                                arguments: reg(2),
3183                            },
3184                            Instruction::Return { value: reg(3) },
3185                        ],
3186                    ),
3187                    async_module_function(1, vec![Instruction::Halt]),
3188                ],
3189                Vec::new(),
3190                Vec::new(),
3191                Vec::new(),
3192            )],
3193            0,
3194        );
3195        let mut host = SilentHost;
3196        let result = NativeEngine::new(&program, &NoEntries, &mut host, Limits::default()).run();
3197        assert!(matches!(
3198            result,
3199            Err(RuntimeError {
3200                kind: RuntimeErrorKind::UncaughtThrow {
3201                    origin: ThrowOrigin::TypeError { .. },
3202                    ..
3203                },
3204                ..
3205            })
3206        ));
3207    }
3208
3209    #[derive(Default)]
3210    struct RecordingEntries {
3211        program_bytes: Vec<u8>,
3212        invoked: RefCell<Vec<(u32, u32)>>,
3213    }
3214
3215    impl NativeEntryTable for RecordingEntries {
3216        fn program_bytes(&self) -> &[u8] {
3217            &self.program_bytes
3218        }
3219
3220        fn invoke(
3221            &self,
3222            module_id: u32,
3223            function_id: u32,
3224            _frame: &mut ShadowFrame,
3225            out: &mut Completion,
3226        ) -> Result<CompletionTag, AbiError> {
3227            self.invoked.borrow_mut().push((module_id, function_id));
3228            *out = Completion::new(Value::UNDEFINED);
3229            Ok(CompletionTag::Normal)
3230        }
3231    }
3232
3233    struct ForeignEntries {
3234        program_bytes: Vec<u8>,
3235        invoked: Cell<bool>,
3236    }
3237
3238    impl NativeEntryTable for ForeignEntries {
3239        fn program_bytes(&self) -> &[u8] {
3240            &self.program_bytes
3241        }
3242
3243        fn invoke(
3244            &self,
3245            _module_id: u32,
3246            _function_id: u32,
3247            _frame: &mut ShadowFrame,
3248            out: &mut Completion,
3249        ) -> Result<CompletionTag, AbiError> {
3250            self.invoked.set(true);
3251            *out = Completion::new(Value::UNDEFINED);
3252            Ok(CompletionTag::Normal)
3253        }
3254    }
3255
3256    struct SmokeEntries {
3257        program_bytes: Vec<u8>,
3258        invoked: Cell<Option<u32>>,
3259    }
3260
3261    impl NativeEntryTable for SmokeEntries {
3262        fn program_bytes(&self) -> &[u8] {
3263            &self.program_bytes
3264        }
3265
3266        fn invoke(
3267            &self,
3268            module_id: u32,
3269            function_id: u32,
3270            _frame: &mut ShadowFrame,
3271            out: &mut Completion,
3272        ) -> Result<CompletionTag, AbiError> {
3273            assert_eq!(module_id, 0);
3274            self.invoked.set(Some(function_id));
3275            *out = Completion::new(Value::UNDEFINED);
3276            Ok(CompletionTag::Normal)
3277        }
3278    }
3279
3280    #[derive(Default)]
3281    struct FailingEntries {
3282        program_bytes: Vec<u8>,
3283    }
3284
3285    impl NativeEntryTable for FailingEntries {
3286        fn program_bytes(&self) -> &[u8] {
3287            &self.program_bytes
3288        }
3289
3290        fn invoke(
3291            &self,
3292            module_id: u32,
3293            function_id: u32,
3294            _frame: &mut ShadowFrame,
3295            _out: &mut Completion,
3296        ) -> Result<CompletionTag, AbiError> {
3297            Err(AbiError::UnknownFunction {
3298                module_id,
3299                function_id,
3300            })
3301        }
3302    }
3303
3304    struct ThrowEntries;
3305
3306    impl NativeEntryTable for ThrowEntries {
3307        fn program_bytes(&self) -> &[u8] {
3308            &[]
3309        }
3310
3311        fn invoke(
3312            &self,
3313            _module_id: u32,
3314            _function_id: u32,
3315            _frame: &mut ShadowFrame,
3316            out: &mut Completion,
3317        ) -> Result<CompletionTag, AbiError> {
3318            *out = Completion::new(Value::UNDEFINED);
3319            Ok(CompletionTag::Throw)
3320        }
3321    }
3322
3323    struct FatalEntries;
3324
3325    impl NativeEntryTable for FatalEntries {
3326        fn program_bytes(&self) -> &[u8] {
3327            &[]
3328        }
3329
3330        fn invoke(
3331            &self,
3332            _module_id: u32,
3333            _function_id: u32,
3334            _frame: &mut ShadowFrame,
3335            out: &mut Completion,
3336        ) -> Result<CompletionTag, AbiError> {
3337            *out = Completion::new(Value::UNDEFINED);
3338            Ok(CompletionTag::FatalTrap)
3339        }
3340    }
3341
3342    #[test]
3343    fn native_program_keeps_same_name_globals_module_local() {
3344        let dependency = |name: &str, value: i32| {
3345            program_module(
3346                name,
3347                vec![
3348                    Constant::String(EcmaString::from_utf8("x")),
3349                    Constant::Int32(value),
3350                ],
3351                vec![module_function(
3352                    0,
3353                    1,
3354                    vec![
3355                        Instruction::LoadConst {
3356                            dst: reg(0),
3357                            constant: cid(2),
3358                        },
3359                        Instruction::StoreGlobal {
3360                            name: cid(1),
3361                            value: reg(0),
3362                        },
3363                        Instruction::Return { value: reg(0) },
3364                    ],
3365                )],
3366                Vec::new(),
3367                vec![Binding {
3368                    name: cid(1),
3369                    kind: BindingKind::Hoisted,
3370                }],
3371                vec![Export {
3372                    name: cid(1),
3373                    source: ExportSource::Local(BindingId::new(0)),
3374                }],
3375            )
3376        };
3377        let root = program_module(
3378            "root",
3379            vec![
3380                Constant::String(EcmaString::from_utf8("left")),
3381                Constant::String(EcmaString::from_utf8("right")),
3382                Constant::String(EcmaString::from_utf8("x")),
3383                Constant::String(EcmaString::from_utf8("one")),
3384                Constant::String(EcmaString::from_utf8("two")),
3385            ],
3386            vec![module_function(
3387                0,
3388                3,
3389                vec![
3390                    Instruction::LoadGlobal {
3391                        dst: reg(0),
3392                        name: cid(1),
3393                    },
3394                    Instruction::LoadGlobal {
3395                        dst: reg(1),
3396                        name: cid(2),
3397                    },
3398                    Instruction::Binary {
3399                        dst: reg(2),
3400                        op: BinaryOp::Add,
3401                        left: reg(0),
3402                        right: reg(1),
3403                    },
3404                    Instruction::Return { value: reg(2) },
3405                ],
3406            )],
3407            vec![
3408                Edge {
3409                    specifier: cid(3),
3410                    target: EdgeTarget::Local(ModuleId::new(0)),
3411                    kind: EdgeKind::Static,
3412                },
3413                Edge {
3414                    specifier: cid(4),
3415                    target: EdgeTarget::Local(ModuleId::new(1)),
3416                    kind: EdgeKind::Static,
3417                },
3418            ],
3419            vec![
3420                Binding {
3421                    name: cid(1),
3422                    kind: BindingKind::Imported {
3423                        edge: EdgeId::new(0),
3424                        name: cid(3),
3425                    },
3426                },
3427                Binding {
3428                    name: cid(2),
3429                    kind: BindingKind::Imported {
3430                        edge: EdgeId::new(1),
3431                        name: cid(3),
3432                    },
3433                },
3434            ],
3435            Vec::new(),
3436        );
3437        let execution = assert_program_parity(&linked(
3438            vec![dependency("one", 1), dependency("two", 2), root],
3439            2,
3440        ))
3441        .unwrap();
3442        assert_eq!(execution.value, Value::int32(3));
3443    }
3444
3445    #[test]
3446    fn native_program_preserves_live_mutation_and_nested_closure_module() {
3447        let dependency = program_module(
3448            "dependency",
3449            vec![
3450                Constant::String(EcmaString::from_utf8("x")),
3451                Constant::Int32(1),
3452                Constant::Int32(2),
3453                Constant::String(EcmaString::from_utf8("set")),
3454            ],
3455            vec![
3456                module_function(
3457                    0,
3458                    3,
3459                    vec![
3460                        Instruction::LoadConst {
3461                            dst: reg(0),
3462                            constant: cid(2),
3463                        },
3464                        Instruction::StoreGlobal {
3465                            name: cid(1),
3466                            value: reg(0),
3467                        },
3468                        Instruction::CreateArray { dst: reg(1) },
3469                        Instruction::CreateClosure {
3470                            dst: reg(2),
3471                            function: FunctionId::new(1),
3472                            captures: reg(1),
3473                        },
3474                        Instruction::StoreGlobal {
3475                            name: cid(4),
3476                            value: reg(2),
3477                        },
3478                        Instruction::Return { value: reg(0) },
3479                    ],
3480                ),
3481                module_function(
3482                    0,
3483                    1,
3484                    vec![
3485                        Instruction::LoadConst {
3486                            dst: reg(0),
3487                            constant: cid(3),
3488                        },
3489                        Instruction::StoreGlobal {
3490                            name: cid(1),
3491                            value: reg(0),
3492                        },
3493                        Instruction::Return { value: reg(0) },
3494                    ],
3495                ),
3496            ],
3497            Vec::new(),
3498            vec![
3499                Binding {
3500                    name: cid(1),
3501                    kind: BindingKind::Hoisted,
3502                },
3503                Binding {
3504                    name: cid(4),
3505                    kind: BindingKind::Hoisted,
3506                },
3507            ],
3508            vec![
3509                Export {
3510                    name: cid(1),
3511                    source: ExportSource::Local(BindingId::new(0)),
3512                },
3513                Export {
3514                    name: cid(4),
3515                    source: ExportSource::Local(BindingId::new(1)),
3516                },
3517            ],
3518        );
3519        let root = program_module(
3520            "root",
3521            vec![
3522                Constant::String(EcmaString::from_utf8("set")),
3523                Constant::String(EcmaString::from_utf8("x")),
3524                Constant::String(EcmaString::from_utf8("dependency")),
3525            ],
3526            vec![module_function(
3527                0,
3528                3,
3529                vec![
3530                    Instruction::LoadGlobal {
3531                        dst: reg(0),
3532                        name: cid(1),
3533                    },
3534                    Instruction::CreateArray { dst: reg(1) },
3535                    Instruction::Call {
3536                        dst: reg(2),
3537                        callee: reg(0),
3538                        this_value: reg(1),
3539                        arguments: reg(1),
3540                    },
3541                    Instruction::LoadGlobal {
3542                        dst: reg(0),
3543                        name: cid(2),
3544                    },
3545                    Instruction::Return { value: reg(0) },
3546                ],
3547            )],
3548            vec![Edge {
3549                specifier: cid(3),
3550                target: EdgeTarget::Local(ModuleId::new(0)),
3551                kind: EdgeKind::Static,
3552            }],
3553            vec![
3554                Binding {
3555                    name: cid(1),
3556                    kind: BindingKind::Imported {
3557                        edge: EdgeId::new(0),
3558                        name: cid(1),
3559                    },
3560                },
3561                Binding {
3562                    name: cid(2),
3563                    kind: BindingKind::Imported {
3564                        edge: EdgeId::new(0),
3565                        name: cid(2),
3566                    },
3567                },
3568            ],
3569            Vec::new(),
3570        );
3571        assert_eq!(
3572            assert_program_parity(&linked(vec![dependency, root], 1))
3573                .unwrap()
3574                .value,
3575            Value::int32(2)
3576        );
3577    }
3578
3579    #[test]
3580    fn native_program_cycle_observes_temporal_dead_zone() {
3581        let first = program_module(
3582            "first",
3583            vec![
3584                Constant::String(EcmaString::from_utf8("a")),
3585                Constant::Int32(1),
3586                Constant::String(EcmaString::from_utf8("second")),
3587            ],
3588            vec![module_function(
3589                0,
3590                1,
3591                vec![
3592                    Instruction::LoadConst {
3593                        dst: reg(0),
3594                        constant: cid(2),
3595                    },
3596                    Instruction::StoreGlobal {
3597                        name: cid(1),
3598                        value: reg(0),
3599                    },
3600                    Instruction::Return { value: reg(0) },
3601                ],
3602            )],
3603            vec![Edge {
3604                specifier: cid(3),
3605                target: EdgeTarget::Local(ModuleId::new(1)),
3606                kind: EdgeKind::Static,
3607            }],
3608            vec![Binding {
3609                name: cid(1),
3610                kind: BindingKind::Lexical,
3611            }],
3612            vec![Export {
3613                name: cid(1),
3614                source: ExportSource::Local(BindingId::new(0)),
3615            }],
3616        );
3617        let second = program_module(
3618            "second",
3619            vec![
3620                Constant::String(EcmaString::from_utf8("a")),
3621                Constant::String(EcmaString::from_utf8("first")),
3622            ],
3623            vec![module_function(
3624                0,
3625                1,
3626                vec![
3627                    Instruction::LoadGlobal {
3628                        dst: reg(0),
3629                        name: cid(1),
3630                    },
3631                    Instruction::Return { value: reg(0) },
3632                ],
3633            )],
3634            vec![Edge {
3635                specifier: cid(2),
3636                target: EdgeTarget::Local(ModuleId::new(0)),
3637                kind: EdgeKind::Static,
3638            }],
3639            vec![Binding {
3640                name: cid(1),
3641                kind: BindingKind::Imported {
3642                    edge: EdgeId::new(0),
3643                    name: cid(1),
3644                },
3645            }],
3646            Vec::new(),
3647        );
3648        let error = assert_program_parity(&linked(vec![first, second], 0)).unwrap_err();
3649        assert!(matches!(
3650            error.kind,
3651            RuntimeErrorKind::TemporalDeadZone { module, binding }
3652                if module == ModuleId::new(1) && binding == BindingId::new(0)
3653        ));
3654    }
3655
3656    #[test]
3657    fn native_program_namespace_reads_shared_export_cell() {
3658        let dependency = program_module(
3659            "dependency",
3660            vec![
3661                Constant::String(EcmaString::from_utf8("x")),
3662                Constant::Int32(7),
3663            ],
3664            vec![module_function(
3665                0,
3666                1,
3667                vec![
3668                    Instruction::LoadConst {
3669                        dst: reg(0),
3670                        constant: cid(2),
3671                    },
3672                    Instruction::StoreGlobal {
3673                        name: cid(1),
3674                        value: reg(0),
3675                    },
3676                    Instruction::Return { value: reg(0) },
3677                ],
3678            )],
3679            Vec::new(),
3680            vec![Binding {
3681                name: cid(1),
3682                kind: BindingKind::Hoisted,
3683            }],
3684            vec![Export {
3685                name: cid(1),
3686                source: ExportSource::Local(BindingId::new(0)),
3687            }],
3688        );
3689        let root = program_module(
3690            "root",
3691            vec![
3692                Constant::String(EcmaString::from_utf8("ns")),
3693                Constant::String(EcmaString::from_utf8("x")),
3694                Constant::String(EcmaString::from_utf8("dependency")),
3695            ],
3696            vec![module_function(
3697                0,
3698                3,
3699                vec![
3700                    Instruction::LoadGlobal {
3701                        dst: reg(0),
3702                        name: cid(1),
3703                    },
3704                    Instruction::LoadConst {
3705                        dst: reg(1),
3706                        constant: cid(2),
3707                    },
3708                    Instruction::GetProperty {
3709                        dst: reg(2),
3710                        object: reg(0),
3711                        key: reg(1),
3712                    },
3713                    Instruction::Return { value: reg(2) },
3714                ],
3715            )],
3716            vec![Edge {
3717                specifier: cid(3),
3718                target: EdgeTarget::Local(ModuleId::new(0)),
3719                kind: EdgeKind::Static,
3720            }],
3721            vec![Binding {
3722                name: cid(1),
3723                kind: BindingKind::Namespace {
3724                    edge: EdgeId::new(0),
3725                },
3726            }],
3727            Vec::new(),
3728        );
3729        assert_eq!(
3730            assert_program_parity(&linked(vec![dependency, root], 1))
3731                .unwrap()
3732                .value,
3733            Value::int32(7)
3734        );
3735    }
3736
3737    #[test]
3738    fn native_program_evaluates_duplicate_static_dependency_once() {
3739        let dependency = program_module(
3740            "dependency",
3741            vec![
3742                Constant::String(EcmaString::from_utf8("count")),
3743                Constant::Int32(0),
3744                Constant::Int32(1),
3745            ],
3746            vec![module_function(
3747                0,
3748                2,
3749                vec![
3750                    Instruction::LoadGlobal {
3751                        dst: reg(0),
3752                        name: cid(1),
3753                    },
3754                    Instruction::JumpIfFalse {
3755                        condition: reg(0),
3756                        target: pc(4),
3757                    },
3758                    Instruction::LoadConst {
3759                        dst: reg(1),
3760                        constant: cid(3),
3761                    },
3762                    Instruction::Jump { target: pc(6) },
3763                    Instruction::LoadConst {
3764                        dst: reg(0),
3765                        constant: cid(2),
3766                    },
3767                    Instruction::LoadConst {
3768                        dst: reg(1),
3769                        constant: cid(3),
3770                    },
3771                    Instruction::Binary {
3772                        dst: reg(0),
3773                        op: BinaryOp::Add,
3774                        left: reg(0),
3775                        right: reg(1),
3776                    },
3777                    Instruction::StoreGlobal {
3778                        name: cid(1),
3779                        value: reg(0),
3780                    },
3781                    Instruction::Return { value: reg(0) },
3782                ],
3783            )],
3784            Vec::new(),
3785            vec![Binding {
3786                name: cid(1),
3787                kind: BindingKind::Hoisted,
3788            }],
3789            vec![Export {
3790                name: cid(1),
3791                source: ExportSource::Local(BindingId::new(0)),
3792            }],
3793        );
3794        let root = program_module(
3795            "root",
3796            vec![
3797                Constant::String(EcmaString::from_utf8("count")),
3798                Constant::String(EcmaString::from_utf8("dependency")),
3799                Constant::String(EcmaString::from_utf8("dependency-again")),
3800            ],
3801            vec![module_function(
3802                0,
3803                1,
3804                vec![
3805                    Instruction::LoadGlobal {
3806                        dst: reg(0),
3807                        name: cid(1),
3808                    },
3809                    Instruction::Return { value: reg(0) },
3810                ],
3811            )],
3812            vec![
3813                Edge {
3814                    specifier: cid(2),
3815                    target: EdgeTarget::Local(ModuleId::new(0)),
3816                    kind: EdgeKind::Static,
3817                },
3818                Edge {
3819                    specifier: cid(3),
3820                    target: EdgeTarget::Local(ModuleId::new(0)),
3821                    kind: EdgeKind::Static,
3822                },
3823            ],
3824            vec![Binding {
3825                name: cid(1),
3826                kind: BindingKind::Imported {
3827                    edge: EdgeId::new(0),
3828                    name: cid(1),
3829                },
3830            }],
3831            Vec::new(),
3832        );
3833        assert_eq!(
3834            assert_program_parity(&linked(vec![dependency, root], 1))
3835                .unwrap()
3836                .value,
3837            Value::int32(1)
3838        );
3839    }
3840
3841    #[test]
3842    fn native_program_memoizes_thrown_object_identity() {
3843        let module = program_module(
3844            "throws",
3845            Vec::new(),
3846            vec![module_function(
3847                0,
3848                1,
3849                vec![
3850                    Instruction::CreateObject { dst: reg(0) },
3851                    Instruction::Throw { value: reg(0) },
3852                ],
3853            )],
3854            Vec::new(),
3855            Vec::new(),
3856            Vec::new(),
3857        );
3858        let error = assert_program_parity(&linked(vec![module], 0)).unwrap_err();
3859        assert!(matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }));
3860    }
3861
3862    struct SilentHost;
3863    impl Host for SilentHost {}
3864
3865    #[test]
3866    fn native_matches_interpreter_on_arithmetic() {
3867        let module = verified(
3868            vec![Constant::Int32(3), Constant::Int32(4)],
3869            vec![entry_function(
3870                2,
3871                vec![
3872                    Instruction::LoadConst {
3873                        dst: reg(0),
3874                        constant: cid(0),
3875                    },
3876                    Instruction::LoadConst {
3877                        dst: reg(1),
3878                        constant: cid(1),
3879                    },
3880                    Instruction::Binary {
3881                        dst: reg(0),
3882                        op: BinaryOp::Add,
3883                        left: reg(0),
3884                        right: reg(1),
3885                    },
3886                    Instruction::Return { value: reg(0) },
3887                ],
3888            )],
3889        );
3890        let value = assert_parity(&module, || SilentHost);
3891        assert_eq!(value.as_int32(), Some(7));
3892    }
3893
3894    #[test]
3895    fn reference_and_interpreter_charge_each_mixed_instruction_once() {
3896        let module = verified(
3897            vec![Constant::Int32(1)],
3898            vec![entry_function(
3899                3,
3900                vec![
3901                    Instruction::LoadConst {
3902                        dst: reg(0),
3903                        constant: cid(0),
3904                    },
3905                    Instruction::Move {
3906                        dst: reg(1),
3907                        src: reg(0),
3908                    },
3909                    Instruction::Binary {
3910                        dst: reg(2),
3911                        op: BinaryOp::Add,
3912                        left: reg(0),
3913                        right: reg(1),
3914                    },
3915                    Instruction::Jump { target: pc(4) },
3916                    Instruction::Halt,
3917                ],
3918            )],
3919        );
3920        let program = one_module_program(&module);
3921
3922        for fuel in [0, 4, 5] {
3923            let limits = Limits {
3924                fuel,
3925                ..Limits::default()
3926            };
3927            let mut interpreter_host = SilentHost;
3928            let interpreter = Machine::new(&program, &mut interpreter_host, limits.clone()).run();
3929            let mut reference_host = SilentHost;
3930            let reference =
3931                NativeEngine::new(&program, &NoEntries, &mut reference_host, limits).run();
3932            assert_eq!(interpreter, reference, "fuel={fuel}");
3933            if fuel == 5 {
3934                assert!(reference.is_ok(), "N instructions must fit fuel N");
3935            } else {
3936                assert!(
3937                    matches!(
3938                        reference,
3939                        Err(RuntimeError {
3940                            kind: RuntimeErrorKind::FuelExhausted { limit },
3941                            ..
3942                        }) if limit == fuel
3943                    ),
3944                    "fuel={fuel} must exhaust before the next instruction"
3945                );
3946            }
3947        }
3948    }
3949
3950    #[test]
3951    fn native_reservations_share_interpreter_depth_and_register_ceilings() {
3952        let module = verified(Vec::new(), vec![entry_function(2, vec![Instruction::Halt])]);
3953        let program = one_module_program(&module);
3954        let mut host = SilentHost;
3955        let mut machine = Machine::new(
3956            &program,
3957            &mut host,
3958            Limits {
3959                max_call_depth: 2,
3960                max_total_registers: 3,
3961                ..Limits::default()
3962            },
3963        );
3964
3965        assert_eq!(machine.frames.len(), 1);
3966        assert_eq!(machine.live_registers, 2);
3967        machine.reserve_native_activation(1).unwrap();
3968        assert_eq!((machine.native_depth, machine.live_registers), (1, 3));
3969        assert!(matches!(
3970            machine.reserve_native_activation(0),
3971            Err(RuntimeErrorKind::CallDepthExceeded { limit: 2 })
3972        ));
3973        assert_eq!(
3974            (machine.native_depth, machine.live_registers),
3975            (1, 3),
3976            "failed depth reservation is atomic"
3977        );
3978        machine.release_native_activation(1);
3979
3980        machine.limits.max_call_depth = 3;
3981        machine.limits.max_total_registers = 2;
3982        assert!(matches!(
3983            machine.reserve_native_activation(1),
3984            Err(RuntimeErrorKind::RegisterLimitExceeded { limit: 2 })
3985        ));
3986        assert_eq!(
3987            (machine.native_depth, machine.live_registers),
3988            (0, 2),
3989            "failed register reservation is atomic"
3990        );
3991    }
3992
3993    #[test]
3994    fn native_matches_interpreter_on_loop_and_branches() {
3995        // acc=0; for i in 1..4 { acc += i } -> 6, exercising Binary compare,
3996        // JumpIfFalse, and Jump through the native driver's own control flow.
3997        let module = verified(
3998            vec![Constant::Int32(0), Constant::Int32(1), Constant::Int32(4)],
3999            vec![entry_function(
4000                5,
4001                vec![
4002                    Instruction::LoadConst {
4003                        dst: reg(0),
4004                        constant: cid(0),
4005                    },
4006                    Instruction::LoadConst {
4007                        dst: reg(1),
4008                        constant: cid(1),
4009                    },
4010                    Instruction::LoadConst {
4011                        dst: reg(2),
4012                        constant: cid(2),
4013                    },
4014                    Instruction::LoadConst {
4015                        dst: reg(3),
4016                        constant: cid(1),
4017                    },
4018                    Instruction::Binary {
4019                        dst: reg(4),
4020                        op: BinaryOp::LessThan,
4021                        left: reg(1),
4022                        right: reg(2),
4023                    },
4024                    Instruction::JumpIfFalse {
4025                        condition: reg(4),
4026                        target: pc(9),
4027                    },
4028                    Instruction::Binary {
4029                        dst: reg(0),
4030                        op: BinaryOp::Add,
4031                        left: reg(0),
4032                        right: reg(1),
4033                    },
4034                    Instruction::Binary {
4035                        dst: reg(1),
4036                        op: BinaryOp::Add,
4037                        left: reg(1),
4038                        right: reg(3),
4039                    },
4040                    Instruction::Jump { target: pc(4) },
4041                    Instruction::Return { value: reg(0) },
4042                ],
4043            )],
4044        );
4045        let value = assert_parity(&module, || SilentHost);
4046        assert_eq!(value.as_int32(), Some(6));
4047    }
4048
4049    #[test]
4050    fn native_matches_interpreter_on_object_property_roundtrip() {
4051        let module = verified(
4052            vec![
4053                Constant::String(EcmaString::from_utf8("x")),
4054                Constant::Int32(5),
4055            ],
4056            vec![entry_function(
4057                3,
4058                vec![
4059                    Instruction::CreateObject { dst: reg(0) },
4060                    Instruction::LoadConst {
4061                        dst: reg(1),
4062                        constant: cid(0),
4063                    },
4064                    Instruction::LoadConst {
4065                        dst: reg(2),
4066                        constant: cid(1),
4067                    },
4068                    Instruction::SetProperty {
4069                        object: reg(0),
4070                        key: reg(1),
4071                        value: reg(2),
4072                    },
4073                    Instruction::GetProperty {
4074                        dst: reg(2),
4075                        object: reg(0),
4076                        key: reg(1),
4077                    },
4078                    Instruction::Return { value: reg(2) },
4079                ],
4080            )],
4081        );
4082        let value = assert_parity(&module, || SilentHost);
4083        assert_eq!(value.as_int32(), Some(5));
4084    }
4085
4086    #[test]
4087    fn native_matches_interpreter_on_closure_call() {
4088        // fn0: build empty captures, close over fn1, call it. fn1: return 42.
4089        let entry = entry_function(
4090            3,
4091            vec![
4092                Instruction::CreateArray { dst: reg(0) },
4093                Instruction::CreateClosure {
4094                    dst: reg(1),
4095                    function: FunctionId::new(1),
4096                    captures: reg(0),
4097                },
4098                Instruction::CreateArray { dst: reg(0) },
4099                Instruction::LoadConst {
4100                    dst: reg(2),
4101                    constant: cid(0),
4102                },
4103                Instruction::Call {
4104                    dst: reg(0),
4105                    callee: reg(1),
4106                    this_value: reg(2),
4107                    arguments: reg(0),
4108                },
4109                Instruction::Return { value: reg(0) },
4110            ],
4111        );
4112        let callee = Function::new(
4113            None,
4114            0,
4115            0,
4116            1,
4117            FunctionFlags::default(),
4118            vec![
4119                Instruction::LoadConst {
4120                    dst: reg(0),
4121                    constant: cid(1),
4122                },
4123                Instruction::Return { value: reg(0) },
4124            ],
4125            Vec::new(),
4126        );
4127        let module = verified(
4128            vec![Constant::Undefined, Constant::Int32(42)],
4129            vec![entry, callee],
4130        );
4131        let value = assert_parity(&module, || SilentHost);
4132        assert_eq!(value.as_int32(), Some(42));
4133    }
4134
4135    #[test]
4136    fn native_matches_interpreter_on_throw_and_catch() {
4137        let module = verified(
4138            vec![Constant::Int32(99)],
4139            vec![Function::new(
4140                None,
4141                0,
4142                0,
4143                2,
4144                FunctionFlags::default(),
4145                vec![
4146                    Instruction::LoadConst {
4147                        dst: reg(0),
4148                        constant: cid(0),
4149                    },
4150                    Instruction::Throw { value: reg(0) },
4151                    Instruction::Return { value: reg(1) },
4152                ],
4153                vec![ExceptionHandler {
4154                    start: pc(0),
4155                    end: pc(2),
4156                    handler: pc(2),
4157                    catch_register: reg(1),
4158                }],
4159            )],
4160        );
4161        let value = assert_parity(&module, || SilentHost);
4162        assert_eq!(value.as_int32(), Some(99));
4163    }
4164
4165    fn one_module_program(module: &Module<Verified>) -> Program<Verified> {
4166        let mut constants = module.constants().to_vec();
4167        let name = ConstantId::new(constants.len() as u32);
4168        constants.push(Constant::String(EcmaString::from_utf8("test-module")));
4169        let code = Module::new(constants, module.functions().to_vec(), module.entry())
4170            .verify()
4171            .expect("test module remains verified");
4172        Program::link(
4173            vec![ProgramModule {
4174                name,
4175                code,
4176                edges: Vec::new(),
4177                bindings: Vec::new(),
4178                exports: Vec::new(),
4179            }],
4180            ModuleId::new(0),
4181        )
4182        .expect("one-module reference program links")
4183    }
4184
4185    fn assert_parity<H: Host, F: Fn() -> H>(module: &Module<Verified>, make_host: F) -> Value {
4186        let limits = Limits::default();
4187        let program = one_module_program(module);
4188
4189        let mut interp_host = make_host();
4190        let interpreter = Machine::new(&program, &mut interp_host, limits.clone())
4191            .run()
4192            .expect("interpreter runs");
4193
4194        let mut native_host = make_host();
4195        let entries = NoEntries;
4196        let native = NativeEngine::new(&program, &entries, &mut native_host, limits)
4197            .run()
4198            .expect("native engine runs");
4199
4200        assert_eq!(
4201            interpreter.value, native.value,
4202            "return value parity: interpreter {:?} vs native {:?}",
4203            interpreter.value, native.value
4204        );
4205        assert_eq!(
4206            interpreter.outcome, native.outcome,
4207            "outcome parity: interpreter {:?} vs native {:?}",
4208            interpreter.outcome, native.outcome
4209        );
4210        assert_eq!(
4211            interpreter.entry_registers, native.entry_registers,
4212            "entry register parity"
4213        );
4214        native.value
4215    }
4216
4217    fn trivial_program() -> Program<Verified> {
4218        let code = verified(
4219            vec![Constant::String(EcmaString::from_utf8("<test>"))],
4220            vec![entry_function(1, vec![Instruction::Halt])],
4221        );
4222        Program::link(
4223            vec![ProgramModule {
4224                name: ConstantId::new(0),
4225                code,
4226                edges: Vec::new(),
4227                bindings: Vec::new(),
4228                exports: Vec::new(),
4229            }],
4230            ModuleId::new(0),
4231        )
4232        .expect("valid native test program")
4233    }
4234
4235    #[test]
4236    fn linked_backend_accepts_metadata_empty_single_module_program() {
4237        let program = trivial_program();
4238        let entries = SmokeEntries {
4239            program_bytes: program.encode(),
4240            invoked: Cell::new(None),
4241        };
4242        let mut host = SilentHost;
4243        let outcome = run_linked_program(&program, &entries, &mut host, &Limits::default())
4244            .expect("linked program runs");
4245        assert_eq!(entries.invoked.get(), Some(0), "entry function 0 invoked");
4246        assert_eq!(outcome.exit_code, 0);
4247        assert!(outcome.stdout.is_empty());
4248    }
4249
4250    #[test]
4251    fn linked_backend_propagates_abi_error() {
4252        let program = trivial_program();
4253        let entries = FailingEntries {
4254            program_bytes: program.encode(),
4255        };
4256        let mut host = SilentHost;
4257        let error = run_linked_program(&program, &entries, &mut host, &Limits::default())
4258            .expect_err("entry table failure surfaces");
4259        assert!(matches!(
4260            error,
4261            NativeError::Abi(AbiError::UnknownFunction {
4262                module_id: 0,
4263                function_id: 0
4264            })
4265        ));
4266    }
4267
4268    #[test]
4269    fn linked_backend_rejects_entries_from_same_shape_different_program_before_invocation() {
4270        let program = |constant| {
4271            linked(
4272                vec![program_module(
4273                    "entry",
4274                    vec![Constant::Int32(constant)],
4275                    vec![entry_function(
4276                        1,
4277                        vec![
4278                            Instruction::LoadConst {
4279                                dst: reg(0),
4280                                constant: cid(1),
4281                            },
4282                            Instruction::Return { value: reg(0) },
4283                        ],
4284                    )],
4285                    Vec::new(),
4286                    Vec::new(),
4287                    Vec::new(),
4288                )],
4289                0,
4290            )
4291        };
4292        let compiled_program = program(1);
4293        let supplied_program = program(2);
4294        let entries = ForeignEntries {
4295            program_bytes: compiled_program.encode(),
4296            invoked: Cell::new(false),
4297        };
4298        let mut host = SilentHost;
4299
4300        assert_eq!(
4301            run_linked_program(&supplied_program, &entries, &mut host, &Limits::default()),
4302            Err(NativeError::ProgramMismatch)
4303        );
4304        assert!(
4305            !entries.invoked.get(),
4306            "mismatched entries must not be invoked"
4307        );
4308    }
4309
4310    #[test]
4311    fn linked_backend_invokes_static_dependencies_before_entry_by_tuple() {
4312        let single = trivial_program();
4313        let first = single.modules()[0].clone();
4314        let second = program_module(
4315            "entry",
4316            vec![Constant::String(EcmaString::from_utf8("dependency"))],
4317            vec![entry_function(1, vec![Instruction::Halt])],
4318            vec![Edge {
4319                specifier: cid(1),
4320                target: EdgeTarget::Local(ModuleId::new(0)),
4321                kind: EdgeKind::Static,
4322            }],
4323            Vec::new(),
4324            Vec::new(),
4325        );
4326        let program = Program::link(vec![first, second], ModuleId::new(1)).unwrap();
4327        let entries = RecordingEntries {
4328            program_bytes: program.encode(),
4329            ..RecordingEntries::default()
4330        };
4331        let mut host = SilentHost;
4332        run_linked_program(&program, &entries, &mut host, &Limits::default()).unwrap();
4333        assert_eq!(entries.invoked.borrow().as_slice(), &[(0, 0), (1, 0)]);
4334    }
4335
4336    #[test]
4337    fn dynamic_import_cycle_matches_the_reference_backend() {
4338        let execution = assert_program_parity(&dynamic_cycle_program()).unwrap();
4339        assert_eq!(execution.value, Value::TRUE);
4340        assert_eq!(execution.entry_registers[0], execution.entry_registers[1]);
4341    }
4342
4343    #[test]
4344    fn dynamic_import_throw_is_caught_at_the_requester_in_both_backends() {
4345        let root = program_module(
4346            "root",
4347            vec![Constant::String(EcmaString::from_utf8("./target"))],
4348            vec![Function::new(
4349                None,
4350                0,
4351                0,
4352                2,
4353                FunctionFlags::default(),
4354                vec![
4355                    Instruction::Import {
4356                        dst: reg(0),
4357                        specifier: cid(1),
4358                    },
4359                    Instruction::Halt,
4360                    Instruction::Return { value: reg(1) },
4361                ],
4362                vec![ExceptionHandler {
4363                    start: pc(0),
4364                    end: pc(1),
4365                    handler: pc(2),
4366                    catch_register: reg(1),
4367                }],
4368            )],
4369            vec![Edge {
4370                specifier: cid(1),
4371                target: EdgeTarget::Local(ModuleId::new(1)),
4372                kind: EdgeKind::Dynamic,
4373            }],
4374            Vec::new(),
4375            Vec::new(),
4376        );
4377        let target = program_module(
4378            "target",
4379            vec![Constant::Int32(9)],
4380            vec![entry_function(
4381                1,
4382                vec![
4383                    Instruction::LoadConst {
4384                        dst: reg(0),
4385                        constant: cid(1),
4386                    },
4387                    Instruction::Throw { value: reg(0) },
4388                ],
4389            )],
4390            Vec::new(),
4391            Vec::new(),
4392            Vec::new(),
4393        );
4394
4395        assert_eq!(
4396            assert_program_parity(&linked(vec![root, target], 0))
4397                .unwrap()
4398                .value,
4399            Value::int32(9)
4400        );
4401    }
4402
4403    #[test]
4404    fn linked_dynamic_import_invokes_the_target_once() {
4405        let program = dynamic_cycle_program();
4406        let entries = RecordingEntries {
4407            program_bytes: program.encode(),
4408            ..RecordingEntries::default()
4409        };
4410        let mut host = SilentHost;
4411        let engine = NativeEngine::build(
4412            &program,
4413            &entries,
4414            &mut host,
4415            Limits::default(),
4416            Backend::Linked,
4417        );
4418        engine.machine.borrow_mut().instantiate_modules().unwrap();
4419        assert!(matches!(
4420            engine
4421                .machine
4422                .borrow_mut()
4423                .begin_module_evaluation(ModuleId::new(0))
4424                .unwrap(),
4425            crate::ModuleEvaluation::Ready(_)
4426        ));
4427
4428        let mut registers = [Value::UNINITIALIZED; 3];
4429        let handles = registers.as_mut_ptr();
4430        let mut shadow =
4431            ShadowFrame::new(std::ptr::null_mut(), 0, 0, handles, registers.len() as u16);
4432        let mut frame = NativeFrame::new(&mut shadow, &mut registers).unwrap();
4433        let first = engine.dispatch(&mut frame, HelperCall::Import { specifier: 1 });
4434        let second = engine.dispatch(&mut frame, HelperCall::Import { specifier: 1 });
4435
4436        assert_eq!(first.tag, CompletionTag::Normal);
4437        assert_eq!(second.tag, CompletionTag::Normal);
4438        assert_eq!(first.value, second.value);
4439        assert_eq!(entries.invoked.borrow().as_slice(), &[(1, 0)]);
4440    }
4441
4442    #[test]
4443    fn native_functions_call_and_construct_with_engine_parity() {
4444        let module = verified(
4445            vec![
4446                Constant::String(EcmaString::from_utf8("Object")),
4447                Constant::String(EcmaString::from_utf8("prototype")),
4448                Constant::String(EcmaString::from_utf8("toString")),
4449                Constant::String(EcmaString::from_utf8("call")),
4450                Constant::String(EcmaString::from_utf8("[object Object]")),
4451                Constant::Undefined,
4452            ],
4453            vec![entry_function(
4454                8,
4455                vec![
4456                    Instruction::LoadGlobal {
4457                        dst: reg(0),
4458                        name: cid(0),
4459                    },
4460                    Instruction::CreateArray { dst: reg(1) },
4461                    Instruction::Construct {
4462                        dst: reg(2),
4463                        callee: reg(0),
4464                        arguments: reg(1),
4465                    },
4466                    Instruction::LoadConst {
4467                        dst: reg(3),
4468                        constant: cid(1),
4469                    },
4470                    Instruction::GetProperty {
4471                        dst: reg(4),
4472                        object: reg(0),
4473                        key: reg(3),
4474                    },
4475                    Instruction::LoadConst {
4476                        dst: reg(3),
4477                        constant: cid(2),
4478                    },
4479                    Instruction::GetProperty {
4480                        dst: reg(4),
4481                        object: reg(4),
4482                        key: reg(3),
4483                    },
4484                    Instruction::LoadConst {
4485                        dst: reg(3),
4486                        constant: cid(3),
4487                    },
4488                    Instruction::GetProperty {
4489                        dst: reg(5),
4490                        object: reg(4),
4491                        key: reg(3),
4492                    },
4493                    Instruction::CreateArray { dst: reg(6) },
4494                    Instruction::ArrayPush {
4495                        array: reg(6),
4496                        value: reg(2),
4497                    },
4498                    Instruction::Call {
4499                        dst: reg(5),
4500                        callee: reg(5),
4501                        this_value: reg(4),
4502                        arguments: reg(6),
4503                    },
4504                    Instruction::LoadConst {
4505                        dst: reg(6),
4506                        constant: cid(4),
4507                    },
4508                    Instruction::Binary {
4509                        dst: reg(7),
4510                        op: BinaryOp::StrictEqual,
4511                        left: reg(5),
4512                        right: reg(6),
4513                    },
4514                    Instruction::Return { value: reg(7) },
4515                ],
4516            )],
4517        );
4518        assert_eq!(assert_parity(&module, || SilentHost), Value::TRUE);
4519    }
4520
4521    #[test]
4522    fn builtin_iterators_match_between_engines() {
4523        let module = verified(
4524            vec![Constant::Int32(8)],
4525            vec![entry_function(
4526                5,
4527                vec![
4528                    Instruction::CreateArray { dst: reg(0) },
4529                    Instruction::LoadConst {
4530                        dst: reg(1),
4531                        constant: cid(0),
4532                    },
4533                    Instruction::ArrayPush {
4534                        array: reg(0),
4535                        value: reg(1),
4536                    },
4537                    Instruction::GetIterator {
4538                        dst: reg(2),
4539                        src: reg(0),
4540                        kind: IteratorKind::Sync,
4541                    },
4542                    Instruction::IteratorNext {
4543                        done: reg(3),
4544                        value: reg(4),
4545                        iterator: reg(2),
4546                    },
4547                    Instruction::Return { value: reg(4) },
4548                ],
4549            )],
4550        );
4551
4552        assert_eq!(assert_parity(&module, || SilentHost), Value::int32(8));
4553    }
4554
4555    #[test]
4556    fn bound_calls_match_between_engines() {
4557        let module = verified(
4558            vec![
4559                Constant::String(EcmaString::from_utf8("bind")),
4560                Constant::String(EcmaString::from_utf8("marker")),
4561                Constant::String(EcmaString::from_utf8("0")),
4562                Constant::String(EcmaString::from_utf8("1")),
4563                Constant::Int32(7),
4564                Constant::Int32(1),
4565                Constant::Int32(2),
4566            ],
4567            vec![
4568                entry_function(
4569                    8,
4570                    vec![
4571                        Instruction::CreateArray { dst: reg(0) },
4572                        Instruction::CreateClosure {
4573                            dst: reg(0),
4574                            function: FunctionId::new(1),
4575                            captures: reg(0),
4576                        },
4577                        Instruction::LoadConst {
4578                            dst: reg(1),
4579                            constant: cid(0),
4580                        },
4581                        Instruction::GetProperty {
4582                            dst: reg(2),
4583                            object: reg(0),
4584                            key: reg(1),
4585                        },
4586                        Instruction::CreateObject { dst: reg(3) },
4587                        Instruction::LoadConst {
4588                            dst: reg(4),
4589                            constant: cid(1),
4590                        },
4591                        Instruction::LoadConst {
4592                            dst: reg(6),
4593                            constant: cid(4),
4594                        },
4595                        Instruction::SetProperty {
4596                            object: reg(3),
4597                            key: reg(4),
4598                            value: reg(6),
4599                        },
4600                        Instruction::CreateArray { dst: reg(5) },
4601                        Instruction::ArrayPush {
4602                            array: reg(5),
4603                            value: reg(3),
4604                        },
4605                        Instruction::LoadConst {
4606                            dst: reg(6),
4607                            constant: cid(5),
4608                        },
4609                        Instruction::ArrayPush {
4610                            array: reg(5),
4611                            value: reg(6),
4612                        },
4613                        Instruction::Call {
4614                            dst: reg(7),
4615                            callee: reg(2),
4616                            this_value: reg(0),
4617                            arguments: reg(5),
4618                        },
4619                        Instruction::CreateArray { dst: reg(5) },
4620                        Instruction::LoadConst {
4621                            dst: reg(6),
4622                            constant: cid(6),
4623                        },
4624                        Instruction::ArrayPush {
4625                            array: reg(5),
4626                            value: reg(6),
4627                        },
4628                        Instruction::Call {
4629                            dst: reg(7),
4630                            callee: reg(7),
4631                            this_value: reg(3),
4632                            arguments: reg(5),
4633                        },
4634                        Instruction::Return { value: reg(7) },
4635                    ],
4636                ),
4637                receiver_sum_function(),
4638            ],
4639        );
4640
4641        assert_eq!(
4642            assert_parity(&module, || SilentHost),
4643            crate::number_value(10.0)
4644        );
4645    }
4646
4647    #[test]
4648    fn applied_calls_match_between_engines() {
4649        let module = verified(
4650            vec![
4651                Constant::String(EcmaString::from_utf8("apply")),
4652                Constant::String(EcmaString::from_utf8("marker")),
4653                Constant::String(EcmaString::from_utf8("0")),
4654                Constant::String(EcmaString::from_utf8("1")),
4655                Constant::Int32(7),
4656                Constant::Int32(1),
4657                Constant::Int32(2),
4658                Constant::String(EcmaString::from_utf8("length")),
4659                Constant::Undefined,
4660            ],
4661            vec![
4662                entry_function(
4663                    8,
4664                    vec![
4665                        Instruction::CreateArray { dst: reg(0) },
4666                        Instruction::CreateClosure {
4667                            dst: reg(0),
4668                            function: FunctionId::new(1),
4669                            captures: reg(0),
4670                        },
4671                        Instruction::LoadConst {
4672                            dst: reg(1),
4673                            constant: cid(0),
4674                        },
4675                        Instruction::GetProperty {
4676                            dst: reg(2),
4677                            object: reg(0),
4678                            key: reg(1),
4679                        },
4680                        Instruction::CreateObject { dst: reg(3) },
4681                        Instruction::LoadConst {
4682                            dst: reg(4),
4683                            constant: cid(1),
4684                        },
4685                        Instruction::LoadConst {
4686                            dst: reg(6),
4687                            constant: cid(4),
4688                        },
4689                        Instruction::SetProperty {
4690                            object: reg(3),
4691                            key: reg(4),
4692                            value: reg(6),
4693                        },
4694                        Instruction::CreateArray { dst: reg(5) },
4695                        Instruction::LoadConst {
4696                            dst: reg(6),
4697                            constant: cid(5),
4698                        },
4699                        Instruction::ArrayPush {
4700                            array: reg(5),
4701                            value: reg(6),
4702                        },
4703                        Instruction::LoadConst {
4704                            dst: reg(6),
4705                            constant: cid(6),
4706                        },
4707                        Instruction::ArrayPush {
4708                            array: reg(5),
4709                            value: reg(6),
4710                        },
4711                        Instruction::CreateArray { dst: reg(6) },
4712                        Instruction::ArrayPush {
4713                            array: reg(6),
4714                            value: reg(3),
4715                        },
4716                        Instruction::ArrayPush {
4717                            array: reg(6),
4718                            value: reg(5),
4719                        },
4720                        Instruction::Call {
4721                            dst: reg(7),
4722                            callee: reg(2),
4723                            this_value: reg(0),
4724                            arguments: reg(6),
4725                        },
4726                        Instruction::CreateArray { dst: reg(0) },
4727                        Instruction::CreateClosure {
4728                            dst: reg(0),
4729                            function: FunctionId::new(2),
4730                            captures: reg(0),
4731                        },
4732                        Instruction::GetProperty {
4733                            dst: reg(2),
4734                            object: reg(0),
4735                            key: reg(1),
4736                        },
4737                        Instruction::CreateArray { dst: reg(6) },
4738                        Instruction::ArrayPush {
4739                            array: reg(6),
4740                            value: reg(3),
4741                        },
4742                        Instruction::LoadConst {
4743                            dst: reg(4),
4744                            constant: cid(8),
4745                        },
4746                        Instruction::ArrayPush {
4747                            array: reg(6),
4748                            value: reg(4),
4749                        },
4750                        Instruction::Call {
4751                            dst: reg(4),
4752                            callee: reg(2),
4753                            this_value: reg(0),
4754                            arguments: reg(6),
4755                        },
4756                        Instruction::Binary {
4757                            dst: reg(7),
4758                            op: BinaryOp::Add,
4759                            left: reg(7),
4760                            right: reg(4),
4761                        },
4762                        Instruction::Return { value: reg(7) },
4763                    ],
4764                ),
4765                receiver_sum_function(),
4766                module_function(
4767                    0,
4768                    3,
4769                    vec![
4770                        Instruction::LoadArguments { dst: reg(0) },
4771                        Instruction::LoadConst {
4772                            dst: reg(1),
4773                            constant: cid(7),
4774                        },
4775                        Instruction::GetProperty {
4776                            dst: reg(2),
4777                            object: reg(0),
4778                            key: reg(1),
4779                        },
4780                        Instruction::Return { value: reg(2) },
4781                    ],
4782                ),
4783            ],
4784        );
4785
4786        assert_eq!(
4787            assert_parity(&module, || SilentHost),
4788            crate::number_value(10.0)
4789        );
4790    }
4791
4792    #[test]
4793    fn bound_construction_matches_between_engines() {
4794        let module = verified(
4795            vec![
4796                Constant::String(EcmaString::from_utf8("bind")),
4797                Constant::String(EcmaString::from_utf8("prototype")),
4798                Constant::String(EcmaString::from_utf8("sum")),
4799                Constant::String(EcmaString::from_utf8("0")),
4800                Constant::String(EcmaString::from_utf8("1")),
4801                Constant::Int32(4),
4802                Constant::Int32(5),
4803                Constant::Int32(9),
4804                Constant::Undefined,
4805            ],
4806            vec![
4807                entry_function(
4808                    10,
4809                    vec![
4810                        Instruction::CreateArray { dst: reg(0) },
4811                        Instruction::CreateClosure {
4812                            dst: reg(0),
4813                            function: FunctionId::new(1),
4814                            captures: reg(0),
4815                        },
4816                        Instruction::CreateObject { dst: reg(2) },
4817                        Instruction::LoadConst {
4818                            dst: reg(1),
4819                            constant: cid(1),
4820                        },
4821                        Instruction::SetProperty {
4822                            object: reg(0),
4823                            key: reg(1),
4824                            value: reg(2),
4825                        },
4826                        Instruction::LoadConst {
4827                            dst: reg(1),
4828                            constant: cid(0),
4829                        },
4830                        Instruction::GetProperty {
4831                            dst: reg(3),
4832                            object: reg(0),
4833                            key: reg(1),
4834                        },
4835                        Instruction::CreateObject { dst: reg(4) },
4836                        Instruction::CreateArray { dst: reg(5) },
4837                        Instruction::ArrayPush {
4838                            array: reg(5),
4839                            value: reg(4),
4840                        },
4841                        Instruction::LoadConst {
4842                            dst: reg(6),
4843                            constant: cid(5),
4844                        },
4845                        Instruction::ArrayPush {
4846                            array: reg(5),
4847                            value: reg(6),
4848                        },
4849                        Instruction::Call {
4850                            dst: reg(7),
4851                            callee: reg(3),
4852                            this_value: reg(0),
4853                            arguments: reg(5),
4854                        },
4855                        Instruction::CreateArray { dst: reg(5) },
4856                        Instruction::LoadConst {
4857                            dst: reg(6),
4858                            constant: cid(6),
4859                        },
4860                        Instruction::ArrayPush {
4861                            array: reg(5),
4862                            value: reg(6),
4863                        },
4864                        Instruction::Construct {
4865                            dst: reg(8),
4866                            callee: reg(7),
4867                            arguments: reg(5),
4868                        },
4869                        Instruction::LoadConst {
4870                            dst: reg(1),
4871                            constant: cid(2),
4872                        },
4873                        Instruction::GetProperty {
4874                            dst: reg(9),
4875                            object: reg(8),
4876                            key: reg(1),
4877                        },
4878                        Instruction::LoadConst {
4879                            dst: reg(6),
4880                            constant: cid(7),
4881                        },
4882                        Instruction::Binary {
4883                            dst: reg(9),
4884                            op: BinaryOp::StrictEqual,
4885                            left: reg(9),
4886                            right: reg(6),
4887                        },
4888                        Instruction::Binary {
4889                            dst: reg(6),
4890                            op: BinaryOp::InstanceOf,
4891                            left: reg(8),
4892                            right: reg(7),
4893                        },
4894                        Instruction::Binary {
4895                            dst: reg(9),
4896                            op: BinaryOp::BitAnd,
4897                            left: reg(9),
4898                            right: reg(6),
4899                        },
4900                        Instruction::Return { value: reg(9) },
4901                    ],
4902                ),
4903                module_function(
4904                    0,
4905                    6,
4906                    vec![
4907                        Instruction::LoadThis { dst: reg(0) },
4908                        Instruction::LoadArguments { dst: reg(1) },
4909                        Instruction::LoadConst {
4910                            dst: reg(2),
4911                            constant: cid(3),
4912                        },
4913                        Instruction::GetProperty {
4914                            dst: reg(3),
4915                            object: reg(1),
4916                            key: reg(2),
4917                        },
4918                        Instruction::LoadConst {
4919                            dst: reg(2),
4920                            constant: cid(4),
4921                        },
4922                        Instruction::GetProperty {
4923                            dst: reg(4),
4924                            object: reg(1),
4925                            key: reg(2),
4926                        },
4927                        Instruction::Binary {
4928                            dst: reg(3),
4929                            op: BinaryOp::Add,
4930                            left: reg(3),
4931                            right: reg(4),
4932                        },
4933                        Instruction::LoadConst {
4934                            dst: reg(2),
4935                            constant: cid(2),
4936                        },
4937                        Instruction::SetProperty {
4938                            object: reg(0),
4939                            key: reg(2),
4940                            value: reg(3),
4941                        },
4942                        Instruction::LoadConst {
4943                            dst: reg(5),
4944                            constant: cid(8),
4945                        },
4946                        Instruction::Return { value: reg(5) },
4947                    ],
4948                ),
4949            ],
4950        );
4951
4952        assert_eq!(assert_parity(&module, || SilentHost), Value::int32(1));
4953    }
4954
4955    #[test]
4956    fn linked_entry_preserves_pending_throw_origin() {
4957        let program = trivial_program();
4958        let mut host = SilentHost;
4959        let mut engine = NativeEngine::build(
4960            &program,
4961            &ThrowEntries,
4962            &mut host,
4963            Limits::default(),
4964            Backend::Linked,
4965        );
4966        engine.pending_throw.set(Some(PendingThrow {
4967            value: Value::UNDEFINED,
4968            origin: ThrowOrigin::ReferenceError {
4969                operation: "fixture",
4970            },
4971        }));
4972        let error = engine.run_linked().unwrap_err();
4973        assert!(matches!(
4974            error,
4975            NativeError::Runtime(RuntimeError {
4976                kind: RuntimeErrorKind::UncaughtThrow {
4977                    origin: ThrowOrigin::ReferenceError {
4978                        operation: "fixture"
4979                    },
4980                    ..
4981                },
4982                ..
4983            })
4984        ));
4985    }
4986
4987    #[test]
4988    fn stale_pending_throw_cannot_replace_a_new_bytecode_throw() {
4989        let program = trivial_program();
4990        let mut host = SilentHost;
4991        let engine = NativeEngine::build(
4992            &program,
4993            &NoEntries,
4994            &mut host,
4995            Limits::default(),
4996            Backend::Linked,
4997        );
4998        engine.pending_throw.set(Some(PendingThrow {
4999            value: Value::int32(1),
5000            origin: ThrowOrigin::ReferenceError { operation: "stale" },
5001        }));
5002
5003        assert_eq!(
5004            engine.take_matching_throw(Value::int32(2)),
5005            (Value::int32(2), ThrowOrigin::Bytecode)
5006        );
5007        assert!(engine.pending_throw.get().is_none());
5008    }
5009
5010    #[test]
5011    fn linked_entry_sources_pending_runtime_fatal_kind() {
5012        let program = trivial_program();
5013        let mut host = SilentHost;
5014        let mut engine = NativeEngine::build(
5015            &program,
5016            &FatalEntries,
5017            &mut host,
5018            Limits::default(),
5019            Backend::Linked,
5020        );
5021        engine
5022            .pending_fatal_kind
5023            .set(Some(RuntimeErrorKind::InvalidValue { value: Value::NULL }));
5024        let error = engine.run_linked().unwrap_err();
5025        assert!(matches!(
5026            error,
5027            NativeError::Runtime(RuntimeError {
5028                kind: RuntimeErrorKind::InvalidValue { value },
5029                ..
5030            }) if value == Value::NULL
5031        ));
5032    }
5033
5034    #[test]
5035    fn nested_linked_unknown_tuple_remains_abi_error() {
5036        let module = verified(
5037            Vec::new(),
5038            vec![
5039                entry_function(1, vec![Instruction::Halt]),
5040                entry_function(1, vec![Instruction::Halt]),
5041            ],
5042        );
5043        let program = one_module_program(&module);
5044        let entries = FailingEntries::default();
5045        let mut host = SilentHost;
5046        let engine = NativeEngine::build(
5047            &program,
5048            &entries,
5049            &mut host,
5050            Limits::default(),
5051            Backend::Linked,
5052        );
5053        let outcome = engine.invoke_runtime(
5054            crate::RuntimeFunction {
5055                module: ModuleId::new(0),
5056                function: FunctionId::new(1),
5057            },
5058            &[],
5059            Value::UNDEFINED,
5060            Value::UNDEFINED,
5061            &[],
5062        );
5063        assert!(matches!(outcome, InvokeOutcome::Fatal));
5064        assert!(matches!(
5065            engine.pending_abi_error.take(),
5066            Some(AbiError::UnknownFunction {
5067                module_id: 0,
5068                function_id: 1
5069            })
5070        ));
5071    }
5072
5073    #[test]
5074    fn linked_abi_failure_leaves_module_retryable() {
5075        let program = trivial_program();
5076        let entries = FailingEntries::default();
5077        let mut host = SilentHost;
5078        let mut engine = NativeEngine::build(
5079            &program,
5080            &entries,
5081            &mut host,
5082            Limits::default(),
5083            Backend::Linked,
5084        );
5085        engine.machine.borrow_mut().instantiate_modules().unwrap();
5086        for _ in 0..2 {
5087            assert!(matches!(
5088                engine.evaluate_linked_module(ModuleId::new(0)),
5089                Err(NativeError::Abi(AbiError::UnknownFunction {
5090                    module_id: 0,
5091                    function_id: 0
5092                }))
5093            ));
5094        }
5095    }
5096
5097    #[test]
5098    fn linked_backend_routes_dynamic_targets_away_from_entry_table() {
5099        let root = one_module_program(&verified(
5100            Vec::new(),
5101            vec![entry_function(1, vec![Instruction::Halt])],
5102        ));
5103        let script = Arc::new(one_module_program(&verified(
5104            vec![Constant::Int32(42)],
5105            vec![entry_function(
5106                1,
5107                vec![
5108                    Instruction::LoadConst {
5109                        dst: reg(0),
5110                        constant: cid(0),
5111                    },
5112                    Instruction::Return { value: reg(0) },
5113                ],
5114            )],
5115        )));
5116        let entries = RecordingEntries::default();
5117        let mut host = SilentHost;
5118        let engine = NativeEngine::build(
5119            &root,
5120            &entries,
5121            &mut host,
5122            Limits::default(),
5123            Backend::Linked,
5124        );
5125        engine.machine.borrow_mut().instantiate_modules().unwrap();
5126        let module = engine
5127            .machine
5128            .borrow_mut()
5129            .install_script_reserving(script, 0, 0)
5130            .unwrap();
5131
5132        let outcome = engine.invoke_runtime(
5133            crate::RuntimeFunction {
5134                module,
5135                function: FunctionId::new(0),
5136            },
5137            &[],
5138            Value::UNDEFINED,
5139            Value::UNDEFINED,
5140            &[],
5141        );
5142        assert!(matches!(outcome, InvokeOutcome::Value(value) if value == Value::int32(42)));
5143        assert!(entries.invoked.borrow().is_empty());
5144    }
5145    fn generator_program(code: Vec<Instruction>, constants: Vec<Constant>) -> Program<Verified> {
5146        let generator = Function::new(
5147            None,
5148            0,
5149            0,
5150            3,
5151            FunctionFlags {
5152                is_async: false,
5153                is_generator: true,
5154            },
5155            code,
5156            Vec::new(),
5157        );
5158        one_module_program(&verified(
5159            constants,
5160            vec![entry_function(1, vec![Instruction::Halt]), generator],
5161        ))
5162    }
5163
5164    fn yielding_generator_program() -> Program<Verified> {
5165        generator_program(
5166            vec![
5167                Instruction::LoadConst {
5168                    dst: reg(0),
5169                    constant: cid(0),
5170                },
5171                Instruction::Suspend {
5172                    dst: reg(1),
5173                    src: reg(0),
5174                    resume: pc(2),
5175                },
5176                Instruction::LoadConst {
5177                    dst: reg(0),
5178                    constant: cid(1),
5179                },
5180                Instruction::Suspend {
5181                    dst: reg(1),
5182                    src: reg(0),
5183                    resume: pc(4),
5184                },
5185                Instruction::Return { value: reg(1) },
5186            ],
5187            vec![Constant::Int32(4), Constant::Int32(5)],
5188        )
5189    }
5190
5191    fn invoke_test_generator<H: Host>(engine: &NativeEngine<'_, '_, H>) -> Value {
5192        match engine.invoke_runtime(
5193            crate::RuntimeFunction {
5194                module: ModuleId::new(0),
5195                function: FunctionId::new(1),
5196            },
5197            &[],
5198            Value::UNDEFINED,
5199            Value::UNDEFINED,
5200            &[],
5201        ) {
5202            InvokeOutcome::Value(generator) => generator,
5203            _ => panic!("generator call must return its lazy generator object"),
5204        }
5205    }
5206
5207    fn assert_iterator_result<H: Host>(
5208        engine: &NativeEngine<'_, '_, H>,
5209        outcome: InvokeOutcome,
5210        expected_value: Value,
5211        expected_done: bool,
5212    ) {
5213        let result = match outcome {
5214            InvokeOutcome::Value(result) => result,
5215            _ => panic!("generator next must return an iterator result"),
5216        };
5217        let mut machine = engine.machine.borrow_mut();
5218        let value = machine
5219            .get_named_property(result, "value")
5220            .expect("iterator result has value");
5221        let done = machine
5222            .get_named_property(result, "done")
5223            .expect("iterator result has done");
5224        assert_eq!(value, expected_value);
5225        assert_eq!(done, Value::boolean(expected_done));
5226    }
5227
5228    #[test]
5229    fn reference_generator_is_lazy_yields_resumes_and_completes_stickily() {
5230        let program = yielding_generator_program();
5231        let mut host = SilentHost;
5232        let entries = NoEntries;
5233        let engine = NativeEngine::new(&program, &entries, &mut host, Limits::default());
5234        let generator = invoke_test_generator(&engine);
5235
5236        {
5237            let machine = engine.machine.borrow();
5238            let index = machine.runtime_slot(generator).unwrap().unwrap();
5239            assert!(matches!(
5240                &machine.heap[index],
5241                HeapEntry::Generator {
5242                    state: GeneratorState::SuspendedStart(_),
5243                    ..
5244                }
5245            ));
5246        }
5247
5248        assert_iterator_result(
5249            &engine,
5250            engine.resume_generator(generator, Value::int32(999)),
5251            Value::int32(4),
5252            false,
5253        );
5254        {
5255            let machine = engine.machine.borrow();
5256            let index = machine.runtime_slot(generator).unwrap().unwrap();
5257            let HeapEntry::Generator {
5258                state: GeneratorState::Suspended(activation),
5259                ..
5260            } = &machine.heap[index]
5261            else {
5262                panic!("generator must retain its suspended activation");
5263            };
5264            assert_eq!(activation.resume_token, 2);
5265            assert_eq!(activation.registers[0], Value::int32(4));
5266        }
5267
5268        assert_iterator_result(
5269            &engine,
5270            engine.resume_generator(generator, Value::int32(99)),
5271            Value::int32(5),
5272            false,
5273        );
5274        assert_iterator_result(
5275            &engine,
5276            engine.resume_generator(generator, Value::int32(7)),
5277            Value::int32(7),
5278            true,
5279        );
5280        assert_iterator_result(
5281            &engine,
5282            engine.resume_generator(generator, Value::int32(8)),
5283            Value::UNDEFINED,
5284            true,
5285        );
5286        let machine = engine.machine.borrow();
5287        assert_eq!(machine.live_registers, 0);
5288        assert_eq!(machine.native_depth, 0);
5289    }
5290
5291    #[test]
5292    fn reference_generator_throw_completes_and_preserves_value_and_origin() {
5293        let program = generator_program(
5294            vec![
5295                Instruction::LoadConst {
5296                    dst: reg(0),
5297                    constant: cid(0),
5298                },
5299                Instruction::Throw { value: reg(0) },
5300            ],
5301            vec![Constant::Int32(42)],
5302        );
5303        let mut host = SilentHost;
5304        let entries = NoEntries;
5305        let engine = NativeEngine::new(&program, &entries, &mut host, Limits::default());
5306        let generator = invoke_test_generator(&engine);
5307        assert!(matches!(
5308            engine.resume_generator(generator, Value::UNDEFINED),
5309            InvokeOutcome::Threw(value, ThrowOrigin::Bytecode) if value == Value::int32(42)
5310        ));
5311        assert_iterator_result(
5312            &engine,
5313            engine.resume_generator(generator, Value::UNDEFINED),
5314            Value::UNDEFINED,
5315            true,
5316        );
5317        let machine = engine.machine.borrow();
5318        assert_eq!(machine.live_registers, 0);
5319        assert_eq!(machine.native_depth, 0);
5320    }
5321
5322    #[test]
5323    fn suspended_reference_generator_registers_enforce_the_global_ceiling() {
5324        let program = yielding_generator_program();
5325        let mut host = SilentHost;
5326        let entries = NoEntries;
5327        let mut limits = Limits::default();
5328        limits.max_total_registers = 3;
5329        let engine = NativeEngine::new(&program, &entries, &mut host, limits);
5330        let first = invoke_test_generator(&engine);
5331        let second = invoke_test_generator(&engine);
5332        assert_iterator_result(
5333            &engine,
5334            engine.resume_generator(first, Value::UNDEFINED),
5335            Value::int32(4),
5336            false,
5337        );
5338        assert!(matches!(
5339            engine.resume_generator(second, Value::UNDEFINED),
5340            InvokeOutcome::Fatal
5341        ));
5342        assert!(matches!(
5343            engine.pending_fatal_kind.take(),
5344            Some(RuntimeErrorKind::RegisterLimitExceeded { limit: 3 })
5345        ));
5346    }
5347
5348    #[derive(Clone, Copy)]
5349    struct GeneratorEntryStep {
5350        token: u32,
5351        next_token: u32,
5352        tag: CompletionTag,
5353        value: Value,
5354    }
5355
5356    struct GeneratorEntries {
5357        steps: Vec<GeneratorEntryStep>,
5358        call: Cell<usize>,
5359        handles: Cell<Option<*mut Value>>,
5360    }
5361
5362    impl GeneratorEntries {
5363        fn yielding() -> Self {
5364            Self {
5365                steps: vec![
5366                    GeneratorEntryStep {
5367                        token: 0,
5368                        next_token: 2,
5369                        tag: CompletionTag::Suspend,
5370                        value: Value::int32(4),
5371                    },
5372                    GeneratorEntryStep {
5373                        token: 2,
5374                        next_token: 4,
5375                        tag: CompletionTag::Suspend,
5376                        value: Value::int32(5),
5377                    },
5378                    GeneratorEntryStep {
5379                        token: 4,
5380                        next_token: 4,
5381                        tag: CompletionTag::Normal,
5382                        value: Value::int32(7),
5383                    },
5384                ],
5385                call: Cell::new(0),
5386                handles: Cell::new(None),
5387            }
5388        }
5389
5390        fn terminal(tag: CompletionTag, value: Value) -> Self {
5391            Self {
5392                steps: vec![GeneratorEntryStep {
5393                    token: 0,
5394                    next_token: 1,
5395                    tag,
5396                    value,
5397                }],
5398                call: Cell::new(0),
5399                handles: Cell::new(None),
5400            }
5401        }
5402    }
5403
5404    impl NativeEntryTable for GeneratorEntries {
5405        fn program_bytes(&self) -> &[u8] {
5406            &[]
5407        }
5408
5409        fn invoke(
5410            &self,
5411            module_id: u32,
5412            function_id: u32,
5413            frame: &mut ShadowFrame,
5414            out: &mut Completion,
5415        ) -> Result<CompletionTag, AbiError> {
5416            assert_eq!(module_id, 0);
5417            assert_eq!(function_id, 1);
5418            let call = self.call.get();
5419            let step = self.steps[call];
5420            self.call.set(call + 1);
5421            assert_eq!(frame.bytecode_pc, step.token);
5422            match self.handles.get() {
5423                Some(handles) => assert_eq!(
5424                    handles, frame.handles,
5425                    "linked resumes must reuse the saved register allocation"
5426                ),
5427                None => self.handles.set(Some(frame.handles)),
5428            }
5429            frame.bytecode_pc = step.next_token;
5430            *out = Completion::new(step.value);
5431            Ok(step.tag)
5432        }
5433    }
5434
5435    #[test]
5436    fn linked_generator_reuses_saved_registers_and_dispatches_resume_tokens() {
5437        let program = yielding_generator_program();
5438        let entries = GeneratorEntries::yielding();
5439        let mut host = SilentHost;
5440        let engine = NativeEngine::build(
5441            &program,
5442            &entries,
5443            &mut host,
5444            Limits::default(),
5445            Backend::Linked,
5446        );
5447        let generator = invoke_test_generator(&engine);
5448        assert_eq!(entries.call.get(), 0, "generator call is lazy");
5449        assert_iterator_result(
5450            &engine,
5451            engine.resume_generator(generator, Value::int32(999)),
5452            Value::int32(4),
5453            false,
5454        );
5455        assert_iterator_result(
5456            &engine,
5457            engine.resume_generator(generator, Value::int32(99)),
5458            Value::int32(5),
5459            false,
5460        );
5461        assert_iterator_result(
5462            &engine,
5463            engine.resume_generator(generator, Value::int32(7)),
5464            Value::int32(7),
5465            true,
5466        );
5467        assert_iterator_result(
5468            &engine,
5469            engine.resume_generator(generator, Value::UNDEFINED),
5470            Value::UNDEFINED,
5471            true,
5472        );
5473        assert_eq!(entries.call.get(), 3);
5474        let machine = engine.machine.borrow();
5475        assert_eq!(machine.live_registers, 0);
5476        assert_eq!(machine.native_depth, 0);
5477    }
5478
5479    #[test]
5480    fn linked_resume_value_dispatch_consumes_the_sent_value_once() {
5481        let program = yielding_generator_program();
5482        let entries = NoEntries;
5483        let mut host = SilentHost;
5484        let engine = NativeEngine::build(
5485            &program,
5486            &entries,
5487            &mut host,
5488            Limits::default(),
5489            Backend::Linked,
5490        );
5491        engine.activations.borrow_mut().push(Activation {
5492            this_value: Value::UNDEFINED,
5493            new_target: Value::UNDEFINED,
5494            args: Vec::new(),
5495            arguments_object: None,
5496            pending_resume: Some(Value::int32(77)),
5497        });
5498        let mut registers = vec![Value::UNINITIALIZED];
5499        let mut shadow = ShadowFrame::new(std::ptr::null_mut(), 2, 0, registers.as_mut_ptr(), 1);
5500        let mut frame = NativeFrame::new(&mut shadow, &mut registers).unwrap();
5501        let resumed = engine.dispatch(&mut frame, HelperCall::ResumeValue);
5502        assert_eq!(resumed, HelperResult::normal(Value::int32(77)));
5503        assert_eq!(
5504            engine.dispatch(&mut frame, HelperCall::ResumeValue).tag,
5505            CompletionTag::FatalTrap
5506        );
5507        engine.activations.borrow_mut().pop();
5508    }
5509
5510    #[test]
5511    fn linked_generator_throw_and_fatal_release_once_and_complete_stickily() {
5512        for (tag, expected_throw) in [
5513            (CompletionTag::Throw, true),
5514            (CompletionTag::FatalTrap, false),
5515        ] {
5516            let program = generator_program(vec![Instruction::Halt], Vec::new());
5517            let entries = GeneratorEntries::terminal(tag, Value::int32(42));
5518            let mut host = SilentHost;
5519            let engine = NativeEngine::build(
5520                &program,
5521                &entries,
5522                &mut host,
5523                Limits::default(),
5524                Backend::Linked,
5525            );
5526            let generator = invoke_test_generator(&engine);
5527            let outcome = engine.resume_generator(generator, Value::UNDEFINED);
5528            if expected_throw {
5529                assert!(matches!(
5530                    outcome,
5531                    InvokeOutcome::Threw(value, ThrowOrigin::Bytecode)
5532                        if value == Value::int32(42)
5533                ));
5534            } else {
5535                assert!(matches!(outcome, InvokeOutcome::Fatal));
5536            }
5537            assert_iterator_result(
5538                &engine,
5539                engine.resume_generator(generator, Value::UNDEFINED),
5540                Value::UNDEFINED,
5541                true,
5542            );
5543            let machine = engine.machine.borrow();
5544            assert_eq!(machine.live_registers, 0);
5545            assert_eq!(machine.native_depth, 0);
5546        }
5547    }
5548
5549    #[test]
5550    fn linked_array_extend_drives_generator_through_linked_entries() {
5551        let program = yielding_generator_program();
5552        let entries = GeneratorEntries::yielding();
5553        let mut host = SilentHost;
5554        let engine = NativeEngine::build(
5555            &program,
5556            &entries,
5557            &mut host,
5558            Limits::default(),
5559            Backend::Linked,
5560        );
5561        let generator = invoke_test_generator(&engine);
5562        let array = {
5563            let prototype = engine.machine.borrow().intrinsics.array_prototype;
5564            engine
5565                .machine
5566                .borrow_mut()
5567                .allocate(HeapEntry::Array {
5568                    elements: Vec::new(),
5569                    properties: PropertyMap::default(),
5570                    prototype: Some(prototype),
5571                    extensible: true,
5572                    length_writable: true,
5573                })
5574                .unwrap()
5575        };
5576        assert!(matches!(
5577            engine.array_extend_active(array, generator),
5578            InvokeOutcome::Value(value) if value == Value::UNDEFINED
5579        ));
5580        assert_eq!(
5581            engine.machine.borrow().arguments_from_array(array).unwrap(),
5582            vec![Value::int32(4), Value::int32(5)]
5583        );
5584        assert_eq!(entries.call.get(), 3);
5585    }
5586    #[test]
5587    fn create_cell_helper_seeds_tdz_and_preserves_reference_error_origin() {
5588        let program = trivial_program();
5589        let entries = NoEntries;
5590        let mut host = SilentHost;
5591        let engine = NativeEngine::build(
5592            &program,
5593            &entries,
5594            &mut host,
5595            Limits::default(),
5596            Backend::Reference,
5597        );
5598        let mut registers = vec![Value::UNINITIALIZED];
5599        let mut shadow = ShadowFrame::new(std::ptr::null_mut(), 0, 0, registers.as_mut_ptr(), 1);
5600        let mut frame = NativeFrame::new(&mut shadow, &mut registers).unwrap();
5601        let created = engine.dispatch(&mut frame, HelperCall::CreateCell);
5602        assert_eq!(created.tag, CompletionTag::Normal);
5603        let read = engine.dispatch(
5604            &mut frame,
5605            HelperCall::GetProperty {
5606                object: created.value,
5607                key: Value::int32(0),
5608            },
5609        );
5610        assert_eq!(read.tag, CompletionTag::Throw);
5611        assert!(matches!(
5612            engine.pending_throw.get(),
5613            Some(PendingThrow {
5614                origin: ThrowOrigin::ReferenceError { .. },
5615                ..
5616            })
5617        ));
5618    }
5619}