Skip to main content

bamts_runtime/
lib.rs

1//! Deterministic register interpreter for verified production BamTS bytecode.
2//!
3//! Persisted constants never carry runtime identity. This interpreter therefore
4//! owns a slot heap for strings, bigints, objects, arrays, closures, private
5//! names, regular expressions, and iterators, and exposes only
6//! `bamts_native::Value` words at host boundaries. Bytecode heap slots use
7//! segment 1; other segments remain host-owned.
8//!
9//! The 36-op dynamic-computation ISA is executed locally: register-keyed
10//! property access (with data properties, accessor descriptors, and private-name
11//! identity), prototype chains, closures with explicit capture environments,
12//! arguments arrays, `this`/`arguments`/`new.target`, globals, arrays with
13//! spread, object spread, iterators (sync/async/keys) with the two-write
14//! `IteratorNext`, and generator/async `Suspend`-resume. The [`Host`] trait owns
15//! only *external* module and builtin operations (foreign calls/constructs,
16//! foreign property access, `import`, `export`, and the suspension driver);
17//! internal objects, arrays, functions, closures, prototypes, and private names
18//! never leave the interpreter.
19
20#![forbid(unsafe_code)]
21
22use std::borrow::Cow;
23use std::cmp::Ordering;
24use std::collections::BTreeMap;
25use std::collections::BTreeSet;
26use std::collections::VecDeque;
27use std::error::Error;
28use std::fmt;
29use std::sync::Arc;
30
31use bamts_bytecode::{
32    AccessorKind, BinaryOp, BindingId, BindingKind, Constant, ConstantId, EcmaString,
33    EcmaStringBuilder, EdgeId, EdgeTarget, Function, FunctionId, Instruction, IteratorKind, Module,
34    ModuleId, Pc, Program, ProgramModule, ResolvedExport, UnaryOp, Verified,
35};
36use bamts_native::{Decoded, SlotId, Value};
37
38mod external_modules;
39mod host_objects;
40mod intrinsics;
41mod native;
42mod vm;
43
44pub use native::{NativeEngine, NativeError, run_linked_program};
45
46const RUNTIME_HEAP_SEGMENT: u16 = 1;
47
48/// The observable result of a terminated execution.
49#[derive(Clone, Debug, Eq, PartialEq)]
50pub struct ExecutionOutcome {
51    /// Reserved for output produced by host builtins.
52    pub stdout: Vec<u8>,
53    /// `0` for normal bytecode termination.
54    pub exit_code: i32,
55}
56
57/// A terminated execution and the entry activation's observable state.
58#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct Execution {
60    pub outcome: ExecutionOutcome,
61    /// The explicit entry return value, or `undefined` for `Halt`.
62    pub value: Value,
63    /// Backward-compatible name for the final returned value.
64    pub link: Value,
65    pub entry_registers: Vec<Value>,
66}
67
68/// Deterministic execution and allocation ceilings.
69#[derive(Clone, Debug, Eq, PartialEq)]
70pub struct Limits {
71    pub fuel: u64,
72    pub max_call_depth: usize,
73    pub max_total_registers: usize,
74    /// Ceiling on the length of a single call's arguments array.
75    pub max_argument_count: u32,
76    pub max_heap_slots: usize,
77    pub max_heap_bytes: usize,
78    /// Ceiling on engine-owned module binding cells.
79    pub max_module_cells: usize,
80    /// Ceiling on host-compiled scripts retained by one machine.
81    pub max_dynamic_modules: usize,
82    /// Ceiling on queued microtasks.
83    pub max_microtasks: usize,
84    /// Ceiling on live timers armed at once.
85    pub max_timers: usize,
86}
87
88impl Default for Limits {
89    fn default() -> Self {
90        Self {
91            fuel: 1_000_000,
92            max_call_depth: 64,
93            max_total_registers: 1 << 20,
94            max_argument_count: 1 << 16,
95            max_heap_slots: 1 << 20,
96            max_heap_bytes: 64 << 20,
97            max_module_cells: 1 << 20,
98            max_dynamic_modules: 1 << 10,
99            max_microtasks: 1 << 20,
100            max_timers: 1 << 20,
101        }
102    }
103}
104
105/// The exact source of one classic script. Source and resource name preserve
106/// UTF-16 code units verbatim, including unpaired surrogates.
107pub struct ScriptSource<'a> {
108    pub source: &'a [u16],
109    pub name: &'a [u16],
110}
111
112/// Why a host compiler refused a classic script.
113#[derive(Clone, Debug, Eq, PartialEq)]
114pub enum ScriptCompileError {
115    IllFormedSource {
116        unit_offset: usize,
117    },
118    Syntax {
119        message: String,
120        line: u32,
121        column: u32,
122    },
123    Unsupported {
124        message: String,
125        line: u32,
126        column: u32,
127    },
128    Capacity {
129        message: String,
130    },
131}
132
133/// A host capability for compiling a classic script without observing machine state.
134pub trait CompileProvider {
135    fn compile_script(
136        &mut self,
137        source: ScriptSource<'_>,
138    ) -> Result<Arc<Program<Verified>>, ScriptCompileError>;
139}
140
141/// External capabilities available to the JavaScript runtime.
142///
143/// Runtime values never cross this boundary. The engine owns all JavaScript
144/// objects and value semantics; hosts provide byte sinks and process services.
145pub trait Host {
146    fn write_stdout(&mut self, _bytes: &[u8]) {}
147
148    fn write_stderr(&mut self, _bytes: &[u8]) {}
149
150    fn exit_code(&self) -> i32 {
151        0
152    }
153
154    fn set_exit_code(&mut self, _exit_code: i32) {}
155
156    fn argv(&self) -> &[String] {
157        &[]
158    }
159
160    fn env(&self, _name: &str) -> Option<&str> {
161        None
162    }
163
164    fn set_env(&mut self, _name: &str, _value: &str) {}
165
166    fn delete_env(&mut self, _name: &str) -> bool {
167        false
168    }
169
170    fn now_ms(&mut self) -> u64 {
171        0
172    }
173
174    fn monotonic_ns(&mut self) -> u64 {
175        0
176    }
177
178    fn random(&mut self) -> f64 {
179        0.0
180    }
181
182    fn hash(&mut self, _algorithm: &str, _data: &[u8]) -> Option<Vec<u8>> {
183        None
184    }
185
186    /// This host's script compiler, or `None` when it provides none.
187    ///
188    /// Presence MUST remain stable for the lifetime of one machine because it
189    /// determines whether `node:vm` is installed during construction.
190    fn script_compiler(&mut self) -> Option<&mut (dyn CompileProvider + 'static)> {
191        None
192    }
193
194    /// This host's timer scheduler, or `None` when it provides none.
195    ///
196    /// Presence MUST remain stable for the lifetime of one machine because it
197    /// determines whether `setTimeout`/`clearTimeout` are installed during
198    /// construction.
199    fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
200        None
201    }
202}
203
204/// A single expired timer reported by a [`TimerProvider`].
205///
206/// `id` echoes the machine-owned JavaScript identifier passed to
207/// [`TimerProvider::schedule`]; `deadline_ms` is the provider's monotonic
208/// absolute-millisecond deadline used as the promotion watermark.
209#[derive(Clone, Copy, Debug, Eq, PartialEq)]
210pub struct TimerWakeup {
211    pub id: u64,
212    pub deadline_ms: u64,
213}
214
215/// An owned failure message produced by a [`TimerProvider`].
216#[derive(Clone, Debug, Eq, PartialEq)]
217pub struct TimerError {
218    message: String,
219}
220
221impl TimerError {
222    #[must_use]
223    pub fn new(message: impl Into<String>) -> Self {
224        Self {
225            message: message.into(),
226        }
227    }
228
229    #[must_use]
230    pub fn message(&self) -> &str {
231        &self.message
232    }
233}
234
235impl fmt::Display for TimerError {
236    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
237        formatter.write_str(&self.message)
238    }
239}
240
241impl Error for TimerError {}
242
243/// A host capability that schedules real time without observing machine state.
244///
245/// The provider owns only opaque `u64` identifiers and monotonic millisecond
246/// deadlines; it never stores a JavaScript `Value`. The machine owns visible
247/// ordering and every callback identity.
248pub trait TimerProvider {
249    /// Arms a timer for `id` after at least `delay_ms` milliseconds, returning
250    /// the provider-monotonic absolute-millisecond deadline.
251    fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError>;
252
253    /// Cancels the armed timer for `id`, returning whether one was removed.
254    fn cancel(&mut self, id: u64) -> Result<bool, TimerError>;
255
256    /// Drains every currently expired timer into `output` without blocking.
257    fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError>;
258
259    /// Blocks until the next timer expires, or returns `None` when none pend.
260    fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError>;
261
262    /// Reports whether the provider has any armed timer.
263    fn has_pending(&self) -> bool;
264}
265
266#[derive(Clone, Copy, Debug, Eq, PartialEq)]
267pub enum ThrowOrigin {
268    Bytecode,
269    TypeError { operation: &'static str },
270    RangeError { operation: &'static str },
271    ReferenceError { operation: &'static str },
272    UriError { operation: &'static str },
273}
274
275/// Source metadata attached to every machine failure.
276#[derive(Clone, Debug, Eq, PartialEq)]
277pub struct RuntimeSource {
278    pub function_name: Option<EcmaString>,
279    pub instruction: Instruction,
280}
281
282#[derive(Clone, Debug, Eq, PartialEq)]
283pub struct RuntimeError {
284    pub kind: RuntimeErrorKind,
285    pub function: FunctionId,
286    pub pc: Pc,
287    pub source: RuntimeSource,
288}
289
290#[derive(Clone, Debug, Eq, PartialEq)]
291pub enum RuntimeErrorKind {
292    UncaughtThrow {
293        value: Value,
294        origin: ThrowOrigin,
295    },
296    FuelExhausted {
297        limit: u64,
298    },
299    CallDepthExceeded {
300        limit: usize,
301    },
302    RegisterLimitExceeded {
303        limit: usize,
304    },
305    ArgumentLimitExceeded {
306        limit: u32,
307        requested: u32,
308    },
309    HeapSlotLimitExceeded {
310        limit: usize,
311    },
312    HeapByteLimitExceeded {
313        limit: usize,
314    },
315    ModuleCellLimitExceeded {
316        limit: usize,
317    },
318    DynamicModuleLimitExceeded {
319        limit: usize,
320    },
321    MicrotaskQueueLimitExceeded {
322        limit: usize,
323    },
324    MicrotaskDrainReentry,
325    TimerProviderFailure {
326        message: String,
327    },
328    TimerCapacityExceeded {
329        limit: usize,
330    },
331    TimerCheckpointReentry,
332    InvalidDynamicScript {
333        reason: &'static str,
334    },
335    TemporalDeadZone {
336        module: ModuleId,
337        binding: BindingId,
338    },
339    ExternalModuleUnavailable {
340        module: ModuleId,
341        edge: EdgeId,
342    },
343    DynamicImportEdgeMissing {
344        module: ModuleId,
345        specifier: ConstantId,
346    },
347    InvalidVerifiedProgram {
348        module: ModuleId,
349        instruction: Instruction,
350    },
351    InvalidValue {
352        value: Value,
353    },
354    InvalidRuntimeHeapReference {
355        slot: u32,
356    },
357}
358
359impl fmt::Display for RuntimeError {
360    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
361        write!(
362            formatter,
363            "runtime error in function {} at pc {}",
364            self.function.get(),
365            self.pc.get()
366        )?;
367        if let Some(name) = &self.source.function_name {
368            write!(formatter, " ({})", name.to_utf8_lossy())?;
369        }
370        write!(formatter, ": ")?;
371        match &self.kind {
372            RuntimeErrorKind::UncaughtThrow { value, origin } => write!(
373                formatter,
374                "uncaught {origin:?} throw (value {:#018x})",
375                value.to_bits()
376            ),
377            RuntimeErrorKind::FuelExhausted { limit } => {
378                write!(formatter, "fuel exhausted after {limit} instructions")
379            }
380            RuntimeErrorKind::CallDepthExceeded { limit } => {
381                write!(formatter, "call depth limit {limit} exceeded")
382            }
383            RuntimeErrorKind::RegisterLimitExceeded { limit } => {
384                write!(formatter, "live register limit {limit} exceeded")
385            }
386            RuntimeErrorKind::ArgumentLimitExceeded { limit, requested } => write!(
387                formatter,
388                "argument count {requested} exceeds runtime limit {limit}"
389            ),
390            RuntimeErrorKind::HeapSlotLimitExceeded { limit } => {
391                write!(formatter, "heap slot limit {limit} exceeded")
392            }
393            RuntimeErrorKind::HeapByteLimitExceeded { limit } => {
394                write!(formatter, "heap byte limit {limit} exceeded")
395            }
396            RuntimeErrorKind::ModuleCellLimitExceeded { limit } => {
397                write!(formatter, "module cell limit {limit} exceeded")
398            }
399            RuntimeErrorKind::DynamicModuleLimitExceeded { limit } => {
400                write!(formatter, "dynamic module limit {limit} exceeded")
401            }
402            RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit } => {
403                write!(formatter, "microtask queue limit {limit} exceeded")
404            }
405            RuntimeErrorKind::MicrotaskDrainReentry => {
406                write!(formatter, "microtask drain is already active")
407            }
408            RuntimeErrorKind::TimerProviderFailure { message } => {
409                write!(formatter, "timer provider failure: {message}")
410            }
411            RuntimeErrorKind::TimerCapacityExceeded { limit } => {
412                write!(formatter, "timer capacity {limit} exceeded")
413            }
414            RuntimeErrorKind::TimerCheckpointReentry => {
415                write!(formatter, "timer checkpoint is already active")
416            }
417            RuntimeErrorKind::InvalidDynamicScript { reason } => {
418                write!(formatter, "invalid dynamic script: {reason}")
419            }
420            RuntimeErrorKind::TemporalDeadZone { module, binding } => write!(
421                formatter,
422                "module {} binding {} read before initialization",
423                module.get(),
424                binding.get()
425            ),
426            RuntimeErrorKind::ExternalModuleUnavailable { module, edge } => write!(
427                formatter,
428                "external module at module {} edge {} is unavailable",
429                module.get(),
430                edge.get()
431            ),
432            RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier } => write!(
433                formatter,
434                "verified module {} has no dynamic edge for constant {}",
435                module.get(),
436                specifier.get()
437            ),
438            RuntimeErrorKind::InvalidVerifiedProgram {
439                module,
440                instruction,
441            } => write!(
442                formatter,
443                "verified module {} contains impossible instruction {instruction:?}",
444                module.get()
445            ),
446            RuntimeErrorKind::InvalidValue { value } => {
447                write!(
448                    formatter,
449                    "malformed or foreign value {:#018x}",
450                    value.to_bits()
451                )
452            }
453            RuntimeErrorKind::InvalidRuntimeHeapReference { slot } => {
454                write!(formatter, "runtime heap slot {slot} does not exist")
455            }
456        }
457    }
458}
459
460impl Error for RuntimeError {}
461
462/// Converts constants that need no runtime identity to ABI values. Strings and
463/// bigints are materialized by `LoadConst` in the machine's slot heap.
464#[must_use]
465pub fn constant_value(constant: &Constant) -> Option<Value> {
466    match constant {
467        Constant::Number(bits) => Some(Value::number(bits.to_f64())),
468        Constant::Int32(value) => Some(Value::int32(*value as u32)),
469        Constant::String(_) | Constant::BigInt(_) => None,
470        Constant::Boolean(value) => Some(Value::boolean(*value)),
471        Constant::Null => Some(Value::NULL),
472        Constant::Undefined => Some(Value::UNDEFINED),
473    }
474}
475
476/// A normalized property key: a string name (which may parse as an array index),
477/// a public symbol, or a language private name, each identified by its heap slot.
478/// Two identity-bearing allocations with the same description remain distinct keys.
479#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
480enum PropertyKey {
481    Named(EcmaString),
482    Symbol(u32),
483    Private(u32),
484}
485
486impl PropertyKey {
487    fn as_string(&self) -> Option<&EcmaString> {
488        match self {
489            PropertyKey::Named(text) => Some(text),
490            PropertyKey::Symbol(_) | PropertyKey::Private(_) => None,
491        }
492    }
493
494    fn eq_ascii(&self, ascii: &str) -> bool {
495        matches!(self, PropertyKey::Named(text) if text.eq_ascii(ascii))
496    }
497
498    fn charge_bytes(&self) -> usize {
499        match self {
500            PropertyKey::Named(text) => text.len_units().saturating_mul(2).saturating_add(8),
501            PropertyKey::Symbol(_) | PropertyKey::Private(_) => 16,
502        }
503    }
504}
505
506/// A stored property: a data value or an accessor descriptor.
507#[derive(Clone, Debug)]
508enum Property {
509    Data {
510        value: Value,
511        writable: bool,
512        enumerable: bool,
513        configurable: bool,
514    },
515    Accessor {
516        getter: Option<Value>,
517        setter: Option<Value>,
518        enumerable: bool,
519        configurable: bool,
520    },
521}
522impl Property {
523    fn enumerable(&self) -> bool {
524        match self {
525            Self::Data { enumerable, .. } | Self::Accessor { enumerable, .. } => *enumerable,
526        }
527    }
528
529    fn configurable(&self) -> bool {
530        match self {
531            Self::Data { configurable, .. } | Self::Accessor { configurable, .. } => *configurable,
532        }
533    }
534}
535
536/// Own properties in creation order. ECMAScript enumerates array-index keys
537/// separately, but preserves this order for all other string and symbol keys.
538#[derive(Clone, Debug, Default)]
539struct PropertyMap(Vec<(PropertyKey, Property)>);
540
541impl PropertyMap {
542    fn get(&self, key: &PropertyKey) -> Option<&Property> {
543        self.0
544            .iter()
545            .find_map(|(candidate, property)| (candidate == key).then_some(property))
546    }
547
548    fn get_mut(&mut self, key: &PropertyKey) -> Option<&mut Property> {
549        self.0
550            .iter_mut()
551            .find_map(|(candidate, property)| (candidate == key).then_some(property))
552    }
553
554    fn contains_key(&self, key: &PropertyKey) -> bool {
555        self.get(key).is_some()
556    }
557
558    fn get_ascii(&self, ascii: &str) -> Option<&Property> {
559        debug_assert!(ascii.is_ascii());
560        self.0
561            .iter()
562            .find_map(|(key, property)| key.eq_ascii(ascii).then_some(property))
563    }
564
565    fn insert(&mut self, key: PropertyKey, property: Property) -> Option<Property> {
566        if let Some(existing) = self.get_mut(&key) {
567            return Some(std::mem::replace(existing, property));
568        }
569        self.0.push((key, property));
570        None
571    }
572
573    fn remove(&mut self, key: &PropertyKey) -> Option<Property> {
574        let index = self.0.iter().position(|(candidate, _)| candidate == key)?;
575        Some(self.0.remove(index).1)
576    }
577
578    fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Property)> {
579        self.0.iter().map(|(key, property)| (key, property))
580    }
581    fn charge_bytes(&self) -> usize {
582        self.0.iter().fold(0, |bytes, (key, _)| {
583            bytes.saturating_add(key.charge_bytes())
584        })
585    }
586}
587
588impl<'a> IntoIterator for &'a PropertyMap {
589    type Item = (&'a PropertyKey, &'a Property);
590    type IntoIter = std::iter::Map<
591        std::slice::Iter<'a, (PropertyKey, Property)>,
592        fn(&(PropertyKey, Property)) -> (&PropertyKey, &Property),
593    >;
594
595    fn into_iter(self) -> Self::IntoIter {
596        fn pair_refs(pair: &(PropertyKey, Property)) -> (&PropertyKey, &Property) {
597            (&pair.0, &pair.1)
598        }
599        self.0.iter().map(pair_refs)
600    }
601}
602
603#[derive(Clone, Copy, Debug, Eq, PartialEq)]
604enum IterationKind {
605    Key,
606    Value,
607    Entry,
608}
609
610#[derive(Clone, Copy, Debug)]
611struct CollectionEntry {
612    order: u64,
613    key: Value,
614    value: Value,
615}
616
617impl CollectionEntry {
618    const BYTES: usize = std::mem::size_of::<Self>();
619}
620
621#[derive(Clone, Copy, Debug)]
622pub(crate) enum IteratorNextPrepared {
623    Ready { done: bool, value: Value },
624    Call { callee: Value, this_value: Value },
625}
626
627#[derive(Clone, Debug)]
628enum IteratorState {
629    Keys { index: usize, keys: Vec<EcmaString> },
630    Protocol { iterator: Value, next: Value },
631}
632
633#[derive(Clone, Debug)]
634pub(crate) struct GeneratorStart {
635    pub(crate) target: RuntimeFunction,
636    pub(crate) captures: Vec<Value>,
637    pub(crate) this_value: Value,
638    pub(crate) new_target: Value,
639    pub(crate) args: Vec<Value>,
640}
641
642#[derive(Clone, Debug)]
643pub(crate) struct SuspendedActivation {
644    pub(crate) target: RuntimeFunction,
645    pub(crate) registers: Vec<Value>,
646    pub(crate) this_value: Value,
647    pub(crate) new_target: Value,
648    pub(crate) args: Vec<Value>,
649    pub(crate) arguments_object: Option<Value>,
650    pub(crate) resume_token: u32,
651}
652
653#[derive(Clone, Debug)]
654pub(crate) enum GeneratorState {
655    SuspendedStart(GeneratorStart),
656    Executing,
657    Suspended(SuspendedActivation),
658    Completed,
659}
660
661#[derive(Clone, Debug)]
662pub(crate) enum GeneratorResume {
663    Yield {
664        value: Value,
665        activation: SuspendedActivation,
666    },
667    Return(Value),
668    Throw {
669        value: Value,
670        origin: ThrowOrigin,
671    },
672}
673
674/// One step of driving a detached async-function activation: it awaited a
675/// value (and must suspend), it returned, or it threw an uncaught value.
676#[derive(Clone, Debug)]
677enum AsyncStep {
678    Suspend {
679        awaited: Value,
680        activation: SuspendedActivation,
681    },
682    Return(Value),
683    Throw {
684        value: Value,
685        origin: ThrowOrigin,
686    },
687}
688
689#[derive(Clone, Debug)]
690pub(crate) enum PromiseState {
691    Pending {
692        fulfill_reactions: Vec<PromiseReaction>,
693        reject_reactions: Vec<PromiseReaction>,
694    },
695    Fulfilled {
696        value: Value,
697    },
698    Rejected {
699        reason: Value,
700        origin: ThrowOrigin,
701    },
702}
703
704#[derive(Clone, Copy, Debug)]
705pub(crate) enum PromiseCompletion {
706    Fulfilled,
707    Rejected,
708}
709
710#[derive(Clone, Debug)]
711pub(crate) enum PromiseReaction {
712    Fulfilled {
713        handler: Value,
714        derived: Value,
715    },
716    Rejected {
717        handler: Value,
718        derived: Value,
719    },
720    Finally {
721        handler: Value,
722        derived: Value,
723        completion: PromiseCompletion,
724    },
725    /// Resumes a suspended async activation on fulfillment. Carries only the
726    /// heap `AsyncActivation` record handle; the awaited value arrives as the
727    /// reaction value.
728    AsyncFulfill {
729        activation: Value,
730    },
731    /// Resumes a suspended async activation on rejection. Carries only the
732    /// heap `AsyncActivation` record handle; the rejection reason arrives as
733    /// the reaction value.
734    AsyncReject {
735        activation: Value,
736    },
737}
738
739#[derive(Clone, Debug)]
740pub(crate) enum MicrotaskJob {
741    Reaction {
742        reaction: PromiseReaction,
743        value: Value,
744        origin: ThrowOrigin,
745    },
746    Thenable {
747        promise: Value,
748        thenable: Value,
749        then: Value,
750    },
751    Callback {
752        callback: Value,
753    },
754}
755
756/// An exception reported by a microtask or timer callback.
757#[derive(Clone, Debug, Eq, PartialEq)]
758pub struct CallbackException {
759    /// The value thrown by the callback.
760    pub value: Value,
761    /// The operation that produced the throw.
762    pub origin: ThrowOrigin,
763}
764
765/// The result of one explicit microtask checkpoint.
766#[derive(Clone, Debug, Default, Eq, PartialEq)]
767pub struct MicrotaskDrain {
768    /// The number of jobs removed from the queue and run.
769    pub executed: usize,
770    /// Callback exceptions, in FIFO execution order.
771    pub uncaught: Vec<CallbackException>,
772}
773
774/// The result of one explicit timer checkpoint. At most one callback runs.
775#[derive(Clone, Debug, Default, Eq, PartialEq)]
776pub struct TimerRun {
777    /// The number of timer callbacks run: `0` or `1`.
778    pub executed: usize,
779    /// Callback exceptions from the run callback, if it threw.
780    pub uncaught: Vec<CallbackException>,
781}
782
783#[derive(Clone, Debug)]
784enum HeapEntry {
785    String(EcmaString),
786    BigInt(String),
787    Object {
788        properties: PropertyMap,
789        prototype: Option<Value>,
790        boxed_primitive: Option<Value>,
791        extensible: bool,
792    },
793    Array {
794        elements: Vec<Value>,
795        properties: PropertyMap,
796        prototype: Option<Value>,
797        extensible: bool,
798        length_writable: bool,
799    },
800    Function {
801        module: ModuleId,
802        function: FunctionId,
803        captures: Vec<Value>,
804        properties: PropertyMap,
805        prototype: Option<Value>,
806        extensible: bool,
807    },
808    /// A `vm.Script` whose entry function retains its machine-owned dynamic module.
809    Script {
810        entry: Value,
811        properties: PropertyMap,
812        prototype: Option<Value>,
813        extensible: bool,
814    },
815    ModuleNamespace {
816        module: ModuleId,
817    },
818    ExternalModuleNamespace {
819        specifier: EcmaString,
820    },
821    HashState {
822        algorithm: String,
823        data: Vec<u8>,
824        digested: bool,
825        update: Value,
826        digest: Value,
827    },
828    Symbol {
829        description: EcmaString,
830    },
831    PrivateName {
832        description: EcmaString,
833    },
834    RegExp {
835        pattern: EcmaString,
836        flags: EcmaString,
837        properties: PropertyMap,
838        prototype: Option<Value>,
839        extensible: bool,
840    },
841    Date {
842        time: f64,
843        properties: PropertyMap,
844        prototype: Option<Value>,
845        extensible: bool,
846    },
847    /// Entries stay in insertion order. Iterators track order IDs, so deletion
848    /// can remove storage without moving an iterator's logical cursor.
849    /// Weak collections share this strong storage until the runtime has a collector.
850    Collection {
851        entries: Vec<CollectionEntry>,
852        next_order: u64,
853        properties: PropertyMap,
854        prototype: Option<Value>,
855        extensible: bool,
856    },
857    BuiltinIterator {
858        source: Value,
859        kind: IterationKind,
860        position: Option<u64>,
861        properties: PropertyMap,
862        prototype: Option<Value>,
863        extensible: bool,
864    },
865    Iterator {
866        state: IteratorState,
867    },
868    Generator {
869        state: GeneratorState,
870        properties: PropertyMap,
871        prototype: Option<Value>,
872        extensible: bool,
873    },
874    ProcessEnv {
875        prototype: Option<Value>,
876        extensible: bool,
877    },
878    Promise {
879        state: PromiseState,
880        properties: PropertyMap,
881        prototype: Option<Value>,
882        extensible: bool,
883    },
884    /// A Node-compatible `Timeout` handle carrying its monotonic timer id and
885    /// ordinary object property/prototype fields.
886    Timeout {
887        id: u64,
888        properties: PropertyMap,
889        prototype: Option<Value>,
890        extensible: bool,
891    },
892    PromiseResolver {
893        promise: Value,
894        used: bool,
895    },
896    PromiseFinally {
897        derived: Value,
898        value: Value,
899        origin: ThrowOrigin,
900        completion: PromiseCompletion,
901    },
902    PromiseAll {
903        promise: Value,
904        values: Vec<Value>,
905        remaining: usize,
906        settled: bool,
907    },
908    PromiseAllElement {
909        aggregate: Value,
910        index: usize,
911        called: bool,
912    },
913    /// One suspended async-function activation together with the implicit
914    /// result Promise it settles. `activation` is taken on resume so a second
915    /// resume is a hard invalid-state error. This record is internal and never
916    /// escapes to user code.
917    AsyncActivation {
918        activation: Option<SuspendedActivation>,
919        promise: Value,
920    },
921    NativeFunction {
922        callable: NativeCallable,
923        properties: PropertyMap,
924        extensible: bool,
925    },
926}
927
928#[derive(Clone, Debug)]
929pub(crate) enum NativeCallable {
930    Builtin(intrinsics::BuiltinId),
931    Bound(Box<BoundCallable>),
932}
933
934#[derive(Clone, Debug)]
935pub(crate) struct BoundCallable {
936    pub(crate) target: Value,
937    pub(crate) this_value: Value,
938    pub(crate) arguments: Vec<Value>,
939}
940
941impl HeapEntry {
942    fn initial_bytes(&self) -> usize {
943        match self {
944            Self::String(text)
945            | Self::Symbol { description: text }
946            | Self::PrivateName { description: text } => text.len_units().saturating_mul(2),
947            Self::BigInt(text) => text.len(),
948            Self::RegExp { pattern, flags, .. } => pattern
949                .len_units()
950                .saturating_add(flags.len_units())
951                .saturating_mul(2),
952            Self::HashState {
953                algorithm, data, ..
954            } => algorithm.len() + data.len(),
955            Self::Collection { entries, .. } => entries
956                .len()
957                .saturating_mul(CollectionEntry::BYTES)
958                .saturating_add(1),
959            Self::NativeFunction { callable, .. } => match callable {
960                NativeCallable::Builtin(_) => 1,
961                NativeCallable::Bound(bound) => bound.arguments.len().saturating_add(1),
962            },
963            Self::Generator { state, .. } => match state {
964                GeneratorState::SuspendedStart(start) => start
965                    .captures
966                    .len()
967                    .saturating_add(start.args.len())
968                    .saturating_add(1),
969                GeneratorState::Suspended(activation) => activation
970                    .registers
971                    .len()
972                    .saturating_add(activation.args.len())
973                    .saturating_add(1),
974                GeneratorState::Executing | GeneratorState::Completed => 1,
975            },
976            Self::Object { properties, .. } | Self::Timeout { properties, .. } => {
977                properties.charge_bytes().saturating_add(1)
978            }
979            Self::Array { .. }
980            | Self::Function { .. }
981            | Self::Script { .. }
982            | Self::ModuleNamespace { .. }
983            | Self::ExternalModuleNamespace { .. }
984            | Self::ProcessEnv { .. }
985            | Self::Date { .. }
986            | Self::BuiltinIterator { .. }
987            | Self::Iterator { .. }
988            | Self::Promise { .. }
989            | Self::PromiseResolver { .. }
990            | Self::PromiseFinally { .. }
991            | Self::PromiseAll { .. }
992            | Self::AsyncActivation { .. }
993            | Self::PromiseAllElement { .. } => 1,
994        }
995    }
996}
997
998#[derive(Clone, Copy, Debug)]
999struct ReturnTo {
1000    /// The caller register receiving the result, or `None` to discard it (an
1001    /// accessor setter invocation produces no observable value).
1002    destination: Option<usize>,
1003    call_pc: usize,
1004    constructed: Option<Value>,
1005}
1006
1007struct CallRequest<'a> {
1008    callee: Value,
1009    this_value: Value,
1010    arguments: &'a [Value],
1011    destination: Option<u32>,
1012    call_pc: usize,
1013    constructed: Option<Value>,
1014    new_target: Value,
1015}
1016
1017pub(crate) struct BoundCall {
1018    pub(crate) target: Value,
1019    pub(crate) this_value: Value,
1020    pub(crate) arguments: Vec<Value>,
1021}
1022
1023#[derive(Clone, Copy, Debug)]
1024pub(crate) struct RuntimeFunction {
1025    pub(crate) module: ModuleId,
1026    pub(crate) function: FunctionId,
1027}
1028
1029#[derive(Clone, Debug)]
1030struct Frame {
1031    module: ModuleId,
1032    function: usize,
1033    pc: usize,
1034    registers: Vec<Value>,
1035    return_to: Option<ReturnTo>,
1036    this_value: Value,
1037    new_target: Value,
1038    args: Vec<Value>,
1039    arguments_object: Option<Value>,
1040}
1041
1042impl Frame {
1043    fn new(
1044        target: RuntimeFunction,
1045        metadata: &Function,
1046        captures: &[Value],
1047        this_value: Value,
1048        new_target: Value,
1049        arguments: &[Value],
1050        return_to: Option<ReturnTo>,
1051    ) -> Self {
1052        let mut registers = vec![Value::UNINITIALIZED; metadata.register_count() as usize];
1053        let capture_count = metadata.capture_count() as usize;
1054        for (index, slot) in registers.iter_mut().take(capture_count).enumerate() {
1055            *slot = captures.get(index).copied().unwrap_or(Value::UNDEFINED);
1056        }
1057        for (index, slot) in registers
1058            .iter_mut()
1059            .skip(capture_count)
1060            .take(metadata.parameter_count() as usize)
1061            .enumerate()
1062        {
1063            *slot = arguments.get(index).copied().unwrap_or(Value::UNDEFINED);
1064        }
1065        Self {
1066            module: target.module,
1067            function: target.function.get() as usize,
1068            pc: 0,
1069            registers,
1070            return_to,
1071            this_value,
1072            new_target,
1073            args: arguments.to_vec(),
1074            arguments_object: None,
1075        }
1076    }
1077}
1078
1079#[derive(Clone, Debug)]
1080pub(crate) enum EvalFailure {
1081    Throw(ThrowOrigin),
1082    ThrowValue(Value),
1083    Runtime(RuntimeErrorKind),
1084    ThrowValueOrigin { value: Value, origin: ThrowOrigin },
1085}
1086
1087pub(crate) fn import_failure(error: &RuntimeError) -> EvalFailure {
1088    match &error.kind {
1089        RuntimeErrorKind::UncaughtThrow { value, .. } => EvalFailure::ThrowValue(*value),
1090        kind => EvalFailure::Runtime(kind.clone()),
1091    }
1092}
1093
1094/// The outcome of resolving a property read.
1095#[derive(Clone, Debug)]
1096enum GetOutcome {
1097    /// A ready ABI value.
1098    Value(Value),
1099    /// Text that must be interned as a fresh heap string.
1100    Text(EcmaString),
1101    /// An accessor getter to invoke with the receiver as `this`.
1102    Getter(Value),
1103}
1104
1105/// The outcome of resolving a property write.
1106#[derive(Clone, Debug)]
1107enum SetOutcome {
1108    Done,
1109    /// An accessor setter to invoke with the receiver as `this` and the value as
1110    /// its sole argument.
1111    Setter(Value),
1112}
1113
1114/// Own-property lookup result during a prototype-chain walk.
1115#[derive(Clone, Debug)]
1116enum Found {
1117    Value(Value),
1118    Text(EcmaString),
1119    Getter(Value),
1120    Failure(RuntimeErrorKind),
1121    /// An accessor property with no getter resolves to `undefined`.
1122    NoGetter,
1123}
1124
1125/// A classified callee, shared by the interpreter and the native engine so both
1126/// route calls through one decision. The native engine reimplements the frame
1127/// push but consumes this classifier verbatim.
1128#[derive(Clone, Debug)]
1129enum CalleeKind {
1130    Runtime {
1131        target: RuntimeFunction,
1132        captures: Vec<Value>,
1133    },
1134    Builtin {
1135        id: intrinsics::BuiltinId,
1136    },
1137    Bound,
1138    NotCallable,
1139}
1140
1141#[derive(Clone, Debug)]
1142struct TimerRecord {
1143    callback: Value,
1144    arguments: Vec<Value>,
1145    handle: Value,
1146    deadline_ms: u64,
1147    sequence: u64,
1148}
1149
1150/// The production bytecode interpreter.
1151pub struct Machine<'a, H: Host> {
1152    program: Option<&'a Program<Verified>>,
1153    module: &'a Module<Verified>,
1154    host: &'a mut H,
1155    limits: Limits,
1156    frames: Vec<Frame>,
1157    heap: Vec<HeapEntry>,
1158    intrinsic_slots: usize,
1159    heap_bytes: usize,
1160    live_registers: usize,
1161    native_depth: usize,
1162    fuel: u64,
1163    globals: BTreeMap<EcmaString, Value>,
1164    last_completion: Option<Value>,
1165    /// Frame depths owned by native-to-runtime callback evaluations. A throw
1166    /// crossing this boundary returns to the native caller so the enclosing
1167    /// bytecode instruction resolves it at its own program counter.
1168    callback_boundaries: Vec<usize>,
1169    generator_boundaries: Vec<usize>,
1170    pending_generator_resume: Option<GeneratorResume>,
1171    async_boundaries: Vec<usize>,
1172    pending_async_suspend: Option<(Value, SuspendedActivation)>,
1173    microtasks: VecDeque<MicrotaskJob>,
1174    microtask_drain_active: bool,
1175    next_timer_id: Option<u64>,
1176    next_timer_sequence: Option<u64>,
1177    timers: BTreeMap<u64, TimerRecord>,
1178    ready_timers: BTreeSet<(u64, u64)>,
1179    timer_watermark: Option<u64>,
1180    timer_checkpoint_active: bool,
1181    intrinsics: intrinsics::Intrinsics<H>,
1182    current_builtin_id: Option<intrinsics::BuiltinId>,
1183    registry: ModuleRegistry,
1184    /// First machine-wide module ID reserved for host-compiled script modules.
1185    dynamic_base: usize,
1186    /// Host-compiled programs retained for the machine lifetime so their
1187    /// closures remain executable after the originating Script object dies.
1188    dynamic: Vec<DynamicModule>,
1189}
1190
1191#[derive(Clone, Debug)]
1192struct DynamicModule {
1193    program: Arc<Program<Verified>>,
1194    bytes: usize,
1195}
1196
1197#[derive(Clone, Debug, Default)]
1198struct ModuleRegistry {
1199    modules: Vec<ModuleInstance>,
1200    cells: Vec<Cell>,
1201    external: BTreeMap<EcmaString, ExternalModuleInstance>,
1202}
1203
1204#[derive(Clone, Debug)]
1205struct ExternalModuleInstance {
1206    namespace: Value,
1207    exports: BTreeMap<EcmaString, ExternalExport>,
1208    internals: BTreeMap<&'static str, Value>,
1209}
1210
1211#[derive(Clone, Copy, Debug)]
1212struct ExternalExport {
1213    value: Value,
1214    cell: Option<CellId>,
1215}
1216
1217#[derive(Clone, Debug)]
1218struct ModuleInstance {
1219    binding_cells: Vec<Option<CellId>>,
1220    constant_cells: Vec<Option<CellId>>,
1221    namespace: Option<Value>,
1222    state: ModuleState,
1223}
1224
1225#[derive(Clone, Debug)]
1226enum ModuleState {
1227    Unevaluated,
1228    Evaluating,
1229    Evaluated(Result<(), RuntimeError>),
1230}
1231
1232#[derive(Clone, Debug)]
1233pub(crate) enum ModuleEvaluation {
1234    Cycle,
1235    Evaluated(Result<(), RuntimeError>),
1236    Ready(Vec<ModuleId>),
1237}
1238
1239#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1240pub(crate) enum ImportTarget {
1241    Local(ModuleId),
1242    External(EdgeId),
1243}
1244
1245#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1246struct CellId(usize);
1247
1248#[derive(Clone, Copy, Debug)]
1249struct Cell {
1250    value: Value,
1251}
1252
1253pub fn run<H: Host>(
1254    program: &Program<Verified>,
1255    host: &mut H,
1256    limits: &Limits,
1257) -> Result<ExecutionOutcome, RuntimeError> {
1258    Machine::new(program, host, limits.clone())
1259        .run()
1260        .map(|execution| execution.outcome)
1261}
1262
1263impl<'a, H: Host> Machine<'a, H> {
1264    #[must_use]
1265    pub fn new(program: &'a Program<Verified>, host: &'a mut H, limits: Limits) -> Self {
1266        let module = &program
1267            .module(program.entry())
1268            .expect("verified program entry exists")
1269            .code;
1270        Self::build(Some(program), module, host, limits)
1271    }
1272
1273    fn build(
1274        program: Option<&'a Program<Verified>>,
1275        module: &'a Module<Verified>,
1276        host: &'a mut H,
1277        limits: Limits,
1278    ) -> Self {
1279        let entry = module.entry().get() as usize;
1280        let module_id = program.map_or(ModuleId::new(0), Program::entry);
1281        let frame = Frame::new(
1282            RuntimeFunction {
1283                module: module_id,
1284                function: FunctionId::new(entry as u32),
1285            },
1286            &module.functions()[entry],
1287            &[],
1288            Value::UNDEFINED,
1289            Value::UNDEFINED,
1290            &[],
1291            None,
1292        );
1293        let live_registers = frame.registers.len();
1294        let mut heap = Vec::new();
1295        let timers_available = host.timers().is_some();
1296        let mut intrinsics = intrinsics::Intrinsics::<H>::initialize(&mut heap, timers_available);
1297        let script_compiler = host.script_compiler().is_some();
1298        let installed_external = external_modules::install(
1299            &mut heap,
1300            &mut intrinsics.builtins,
1301            intrinsics.object_prototype,
1302            script_compiler,
1303        );
1304        let argv_text = host.argv().to_vec();
1305        let argv_values: Vec<Value> = argv_text
1306            .into_iter()
1307            .map(|text| {
1308                intrinsics::push(&mut heap, HeapEntry::String(EcmaString::from_utf8(&text)))
1309            })
1310            .collect();
1311        let process = intrinsics
1312            .global("process")
1313            .expect("host objects install process");
1314        let Some(Decoded::HeapRef(process_id)) = process.decode() else {
1315            unreachable!("process is an engine object");
1316        };
1317        let process_index = process_id.slot() as usize - 1;
1318        let HeapEntry::Object { properties, .. } = &heap[process_index] else {
1319            unreachable!("process is an ordinary object");
1320        };
1321        let Some(Property::Data { value: argv, .. }) = properties.get_ascii("argv") else {
1322            unreachable!("process owns argv");
1323        };
1324        let Some(Decoded::HeapRef(argv_id)) = argv.decode() else {
1325            unreachable!("process.argv is an engine array");
1326        };
1327        let argv_index = argv_id.slot() as usize - 1;
1328        let HeapEntry::Array { elements, .. } = &mut heap[argv_index] else {
1329            unreachable!("process.argv is an array");
1330        };
1331        *elements = argv_values;
1332        let intrinsic_slots = heap.len();
1333        let fuel = limits.fuel;
1334        Self {
1335            program,
1336            module,
1337            host,
1338            limits,
1339            fuel,
1340            frames: vec![frame],
1341            heap,
1342            heap_bytes: 0,
1343            intrinsic_slots,
1344            live_registers,
1345            native_depth: 0,
1346            last_completion: None,
1347            callback_boundaries: Vec::new(),
1348            generator_boundaries: Vec::new(),
1349            pending_generator_resume: None,
1350            async_boundaries: Vec::new(),
1351            pending_async_suspend: None,
1352            microtasks: VecDeque::new(),
1353            microtask_drain_active: false,
1354            next_timer_id: Some(1),
1355            next_timer_sequence: Some(0),
1356            timers: BTreeMap::new(),
1357            ready_timers: BTreeSet::new(),
1358            timer_watermark: None,
1359            timer_checkpoint_active: false,
1360            globals: BTreeMap::new(),
1361            registry: ModuleRegistry {
1362                external: installed_external
1363                    .into_iter()
1364                    .map(|module| {
1365                        let mut exports: BTreeMap<_, _> = module
1366                            .exports
1367                            .into_iter()
1368                            .map(|(name, value)| (name, ExternalExport { value, cell: None }))
1369                            .collect();
1370                        exports.insert(
1371                            EcmaString::from_utf8("default"),
1372                            ExternalExport {
1373                                value: module.namespace,
1374                                cell: None,
1375                            },
1376                        );
1377                        (
1378                            module.specifier,
1379                            ExternalModuleInstance {
1380                                namespace: module.namespace,
1381                                exports,
1382                                internals: module.internals,
1383                            },
1384                        )
1385                    })
1386                    .collect(),
1387                ..ModuleRegistry::default()
1388            },
1389            dynamic_base: program.map_or(1, |program| program.modules().len()),
1390            dynamic: Vec::new(),
1391            current_builtin_id: None,
1392            intrinsics,
1393        }
1394    }
1395
1396    pub fn run(mut self) -> Result<Execution, RuntimeError> {
1397        self.evaluate()
1398    }
1399
1400    /// Evaluates the program without draining queued microtasks.
1401    ///
1402    /// The machine remains available for an explicit [`Self::drain_microtasks`]
1403    /// checkpoint.
1404    pub fn evaluate(&mut self) -> Result<Execution, RuntimeError> {
1405        if let Some(program) = self.program {
1406            let entry = program.entry();
1407            self.frames.clear();
1408            self.live_registers = 0;
1409            self.instantiate_modules()?;
1410            return self.evaluate_module(entry)?.ok_or_else(|| {
1411                self.program_error(
1412                    entry,
1413                    RuntimeErrorKind::InvalidVerifiedProgram {
1414                        module: entry,
1415                        instruction: Instruction::Halt,
1416                    },
1417                )
1418            });
1419        }
1420        Ok(self
1421            .run_loop(0)?
1422            .expect("the entry frame completes before the run loop stops"))
1423    }
1424
1425    /// Runs at most one expired live timer callback.
1426    ///
1427    /// This explicit checkpoint never drains microtasks. Expiry reports only
1428    /// advance a monotonic watermark; visible delivery is ordered by the
1429    /// machine-owned `(deadline, sequence)` key.
1430    pub fn run_one_expired_timer(&mut self) -> Result<TimerRun, RuntimeError> {
1431        if self.timer_checkpoint_active {
1432            return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1433        }
1434        self.timer_checkpoint_active = true;
1435        let result = (|| {
1436            self.poll_timer_expiries()
1437                .map_err(|kind| self.checkpoint_error(kind))?;
1438            let Some(order) = self.ready_timers.first().copied() else {
1439                return Ok(TimerRun::default());
1440            };
1441            let Some(id) = self.timers.iter().find_map(|(id, timer)| {
1442                ((timer.deadline_ms, timer.sequence) == order).then_some(*id)
1443            }) else {
1444                return Err(self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1445                    value: Value::UNDEFINED,
1446                }));
1447            };
1448            // Preserve both the ready key and live record when fuel is empty.
1449            self.consume_fuel(1)
1450                .map_err(|kind| self.checkpoint_error(kind))?;
1451            self.ready_timers.remove(&order);
1452            let timer = self
1453                .timers
1454                .remove(&id)
1455                .expect("ready timer remains live until after fuel charging");
1456            let mut report = TimerRun {
1457                executed: 1,
1458                uncaught: Vec::new(),
1459            };
1460            match self.call_value(timer.callback, timer.handle, &timer.arguments) {
1461                Ok(_) => {}
1462                Err(EvalFailure::Runtime(kind)) => {
1463                    return Err(self.checkpoint_error(kind));
1464                }
1465                Err(failure) => {
1466                    let (value, origin) =
1467                        self.promise_rejection_value(failure)
1468                            .map_err(|failure| match failure {
1469                                EvalFailure::Runtime(kind) => self.checkpoint_error(kind),
1470                                _ => self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1471                                    value: timer.callback,
1472                                }),
1473                            })?;
1474                    report.uncaught.try_reserve(1).map_err(|_| {
1475                        self.checkpoint_error(RuntimeErrorKind::HeapByteLimitExceeded {
1476                            limit: self.limits.max_heap_bytes,
1477                        })
1478                    })?;
1479                    report.uncaught.push(CallbackException { value, origin });
1480                }
1481            }
1482            Ok(report)
1483        })();
1484        self.timer_checkpoint_active = false;
1485        result
1486    }
1487
1488    /// Blocks in the host provider until an expiry report is available.
1489    /// Returns whether at least one live timer became ready.
1490    pub fn wait_for_timer_expiry(&mut self) -> Result<bool, RuntimeError> {
1491        if self.timer_checkpoint_active {
1492            return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1493        }
1494        if !self.ready_timers.is_empty() {
1495            return Ok(true);
1496        }
1497        self.timer_checkpoint_active = true;
1498        let result = (|| {
1499            let wakeup = match self.host.timers() {
1500                Some(provider) => provider.wait_expired(),
1501                None => return Ok(false),
1502            }
1503            .map_err(|error| {
1504                self.checkpoint_error(RuntimeErrorKind::TimerProviderFailure {
1505                    message: error.to_string(),
1506                })
1507            })?;
1508            if let Some(wakeup) = wakeup {
1509                self.promote_timer_wakeup(wakeup);
1510            }
1511            Ok(!self.ready_timers.is_empty())
1512        })();
1513        self.timer_checkpoint_active = false;
1514        result
1515    }
1516
1517    /// Returns whether any machine-owned live timer remains.
1518    #[must_use]
1519    pub fn has_pending_timers(&self) -> bool {
1520        !self.timers.is_empty()
1521    }
1522
1523    pub(crate) fn schedule_timeout(
1524        &mut self,
1525        callback: Value,
1526        delay_ms: u32,
1527        arguments: Vec<Value>,
1528    ) -> Result<Value, EvalFailure> {
1529        if self.timers.len() >= self.limits.max_timers {
1530            return Err(EvalFailure::Runtime(
1531                RuntimeErrorKind::TimerCapacityExceeded {
1532                    limit: self.limits.max_timers,
1533                },
1534            ));
1535        }
1536        let id = self.next_timer_id.take().ok_or(EvalFailure::Runtime(
1537            RuntimeErrorKind::TimerCapacityExceeded {
1538                limit: self.limits.max_timers,
1539            },
1540        ))?;
1541        self.next_timer_id = id.checked_add(1);
1542        let sequence = self.next_timer_sequence.take().ok_or(EvalFailure::Runtime(
1543            RuntimeErrorKind::TimerCapacityExceeded {
1544                limit: self.limits.max_timers,
1545            },
1546        ))?;
1547        self.next_timer_sequence = sequence.checked_add(1);
1548        let deadline_ms = self
1549            .host
1550            .timers()
1551            .ok_or(EvalFailure::Runtime(
1552                RuntimeErrorKind::TimerProviderFailure {
1553                    message: "timer capability is unavailable".to_owned(),
1554                },
1555            ))?
1556            .schedule(id, delay_ms)
1557            .map_err(|error| {
1558                EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1559                    message: error.to_string(),
1560                })
1561            })?;
1562        let handle = match self.allocate(HeapEntry::Timeout {
1563            id,
1564            properties: PropertyMap::default(),
1565            prototype: Some(self.intrinsics.object_prototype),
1566            extensible: true,
1567        }) {
1568            Ok(handle) => handle,
1569            Err(kind) => {
1570                if let Some(provider) = self.host.timers() {
1571                    let _ = provider.cancel(id);
1572                }
1573                return Err(EvalFailure::Runtime(kind));
1574            }
1575        };
1576        self.timers.insert(
1577            id,
1578            TimerRecord {
1579                callback,
1580                arguments,
1581                handle,
1582                deadline_ms,
1583                sequence,
1584            },
1585        );
1586        Ok(handle)
1587    }
1588
1589    pub(crate) fn clear_timeout(&mut self, handle: Value) -> Result<(), EvalFailure> {
1590        let id = match handle.decode() {
1591            Some(Decoded::Int32(raw)) if (raw as i32) > 0 => Some(u64::from(raw)),
1592            Some(Decoded::Number(number))
1593                if number.is_finite()
1594                    && number > 0.0
1595                    && number.fract() == 0.0
1596                    && number < u64::MAX as f64 =>
1597            {
1598                Some(number as u64)
1599            }
1600            Some(Decoded::HeapRef(_)) => {
1601                self.runtime_slot(handle)
1602                    .ok()
1603                    .flatten()
1604                    .and_then(|index| match &self.heap[index] {
1605                        HeapEntry::Timeout { id, .. } => Some(*id),
1606                        _ => None,
1607                    })
1608            }
1609            _ => None,
1610        };
1611        let Some(id) = id else {
1612            return Ok(());
1613        };
1614        let Some(timer) = self.timers.remove(&id) else {
1615            return Ok(());
1616        };
1617        self.ready_timers
1618            .remove(&(timer.deadline_ms, timer.sequence));
1619        if let Some(provider) = self.host.timers() {
1620            provider.cancel(id).map_err(|error| {
1621                EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1622                    message: error.to_string(),
1623                })
1624            })?;
1625        }
1626        Ok(())
1627    }
1628
1629    fn poll_timer_expiries(&mut self) -> Result<(), RuntimeErrorKind> {
1630        let mut wakeups = Vec::new();
1631        let Some(provider) = self.host.timers() else {
1632            return Ok(());
1633        };
1634        provider.poll_expired(&mut wakeups).map_err(|error| {
1635            RuntimeErrorKind::TimerProviderFailure {
1636                message: error.to_string(),
1637            }
1638        })?;
1639        // One live report authorizes every earlier deadline. Collapse a host
1640        // batch to one watermark instead of rescanning all live timers per report.
1641        if let Some(wakeup) = wakeups
1642            .into_iter()
1643            .filter(|wakeup| self.timers.contains_key(&wakeup.id))
1644            .max_by_key(|wakeup| wakeup.deadline_ms)
1645        {
1646            self.promote_timer_wakeup(wakeup);
1647        }
1648        Ok(())
1649    }
1650
1651    fn promote_timer_wakeup(&mut self, wakeup: TimerWakeup) {
1652        // Unknown IDs are stale cancellation races and carry no authority to
1653        // advance the watermark.
1654        if !self.timers.contains_key(&wakeup.id) {
1655            return;
1656        }
1657        let watermark = self.timer_watermark.map_or(wakeup.deadline_ms, |current| {
1658            current.max(wakeup.deadline_ms)
1659        });
1660        self.timer_watermark = Some(watermark);
1661        for timer in self.timers.values() {
1662            if timer.deadline_ms <= watermark {
1663                self.ready_timers
1664                    .insert((timer.deadline_ms, timer.sequence));
1665            }
1666        }
1667    }
1668
1669    /// Runs queued microtasks and expired timers until both are idle.
1670    pub fn run_to_quiescence(&mut self) -> Result<(), RuntimeError> {
1671        loop {
1672            let microtasks = self.drain_microtasks()?;
1673            let timer = self.run_one_expired_timer()?;
1674            if microtasks.executed == 0 && timer.executed == 0 {
1675                break;
1676            }
1677        }
1678        Ok(())
1679    }
1680
1681    /// Runs queued microtasks in FIFO order until the queue is empty.
1682    ///
1683    /// Jobs queued by another job run in the same checkpoint. Promise callback
1684    /// throws settle their derived promises. Throws from `queueMicrotask`
1685    /// callbacks are returned in [`MicrotaskDrain::uncaught`].
1686    pub fn drain_microtasks(&mut self) -> Result<MicrotaskDrain, RuntimeError> {
1687        if self.microtask_drain_active {
1688            return Err(self.checkpoint_error(RuntimeErrorKind::MicrotaskDrainReentry));
1689        }
1690        self.microtask_drain_active = true;
1691        let result = (|| {
1692            let mut report = MicrotaskDrain::default();
1693            while self.microtasks.front().is_some() {
1694                self.consume_fuel(1)
1695                    .map_err(|kind| self.checkpoint_error(kind))?;
1696                let job = self
1697                    .microtasks
1698                    .pop_front()
1699                    .expect("the queued microtask remains present after fuel charging");
1700                report.executed = report.executed.saturating_add(1);
1701                self.execute_microtask_job(job, &mut report)
1702                    .map_err(|kind| self.checkpoint_error(kind))?;
1703            }
1704            Ok(report)
1705        })();
1706        self.microtask_drain_active = false;
1707        result
1708    }
1709
1710    fn checkpoint_error(&self, kind: RuntimeErrorKind) -> RuntimeError {
1711        let function = self.module.entry();
1712        let instruction = self.module.functions()[function.get() as usize]
1713            .code()
1714            .first()
1715            .copied()
1716            .unwrap_or(Instruction::Halt);
1717        RuntimeError {
1718            kind,
1719            function,
1720            pc: Pc::new(0),
1721            source: RuntimeSource {
1722                function_name: None,
1723                instruction,
1724            },
1725        }
1726    }
1727
1728    fn execute_microtask_job(
1729        &mut self,
1730        job: MicrotaskJob,
1731        report: &mut MicrotaskDrain,
1732    ) -> Result<(), RuntimeErrorKind> {
1733        match job {
1734            MicrotaskJob::Reaction {
1735                reaction,
1736                value,
1737                origin,
1738            } => self.execute_promise_reaction(reaction, value, origin),
1739            MicrotaskJob::Thenable {
1740                promise,
1741                thenable,
1742                then,
1743            } => self.execute_thenable_job(promise, thenable, then),
1744            MicrotaskJob::Callback { callback } => {
1745                self.execute_callback_microtask(callback, report)
1746            }
1747        }
1748    }
1749
1750    fn execute_callback_microtask(
1751        &mut self,
1752        callback: Value,
1753        report: &mut MicrotaskDrain,
1754    ) -> Result<(), RuntimeErrorKind> {
1755        match self.call_value(callback, Value::UNDEFINED, &[]) {
1756            Ok(_) => Ok(()),
1757            Err(EvalFailure::Runtime(kind)) => Err(kind),
1758            Err(failure) => {
1759                let (value, origin) =
1760                    self.promise_rejection_value(failure)
1761                        .map_err(|failure| match failure {
1762                            EvalFailure::Runtime(kind) => kind,
1763                            _ => RuntimeErrorKind::InvalidValue { value: callback },
1764                        })?;
1765                report.uncaught.try_reserve(1).map_err(|_| {
1766                    RuntimeErrorKind::HeapByteLimitExceeded {
1767                        limit: self.limits.max_heap_bytes,
1768                    }
1769                })?;
1770                report.uncaught.push(CallbackException { value, origin });
1771                Ok(())
1772            }
1773        }
1774    }
1775
1776    fn execute_thenable_job(
1777        &mut self,
1778        promise: Value,
1779        thenable: Value,
1780        then: Value,
1781    ) -> Result<(), RuntimeErrorKind> {
1782        let record = self
1783            .create_promise_resolver(promise)
1784            .map_err(|failure| match failure {
1785                EvalFailure::Runtime(kind) => kind,
1786                _ => RuntimeErrorKind::InvalidValue { value: promise },
1787            })?;
1788        let (resolve_target, reject_target) = self.intrinsics.builtins.promise_resolver_targets();
1789        let resolve = self
1790            .create_promise_resolver_function(resolve_target, record)
1791            .map_err(|failure| match failure {
1792                EvalFailure::Runtime(kind) => kind,
1793                _ => RuntimeErrorKind::InvalidValue { value: record },
1794            })?;
1795        let reject = self
1796            .create_promise_resolver_function(reject_target, record)
1797            .map_err(|failure| match failure {
1798                EvalFailure::Runtime(kind) => kind,
1799                _ => RuntimeErrorKind::InvalidValue { value: record },
1800            })?;
1801        match self.call_value(then, thenable, &[resolve, reject]) {
1802            Ok(_) => Ok(()),
1803            Err(EvalFailure::Runtime(kind)) => Err(kind),
1804            Err(failure) => self
1805                .reject_promise_resolver_failure(record, failure)
1806                .map_err(|failure| match failure {
1807                    EvalFailure::Runtime(kind) => kind,
1808                    _ => RuntimeErrorKind::InvalidValue { value: record },
1809                }),
1810        }
1811    }
1812
1813    fn execute_promise_reaction(
1814        &mut self,
1815        reaction: PromiseReaction,
1816        value: Value,
1817        origin: ThrowOrigin,
1818    ) -> Result<(), RuntimeErrorKind> {
1819        match reaction {
1820            PromiseReaction::Fulfilled { handler, derived } => self.execute_promise_handler(
1821                handler,
1822                derived,
1823                value,
1824                origin,
1825                PromiseCompletion::Fulfilled,
1826            ),
1827            PromiseReaction::Rejected { handler, derived } => self.execute_promise_handler(
1828                handler,
1829                derived,
1830                value,
1831                origin,
1832                PromiseCompletion::Rejected,
1833            ),
1834            PromiseReaction::Finally {
1835                handler,
1836                derived,
1837                completion,
1838            } => self.execute_promise_finally(handler, derived, value, origin, completion),
1839            PromiseReaction::AsyncFulfill { activation } => {
1840                self.resume_async(activation, value, None)
1841            }
1842            PromiseReaction::AsyncReject { activation } => {
1843                self.resume_async(activation, value, Some(origin))
1844            }
1845        }
1846    }
1847
1848    fn execute_promise_handler(
1849        &mut self,
1850        handler: Value,
1851        derived: Value,
1852        value: Value,
1853        origin: ThrowOrigin,
1854        completion: PromiseCompletion,
1855    ) -> Result<(), RuntimeErrorKind> {
1856        if !self.is_callable(handler).map_err(|failure| match failure {
1857            EvalFailure::Runtime(kind) => kind,
1858            _ => RuntimeErrorKind::InvalidValue { value: handler },
1859        })? {
1860            return match completion {
1861                PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1862                PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1863            };
1864        }
1865        match self.call_value(handler, Value::UNDEFINED, &[value]) {
1866            Ok(result) => self.resolve_promise(derived, result),
1867            Err(EvalFailure::Runtime(kind)) => Err(kind),
1868            Err(failure) => self
1869                .reject_promise_failure(derived, failure)
1870                .map_err(|failure| match failure {
1871                    EvalFailure::Runtime(kind) => kind,
1872                    _ => RuntimeErrorKind::InvalidValue { value: derived },
1873                }),
1874        }
1875    }
1876
1877    fn execute_promise_finally(
1878        &mut self,
1879        handler: Value,
1880        derived: Value,
1881        value: Value,
1882        origin: ThrowOrigin,
1883        completion: PromiseCompletion,
1884    ) -> Result<(), RuntimeErrorKind> {
1885        if !self.is_callable(handler).map_err(|failure| match failure {
1886            EvalFailure::Runtime(kind) => kind,
1887            _ => RuntimeErrorKind::InvalidValue { value: handler },
1888        })? {
1889            return match completion {
1890                PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1891                PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1892            };
1893        }
1894        let cleanup = self.create_promise().map_err(|failure| match failure {
1895            EvalFailure::Runtime(kind) => kind,
1896            _ => RuntimeErrorKind::InvalidValue { value: derived },
1897        })?;
1898        let record = self
1899            .create_promise_finally(derived, value, origin, completion)
1900            .map_err(|failure| match failure {
1901                EvalFailure::Runtime(kind) => kind,
1902                _ => RuntimeErrorKind::InvalidValue { value: derived },
1903            })?;
1904        let (on_fulfilled, on_rejected) = self.intrinsics.builtins.promise_finally_targets();
1905        let on_fulfilled = self
1906            .create_promise_resolver_function(on_fulfilled, record)
1907            .map_err(|failure| match failure {
1908                EvalFailure::Runtime(kind) => kind,
1909                _ => RuntimeErrorKind::InvalidValue { value: record },
1910            })?;
1911        let on_rejected = self
1912            .create_promise_resolver_function(on_rejected, record)
1913            .map_err(|failure| match failure {
1914                EvalFailure::Runtime(kind) => kind,
1915                _ => RuntimeErrorKind::InvalidValue { value: record },
1916            })?;
1917        self.promise_then(cleanup, on_fulfilled, on_rejected)
1918            .map_err(|failure| match failure {
1919                EvalFailure::Runtime(kind) => kind,
1920                _ => RuntimeErrorKind::InvalidValue { value: cleanup },
1921            })?;
1922        match self.call_value(handler, Value::UNDEFINED, &[]) {
1923            Ok(result) => self.resolve_promise(cleanup, result),
1924            Err(EvalFailure::Runtime(kind)) => Err(kind),
1925            Err(failure) => self
1926                .reject_promise_failure(cleanup, failure)
1927                .map_err(|failure| match failure {
1928                    EvalFailure::Runtime(kind) => kind,
1929                    _ => RuntimeErrorKind::InvalidValue { value: cleanup },
1930                }),
1931        }
1932    }
1933
1934    pub(crate) fn enqueue_microtask_callback(
1935        &mut self,
1936        callback: Value,
1937    ) -> Result<(), EvalFailure> {
1938        self.ensure_microtask_capacity(1)
1939            .map_err(EvalFailure::Runtime)?;
1940        self.microtasks
1941            .push_back(MicrotaskJob::Callback { callback });
1942        Ok(())
1943    }
1944
1945    fn ensure_microtask_capacity(&mut self, additional: usize) -> Result<(), RuntimeErrorKind> {
1946        if self
1947            .microtasks
1948            .len()
1949            .checked_add(additional)
1950            .is_none_or(|length| length > self.limits.max_microtasks)
1951        {
1952            return Err(RuntimeErrorKind::MicrotaskQueueLimitExceeded {
1953                limit: self.limits.max_microtasks,
1954            });
1955        }
1956        self.microtasks.try_reserve(additional).map_err(|_| {
1957            RuntimeErrorKind::HeapByteLimitExceeded {
1958                limit: self.limits.max_heap_bytes,
1959            }
1960        })
1961    }
1962
1963    pub(crate) fn create_promise(&mut self) -> Result<Value, EvalFailure> {
1964        self.allocate(HeapEntry::Promise {
1965            state: PromiseState::Pending {
1966                fulfill_reactions: Vec::new(),
1967                reject_reactions: Vec::new(),
1968            },
1969            properties: PropertyMap::default(),
1970            prototype: Some(self.intrinsics.builtins.promise_prototype()),
1971            extensible: true,
1972        })
1973        .map_err(EvalFailure::Runtime)
1974    }
1975
1976    pub(crate) fn create_promise_resolver(&mut self, promise: Value) -> Result<Value, EvalFailure> {
1977        self.allocate(HeapEntry::PromiseResolver {
1978            promise,
1979            used: false,
1980        })
1981        .map_err(EvalFailure::Runtime)
1982    }
1983
1984    pub(crate) fn create_promise_resolver_function(
1985        &mut self,
1986        target: Value,
1987        record: Value,
1988    ) -> Result<Value, EvalFailure> {
1989        self.allocate(HeapEntry::NativeFunction {
1990            callable: NativeCallable::Bound(Box::new(BoundCallable {
1991                target,
1992                this_value: Value::UNDEFINED,
1993                arguments: vec![record],
1994            })),
1995            properties: PropertyMap::default(),
1996            extensible: true,
1997        })
1998        .map_err(EvalFailure::Runtime)
1999    }
2000
2001    pub(crate) fn resolve_promise_resolver(
2002        &mut self,
2003        record: Value,
2004        value: Value,
2005    ) -> Result<(), EvalFailure> {
2006        if let Some(promise) = self.use_promise_resolver(record)? {
2007            self.resolve_promise(promise, value)
2008                .map_err(EvalFailure::Runtime)?;
2009        }
2010        Ok(())
2011    }
2012
2013    pub(crate) fn reject_promise_resolver(
2014        &mut self,
2015        record: Value,
2016        reason: Value,
2017    ) -> Result<(), EvalFailure> {
2018        if let Some(promise) = self.use_promise_resolver(record)? {
2019            self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2020                .map_err(EvalFailure::Runtime)?;
2021        }
2022        Ok(())
2023    }
2024
2025    pub(crate) fn reject_promise_resolver_failure(
2026        &mut self,
2027        record: Value,
2028        failure: EvalFailure,
2029    ) -> Result<(), EvalFailure> {
2030        if let Some(promise) = self.use_promise_resolver(record)? {
2031            self.reject_promise_failure(promise, failure)?;
2032        }
2033        Ok(())
2034    }
2035
2036    fn use_promise_resolver(&mut self, record: Value) -> Result<Option<Value>, EvalFailure> {
2037        let index = self
2038            .runtime_slot(record)
2039            .map_err(EvalFailure::Runtime)?
2040            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2041                operation: "Promise resolver",
2042            }))?;
2043        let HeapEntry::PromiseResolver { promise, used } = &mut self.heap[index] else {
2044            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2045                operation: "Promise resolver",
2046            }));
2047        };
2048        if *used {
2049            return Ok(None);
2050        }
2051        *used = true;
2052        Ok(Some(*promise))
2053    }
2054
2055    fn charge_promise_reactions(&mut self, count: usize) -> Result<(), EvalFailure> {
2056        let bytes = std::mem::size_of::<PromiseReaction>()
2057            .checked_mul(count)
2058            .ok_or(EvalFailure::Runtime(
2059                RuntimeErrorKind::HeapByteLimitExceeded {
2060                    limit: self.limits.max_heap_bytes,
2061                },
2062            ))?;
2063        self.charge_heap(bytes).map_err(EvalFailure::Runtime)
2064    }
2065
2066    pub(crate) fn promise_then(
2067        &mut self,
2068        promise: Value,
2069        on_fulfilled: Value,
2070        on_rejected: Value,
2071    ) -> Result<Value, EvalFailure> {
2072        let index = self
2073            .runtime_slot(promise)
2074            .map_err(EvalFailure::Runtime)?
2075            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2076                operation: "Promise.prototype.then",
2077            }))?;
2078        let settled = match &self.heap[index] {
2079            HeapEntry::Promise {
2080                state: PromiseState::Pending { .. },
2081                ..
2082            } => None,
2083            HeapEntry::Promise {
2084                state: PromiseState::Fulfilled { value },
2085                ..
2086            } => Some((true, *value, ThrowOrigin::Bytecode)),
2087            HeapEntry::Promise {
2088                state: PromiseState::Rejected { reason, origin },
2089                ..
2090            } => Some((false, *reason, *origin)),
2091            _ => {
2092                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2093                    operation: "Promise.prototype.then",
2094                }));
2095            }
2096        };
2097        let derived = self.create_promise()?;
2098        if let Some((fulfilled, value, origin)) = settled {
2099            self.ensure_microtask_capacity(1)
2100                .map_err(EvalFailure::Runtime)?;
2101            let reaction = if fulfilled {
2102                PromiseReaction::Fulfilled {
2103                    handler: on_fulfilled,
2104                    derived,
2105                }
2106            } else {
2107                PromiseReaction::Rejected {
2108                    handler: on_rejected,
2109                    derived,
2110                }
2111            };
2112            self.microtasks.push_back(MicrotaskJob::Reaction {
2113                reaction,
2114                value,
2115                origin,
2116            });
2117            return Ok(derived);
2118        }
2119        self.charge_promise_reactions(2)?;
2120        let HeapEntry::Promise {
2121            state:
2122                PromiseState::Pending {
2123                    fulfill_reactions,
2124                    reject_reactions,
2125                },
2126            ..
2127        } = &mut self.heap[index]
2128        else {
2129            unreachable!("pending Promise state was checked before derived allocation");
2130        };
2131        fulfill_reactions.push(PromiseReaction::Fulfilled {
2132            handler: on_fulfilled,
2133            derived,
2134        });
2135        reject_reactions.push(PromiseReaction::Rejected {
2136            handler: on_rejected,
2137            derived,
2138        });
2139        Ok(derived)
2140    }
2141
2142    pub(crate) fn promise_finally(
2143        &mut self,
2144        promise: Value,
2145        handler: Value,
2146    ) -> Result<Value, EvalFailure> {
2147        let index = self
2148            .runtime_slot(promise)
2149            .map_err(EvalFailure::Runtime)?
2150            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2151                operation: "Promise.prototype.finally",
2152            }))?;
2153        let settled = match &self.heap[index] {
2154            HeapEntry::Promise {
2155                state: PromiseState::Pending { .. },
2156                ..
2157            } => None,
2158            HeapEntry::Promise {
2159                state: PromiseState::Fulfilled { value },
2160                ..
2161            } => Some((true, *value, ThrowOrigin::Bytecode)),
2162            HeapEntry::Promise {
2163                state: PromiseState::Rejected { reason, origin },
2164                ..
2165            } => Some((false, *reason, *origin)),
2166            _ => {
2167                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2168                    operation: "Promise.prototype.finally",
2169                }));
2170            }
2171        };
2172        let derived = self.create_promise()?;
2173        let reaction = |completion| PromiseReaction::Finally {
2174            handler,
2175            derived,
2176            completion,
2177        };
2178        if let Some((fulfilled, value, origin)) = settled {
2179            self.ensure_microtask_capacity(1)
2180                .map_err(EvalFailure::Runtime)?;
2181            self.microtasks.push_back(MicrotaskJob::Reaction {
2182                reaction: reaction(if fulfilled {
2183                    PromiseCompletion::Fulfilled
2184                } else {
2185                    PromiseCompletion::Rejected
2186                }),
2187                value,
2188                origin,
2189            });
2190            return Ok(derived);
2191        }
2192        self.charge_promise_reactions(2)?;
2193        let HeapEntry::Promise {
2194            state:
2195                PromiseState::Pending {
2196                    fulfill_reactions,
2197                    reject_reactions,
2198                },
2199            ..
2200        } = &mut self.heap[index]
2201        else {
2202            unreachable!("pending Promise state was checked before derived allocation");
2203        };
2204        fulfill_reactions.push(reaction(PromiseCompletion::Fulfilled));
2205        reject_reactions.push(reaction(PromiseCompletion::Rejected));
2206        Ok(derived)
2207    }
2208
2209    pub(crate) fn create_promise_finally(
2210        &mut self,
2211        derived: Value,
2212        value: Value,
2213        origin: ThrowOrigin,
2214        completion: PromiseCompletion,
2215    ) -> Result<Value, EvalFailure> {
2216        self.allocate(HeapEntry::PromiseFinally {
2217            derived,
2218            value,
2219            origin,
2220            completion,
2221        })
2222        .map_err(EvalFailure::Runtime)
2223    }
2224
2225    pub(crate) fn fulfill_promise_finally(&mut self, record: Value) -> Result<(), EvalFailure> {
2226        let (derived, value, origin, completion) = self.promise_finally_record(record)?;
2227        match completion {
2228            PromiseCompletion::Fulfilled => self
2229                .resolve_promise(derived, value)
2230                .map_err(EvalFailure::Runtime),
2231            PromiseCompletion::Rejected => self
2232                .reject_promise(derived, value, origin)
2233                .map_err(EvalFailure::Runtime),
2234        }
2235    }
2236
2237    pub(crate) fn reject_promise_finally(
2238        &mut self,
2239        record: Value,
2240        reason: Value,
2241    ) -> Result<(), EvalFailure> {
2242        let (derived, _, _, _) = self.promise_finally_record(record)?;
2243        self.reject_promise(derived, reason, ThrowOrigin::Bytecode)
2244            .map_err(EvalFailure::Runtime)
2245    }
2246
2247    fn promise_finally_record(
2248        &mut self,
2249        record: Value,
2250    ) -> Result<(Value, Value, ThrowOrigin, PromiseCompletion), EvalFailure> {
2251        let index = self
2252            .runtime_slot(record)
2253            .map_err(EvalFailure::Runtime)?
2254            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2255                operation: "Promise finally target",
2256            }))?;
2257        let HeapEntry::PromiseFinally {
2258            derived,
2259            value,
2260            origin,
2261            completion,
2262        } = &self.heap[index]
2263        else {
2264            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2265                operation: "Promise finally target",
2266            }));
2267        };
2268        Ok((*derived, *value, *origin, *completion))
2269    }
2270
2271    pub(crate) fn promise_resolve(&mut self, value: Value) -> Result<Value, EvalFailure> {
2272        if matches!(self.runtime_slot(value).map_err(EvalFailure::Runtime)?, Some(index) if matches!(self.heap[index], HeapEntry::Promise { .. }))
2273        {
2274            return Ok(value);
2275        }
2276        let promise = self.create_promise()?;
2277        self.resolve_promise(promise, value)
2278            .map_err(EvalFailure::Runtime)?;
2279        Ok(promise)
2280    }
2281
2282    pub(crate) fn promise_reject(&mut self, reason: Value) -> Result<Value, EvalFailure> {
2283        let promise = self.create_promise()?;
2284        self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2285            .map_err(EvalFailure::Runtime)?;
2286        Ok(promise)
2287    }
2288
2289    pub(crate) fn promise_all(&mut self, iterable: Value) -> Result<Value, EvalFailure> {
2290        let promise = self.create_promise()?;
2291        let aggregate = self
2292            .allocate(HeapEntry::PromiseAll {
2293                promise,
2294                values: Vec::new(),
2295                remaining: 1,
2296                settled: false,
2297            })
2298            .map_err(EvalFailure::Runtime)?;
2299        let iterator = match self.create_iterator(iterable, IteratorKind::Sync) {
2300            Ok(iterator) => iterator,
2301            Err(failure) => {
2302                self.mark_promise_all_settled(aggregate)?;
2303                self.reject_promise_failure(promise, failure)?;
2304                return Ok(promise);
2305            }
2306        };
2307        loop {
2308            let value = match self.iterator_next(iterator) {
2309                Ok((true, _)) => break,
2310                Ok((false, value)) => value,
2311                Err(failure) => {
2312                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2313                }
2314            };
2315            let index = match self.add_promise_all_element(aggregate) {
2316                Ok(index) => index,
2317                Err(failure) => {
2318                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2319                }
2320            };
2321            let element = match self
2322                .allocate(HeapEntry::PromiseAllElement {
2323                    aggregate,
2324                    index,
2325                    called: false,
2326                })
2327                .map_err(EvalFailure::Runtime)
2328            {
2329                Ok(element) => element,
2330                Err(failure) => {
2331                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2332                }
2333            };
2334            let (fulfill_target, reject_target) = self.intrinsics.builtins.promise_all_targets();
2335            let on_fulfilled = match self.create_promise_resolver_function(fulfill_target, element)
2336            {
2337                Ok(callback) => callback,
2338                Err(failure) => {
2339                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2340                }
2341            };
2342            let on_rejected = match self.create_promise_resolver_function(reject_target, element) {
2343                Ok(callback) => callback,
2344                Err(failure) => {
2345                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2346                }
2347            };
2348            let resolved = match self.promise_resolve(value) {
2349                Ok(resolved) => resolved,
2350                Err(failure) => {
2351                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2352                }
2353            };
2354            if let Err(failure) = self.promise_then(resolved, on_fulfilled, on_rejected) {
2355                return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2356            }
2357        }
2358        if let Some(values) = self.finish_promise_all(aggregate)? {
2359            let array = self.create_array(values)?;
2360            self.fulfill_promise(promise, array)
2361                .map_err(EvalFailure::Runtime)?;
2362        }
2363        Ok(promise)
2364    }
2365
2366    fn reject_promise_all_abrupt(
2367        &mut self,
2368        aggregate: Value,
2369        promise: Value,
2370        iterator: Value,
2371        failure: EvalFailure,
2372    ) -> Result<Value, EvalFailure> {
2373        self.mark_promise_all_settled(aggregate)?;
2374        if let Err(EvalFailure::Runtime(kind)) = self.close_iterator(iterator) {
2375            return Err(EvalFailure::Runtime(kind));
2376        }
2377        self.reject_promise_failure(promise, failure)?;
2378        Ok(promise)
2379    }
2380
2381    fn close_iterator(&mut self, iterator: Value) -> Result<(), EvalFailure> {
2382        let Some(index) = self.runtime_slot(iterator).map_err(EvalFailure::Runtime)? else {
2383            return Ok(());
2384        };
2385        let HeapEntry::Iterator {
2386            state: IteratorState::Protocol { iterator, .. },
2387        } = &self.heap[index]
2388        else {
2389            return Ok(());
2390        };
2391        let iterator = *iterator;
2392        let close = self.get_named_property(iterator, "return")?;
2393        if self.is_callable(close)? {
2394            let _ = self.call_value(close, iterator, &[])?;
2395        }
2396        Ok(())
2397    }
2398
2399    fn mark_promise_all_settled(&mut self, aggregate: Value) -> Result<bool, EvalFailure> {
2400        let index = self
2401            .runtime_slot(aggregate)
2402            .map_err(EvalFailure::Runtime)?
2403            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2404                operation: "Promise.all target",
2405            }))?;
2406        let HeapEntry::PromiseAll { settled, .. } = &mut self.heap[index] else {
2407            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2408                operation: "Promise.all target",
2409            }));
2410        };
2411        let changed = !*settled;
2412        *settled = true;
2413        Ok(changed)
2414    }
2415
2416    fn add_promise_all_element(&mut self, aggregate: Value) -> Result<usize, EvalFailure> {
2417        let index = self
2418            .runtime_slot(aggregate)
2419            .map_err(EvalFailure::Runtime)?
2420            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2421                operation: "Promise.all target",
2422            }))?;
2423        let next_remaining = match &self.heap[index] {
2424            HeapEntry::PromiseAll {
2425                remaining,
2426                settled: false,
2427                ..
2428            } => remaining.checked_add(1).ok_or(EvalFailure::Runtime(
2429                RuntimeErrorKind::HeapByteLimitExceeded {
2430                    limit: self.limits.max_heap_bytes,
2431                },
2432            ))?,
2433            HeapEntry::PromiseAll { .. } => {
2434                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2435                    operation: "Promise.all target",
2436                }));
2437            }
2438            _ => {
2439                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2440                    operation: "Promise.all target",
2441                }));
2442            }
2443        };
2444        self.charge_heap(std::mem::size_of::<Value>())
2445            .map_err(EvalFailure::Runtime)?;
2446        let HeapEntry::PromiseAll {
2447            values, remaining, ..
2448        } = &mut self.heap[index]
2449        else {
2450            unreachable!("Promise.all aggregate was checked before its heap charge");
2451        };
2452        values.try_reserve(1).map_err(|_| {
2453            EvalFailure::Runtime(RuntimeErrorKind::HeapByteLimitExceeded {
2454                limit: self.limits.max_heap_bytes,
2455            })
2456        })?;
2457        let index = values.len();
2458        values.push(Value::UNDEFINED);
2459        *remaining = next_remaining;
2460        Ok(index)
2461    }
2462
2463    fn finish_promise_all(&mut self, aggregate: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
2464        let index = self
2465            .runtime_slot(aggregate)
2466            .map_err(EvalFailure::Runtime)?
2467            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2468                operation: "Promise.all target",
2469            }))?;
2470        let HeapEntry::PromiseAll {
2471            values,
2472            remaining,
2473            settled,
2474            ..
2475        } = &mut self.heap[index]
2476        else {
2477            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2478                operation: "Promise.all target",
2479            }));
2480        };
2481        if *settled {
2482            return Ok(None);
2483        }
2484        *remaining -= 1;
2485        if *remaining != 0 {
2486            return Ok(None);
2487        }
2488        *settled = true;
2489        Ok(Some(std::mem::take(values)))
2490    }
2491
2492    pub(crate) fn resolve_promise_all_element(
2493        &mut self,
2494        element: Value,
2495        value: Value,
2496    ) -> Result<(), EvalFailure> {
2497        let index = self
2498            .runtime_slot(element)
2499            .map_err(EvalFailure::Runtime)?
2500            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2501                operation: "Promise.all target",
2502            }))?;
2503        let (aggregate, output_index) = {
2504            let HeapEntry::PromiseAllElement {
2505                aggregate,
2506                index: output_index,
2507                called,
2508            } = &mut self.heap[index]
2509            else {
2510                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2511                    operation: "Promise.all target",
2512                }));
2513            };
2514            if *called {
2515                return Ok(());
2516            }
2517            *called = true;
2518            (*aggregate, *output_index)
2519        };
2520        let aggregate_index = self
2521            .runtime_slot(aggregate)
2522            .map_err(EvalFailure::Runtime)?
2523            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2524                operation: "Promise.all target",
2525            }))?;
2526        let (promise, values) = {
2527            let HeapEntry::PromiseAll {
2528                promise,
2529                values,
2530                remaining,
2531                settled,
2532            } = &mut self.heap[aggregate_index]
2533            else {
2534                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2535                    operation: "Promise.all target",
2536                }));
2537            };
2538            if *settled {
2539                return Ok(());
2540            }
2541            values[output_index] = value;
2542            *remaining -= 1;
2543            let values = (*remaining == 0).then(|| {
2544                *settled = true;
2545                std::mem::take(values)
2546            });
2547            (*promise, values)
2548        };
2549        if let Some(values) = values {
2550            let array = self.create_array(values)?;
2551            self.fulfill_promise(promise, array)
2552                .map_err(EvalFailure::Runtime)?;
2553        }
2554        Ok(())
2555    }
2556
2557    pub(crate) fn reject_promise_all_element(
2558        &mut self,
2559        element: Value,
2560        reason: Value,
2561    ) -> Result<(), EvalFailure> {
2562        let index = self
2563            .runtime_slot(element)
2564            .map_err(EvalFailure::Runtime)?
2565            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2566                operation: "Promise.all target",
2567            }))?;
2568        let aggregate = {
2569            let HeapEntry::PromiseAllElement {
2570                aggregate, called, ..
2571            } = &mut self.heap[index]
2572            else {
2573                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2574                    operation: "Promise.all target",
2575                }));
2576            };
2577            if *called {
2578                return Ok(());
2579            }
2580            *called = true;
2581            *aggregate
2582        };
2583        let aggregate_index = self
2584            .runtime_slot(aggregate)
2585            .map_err(EvalFailure::Runtime)?
2586            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2587                operation: "Promise.all target",
2588            }))?;
2589        let HeapEntry::PromiseAll { promise, .. } = &self.heap[aggregate_index] else {
2590            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2591                operation: "Promise.all target",
2592            }));
2593        };
2594        let promise = *promise;
2595        if !self.mark_promise_all_settled(aggregate)? {
2596            return Ok(());
2597        }
2598        self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2599            .map_err(EvalFailure::Runtime)
2600    }
2601
2602    fn create_array(&mut self, elements: Vec<Value>) -> Result<Value, EvalFailure> {
2603        self.allocate(HeapEntry::Array {
2604            elements,
2605            properties: PropertyMap::default(),
2606            prototype: Some(self.intrinsics.array_prototype),
2607            extensible: true,
2608            length_writable: true,
2609        })
2610        .map_err(EvalFailure::Runtime)
2611    }
2612
2613    fn resolve_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2614        if promise == value {
2615            return self
2616                .reject_promise_failure(
2617                    promise,
2618                    EvalFailure::Throw(ThrowOrigin::TypeError {
2619                        operation: "Promise cannot resolve itself",
2620                    }),
2621                )
2622                .map_err(|failure| match failure {
2623                    EvalFailure::Runtime(kind) => kind,
2624                    _ => RuntimeErrorKind::InvalidValue { value: promise },
2625                });
2626        }
2627        if !self.is_object(value) {
2628            return self.fulfill_promise(promise, value);
2629        }
2630        let then = match self.get_named_property(value, "then") {
2631            Ok(then) => then,
2632            Err(EvalFailure::Runtime(kind)) => return Err(kind),
2633            Err(failure) => {
2634                return self.reject_promise_failure(promise, failure).map_err(
2635                    |failure| match failure {
2636                        EvalFailure::Runtime(kind) => kind,
2637                        _ => RuntimeErrorKind::InvalidValue { value: promise },
2638                    },
2639                );
2640            }
2641        };
2642        if !self.is_callable(then).map_err(|failure| match failure {
2643            EvalFailure::Runtime(kind) => kind,
2644            _ => RuntimeErrorKind::InvalidValue { value: then },
2645        })? {
2646            return self.fulfill_promise(promise, value);
2647        }
2648        self.ensure_microtask_capacity(1)?;
2649        self.microtasks.push_back(MicrotaskJob::Thenable {
2650            promise,
2651            thenable: value,
2652            then,
2653        });
2654        Ok(())
2655    }
2656
2657    fn reject_promise(
2658        &mut self,
2659        promise: Value,
2660        reason: Value,
2661        origin: ThrowOrigin,
2662    ) -> Result<(), RuntimeErrorKind> {
2663        self.settle_promise(promise, PromiseState::Rejected { reason, origin })
2664    }
2665
2666    fn fulfill_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2667        self.settle_promise(promise, PromiseState::Fulfilled { value })
2668    }
2669
2670    fn settle_promise(
2671        &mut self,
2672        promise: Value,
2673        terminal: PromiseState,
2674    ) -> Result<(), RuntimeErrorKind> {
2675        let index = self
2676            .runtime_slot(promise)?
2677            .ok_or(RuntimeErrorKind::InvalidValue { value: promise })?;
2678        let reaction_count = match &self.heap[index] {
2679            HeapEntry::Promise {
2680                state:
2681                    PromiseState::Pending {
2682                        fulfill_reactions,
2683                        reject_reactions,
2684                    },
2685                ..
2686            } => match &terminal {
2687                PromiseState::Fulfilled { .. } => fulfill_reactions.len(),
2688                PromiseState::Rejected { .. } => reject_reactions.len(),
2689                PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2690            },
2691            HeapEntry::Promise { .. } => return Ok(()),
2692            _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2693        };
2694        self.ensure_microtask_capacity(reaction_count)?;
2695        let reactions = match &mut self.heap[index] {
2696            HeapEntry::Promise { state, .. } => {
2697                let reactions = match state {
2698                    PromiseState::Pending {
2699                        fulfill_reactions,
2700                        reject_reactions,
2701                    } => match &terminal {
2702                        PromiseState::Fulfilled { .. } => std::mem::take(fulfill_reactions),
2703                        PromiseState::Rejected { .. } => std::mem::take(reject_reactions),
2704                        PromiseState::Pending { .. } => {
2705                            unreachable!("Promise settlement is terminal")
2706                        }
2707                    },
2708                    _ => return Ok(()),
2709                };
2710                *state = terminal.clone();
2711                reactions
2712            }
2713            _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2714        };
2715        let (value, origin) = match terminal {
2716            PromiseState::Fulfilled { value } => (value, ThrowOrigin::Bytecode),
2717            PromiseState::Rejected { reason, origin } => (reason, origin),
2718            PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2719        };
2720        for reaction in reactions {
2721            self.microtasks.push_back(MicrotaskJob::Reaction {
2722                reaction,
2723                value,
2724                origin,
2725            });
2726        }
2727        Ok(())
2728    }
2729
2730    fn reject_promise_failure(
2731        &mut self,
2732        promise: Value,
2733        failure: EvalFailure,
2734    ) -> Result<(), EvalFailure> {
2735        let (reason, origin) = self.promise_rejection_value(failure)?;
2736        self.reject_promise(promise, reason, origin)
2737            .map_err(EvalFailure::Runtime)
2738    }
2739
2740    fn promise_rejection_value(
2741        &mut self,
2742        failure: EvalFailure,
2743    ) -> Result<(Value, ThrowOrigin), EvalFailure> {
2744        match failure {
2745            EvalFailure::ThrowValue(value) => Ok((value, ThrowOrigin::Bytecode)),
2746            EvalFailure::ThrowValueOrigin { value, origin } => Ok((value, origin)),
2747            EvalFailure::Throw(ThrowOrigin::Bytecode) => {
2748                Ok((Value::UNDEFINED, ThrowOrigin::Bytecode))
2749            }
2750            EvalFailure::Throw(origin) => {
2751                let (name, message) = match origin {
2752                    ThrowOrigin::TypeError { operation } => ("TypeError", operation),
2753                    ThrowOrigin::RangeError { operation } => ("RangeError", operation),
2754                    ThrowOrigin::ReferenceError { operation } => ("ReferenceError", operation),
2755                    ThrowOrigin::UriError { operation } => ("URIError", operation),
2756                    ThrowOrigin::Bytecode => unreachable!("handled above"),
2757                };
2758                let id = self
2759                    .intrinsics
2760                    .builtins
2761                    .id_named(name)
2762                    .expect("error constructor is installed");
2763                match self.throw_error(id, message.to_owned()) {
2764                    EvalFailure::ThrowValue(value) => Ok((value, origin)),
2765                    EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2766                    _ => unreachable!("error materialization returns a thrown value"),
2767                }
2768            }
2769            EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2770        }
2771    }
2772
2773    fn program(&self) -> &Program<Verified> {
2774        self.program
2775            .expect("module registry operations require a whole program")
2776    }
2777
2778    fn module_code(&self, module: ModuleId) -> &Module<Verified> {
2779        let index = module.get() as usize;
2780        if index >= self.dynamic_base {
2781            return &self.dynamic[index - self.dynamic_base].program.modules()[0].code;
2782        }
2783        match self.program {
2784            Some(program) => {
2785                &program
2786                    .module(module)
2787                    .expect("verified module id remains in bounds")
2788                    .code
2789            }
2790            None => self.module,
2791        }
2792    }
2793
2794    fn program_module(&self, module: ModuleId) -> &ProgramModule<Verified> {
2795        let index = module.get() as usize;
2796        if index >= self.dynamic_base {
2797            return &self.dynamic[index - self.dynamic_base].program.modules()[0];
2798        }
2799        self.program
2800            .and_then(|program| program.module(module))
2801            .expect("verified module id remains in bounds")
2802    }
2803
2804    /// Validates host-provided code against this machine's classic-script realm.
2805    fn validate_dynamic_script(program: &Program<Verified>) -> Result<(), &'static str> {
2806        if program.modules().len() != 1 {
2807            return Err("script program must contain exactly one module");
2808        }
2809        if program.entry() != ModuleId::new(0) {
2810            return Err("script program entry must be module zero");
2811        }
2812        let module = &program.modules()[0];
2813        if !module.edges.is_empty() || !module.bindings.is_empty() || !module.exports.is_empty() {
2814            return Err("script program must not contain linkage metadata");
2815        }
2816        if module
2817            .code
2818            .functions()
2819            .iter()
2820            .flat_map(|function| function.code())
2821            .any(|instruction| {
2822                matches!(
2823                    instruction,
2824                    Instruction::Import { .. } | Instruction::Export { .. }
2825                )
2826            })
2827        {
2828            return Err("script program must not contain import or export instructions");
2829        }
2830        Ok(())
2831    }
2832
2833    fn script_heap_cost(program: &Program<Verified>) -> usize {
2834        const MODULE_BYTES: usize = 64;
2835        const FUNCTION_BYTES: usize = 32;
2836        program.modules().iter().fold(0usize, |total, module| {
2837            let constant_bytes = module
2838                .code
2839                .constants()
2840                .iter()
2841                .fold(0usize, |bytes, constant| {
2842                    let payload = match constant {
2843                        Constant::String(text) => text.len_units().saturating_mul(2),
2844                        Constant::BigInt(value) => value.as_str().len(),
2845                        Constant::Number(_)
2846                        | Constant::Int32(_)
2847                        | Constant::Boolean(_)
2848                        | Constant::Null
2849                        | Constant::Undefined => 0,
2850                    };
2851                    bytes
2852                        .saturating_add(std::mem::size_of::<Constant>())
2853                        .saturating_add(payload)
2854                });
2855            let function_bytes =
2856                module
2857                    .code
2858                    .functions()
2859                    .iter()
2860                    .fold(0usize, |bytes, function| {
2861                        bytes
2862                            .saturating_add(FUNCTION_BYTES)
2863                            .saturating_add(
2864                                function
2865                                    .code()
2866                                    .len()
2867                                    .saturating_mul(std::mem::size_of::<Instruction>()),
2868                            )
2869                            .saturating_add(function.handlers().len().saturating_mul(
2870                                std::mem::size_of::<bamts_bytecode::ExceptionHandler>(),
2871                            ))
2872                    });
2873            total
2874                .saturating_add(MODULE_BYTES)
2875                .saturating_add(constant_bytes)
2876                .saturating_add(function_bytes)
2877                .saturating_add(module.code.verification_bytes())
2878        })
2879    }
2880
2881    fn install_script_reserving(
2882        &mut self,
2883        program: Arc<Program<Verified>>,
2884        reserved_slots: usize,
2885        reserved_bytes: usize,
2886    ) -> Result<ModuleId, RuntimeErrorKind> {
2887        Self::validate_dynamic_script(&program)
2888            .map_err(|reason| RuntimeErrorKind::InvalidDynamicScript { reason })?;
2889        if self.dynamic.len() >= self.limits.max_dynamic_modules {
2890            return Err(RuntimeErrorKind::DynamicModuleLimitExceeded {
2891                limit: self.limits.max_dynamic_modules,
2892            });
2893        }
2894        let bytes = Self::script_heap_cost(&program);
2895        let retained_bytes =
2896            bytes
2897                .checked_add(reserved_bytes)
2898                .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
2899                    limit: self.limits.max_heap_bytes,
2900                })?;
2901        self.ensure_allocation_capacity(reserved_slots, retained_bytes)?;
2902        self.charge_heap(bytes)?;
2903        let index = self.dynamic_base.checked_add(self.dynamic.len()).ok_or(
2904            RuntimeErrorKind::DynamicModuleLimitExceeded {
2905                limit: self.limits.max_dynamic_modules,
2906            },
2907        )?;
2908        let module = ModuleId::new(u32::try_from(index).map_err(|_| {
2909            RuntimeErrorKind::DynamicModuleLimitExceeded {
2910                limit: self.limits.max_dynamic_modules,
2911            }
2912        })?);
2913        self.dynamic.push(DynamicModule { program, bytes });
2914        self.registry.modules.push(ModuleInstance {
2915            binding_cells: Vec::new(),
2916            constant_cells: Vec::new(),
2917            namespace: None,
2918            state: ModuleState::Unevaluated,
2919        });
2920        debug_assert_eq!(
2921            self.dynamic
2922                .last()
2923                .expect("installed script remains retained")
2924                .bytes,
2925            bytes
2926        );
2927        debug_assert_eq!(
2928            self.registry.modules.len(),
2929            self.dynamic_base + self.dynamic.len()
2930        );
2931        Ok(module)
2932    }
2933
2934    fn allocate_cell(&mut self, value: Value, module: ModuleId) -> Result<CellId, RuntimeError> {
2935        if self.registry.cells.len() >= self.limits.max_module_cells {
2936            return Err(self.program_error(
2937                module,
2938                RuntimeErrorKind::ModuleCellLimitExceeded {
2939                    limit: self.limits.max_module_cells,
2940                },
2941            ));
2942        }
2943        let id = CellId(self.registry.cells.len());
2944        self.registry.cells.push(Cell { value });
2945        Ok(id)
2946    }
2947
2948    pub(crate) fn instantiate_modules(&mut self) -> Result<(), RuntimeError> {
2949        debug_assert!(
2950            self.dynamic.is_empty(),
2951            "module instantiation precedes dynamic script installation"
2952        );
2953        let program = self
2954            .program
2955            .expect("module registry operations require a whole program");
2956        self.registry.modules = program
2957            .modules()
2958            .iter()
2959            .map(|module| ModuleInstance {
2960                binding_cells: vec![None; module.bindings.len()],
2961                constant_cells: vec![None; module.code.constants().len()],
2962                namespace: None,
2963                state: ModuleState::Unevaluated,
2964            })
2965            .collect();
2966
2967        for module_index in 0..program.modules().len() {
2968            let module_id = ModuleId::new(module_index as u32);
2969            let bindings = program.modules()[module_index].bindings.clone();
2970            for (binding_index, binding) in bindings.into_iter().enumerate() {
2971                let initial = match binding.kind {
2972                    BindingKind::Hoisted => Some(Value::UNDEFINED),
2973                    BindingKind::Lexical => Some(Value::UNINITIALIZED),
2974                    BindingKind::Imported { .. } | BindingKind::Namespace { .. } => None,
2975                };
2976                if let Some(value) = initial {
2977                    let cell = self.allocate_cell(value, module_id)?;
2978                    self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
2979                }
2980            }
2981        }
2982
2983        for module_index in 0..program.modules().len() {
2984            let module_id = ModuleId::new(module_index as u32);
2985            let bindings = program.modules()[module_index].bindings.clone();
2986            for (binding_index, binding) in bindings.into_iter().enumerate() {
2987                let cell = match binding.kind {
2988                    BindingKind::Hoisted | BindingKind::Lexical => continue,
2989                    BindingKind::Imported { edge, name } => {
2990                        let dependency = program.modules()[module_index].edges[edge.get() as usize];
2991                        match dependency.target {
2992                            EdgeTarget::External => {
2993                                let name = self.constant_text(module_id, name).clone();
2994                                self.external_export_cell(module_id, edge, &name)?
2995                            }
2996                            EdgeTarget::Local(target) => match program
2997                                .resolve_export(target, self.constant_text(module_id, name))
2998                            {
2999                                Some(ResolvedExport::Local { module, binding }) => {
3000                                    self.registry.modules[module.get() as usize].binding_cells
3001                                        [binding.get() as usize]
3002                                        .expect("own cells are allocated before aliases link")
3003                                }
3004                                Some(ResolvedExport::External { module, edge, name }) => {
3005                                    let name = self.constant_text(module, name).clone();
3006                                    self.external_export_cell(module, edge, &name)?
3007                                }
3008                                None => {
3009                                    return Err(self.program_error(
3010                                        module_id,
3011                                        RuntimeErrorKind::InvalidVerifiedProgram {
3012                                            module: module_id,
3013                                            instruction: Instruction::Import {
3014                                                dst: bamts_bytecode::Register::new(0),
3015                                                specifier: name,
3016                                            },
3017                                        },
3018                                    ));
3019                                }
3020                            },
3021                        }
3022                    }
3023                    BindingKind::Namespace { edge } => {
3024                        let dependency = program.modules()[module_index].edges[edge.get() as usize];
3025                        let namespace = match dependency.target {
3026                            EdgeTarget::Local(target) => {
3027                                self.module_namespace(target, module_id)?
3028                            }
3029                            EdgeTarget::External => self.external_namespace(module_id, edge)?,
3030                        };
3031                        self.allocate_cell(namespace, module_id)?
3032                    }
3033                };
3034                self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
3035            }
3036        }
3037
3038        for module_index in 0..program.modules().len() {
3039            let bindings = &program.modules()[module_index].bindings;
3040            let constants = program.modules()[module_index].code.constants();
3041            for (constant_index, constant) in constants.iter().enumerate() {
3042                let Constant::String(name) = constant else {
3043                    continue;
3044                };
3045                if let Some((binding_index, _)) =
3046                    bindings.iter().enumerate().find(|(_, binding)| {
3047                        self.constant_text(ModuleId::new(module_index as u32), binding.name) == name
3048                    })
3049                {
3050                    self.registry.modules[module_index].constant_cells[constant_index] =
3051                        self.registry.modules[module_index].binding_cells[binding_index];
3052                }
3053            }
3054        }
3055        Ok(())
3056    }
3057
3058    fn module_namespace(
3059        &mut self,
3060        target: ModuleId,
3061        requester: ModuleId,
3062    ) -> Result<Value, RuntimeError> {
3063        if let Some(value) = self.registry.modules[target.get() as usize].namespace {
3064            return Ok(value);
3065        }
3066        let exported_names: Vec<EcmaString> = self
3067            .program_module(target)
3068            .exports
3069            .iter()
3070            .map(|export| self.constant_text(target, export.name).clone())
3071            .collect();
3072        for exported_name in exported_names {
3073            if let Some(ResolvedExport::External { module, edge, name }) =
3074                self.program().resolve_export(target, &exported_name)
3075            {
3076                let name = self.constant_text(module, name).clone();
3077                self.external_export_cell(module, edge, &name)?;
3078            }
3079        }
3080        let value = self
3081            .allocate(HeapEntry::ModuleNamespace { module: target })
3082            .map_err(|kind| self.program_error(requester, kind))?;
3083        self.registry.modules[target.get() as usize].namespace = Some(value);
3084        Ok(value)
3085    }
3086
3087    fn external_specifier(&self, module: ModuleId, edge: EdgeId) -> Option<EcmaString> {
3088        let dependency = self.program_module(module).edges[edge.get() as usize];
3089        let specifier = self.constant_text(module, dependency.specifier);
3090        self.registry
3091            .external
3092            .contains_key(specifier)
3093            .then(|| specifier.clone())
3094    }
3095
3096    fn external_namespace(
3097        &mut self,
3098        module: ModuleId,
3099        edge: EdgeId,
3100    ) -> Result<Value, RuntimeError> {
3101        let Some(specifier) = self.external_specifier(module, edge) else {
3102            return Err(self.program_error(
3103                module,
3104                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3105            ));
3106        };
3107        let export_names: Vec<EcmaString> = self.registry.external[&specifier]
3108            .exports
3109            .keys()
3110            .cloned()
3111            .collect();
3112        for name in export_names {
3113            self.external_export_cell(module, edge, &name)?;
3114        }
3115        Ok(self.registry.external[&specifier].namespace)
3116    }
3117
3118    fn external_export_cell(
3119        &mut self,
3120        module: ModuleId,
3121        edge: EdgeId,
3122        name: &EcmaString,
3123    ) -> Result<CellId, RuntimeError> {
3124        let Some(specifier) = self.external_specifier(module, edge) else {
3125            return Err(self.program_error(
3126                module,
3127                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3128            ));
3129        };
3130        let Some(export) = self.registry.external[&specifier]
3131            .exports
3132            .get(name)
3133            .copied()
3134        else {
3135            return Err(self.program_error(
3136                module,
3137                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3138            ));
3139        };
3140        if let Some(cell) = export.cell {
3141            return Ok(cell);
3142        }
3143        let cell = self.allocate_cell(export.value, module)?;
3144        self.registry
3145            .external
3146            .get_mut(&specifier)
3147            .expect("external module remains registered")
3148            .exports
3149            .get_mut(name)
3150            .expect("external export remains registered")
3151            .cell = Some(cell);
3152        Ok(cell)
3153    }
3154
3155    pub(crate) fn resolve_import(
3156        &self,
3157        module: ModuleId,
3158        specifier: ConstantId,
3159    ) -> Result<ImportTarget, RuntimeErrorKind> {
3160        let name = self.constant_text(module, specifier);
3161        self.program_module(module)
3162            .edges
3163            .iter()
3164            .enumerate()
3165            .find(|(_, edge)| {
3166                edge.kind.has_dynamic() && self.constant_text(module, edge.specifier) == name
3167            })
3168            .map(|(index, edge)| match edge.target {
3169                EdgeTarget::Local(target) => ImportTarget::Local(target),
3170                EdgeTarget::External => ImportTarget::External(EdgeId::new(index as u32)),
3171            })
3172            .ok_or(RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier })
3173    }
3174
3175    pub(crate) fn imported_namespace(
3176        &mut self,
3177        requester: ModuleId,
3178        target: ImportTarget,
3179    ) -> Result<Value, RuntimeErrorKind> {
3180        match target {
3181            ImportTarget::Local(target) => self.module_namespace(target, requester),
3182            ImportTarget::External(edge) => self.external_namespace(requester, edge),
3183        }
3184        .map_err(|error| error.kind)
3185    }
3186
3187    fn run_import_entry(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3188        let function = self.module_code(module).entry();
3189        let stop_depth = self.frames.len();
3190        self.push_frame(
3191            RuntimeFunction { module, function },
3192            &[],
3193            Value::UNDEFINED,
3194            Value::UNDEFINED,
3195            &[],
3196            None,
3197        )?;
3198        let result = self.run_loop(stop_depth).and_then(|execution| {
3199            execution.map(|_| ()).ok_or_else(|| {
3200                self.program_error(
3201                    module,
3202                    RuntimeErrorKind::InvalidVerifiedProgram {
3203                        module,
3204                        instruction: Instruction::Halt,
3205                    },
3206                )
3207            })
3208        });
3209        if result.is_err() {
3210            self.unwind_frames_to(stop_depth);
3211        }
3212        result
3213    }
3214
3215    fn evaluate_import(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3216        let dependencies = match self.begin_module_evaluation(module)? {
3217            ModuleEvaluation::Cycle => return Ok(()),
3218            ModuleEvaluation::Evaluated(result) => return result,
3219            ModuleEvaluation::Ready(dependencies) => dependencies,
3220        };
3221        for dependency in dependencies {
3222            if let Err(error) = self.evaluate_import(dependency) {
3223                self.settle_module_evaluation(module, Err(error.clone()));
3224                return Err(error);
3225            }
3226        }
3227        let result = self.run_import_entry(module);
3228        self.settle_module_evaluation(module, result.clone());
3229        result
3230    }
3231
3232    fn import_namespace(
3233        &mut self,
3234        requester: ModuleId,
3235        specifier: ConstantId,
3236    ) -> Result<Value, EvalFailure> {
3237        let target = self
3238            .resolve_import(requester, specifier)
3239            .map_err(EvalFailure::Runtime)?;
3240        if let ImportTarget::Local(module) = target {
3241            self.evaluate_import(module)
3242                .map_err(|error| import_failure(&error))?;
3243        }
3244        self.imported_namespace(requester, target)
3245            .map_err(EvalFailure::Runtime)
3246    }
3247    fn evaluate_module(&mut self, module: ModuleId) -> Result<Option<Execution>, RuntimeError> {
3248        let dependencies = match self.begin_module_evaluation(module)? {
3249            ModuleEvaluation::Cycle => return Ok(None),
3250            ModuleEvaluation::Evaluated(result) => return result.map(|()| None),
3251            ModuleEvaluation::Ready(dependencies) => dependencies,
3252        };
3253        for dependency in dependencies {
3254            if let Err(error) = self.evaluate_module(dependency) {
3255                return self.finish_module_evaluation(module, Err(error)).map(Some);
3256            }
3257        }
3258
3259        let code = self.module_code(module);
3260        let function = code.entry().get() as usize;
3261        let metadata = &code.functions()[function];
3262        let register_count = metadata.register_count() as usize;
3263        let result = if self.limits.max_call_depth < 1 {
3264            Err(self.program_error(
3265                module,
3266                RuntimeErrorKind::CallDepthExceeded {
3267                    limit: self.limits.max_call_depth,
3268                },
3269            ))
3270        } else if register_count > self.limits.max_total_registers {
3271            Err(self.program_error(
3272                module,
3273                RuntimeErrorKind::RegisterLimitExceeded {
3274                    limit: self.limits.max_total_registers,
3275                },
3276            ))
3277        } else {
3278            self.frames.push(Frame::new(
3279                RuntimeFunction {
3280                    module,
3281                    function: FunctionId::new(function as u32),
3282                },
3283                metadata,
3284                &[],
3285                Value::UNDEFINED,
3286                Value::UNDEFINED,
3287                &[],
3288                None,
3289            ));
3290            self.live_registers = register_count;
3291            self.run_loop(0).and_then(|execution| {
3292                execution.ok_or_else(|| {
3293                    self.program_error(
3294                        module,
3295                        RuntimeErrorKind::InvalidVerifiedProgram {
3296                            module,
3297                            instruction: Instruction::Halt,
3298                        },
3299                    )
3300                })
3301            })
3302        };
3303        self.finish_module_evaluation(module, result).map(Some)
3304    }
3305
3306    pub(crate) fn begin_module_evaluation(
3307        &mut self,
3308        module: ModuleId,
3309    ) -> Result<ModuleEvaluation, RuntimeError> {
3310        match self.registry.modules[module.get() as usize].state.clone() {
3311            ModuleState::Evaluating => return Ok(ModuleEvaluation::Cycle),
3312            ModuleState::Evaluated(result) => return Ok(ModuleEvaluation::Evaluated(result)),
3313            ModuleState::Unevaluated => {}
3314        }
3315        self.registry.modules[module.get() as usize].state = ModuleState::Evaluating;
3316
3317        let mut dependencies = Vec::new();
3318        for (edge_index, edge) in self
3319            .program_module(module)
3320            .edges
3321            .iter()
3322            .copied()
3323            .enumerate()
3324        {
3325            if !edge.kind.has_static() {
3326                continue;
3327            }
3328            match edge.target {
3329                EdgeTarget::Local(dependency) => dependencies.push(dependency),
3330                EdgeTarget::External
3331                    if self
3332                        .external_specifier(module, EdgeId::new(edge_index as u32))
3333                        .is_some() => {}
3334                EdgeTarget::External => {
3335                    let error = self.program_error(
3336                        module,
3337                        RuntimeErrorKind::ExternalModuleUnavailable {
3338                            module,
3339                            edge: EdgeId::new(edge_index as u32),
3340                        },
3341                    );
3342                    self.settle_module_evaluation(module, Err(error.clone()));
3343                    return Err(error);
3344                }
3345            }
3346        }
3347        Ok(ModuleEvaluation::Ready(dependencies))
3348    }
3349
3350    pub(crate) fn finish_module_evaluation(
3351        &mut self,
3352        module: ModuleId,
3353        result: Result<Execution, RuntimeError>,
3354    ) -> Result<Execution, RuntimeError> {
3355        if result.is_err() {
3356            self.frames.clear();
3357            self.live_registers = 0;
3358        }
3359        let stored = result.as_ref().map(|_| ()).map_err(Clone::clone);
3360        self.settle_module_evaluation(module, stored);
3361        result
3362    }
3363
3364    pub(crate) fn settle_module_evaluation(
3365        &mut self,
3366        module: ModuleId,
3367        result: Result<(), RuntimeError>,
3368    ) {
3369        match result {
3370            Ok(()) => {
3371                self.registry.modules[module.get() as usize].state = ModuleState::Evaluated(Ok(()));
3372            }
3373            Err(error) if matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }) => {
3374                self.registry.modules[module.get() as usize].state =
3375                    ModuleState::Evaluated(Err(error));
3376            }
3377            Err(_) => self.abort_module_evaluation(module),
3378        }
3379    }
3380
3381    pub(crate) fn abort_module_evaluation(&mut self, module: ModuleId) {
3382        if matches!(
3383            self.registry.modules[module.get() as usize].state,
3384            ModuleState::Evaluating
3385        ) {
3386            self.registry.modules[module.get() as usize].state = ModuleState::Unevaluated;
3387        }
3388    }
3389
3390    pub(crate) fn constant_text(&self, module: ModuleId, id: ConstantId) -> &EcmaString {
3391        match &self.module_code(module).constants()[id.get() as usize] {
3392            Constant::String(text) => text,
3393            _ => unreachable!("verified module names are strings"),
3394        }
3395    }
3396
3397    fn program_error(&self, module: ModuleId, kind: RuntimeErrorKind) -> RuntimeError {
3398        let code = self.module_code(module);
3399        let function = code.entry().get() as usize;
3400        let instruction = code.functions()[function]
3401            .code()
3402            .first()
3403            .copied()
3404            .unwrap_or(Instruction::Halt);
3405        RuntimeError {
3406            kind,
3407            function: FunctionId::new(function as u32),
3408            pc: Pc::new(0),
3409            source: RuntimeSource {
3410                function_name: None,
3411                instruction,
3412            },
3413        }
3414    }
3415
3416    fn run_loop(&mut self, stop_depth: usize) -> Result<Option<Execution>, RuntimeError> {
3417        if self.frames.len().saturating_add(self.native_depth) > self.limits.max_call_depth {
3418            return Err(self.error_here(RuntimeErrorKind::CallDepthExceeded {
3419                limit: self.limits.max_call_depth,
3420            }));
3421        }
3422        if self.live_registers > self.limits.max_total_registers {
3423            return Err(self.error_here(RuntimeErrorKind::RegisterLimitExceeded {
3424                limit: self.limits.max_total_registers,
3425            }));
3426        }
3427
3428        loop {
3429            let frame_index = self.frames.len() - 1;
3430            let (module_id, function_index, pc) = {
3431                let frame = &self.frames[frame_index];
3432                (frame.module, frame.function, frame.pc)
3433            };
3434            if let Err(kind) = self.consume_fuel(1) {
3435                return Err(self.error_at(kind, function_index, pc));
3436            }
3437            let instruction = self.module_code(module_id).functions()[function_index].code()[pc];
3438
3439            match instruction {
3440                Instruction::LoadConst { dst, constant } => {
3441                    let value = self.load_constant(constant, function_index, pc)?;
3442                    self.write_register(frame_index, dst.get(), value);
3443                    self.frames[frame_index].pc = pc + 1;
3444                }
3445                Instruction::Move { dst, src } => {
3446                    let value = self.read_register(frame_index, src.get());
3447                    self.write_register(frame_index, dst.get(), value);
3448                    self.frames[frame_index].pc = pc + 1;
3449                }
3450                Instruction::Unary { dst, op, operand } => {
3451                    let value = self.read_register(frame_index, operand.get());
3452                    match self.eval_unary(op, value) {
3453                        Ok(result) => {
3454                            self.write_register(frame_index, dst.get(), result);
3455                            self.frames[frame_index].pc = pc + 1;
3456                        }
3457                        Err(failure) => self.resolve_failure(failure, pc)?,
3458                    }
3459                }
3460                Instruction::Binary {
3461                    dst,
3462                    op,
3463                    left,
3464                    right,
3465                } => {
3466                    let left = self.read_register(frame_index, left.get());
3467                    let right = self.read_register(frame_index, right.get());
3468                    match self.eval_binary(op, left, right) {
3469                        Ok(result) => {
3470                            self.write_register(frame_index, dst.get(), result);
3471                            self.frames[frame_index].pc = pc + 1;
3472                        }
3473                        Err(failure) => self.resolve_failure(failure, pc)?,
3474                    }
3475                }
3476                Instruction::CreateObject { dst } => {
3477                    let value = self
3478                        .allocate(HeapEntry::Object {
3479                            properties: PropertyMap::default(),
3480                            prototype: Some(self.intrinsics.object_prototype),
3481                            boxed_primitive: None,
3482                            extensible: true,
3483                        })
3484                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3485                    self.write_register(frame_index, dst.get(), value);
3486                    self.frames[frame_index].pc = pc + 1;
3487                }
3488                Instruction::CreateArray { dst } => {
3489                    let value = self
3490                        .allocate(HeapEntry::Array {
3491                            elements: Vec::new(),
3492                            properties: PropertyMap::default(),
3493                            prototype: Some(self.intrinsics.array_prototype),
3494                            extensible: true,
3495                            length_writable: true,
3496                        })
3497                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3498                    self.write_register(frame_index, dst.get(), value);
3499                    self.frames[frame_index].pc = pc + 1;
3500                }
3501                Instruction::CreateCell { dst } => {
3502                    let value = self
3503                        .allocate(HeapEntry::Array {
3504                            elements: vec![Value::UNINITIALIZED],
3505                            properties: PropertyMap::default(),
3506                            prototype: Some(self.intrinsics.array_prototype),
3507                            extensible: true,
3508                            length_writable: true,
3509                        })
3510                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3511                    self.write_register(frame_index, dst.get(), value);
3512                    self.frames[frame_index].pc = pc + 1;
3513                }
3514                Instruction::CreateClosure {
3515                    dst,
3516                    function,
3517                    captures,
3518                } => match self.read_captures(frame_index, captures.get(), function) {
3519                    Ok(captures) => {
3520                        let value = self
3521                            .allocate(HeapEntry::Function {
3522                                module: module_id,
3523                                function,
3524                                captures,
3525                                properties: PropertyMap::default(),
3526                                prototype: Some(self.intrinsics.function_prototype),
3527                                extensible: true,
3528                            })
3529                            .map_err(|kind| self.error_at(kind, function_index, pc))?;
3530                        self.write_register(frame_index, dst.get(), value);
3531                        self.frames[frame_index].pc = pc + 1;
3532                    }
3533                    Err(failure) => self.resolve_failure(failure, pc)?,
3534                },
3535                Instruction::GetProperty { dst, object, key } => {
3536                    let object = self.read_register(frame_index, object.get());
3537                    let key_value = self.read_register(frame_index, key.get());
3538                    let key = match self.to_property_key(key_value) {
3539                        Ok(key) => key,
3540                        Err(failure) => {
3541                            self.resolve_failure(failure, pc)?;
3542                            continue;
3543                        }
3544                    };
3545                    match self.resolve_get(object, &key) {
3546                        Ok(GetOutcome::Value(value)) => {
3547                            self.write_register(frame_index, dst.get(), value);
3548                            self.frames[frame_index].pc = pc + 1;
3549                        }
3550                        Ok(GetOutcome::Text(text)) => {
3551                            let value = self
3552                                .allocate(HeapEntry::String(text))
3553                                .map_err(|kind| self.error_at(kind, function_index, pc))?;
3554                            self.write_register(frame_index, dst.get(), value);
3555                            self.frames[frame_index].pc = pc + 1;
3556                        }
3557                        Ok(GetOutcome::Getter(getter)) => {
3558                            self.frames[frame_index].pc = pc + 1;
3559                            self.execute_call(CallRequest {
3560                                callee: getter,
3561                                this_value: object,
3562                                arguments: &[],
3563                                destination: Some(dst.get()),
3564                                call_pc: pc,
3565                                constructed: None,
3566                                new_target: Value::UNDEFINED,
3567                            })?;
3568                        }
3569                        Err(failure) => self.resolve_failure(failure, pc)?,
3570                    }
3571                }
3572                Instruction::SetProperty { object, key, value } => {
3573                    let object = self.read_register(frame_index, object.get());
3574                    let value = self.read_register(frame_index, value.get());
3575                    let key_value = self.read_register(frame_index, key.get());
3576                    let key = match self.to_property_key(key_value) {
3577                        Ok(key) => key,
3578                        Err(failure) => {
3579                            self.resolve_failure(failure, pc)?;
3580                            continue;
3581                        }
3582                    };
3583                    match self.resolve_set(object, key, value) {
3584                        Ok(SetOutcome::Done) => self.frames[frame_index].pc = pc + 1,
3585                        Ok(SetOutcome::Setter(setter)) => {
3586                            self.frames[frame_index].pc = pc + 1;
3587                            self.execute_call(CallRequest {
3588                                callee: setter,
3589                                this_value: object,
3590                                arguments: &[value],
3591                                destination: None,
3592                                call_pc: pc,
3593                                constructed: None,
3594                                new_target: Value::UNDEFINED,
3595                            })?;
3596                        }
3597                        Err(failure) => self.resolve_failure(failure, pc)?,
3598                    }
3599                }
3600                Instruction::DeleteProperty { dst, object, key } => {
3601                    let object = self.read_register(frame_index, object.get());
3602                    let key_value = self.read_register(frame_index, key.get());
3603                    let key = match self.to_property_key(key_value) {
3604                        Ok(key) => key,
3605                        Err(failure) => {
3606                            self.resolve_failure(failure, pc)?;
3607                            continue;
3608                        }
3609                    };
3610                    match self.delete_property(object, &key) {
3611                        Ok(deleted) => {
3612                            self.write_register(frame_index, dst.get(), Value::boolean(deleted));
3613                            self.frames[frame_index].pc = pc + 1;
3614                        }
3615                        Err(failure) => self.resolve_failure(failure, pc)?,
3616                    }
3617                }
3618                Instruction::DefineAccessor {
3619                    object,
3620                    key,
3621                    accessor,
3622                    kind,
3623                } => {
3624                    let object = self.read_register(frame_index, object.get());
3625                    let accessor = self.read_register(frame_index, accessor.get());
3626                    let key_value = self.read_register(frame_index, key.get());
3627                    let key = match self.to_property_key(key_value) {
3628                        Ok(key) => key,
3629                        Err(failure) => {
3630                            self.resolve_failure(failure, pc)?;
3631                            continue;
3632                        }
3633                    };
3634                    match self.define_accessor(object, key, accessor, kind) {
3635                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3636                        Err(failure) => self.resolve_failure(failure, pc)?,
3637                    }
3638                }
3639                Instruction::Call {
3640                    dst,
3641                    callee,
3642                    this_value,
3643                    arguments,
3644                } => {
3645                    let callee = self.read_register(frame_index, callee.get());
3646                    let this_value = self.read_register(frame_index, this_value.get());
3647                    match self.read_arguments(frame_index, arguments.get()) {
3648                        Ok(arguments) => {
3649                            self.frames[frame_index].pc = pc + 1;
3650                            self.execute_call(CallRequest {
3651                                callee,
3652                                this_value,
3653                                arguments: &arguments,
3654                                destination: Some(dst.get()),
3655                                call_pc: pc,
3656                                constructed: None,
3657                                new_target: Value::UNDEFINED,
3658                            })?;
3659                        }
3660                        Err(failure) => self.resolve_failure(failure, pc)?,
3661                    }
3662                }
3663                Instruction::Construct {
3664                    dst,
3665                    callee,
3666                    arguments,
3667                } => {
3668                    let callee = self.read_register(frame_index, callee.get());
3669                    match self.read_arguments(frame_index, arguments.get()) {
3670                        Ok(arguments) => {
3671                            self.frames[frame_index].pc = pc + 1;
3672                            self.execute_construct(callee, &arguments, dst.get(), pc)?;
3673                        }
3674                        Err(failure) => self.resolve_failure(failure, pc)?,
3675                    }
3676                }
3677                Instruction::LoadGlobal { dst, name } => match self.load_global(module_id, name) {
3678                    Ok(Some(value)) => {
3679                        self.write_register(frame_index, dst.get(), value);
3680                        self.frames[frame_index].pc = pc + 1;
3681                    }
3682                    Ok(None) => self.throw(
3683                        Value::UNDEFINED,
3684                        ThrowOrigin::ReferenceError {
3685                            operation: "global is not defined",
3686                        },
3687                        pc,
3688                    )?,
3689                    Err(kind) => return Err(self.error_here_at(kind, pc)),
3690                },
3691                Instruction::StoreGlobal { name, value } => {
3692                    let value = self.read_register(frame_index, value.get());
3693                    match self.store_global(module_id, name, value) {
3694                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3695                        Err(failure) => self.resolve_failure(failure, pc)?,
3696                    }
3697                }
3698                Instruction::TypeOfGlobal { dst, name } => {
3699                    let text = match self.load_global(module_id, name) {
3700                        Ok(value) => value.map_or("undefined", |value| self.type_of(value)),
3701                        Err(kind) => return Err(self.error_here_at(kind, pc)),
3702                    };
3703                    let value = self
3704                        .allocate(HeapEntry::String(EcmaString::from_utf8(text)))
3705                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3706                    self.write_register(frame_index, dst.get(), value);
3707                    self.frames[frame_index].pc = pc + 1;
3708                }
3709                Instruction::LoadThis { dst } => {
3710                    let value = self.frames[frame_index].this_value;
3711                    self.write_register(frame_index, dst.get(), value);
3712                    self.frames[frame_index].pc = pc + 1;
3713                }
3714                Instruction::LoadArguments { dst } => {
3715                    let value = self.materialize_arguments(frame_index, function_index, pc)?;
3716                    self.write_register(frame_index, dst.get(), value);
3717                    self.frames[frame_index].pc = pc + 1;
3718                }
3719                Instruction::LoadNewTarget { dst } => {
3720                    let value = self.frames[frame_index].new_target;
3721                    self.write_register(frame_index, dst.get(), value);
3722                    self.frames[frame_index].pc = pc + 1;
3723                }
3724                Instruction::ArrayPush { array, value } => {
3725                    let array = self.read_register(frame_index, array.get());
3726                    let value = self.read_register(frame_index, value.get());
3727                    match self.array_push(array, value) {
3728                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3729                        Err(failure) => self.resolve_failure(failure, pc)?,
3730                    }
3731                }
3732                Instruction::ArrayExtend { array, iterable } => {
3733                    let array = self.read_register(frame_index, array.get());
3734                    let iterable = self.read_register(frame_index, iterable.get());
3735                    match self.array_extend(array, iterable) {
3736                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3737                        Err(failure) => self.resolve_failure(failure, pc)?,
3738                    }
3739                }
3740                Instruction::ObjectSpread { target, source } => {
3741                    let target = self.read_register(frame_index, target.get());
3742                    let source = self.read_register(frame_index, source.get());
3743                    match self.object_spread(target, source) {
3744                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3745                        Err(failure) => self.resolve_failure(failure, pc)?,
3746                    }
3747                }
3748                Instruction::SetPrototype { object, prototype } => {
3749                    let object = self.read_register(frame_index, object.get());
3750                    let prototype = self.read_register(frame_index, prototype.get());
3751                    match self.set_prototype(object, prototype) {
3752                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3753                        Err(failure) => self.resolve_failure(failure, pc)?,
3754                    }
3755                }
3756                Instruction::CreatePrivateName { dst, description } => {
3757                    let description = self.constant_string(description).clone();
3758                    let value = self
3759                        .allocate(HeapEntry::PrivateName { description })
3760                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3761                    self.write_register(frame_index, dst.get(), value);
3762                    self.frames[frame_index].pc = pc + 1;
3763                }
3764                Instruction::CreateRegExp {
3765                    dst,
3766                    pattern,
3767                    flags,
3768                } => {
3769                    let pattern = self.constant_string(pattern).clone();
3770                    let flags = self.constant_string(flags).clone();
3771                    let value = self
3772                        .allocate(HeapEntry::RegExp {
3773                            pattern,
3774                            flags,
3775                            properties: PropertyMap::default(),
3776                            prototype: Some(self.intrinsics.regexp_prototype()),
3777                            extensible: true,
3778                        })
3779                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3780                    self.write_register(frame_index, dst.get(), value);
3781                    self.frames[frame_index].pc = pc + 1;
3782                }
3783                Instruction::GetIterator { dst, src, kind } => {
3784                    let src = self.read_register(frame_index, src.get());
3785                    match self.create_iterator(src, kind) {
3786                        Ok(value) => {
3787                            self.write_register(frame_index, dst.get(), value);
3788                            self.frames[frame_index].pc = pc + 1;
3789                        }
3790                        Err(failure) => self.resolve_failure(failure, pc)?,
3791                    }
3792                }
3793                Instruction::IteratorNext {
3794                    done,
3795                    value,
3796                    iterator,
3797                } => {
3798                    let iterator = self.read_register(frame_index, iterator.get());
3799                    match self.iterator_next(iterator) {
3800                        Ok((is_done, produced)) => {
3801                            self.write_register(frame_index, done.get(), Value::boolean(is_done));
3802                            self.write_register(frame_index, value.get(), produced);
3803                            self.frames[frame_index].pc = pc + 1;
3804                        }
3805                        Err(failure) => self.resolve_failure(failure, pc)?,
3806                    }
3807                }
3808                Instruction::Jump { target } => {
3809                    self.frames[frame_index].pc = target.get() as usize;
3810                }
3811                Instruction::JumpIfTrue { condition, target } => {
3812                    let condition = self.read_register(frame_index, condition.get());
3813                    self.frames[frame_index].pc = if self.truthy(condition) {
3814                        target.get() as usize
3815                    } else {
3816                        pc + 1
3817                    };
3818                }
3819                Instruction::JumpIfFalse { condition, target } => {
3820                    let condition = self.read_register(frame_index, condition.get());
3821                    self.frames[frame_index].pc = if self.truthy(condition) {
3822                        pc + 1
3823                    } else {
3824                        target.get() as usize
3825                    };
3826                }
3827                Instruction::Return { value } => {
3828                    let value = self.read_register(frame_index, value.get());
3829                    if let Some(execution) = self.complete_frame(value) {
3830                        return Ok(Some(execution));
3831                    }
3832                    if self.frames.len() == stop_depth {
3833                        return Ok(None);
3834                    }
3835                }
3836                Instruction::Throw { value } => {
3837                    let value = self.read_register(frame_index, value.get());
3838                    self.throw(value, ThrowOrigin::Bytecode, pc)?;
3839                }
3840                Instruction::Suspend { src, .. }
3841                    if self
3842                        .async_boundaries
3843                        .last()
3844                        .is_some_and(|boundary| *boundary == frame_index) =>
3845                {
3846                    let awaited = self.read_register(frame_index, src.get());
3847                    let frame = self.frames.pop().expect("async activation is executing");
3848                    self.pending_async_suspend = Some((
3849                        awaited,
3850                        SuspendedActivation {
3851                            target: RuntimeFunction {
3852                                module: frame.module,
3853                                function: FunctionId::new(frame.function as u32),
3854                            },
3855                            registers: frame.registers,
3856                            this_value: frame.this_value,
3857                            new_target: frame.new_target,
3858                            args: frame.args,
3859                            arguments_object: frame.arguments_object,
3860                            resume_token: pc as u32 + 1,
3861                        },
3862                    ));
3863                    return Ok(None);
3864                }
3865                Instruction::Suspend { src, .. }
3866                    if self
3867                        .generator_boundaries
3868                        .last()
3869                        .is_some_and(|boundary| *boundary == frame_index) =>
3870                {
3871                    let value = self.read_register(frame_index, src.get());
3872                    let frame = self
3873                        .frames
3874                        .pop()
3875                        .expect("generator activation is executing");
3876                    self.pending_generator_resume = Some(GeneratorResume::Yield {
3877                        value,
3878                        activation: SuspendedActivation {
3879                            target: RuntimeFunction {
3880                                module: frame.module,
3881                                function: FunctionId::new(frame.function as u32),
3882                            },
3883                            registers: frame.registers,
3884                            this_value: frame.this_value,
3885                            new_target: frame.new_target,
3886                            args: frame.args,
3887                            arguments_object: frame.arguments_object,
3888                            resume_token: pc as u32 + 1,
3889                        },
3890                    });
3891                    return Ok(None);
3892                }
3893                Instruction::Suspend { .. } => {
3894                    self.throw_type("suspend outside an engine-owned event loop", pc)?;
3895                }
3896                Instruction::Import { dst, specifier } => {
3897                    match self.import_namespace(module_id, specifier) {
3898                        Ok(namespace) => {
3899                            self.write_register(frame_index, dst.get(), namespace);
3900                            self.frames[frame_index].pc = pc + 1;
3901                        }
3902                        Err(failure) => self.resolve_failure(failure, pc)?,
3903                    }
3904                }
3905                Instruction::Export { .. } => {
3906                    return Err(self.error_here_at(
3907                        RuntimeErrorKind::InvalidVerifiedProgram {
3908                            module: module_id,
3909                            instruction,
3910                        },
3911                        pc,
3912                    ));
3913                }
3914                Instruction::Halt => {
3915                    if let Some(execution) = self.complete_frame(Value::UNDEFINED) {
3916                        return Ok(Some(execution));
3917                    }
3918                    if self.frames.len() == stop_depth {
3919                        return Ok(None);
3920                    }
3921                }
3922            }
3923        }
3924    }
3925
3926    fn read_register(&self, frame: usize, register: u32) -> Value {
3927        self.frames[frame].registers[register as usize]
3928    }
3929
3930    fn write_register(&mut self, frame: usize, register: u32, value: Value) {
3931        self.frames[frame].registers[register as usize] = value;
3932    }
3933
3934    fn constant_string(&self, id: ConstantId) -> &EcmaString {
3935        self.constant_text(self.active_module_id(), id)
3936    }
3937
3938    fn load_constant(
3939        &mut self,
3940        id: ConstantId,
3941        function: usize,
3942        pc: usize,
3943    ) -> Result<Value, RuntimeError> {
3944        self.load_constant_value(self.active_module_id(), id)
3945            .map_err(|kind| self.error_at(kind, function, pc))
3946    }
3947
3948    fn allocate(&mut self, entry: HeapEntry) -> Result<Value, RuntimeErrorKind> {
3949        let bytes = entry.initial_bytes();
3950        self.ensure_allocation_capacity(1, bytes)?;
3951        self.heap_bytes += bytes;
3952        let slot = self.heap.len() as u32 + 1;
3953        self.heap.push(entry);
3954        let id = SlotId::from_parts(RUNTIME_HEAP_SEGMENT, slot)
3955            .expect("runtime segment and one-based slot are nonzero");
3956        Ok(Value::heap_ref(id))
3957    }
3958
3959    fn ensure_allocation_capacity(
3960        &self,
3961        additional_slots: usize,
3962        additional_bytes: usize,
3963    ) -> Result<(), RuntimeErrorKind> {
3964        let used_slots = self.heap.len().saturating_sub(self.intrinsic_slots);
3965        let slots_fit_limit = used_slots
3966            .checked_add(additional_slots)
3967            .is_some_and(|total| total <= self.limits.max_heap_slots);
3968        let slots_fit_value = self
3969            .heap
3970            .len()
3971            .checked_add(additional_slots)
3972            .is_some_and(|total| total <= u32::MAX as usize);
3973        if !slots_fit_limit || !slots_fit_value {
3974            return Err(RuntimeErrorKind::HeapSlotLimitExceeded {
3975                limit: self.limits.max_heap_slots,
3976            });
3977        }
3978        let bytes_fit = self
3979            .heap_bytes
3980            .checked_add(additional_bytes)
3981            .is_some_and(|total| total <= self.limits.max_heap_bytes);
3982        if !bytes_fit {
3983            return Err(RuntimeErrorKind::HeapByteLimitExceeded {
3984                limit: self.limits.max_heap_bytes,
3985            });
3986        }
3987        Ok(())
3988    }
3989
3990    fn ensure_object_property_capacity(
3991        &self,
3992        property_bytes: usize,
3993    ) -> Result<(), RuntimeErrorKind> {
3994        let bytes =
3995            property_bytes
3996                .checked_add(1)
3997                .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
3998                    limit: self.limits.max_heap_bytes,
3999                })?;
4000        self.ensure_allocation_capacity(1, bytes)
4001    }
4002    fn charge_heap(&mut self, bytes: usize) -> Result<(), RuntimeErrorKind> {
4003        self.ensure_allocation_capacity(0, bytes)?;
4004        self.heap_bytes += bytes;
4005        Ok(())
4006    }
4007
4008    fn runtime_slot(&self, value: Value) -> Result<Option<usize>, RuntimeErrorKind> {
4009        let Some(decoded) = value.decode() else {
4010            return Err(RuntimeErrorKind::InvalidValue { value });
4011        };
4012        let Decoded::HeapRef(id) = decoded else {
4013            return Ok(None);
4014        };
4015        if id.segment() != RUNTIME_HEAP_SEGMENT {
4016            return Err(RuntimeErrorKind::InvalidValue { value });
4017        }
4018        let index = id.slot() as usize - 1;
4019        if index >= self.heap.len() {
4020            return Err(RuntimeErrorKind::InvalidRuntimeHeapReference { slot: id.slot() });
4021        }
4022        Ok(Some(index))
4023    }
4024
4025    fn active_module_id(&self) -> ModuleId {
4026        self.frames
4027            .last()
4028            .map_or(ModuleId::new(0), |frame| frame.module)
4029    }
4030
4031    pub(crate) fn load_global(
4032        &self,
4033        module: ModuleId,
4034        name: ConstantId,
4035    ) -> Result<Option<Value>, RuntimeErrorKind> {
4036        if let Some(cell) = self
4037            .registry
4038            .modules
4039            .get(module.get() as usize)
4040            .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4041            .copied()
4042            .flatten()
4043        {
4044            let value = self.registry.cells[cell.0].value;
4045            if value.is_uninitialized() {
4046                let binding = self.registry.modules[module.get() as usize]
4047                    .binding_cells
4048                    .iter()
4049                    .position(|candidate| *candidate == Some(cell))
4050                    .map(|index| BindingId::new(index as u32))
4051                    .expect("linked cell belongs to a binding");
4052                return Err(RuntimeErrorKind::TemporalDeadZone { module, binding });
4053            }
4054            return Ok(Some(value));
4055        }
4056        Ok(self.resolve_global_binding(self.constant_text(module, name)))
4057    }
4058
4059    pub(crate) fn store_global(
4060        &mut self,
4061        module: ModuleId,
4062        name: ConstantId,
4063        value: Value,
4064    ) -> Result<(), EvalFailure> {
4065        let cell = self
4066            .registry
4067            .modules
4068            .get(module.get() as usize)
4069            .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4070            .copied()
4071            .flatten();
4072        if let Some(cell) = cell {
4073            let binding = self.registry.modules[module.get() as usize]
4074                .binding_cells
4075                .iter()
4076                .position(|candidate| *candidate == Some(cell))
4077                .expect("mapped module cell belongs to a binding");
4078            if matches!(
4079                self.program_module(module).bindings[binding].kind,
4080                BindingKind::Imported { .. } | BindingKind::Namespace { .. }
4081            ) {
4082                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4083                    operation: "assign to immutable module binding",
4084                }));
4085            }
4086            self.registry.cells[cell.0].value = value;
4087        } else {
4088            let name = self.constant_text(module, name).to_owned();
4089            if let Some(global_this) = self.intrinsics.global("globalThis") {
4090                let key = PropertyKey::Named(name.clone());
4091                if matches!(
4092                    self.own_descriptor(global_this, &key)?,
4093                    Some(
4094                        Property::Data {
4095                            writable: false,
4096                            ..
4097                        } | Property::Accessor { setter: None, .. }
4098                    )
4099                ) {
4100                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4101                        operation: "assign to non-writable global property",
4102                    }));
4103                }
4104            }
4105            self.globals.insert(name, value);
4106        }
4107        Ok(())
4108    }
4109
4110    /// Resolves a true realm global after module bindings have been considered.
4111    fn resolve_global_binding(&self, name: &EcmaString) -> Option<Value> {
4112        self.globals.get(name).copied().or_else(|| {
4113            self.intrinsics
4114                .globals
4115                .iter()
4116                .find_map(|(candidate, value)| (candidate == name).then_some(*value))
4117        })
4118    }
4119
4120    /// Classifies a callee into the shared dispatch categories.
4121    fn callee_kind(&self, callee: Value) -> Result<CalleeKind, RuntimeErrorKind> {
4122        match self.runtime_slot(callee)? {
4123            Some(index) => match &self.heap[index] {
4124                HeapEntry::Function {
4125                    module,
4126                    function,
4127                    captures,
4128                    ..
4129                } => Ok(CalleeKind::Runtime {
4130                    target: RuntimeFunction {
4131                        module: *module,
4132                        function: *function,
4133                    },
4134                    captures: captures.clone(),
4135                }),
4136                HeapEntry::NativeFunction { callable, .. } => match callable {
4137                    NativeCallable::Builtin(id) => Ok(CalleeKind::Builtin { id: *id }),
4138                    NativeCallable::Bound(_) => Ok(CalleeKind::Bound),
4139                },
4140                _ => Ok(CalleeKind::NotCallable),
4141            },
4142            None => Ok(CalleeKind::NotCallable),
4143        }
4144    }
4145
4146    pub(crate) fn flatten_bound(
4147        &self,
4148        callee: Value,
4149        this_value: Value,
4150        arguments: &[Value],
4151    ) -> Result<BoundCall, RuntimeErrorKind> {
4152        let mut target = callee;
4153        let mut receiver = this_value;
4154        let mut segments = Vec::new();
4155        let mut total = arguments.len();
4156        while let Some(index) = self.runtime_slot(target)? {
4157            let HeapEntry::NativeFunction {
4158                callable: NativeCallable::Bound(bound),
4159                ..
4160            } = &self.heap[index]
4161            else {
4162                break;
4163            };
4164            total = total.checked_add(bound.arguments.len()).ok_or(
4165                RuntimeErrorKind::ArgumentLimitExceeded {
4166                    limit: self.limits.max_argument_count,
4167                    requested: u32::MAX,
4168                },
4169            )?;
4170            if total > self.limits.max_argument_count as usize {
4171                return Err(RuntimeErrorKind::ArgumentLimitExceeded {
4172                    limit: self.limits.max_argument_count,
4173                    requested: u32::try_from(total).unwrap_or(u32::MAX),
4174                });
4175            }
4176            segments.push(bound.arguments.as_slice());
4177            receiver = bound.this_value;
4178            target = bound.target;
4179        }
4180        let mut flattened = Vec::with_capacity(total);
4181        for segment in segments.iter().rev() {
4182            flattened.extend_from_slice(segment);
4183        }
4184        flattened.extend_from_slice(arguments);
4185        Ok(BoundCall {
4186            target,
4187            this_value: receiver,
4188            arguments: flattened,
4189        })
4190    }
4191
4192    fn bound_target(&self, mut value: Value) -> Result<Value, RuntimeErrorKind> {
4193        loop {
4194            let Some(index) = self.runtime_slot(value)? else {
4195                return Ok(value);
4196            };
4197            let HeapEntry::NativeFunction {
4198                callable: NativeCallable::Bound(bound),
4199                ..
4200            } = &self.heap[index]
4201            else {
4202                return Ok(value);
4203            };
4204            value = bound.target;
4205        }
4206    }
4207
4208    /// Materializes a constant into an ABI value, interning strings and bigints
4209    /// into the slot heap. Shared with the native engine.
4210    pub(crate) fn load_constant_value(
4211        &mut self,
4212        module: ModuleId,
4213        id: ConstantId,
4214    ) -> Result<Value, RuntimeErrorKind> {
4215        match &self.module_code(module).constants()[id.get() as usize] {
4216            Constant::String(text) => self.allocate(HeapEntry::String(text.clone())),
4217            Constant::BigInt(value) => self.allocate(HeapEntry::BigInt(value.as_str().to_owned())),
4218            constant => Ok(constant_value(constant).expect("non-heap constant")),
4219        }
4220    }
4221
4222    /// Reads a call/construct arguments array from a register: it must hold a
4223    /// runtime array, whose length is capped by `max_argument_count`.
4224    fn read_arguments(&self, frame: usize, register: u32) -> Result<Vec<Value>, EvalFailure> {
4225        let value = self.read_register(frame, register);
4226        self.arguments_from_array(value)
4227    }
4228
4229    /// Validates a call/construct arguments array value: it must be a runtime
4230    /// array whose length is capped by `max_argument_count`, with holes read as
4231    /// `undefined`. Shared with the native engine.
4232    fn arguments_from_array(&self, arguments: Value) -> Result<Vec<Value>, EvalFailure> {
4233        match self.runtime_slot(arguments).map_err(EvalFailure::Runtime)? {
4234            Some(index) => match &self.heap[index] {
4235                HeapEntry::Array { elements, .. } => {
4236                    if elements.len() as u64 > u64::from(self.limits.max_argument_count) {
4237                        return Err(EvalFailure::Runtime(
4238                            RuntimeErrorKind::ArgumentLimitExceeded {
4239                                limit: self.limits.max_argument_count,
4240                                requested: u32::try_from(elements.len()).unwrap_or(u32::MAX),
4241                            },
4242                        ));
4243                    }
4244                    Ok(elements
4245                        .iter()
4246                        .map(|value| {
4247                            if *value == Value::HOLE {
4248                                Value::UNDEFINED
4249                            } else {
4250                                *value
4251                            }
4252                        })
4253                        .collect())
4254                }
4255                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4256                    operation: "call arguments are not an array",
4257                })),
4258            },
4259            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4260                operation: "call arguments are not an array",
4261            })),
4262        }
4263    }
4264
4265    /// Reads a `CreateClosure` captures array: it must hold a runtime array whose
4266    /// length matches the target function's capture count.
4267    fn read_captures(
4268        &self,
4269        frame: usize,
4270        register: u32,
4271        function: FunctionId,
4272    ) -> Result<Vec<Value>, EvalFailure> {
4273        let value = self.read_register(frame, register);
4274        self.captures_from_array(self.active_module_id(), value, function)
4275    }
4276
4277    /// Validates a `CreateClosure` captures array value: it must be a runtime
4278    /// array whose length matches the target function's capture count, with
4279    /// holes read as `undefined`. Shared with the native engine.
4280    pub(crate) fn captures_from_array(
4281        &self,
4282        module: ModuleId,
4283        captures: Value,
4284        function: FunctionId,
4285    ) -> Result<Vec<Value>, EvalFailure> {
4286        let expected =
4287            self.module_code(module).functions()[function.get() as usize].capture_count() as usize;
4288        match self.runtime_slot(captures).map_err(EvalFailure::Runtime)? {
4289            Some(index) => match &self.heap[index] {
4290                HeapEntry::Array { elements, .. } => {
4291                    if elements.len() != expected {
4292                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4293                            operation: "closure capture array arity",
4294                        }));
4295                    }
4296                    Ok(elements
4297                        .iter()
4298                        .map(|value| {
4299                            if *value == Value::HOLE {
4300                                Value::UNDEFINED
4301                            } else {
4302                                *value
4303                            }
4304                        })
4305                        .collect())
4306                }
4307                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4308                    operation: "closure captures are not an array",
4309                })),
4310            },
4311            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4312                operation: "closure captures are not an array",
4313            })),
4314        }
4315    }
4316
4317    pub(crate) fn materialize_arguments(
4318        &mut self,
4319        frame: usize,
4320        function: usize,
4321        pc: usize,
4322    ) -> Result<Value, RuntimeError> {
4323        if let Some(existing) = self.frames[frame].arguments_object {
4324            return Ok(existing);
4325        }
4326        let args = self.frames[frame].args.clone();
4327        let value = self
4328            .allocate(HeapEntry::Array {
4329                elements: args,
4330                properties: PropertyMap::default(),
4331                prototype: Some(self.intrinsics.array_prototype),
4332                extensible: true,
4333                length_writable: true,
4334            })
4335            .map_err(|kind| self.error_at(kind, function, pc))?;
4336        self.frames[frame].arguments_object = Some(value);
4337        Ok(value)
4338    }
4339
4340    fn push_frame(
4341        &mut self,
4342        target: RuntimeFunction,
4343        captures: &[Value],
4344        this_value: Value,
4345        new_target: Value,
4346        arguments: &[Value],
4347        return_to: Option<ReturnTo>,
4348    ) -> Result<(), RuntimeError> {
4349        let function_index = target.function.get() as usize;
4350        let metadata = &self.module_code(target.module).functions()[function_index];
4351        let limit_error = |kind| match (self.frames.last(), return_to) {
4352            (Some(caller), Some(return_to)) => {
4353                self.error_at_in_module(kind, caller.module, caller.function, return_to.call_pc)
4354            }
4355            (_, None) => self.error_at_in_module(kind, target.module, function_index, 0),
4356            (None, Some(_)) => unreachable!("a returning frame has a caller"),
4357        };
4358        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4359            return Err(limit_error(RuntimeErrorKind::CallDepthExceeded {
4360                limit: self.limits.max_call_depth,
4361            }));
4362        }
4363        let next_registers = metadata.register_count() as usize;
4364        if self.live_registers.saturating_add(next_registers) > self.limits.max_total_registers {
4365            return Err(limit_error(RuntimeErrorKind::RegisterLimitExceeded {
4366                limit: self.limits.max_total_registers,
4367            }));
4368        }
4369        let frame = Frame::new(
4370            target, metadata, captures, this_value, new_target, arguments, return_to,
4371        );
4372        self.live_registers += next_registers;
4373        self.frames.push(frame);
4374        Ok(())
4375    }
4376
4377    pub(crate) fn consume_fuel(&mut self, amount: u64) -> Result<(), RuntimeErrorKind> {
4378        if self.fuel < amount {
4379            self.fuel = 0;
4380            return Err(RuntimeErrorKind::FuelExhausted {
4381                limit: self.limits.fuel,
4382            });
4383        }
4384        self.fuel -= amount;
4385        Ok(())
4386    }
4387
4388    pub(crate) fn reserve_native_activation(
4389        &mut self,
4390        register_count: usize,
4391    ) -> Result<(), RuntimeErrorKind> {
4392        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4393            return Err(RuntimeErrorKind::CallDepthExceeded {
4394                limit: self.limits.max_call_depth,
4395            });
4396        }
4397        if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4398            return Err(RuntimeErrorKind::RegisterLimitExceeded {
4399                limit: self.limits.max_total_registers,
4400            });
4401        }
4402        self.native_depth += 1;
4403        self.live_registers += register_count;
4404        Ok(())
4405    }
4406
4407    pub(crate) fn release_native_activation(&mut self, register_count: usize) {
4408        self.native_depth -= 1;
4409        self.live_registers -= register_count;
4410    }
4411
4412    pub(crate) fn reserve_suspended_activation_registers(
4413        &mut self,
4414        register_count: usize,
4415    ) -> Result<(), RuntimeErrorKind> {
4416        if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4417            return Err(RuntimeErrorKind::RegisterLimitExceeded {
4418                limit: self.limits.max_total_registers,
4419            });
4420        }
4421        self.live_registers += register_count;
4422        Ok(())
4423    }
4424
4425    pub(crate) fn release_suspended_activation_registers(&mut self, register_count: usize) {
4426        self.live_registers -= register_count;
4427    }
4428
4429    pub(crate) fn enter_native_generator(&mut self) -> Result<(), RuntimeErrorKind> {
4430        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4431            return Err(RuntimeErrorKind::CallDepthExceeded {
4432                limit: self.limits.max_call_depth,
4433            });
4434        }
4435        self.native_depth += 1;
4436        Ok(())
4437    }
4438
4439    pub(crate) fn leave_native_generator(&mut self) {
4440        self.native_depth -= 1;
4441    }
4442
4443    fn execute_call(&mut self, request: CallRequest<'_>) -> Result<(), RuntimeError> {
4444        let CallRequest {
4445            callee,
4446            this_value,
4447            arguments,
4448            destination,
4449            call_pc,
4450            constructed,
4451            new_target,
4452        } = request;
4453        let mut callee = callee;
4454        let mut this_value = this_value;
4455        let mut arguments = Cow::Borrowed(arguments);
4456        loop {
4457            match self.callee_kind(callee) {
4458                Ok(CalleeKind::Runtime { target, captures }) => {
4459                    let flags = self.module_code(target.module).functions()
4460                        [target.function.get() as usize]
4461                        .flags();
4462                    if flags.is_generator && !flags.is_async {
4463                        let generator = self
4464                            .create_generator(GeneratorStart {
4465                                target,
4466                                captures,
4467                                this_value,
4468                                new_target,
4469                                args: arguments.as_ref().to_vec(),
4470                            })
4471                            .map_err(|kind| self.error_here_at(kind, call_pc))?;
4472                        if let Some(register) = destination {
4473                            self.write_register(self.frames.len() - 1, register, generator);
4474                        }
4475                        return Ok(());
4476                    }
4477                    if flags.is_async && !flags.is_generator {
4478                        return match self.start_async_call(
4479                            target,
4480                            &captures,
4481                            this_value,
4482                            new_target,
4483                            arguments.as_ref(),
4484                        ) {
4485                            Ok(promise) => {
4486                                if let Some(register) = destination {
4487                                    self.write_register(self.frames.len() - 1, register, promise);
4488                                }
4489                                Ok(())
4490                            }
4491                            Err(failure) => self.resolve_failure(failure, call_pc),
4492                        };
4493                    }
4494                    return self.push_frame(
4495                        target,
4496                        &captures,
4497                        this_value,
4498                        new_target,
4499                        arguments.as_ref(),
4500                        Some(ReturnTo {
4501                            destination: destination.map(|register| register as usize),
4502                            call_pc,
4503                            constructed,
4504                        }),
4505                    );
4506                }
4507                Ok(CalleeKind::Builtin { id }) => {
4508                    match self.call_builtin(id, this_value, arguments.as_ref(), false) {
4509                        Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4510                            if let Some(register) = destination {
4511                                self.write_register(self.frames.len() - 1, register, value);
4512                            }
4513                            return Ok(());
4514                        }
4515                        Ok(intrinsics::BuiltinOutcome::Call {
4516                            callee: next,
4517                            this_value: next_this,
4518                            arguments: next_arguments,
4519                        }) => {
4520                            callee = next;
4521                            this_value = next_this;
4522                            arguments = Cow::Owned(next_arguments);
4523                        }
4524                        Ok(intrinsics::BuiltinOutcome::GeneratorNext {
4525                            generator,
4526                            resume_value,
4527                        }) => match self.resume_generator(generator, resume_value) {
4528                            Ok(value) => {
4529                                if let Some(register) = destination {
4530                                    self.write_register(self.frames.len() - 1, register, value);
4531                                }
4532                                return Ok(());
4533                            }
4534                            Err(failure) => return self.resolve_failure(failure, call_pc),
4535                        },
4536                        Ok(intrinsics::BuiltinOutcome::ConstructCall { .. }) => {
4537                            return self.throw_type("call", call_pc);
4538                        }
4539                        Err(failure) => return self.resolve_failure(failure, call_pc),
4540                    }
4541                }
4542                Ok(CalleeKind::Bound) => {
4543                    let bound = self
4544                        .flatten_bound(callee, this_value, arguments.as_ref())
4545                        .map_err(|kind| self.error_here_at(kind, call_pc))?;
4546                    callee = bound.target;
4547                    if constructed.is_none() {
4548                        this_value = bound.this_value;
4549                    }
4550                    arguments = Cow::Owned(bound.arguments);
4551                }
4552                Ok(CalleeKind::NotCallable) => return self.throw_type("call", call_pc),
4553                Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4554            }
4555        }
4556    }
4557
4558    fn execute_construct(
4559        &mut self,
4560        callee: Value,
4561        arguments: &[Value],
4562        destination: u32,
4563        call_pc: usize,
4564    ) -> Result<(), RuntimeError> {
4565        let mut callee = callee;
4566        let mut arguments = Cow::Borrowed(arguments);
4567        if matches!(self.callee_kind(callee), Ok(CalleeKind::Bound)) {
4568            let bound = self
4569                .flatten_bound(callee, Value::UNDEFINED, arguments.as_ref())
4570                .map_err(|kind| self.error_here_at(kind, call_pc))?;
4571            callee = bound.target;
4572            arguments = Cow::Owned(bound.arguments);
4573        }
4574        let index = match self.runtime_slot(callee) {
4575            Ok(Some(index)) => index,
4576            Ok(None) => return self.throw_type("construct", call_pc),
4577            Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4578        };
4579        let builtin = match &self.heap[index] {
4580            HeapEntry::NativeFunction {
4581                callable: NativeCallable::Builtin(id),
4582                ..
4583            } => Some(*id),
4584            _ => None,
4585        };
4586        if let Some(id) = builtin {
4587            return match self.call_builtin(id, Value::UNDEFINED, arguments.as_ref(), true) {
4588                Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4589                    self.write_register(self.frames.len() - 1, destination, value);
4590                    Ok(())
4591                }
4592                Ok(
4593                    intrinsics::BuiltinOutcome::Call { .. }
4594                    | intrinsics::BuiltinOutcome::GeneratorNext { .. },
4595                ) => self.throw_type("construct", call_pc),
4596                Ok(intrinsics::BuiltinOutcome::ConstructCall {
4597                    callee: continuation,
4598                    this_value,
4599                    arguments: continuation_arguments,
4600                    prototype,
4601                }) => {
4602                    let object = self
4603                        .allocate_constructed_receiver_with(prototype)
4604                        .map_err(|kind| self.error_here_at(kind, call_pc))?;
4605                    self.execute_call(CallRequest {
4606                        callee: continuation,
4607                        this_value,
4608                        arguments: &continuation_arguments,
4609                        destination: Some(destination),
4610                        call_pc,
4611                        constructed: Some(object),
4612                        new_target: callee,
4613                    })
4614                }
4615                Err(failure) => self.resolve_failure(failure, call_pc),
4616            };
4617        }
4618        if !matches!(
4619            self.heap[index],
4620            HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
4621        ) {
4622            return self.throw_type("construct", call_pc);
4623        }
4624        if let HeapEntry::Function {
4625            module, function, ..
4626        } = self.heap[index]
4627        {
4628            if self.module_code(module).functions()[function.get() as usize]
4629                .flags()
4630                .is_async
4631            {
4632                return self.throw_type("construct", call_pc);
4633            }
4634        }
4635        let object = self
4636            .allocate_constructed_receiver(callee)
4637            .map_err(|kind| self.error_here_at(kind, call_pc))?;
4638        self.execute_call(CallRequest {
4639            callee,
4640            this_value: object,
4641            arguments: arguments.as_ref(),
4642            destination: Some(destination),
4643            call_pc,
4644            constructed: Some(object),
4645            new_target: callee,
4646        })
4647    }
4648
4649    fn constructed_prototype(&self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4650        let index = self
4651            .runtime_slot(callee)?
4652            .ok_or(RuntimeErrorKind::InvalidValue { value: callee })?;
4653        Ok(match self.own_data_property(index, "prototype") {
4654            Some(value) if self.is_object(value) => value,
4655            _ => self.intrinsics.object_prototype,
4656        })
4657    }
4658
4659    fn allocate_constructed_receiver(&mut self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4660        let prototype = self.constructed_prototype(callee)?;
4661        self.allocate_constructed_receiver_with(prototype)
4662    }
4663
4664    fn allocate_constructed_receiver_with(
4665        &mut self,
4666        prototype: Value,
4667    ) -> Result<Value, RuntimeErrorKind> {
4668        self.allocate(HeapEntry::Object {
4669            properties: PropertyMap::default(),
4670            prototype: Some(prototype),
4671            boxed_primitive: None,
4672            extensible: true,
4673        })
4674    }
4675
4676    pub(crate) fn array_elements(&self, value: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
4677        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4678            return Ok(None);
4679        };
4680        match &self.heap[index] {
4681            HeapEntry::Array { elements, .. } => Ok(Some(elements.clone())),
4682            _ => Ok(None),
4683        }
4684    }
4685
4686    pub(crate) fn array_length(&self, value: Value) -> Result<usize, EvalFailure> {
4687        self.array_elements(value)?
4688            .map(|elements| elements.len())
4689            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
4690                operation: "array method called on incompatible receiver",
4691            }))
4692    }
4693
4694    pub(crate) fn replace_array_elements(
4695        &mut self,
4696        value: Value,
4697        elements: Vec<Value>,
4698    ) -> Result<(), EvalFailure> {
4699        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4700            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4701                operation: "array method called on incompatible receiver",
4702            }));
4703        };
4704        let HeapEntry::Array {
4705            elements: current, ..
4706        } = &mut self.heap[index]
4707        else {
4708            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4709                operation: "array method called on incompatible receiver",
4710            }));
4711        };
4712        *current = elements;
4713        Ok(())
4714    }
4715
4716    pub(crate) fn string_value(&self, value: Value) -> Option<EcmaString> {
4717        let index = self.runtime_slot(value).ok().flatten()?;
4718        match &self.heap[index] {
4719            HeapEntry::String(text) => Some(text.clone()),
4720            _ => None,
4721        }
4722    }
4723
4724    pub(crate) fn get_named_property(
4725        &mut self,
4726        object: Value,
4727        name: &str,
4728    ) -> Result<Value, EvalFailure> {
4729        self.get_property_ascii(object, name)
4730    }
4731
4732    fn get_property_ascii(&mut self, object: Value, name: &str) -> Result<Value, EvalFailure> {
4733        debug_assert!(name.is_ascii());
4734        match self.resolve_get_ascii(object, name)? {
4735            GetOutcome::Value(value) => Ok(value),
4736            GetOutcome::Text(text) => self
4737                .allocate(HeapEntry::String(text))
4738                .map_err(EvalFailure::Runtime),
4739            GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4740        }
4741    }
4742
4743    pub(crate) fn get_property_key(
4744        &mut self,
4745        object: Value,
4746        key: &PropertyKey,
4747    ) -> Result<Value, EvalFailure> {
4748        match self.resolve_get(object, key)? {
4749            GetOutcome::Value(value) => Ok(value),
4750            GetOutcome::Text(text) => self
4751                .allocate(HeapEntry::String(text))
4752                .map_err(EvalFailure::Runtime),
4753            GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4754        }
4755    }
4756
4757    pub(crate) fn set_data_property(
4758        &mut self,
4759        object: Value,
4760        name: &str,
4761        value: Value,
4762    ) -> Result<(), EvalFailure> {
4763        self.set_data_property_key(
4764            object,
4765            PropertyKey::Named(EcmaString::from_utf8(name)),
4766            value,
4767        )
4768    }
4769
4770    pub(crate) fn set_data_property_key(
4771        &mut self,
4772        object: Value,
4773        key: PropertyKey,
4774        value: Value,
4775    ) -> Result<(), EvalFailure> {
4776        match self.resolve_set(object, key, value)? {
4777            SetOutcome::Done => Ok(()),
4778            SetOutcome::Setter(setter) => {
4779                self.call_value(setter, object, &[value])?;
4780                Ok(())
4781            }
4782        }
4783    }
4784
4785    pub(crate) fn is_callable(&self, value: Value) -> Result<bool, EvalFailure> {
4786        Ok(!matches!(
4787            self.callee_kind(value).map_err(EvalFailure::Runtime)?,
4788            CalleeKind::NotCallable
4789        ))
4790    }
4791
4792    pub(crate) fn box_primitive(&mut self, value: Value) -> Result<Value, EvalFailure> {
4793        let prototype = match value.decode() {
4794            Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
4795            Some(Decoded::Number(_) | Decoded::Int32(_)) => self.intrinsics.number_prototype,
4796            Some(Decoded::HeapRef(_)) if self.string_value(value).is_some() => {
4797                self.intrinsics.string_prototype
4798            }
4799            _ => self.intrinsics.object_prototype,
4800        };
4801        self.allocate(HeapEntry::Object {
4802            properties: PropertyMap::default(),
4803            prototype: Some(prototype),
4804            boxed_primitive: Some(value),
4805            extensible: true,
4806        })
4807        .map_err(EvalFailure::Runtime)
4808    }
4809
4810    pub(crate) fn unbox_primitive_or_self(&self, value: Value) -> Result<Value, EvalFailure> {
4811        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4812            return Ok(value);
4813        };
4814        match self.heap[index] {
4815            HeapEntry::Object {
4816                boxed_primitive: Some(primitive),
4817                ..
4818            } => Ok(primitive),
4819            _ => Ok(value),
4820        }
4821    }
4822
4823    pub(crate) fn unbox_primitive(
4824        &self,
4825        value: Value,
4826        operation: &'static str,
4827    ) -> Result<Value, EvalFailure> {
4828        let unboxed = self.unbox_primitive_or_self(value)?;
4829        if unboxed == value && self.is_object(value) {
4830            Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }))
4831        } else {
4832            Ok(unboxed)
4833        }
4834    }
4835
4836    pub(crate) fn current_builtin_id(&self) -> Option<intrinsics::BuiltinId> {
4837        self.current_builtin_id
4838    }
4839
4840    pub(crate) fn throw_error(
4841        &mut self,
4842        id: intrinsics::BuiltinId,
4843        message: String,
4844    ) -> EvalFailure {
4845        let message = match self.allocate(HeapEntry::String(EcmaString::from_utf8(&message))) {
4846            Ok(value) => value,
4847            Err(kind) => return EvalFailure::Runtime(kind),
4848        };
4849        let mut properties = PropertyMap::default();
4850        properties.insert(
4851            PropertyKey::Named(EcmaString::from_utf8("message")),
4852            Property::Data {
4853                value: message,
4854                writable: true,
4855                enumerable: true,
4856                configurable: true,
4857            },
4858        );
4859        match self.allocate(HeapEntry::Object {
4860            properties,
4861            prototype: Some(self.intrinsics.error_prototype(id)),
4862            boxed_primitive: None,
4863            extensible: true,
4864        }) {
4865            Ok(value) => EvalFailure::ThrowValue(value),
4866            Err(kind) => EvalFailure::Runtime(kind),
4867        }
4868    }
4869
4870    pub(crate) fn has_own_property_key(
4871        &self,
4872        object: Value,
4873        key: &PropertyKey,
4874    ) -> Result<bool, EvalFailure> {
4875        let Some(index) = self.runtime_slot(object).map_err(EvalFailure::Runtime)? else {
4876            return Ok(false);
4877        };
4878        Ok(self.own_get(index, key).is_some())
4879    }
4880
4881    pub(crate) fn call_value(
4882        &mut self,
4883        callee: Value,
4884        this_value: Value,
4885        arguments: &[Value],
4886    ) -> Result<Value, EvalFailure> {
4887        let mut callee = callee;
4888        let mut this_value = this_value;
4889        let mut arguments = Cow::Borrowed(arguments);
4890        loop {
4891            match self.callee_kind(callee).map_err(EvalFailure::Runtime)? {
4892                CalleeKind::Builtin { id } => {
4893                    match self.call_builtin(id, this_value, arguments.as_ref(), false)? {
4894                        intrinsics::BuiltinOutcome::Value(value) => return Ok(value),
4895                        intrinsics::BuiltinOutcome::Call {
4896                            callee: next,
4897                            this_value: next_this,
4898                            arguments: next_arguments,
4899                        } => {
4900                            callee = next;
4901                            this_value = next_this;
4902                            arguments = Cow::Owned(next_arguments);
4903                        }
4904                        intrinsics::BuiltinOutcome::GeneratorNext {
4905                            generator,
4906                            resume_value,
4907                        } => return self.resume_generator(generator, resume_value),
4908                        intrinsics::BuiltinOutcome::ConstructCall { .. } => {
4909                            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4910                                operation: "call",
4911                            }));
4912                        }
4913                    }
4914                }
4915                CalleeKind::Runtime { target, captures } => {
4916                    let flags = self.module_code(target.module).functions()
4917                        [target.function.get() as usize]
4918                        .flags();
4919                    if flags.is_generator && !flags.is_async {
4920                        return self
4921                            .create_generator(GeneratorStart {
4922                                target,
4923                                captures,
4924                                this_value,
4925                                new_target: Value::UNDEFINED,
4926                                args: arguments.as_ref().to_vec(),
4927                            })
4928                            .map_err(EvalFailure::Runtime);
4929                    }
4930                    if flags.is_async && !flags.is_generator {
4931                        return self.start_async_call(
4932                            target,
4933                            &captures,
4934                            this_value,
4935                            Value::UNDEFINED,
4936                            arguments.as_ref(),
4937                        );
4938                    }
4939                    let stop_depth = self.frames.len();
4940                    let return_to = self.frames.last().map(|frame| ReturnTo {
4941                        destination: None,
4942                        call_pc: frame.pc,
4943                        constructed: None,
4944                    });
4945                    self.push_frame(
4946                        target,
4947                        &captures,
4948                        this_value,
4949                        Value::UNDEFINED,
4950                        arguments.as_ref(),
4951                        return_to,
4952                    )
4953                    .map_err(|error| EvalFailure::Runtime(error.kind))?;
4954                    self.callback_boundaries.push(stop_depth);
4955                    let result = self.run_loop(stop_depth);
4956                    self.callback_boundaries
4957                        .pop()
4958                        .expect("nested runtime callback owns its unwind boundary");
4959                    return match result {
4960                        Ok(None) => self.last_completion.take().ok_or(EvalFailure::Runtime(
4961                            RuntimeErrorKind::InvalidValue {
4962                                value: Value::UNDEFINED,
4963                            },
4964                        )),
4965                        Ok(Some(execution)) => Ok(execution.value),
4966                        Err(error) => {
4967                            self.unwind_frames_to(stop_depth);
4968                            match error.kind {
4969                                RuntimeErrorKind::UncaughtThrow { value, .. } => {
4970                                    Err(EvalFailure::ThrowValue(value))
4971                                }
4972                                kind => Err(EvalFailure::Runtime(kind)),
4973                            }
4974                        }
4975                    };
4976                }
4977                CalleeKind::Bound => {
4978                    let bound = self
4979                        .flatten_bound(callee, this_value, arguments.as_ref())
4980                        .map_err(EvalFailure::Runtime)?;
4981                    callee = bound.target;
4982                    this_value = bound.this_value;
4983                    arguments = Cow::Owned(bound.arguments);
4984                }
4985                CalleeKind::NotCallable => {
4986                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4987                        operation: "call",
4988                    }));
4989                }
4990            }
4991        }
4992    }
4993
4994    fn unwind_frames_to(&mut self, depth: usize) {
4995        while self.frames.len() > depth {
4996            let frame = self.frames.pop().expect("frame depth was checked");
4997            self.live_registers -= frame.registers.len();
4998        }
4999    }
5000
5001    fn complete_frame(&mut self, returned: Value) -> Option<Execution> {
5002        let frame = self.frames.pop().expect("an activation is executing");
5003        self.live_registers -= frame.registers.len();
5004        match frame.return_to {
5005            None => {
5006                let outcome = ExecutionOutcome {
5007                    stdout: Vec::new(),
5008                    exit_code: 0,
5009                };
5010                Some(Execution {
5011                    outcome,
5012                    value: returned,
5013                    link: returned,
5014                    entry_registers: frame.registers,
5015                })
5016            }
5017            Some(return_to) => {
5018                let value = match return_to.constructed {
5019                    Some(object) if !self.is_object(returned) => object,
5020                    _ => returned,
5021                };
5022                if let Some(destination) = return_to.destination {
5023                    self.frames.last_mut().expect("callee has caller").registers[destination] =
5024                        value;
5025                } else {
5026                    self.last_completion = Some(value);
5027                }
5028                None
5029            }
5030        }
5031    }
5032
5033    fn resolve_failure(&mut self, failure: EvalFailure, pc: usize) -> Result<(), RuntimeError> {
5034        match failure {
5035            EvalFailure::Throw(origin) => self.throw(Value::UNDEFINED, origin, pc),
5036            EvalFailure::ThrowValue(value) => self.throw(value, ThrowOrigin::Bytecode, pc),
5037            EvalFailure::ThrowValueOrigin { value, origin } => self.throw(value, origin, pc),
5038            EvalFailure::Runtime(kind) => Err(self.error_here_at(kind, pc)),
5039        }
5040    }
5041
5042    fn throw_type(&mut self, operation: &'static str, pc: usize) -> Result<(), RuntimeError> {
5043        self.throw(Value::UNDEFINED, ThrowOrigin::TypeError { operation }, pc)
5044    }
5045
5046    fn throw(
5047        &mut self,
5048        value: Value,
5049        origin: ThrowOrigin,
5050        faulting_pc: usize,
5051    ) -> Result<(), RuntimeError> {
5052        let site_module = self
5053            .frames
5054            .last()
5055            .expect("an activation is executing")
5056            .module;
5057        let site_function = self
5058            .frames
5059            .last()
5060            .expect("an activation is executing")
5061            .function;
5062        let mut search_pc = faulting_pc;
5063        loop {
5064            if self
5065                .callback_boundaries
5066                .last()
5067                .is_some_and(|boundary| self.frames.len() == *boundary)
5068            {
5069                return Err(self.error_at_in_module(
5070                    RuntimeErrorKind::UncaughtThrow { value, origin },
5071                    site_module,
5072                    site_function,
5073                    faulting_pc,
5074                ));
5075            }
5076            let frame_index = self.frames.len() - 1;
5077            let function_index = self.frames[frame_index].function;
5078            let module = self.frames[frame_index].module;
5079            let function = &self.module_code(module).functions()[function_index];
5080            if let Some(handler) = innermost_handler(function, search_pc) {
5081                let frame = &mut self.frames[frame_index];
5082                frame.registers[handler.catch_register.get() as usize] = value;
5083                frame.pc = handler.handler.get() as usize;
5084                return Ok(());
5085            }
5086            let frame = self.frames.pop().expect("throw walks live frames");
5087            self.live_registers -= frame.registers.len();
5088            match frame.return_to {
5089                Some(return_to) => search_pc = return_to.call_pc,
5090                None => {
5091                    return Err(self.error_at_in_module(
5092                        RuntimeErrorKind::UncaughtThrow { value, origin },
5093                        site_module,
5094                        site_function,
5095                        faulting_pc,
5096                    ));
5097                }
5098            }
5099        }
5100    }
5101
5102    fn error_here(&self, kind: RuntimeErrorKind) -> RuntimeError {
5103        let frame = self.frames.last().expect("an activation is executing");
5104        self.error_at(kind, frame.function, frame.pc)
5105    }
5106
5107    fn error_here_at(&self, kind: RuntimeErrorKind, pc: usize) -> RuntimeError {
5108        let function = self
5109            .frames
5110            .last()
5111            .expect("an activation is executing")
5112            .function;
5113        self.error_at(kind, function, pc)
5114    }
5115
5116    fn error_at(&self, kind: RuntimeErrorKind, function: usize, pc: usize) -> RuntimeError {
5117        self.error_at_in_module(kind, self.active_module_id(), function, pc)
5118    }
5119
5120    pub(crate) fn error_at_in_module(
5121        &self,
5122        kind: RuntimeErrorKind,
5123        module: ModuleId,
5124        function: usize,
5125        pc: usize,
5126    ) -> RuntimeError {
5127        let code = self.module_code(module);
5128        let metadata = &code.functions()[function];
5129        let function_name =
5130            metadata
5131                .name()
5132                .and_then(|id| match &code.constants()[id.get() as usize] {
5133                    Constant::String(name) => Some(name.clone()),
5134                    _ => None,
5135                });
5136        RuntimeError {
5137            kind,
5138            function: FunctionId::new(function as u32),
5139            pc: Pc::new(pc as u32),
5140            source: RuntimeSource {
5141                function_name,
5142                instruction: metadata.code()[pc],
5143            },
5144        }
5145    }
5146
5147    // ---- property keys -----------------------------------------------------
5148
5149    /// Normalizes a register value into a property key. A runtime string borrows
5150    /// its text; a private name yields its slot identity; everything else is
5151    /// coerced with `ToString`.
5152    fn to_property_key(&self, value: Value) -> Result<PropertyKey, EvalFailure> {
5153        match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
5154            Some(index) => match &self.heap[index] {
5155                HeapEntry::String(text) => Ok(PropertyKey::Named(text.clone())),
5156                HeapEntry::Symbol { .. } => Ok(PropertyKey::Symbol(index as u32)),
5157                HeapEntry::PrivateName { .. } => Ok(PropertyKey::Private(index as u32)),
5158                _ => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5159            },
5160            None => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5161        }
5162    }
5163
5164    // ---- property get ------------------------------------------------------
5165
5166    fn resolve_get(&mut self, object: Value, key: &PropertyKey) -> Result<GetOutcome, EvalFailure> {
5167        let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5168        let start = match slot {
5169            Some(index) => {
5170                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5171                    let PropertyKey::Named(name) = key else {
5172                        return Ok(GetOutcome::Value(Value::UNDEFINED));
5173                    };
5174                    let text = name
5175                        .to_utf8_strict()
5176                        .ok()
5177                        .and_then(|name| self.host.env(&name))
5178                        .map(EcmaString::from_utf8);
5179                    return match text {
5180                        Some(text) => self
5181                            .allocate(HeapEntry::String(text))
5182                            .map(GetOutcome::Value)
5183                            .map_err(EvalFailure::Runtime),
5184                        None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5185                    };
5186                }
5187                if let Some(found) = self.primitive_get(index, key) {
5188                    return self.found_outcome(found);
5189                }
5190                match self.heap[index] {
5191                    HeapEntry::String(_) => self
5192                        .runtime_slot(self.intrinsics.string_prototype)
5193                        .map_err(EvalFailure::Runtime)?,
5194                    HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5195                        .runtime_slot(self.intrinsics.object_prototype)
5196                        .map_err(EvalFailure::Runtime)?,
5197                    HeapEntry::Symbol { .. } => self
5198                        .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5199                        .map_err(EvalFailure::Runtime)?,
5200                    _ => Some(index),
5201                }
5202            }
5203            None => {
5204                let prototype = match object.decode() {
5205                    Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5206                    Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5207                        self.intrinsics.number_prototype
5208                    }
5209                    _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5210                };
5211                self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5212            }
5213        };
5214        let Some(mut node) = start else {
5215            return Ok(GetOutcome::Value(Value::UNDEFINED));
5216        };
5217        for _ in 0..=self.heap.len() {
5218            if let Some(found) = self.own_get(node, key) {
5219                return self.found_outcome(found);
5220            }
5221            match self.prototype_index(node)? {
5222                Some(next) => node = next,
5223                None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5224            }
5225        }
5226        Ok(GetOutcome::Value(Value::UNDEFINED))
5227    }
5228
5229    fn resolve_get_ascii(&mut self, object: Value, name: &str) -> Result<GetOutcome, EvalFailure> {
5230        debug_assert!(name.is_ascii());
5231        let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5232        let start = match slot {
5233            Some(index) => {
5234                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5235                    return match self.host.env(name).map(EcmaString::from_utf8) {
5236                        Some(text) => self
5237                            .allocate(HeapEntry::String(text))
5238                            .map(GetOutcome::Value)
5239                            .map_err(EvalFailure::Runtime),
5240                        None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5241                    };
5242                }
5243                if let HeapEntry::String(text) = &self.heap[index] {
5244                    if name == "length" {
5245                        return Ok(GetOutcome::Value(number_value(text.len_units() as f64)));
5246                    }
5247                    if let Some(offset) = array_index_ascii(name)
5248                        && let Some(unit) = text.unit_at(offset as usize)
5249                    {
5250                        return Ok(GetOutcome::Text(EcmaString::from_units(&[unit])));
5251                    }
5252                }
5253                match self.heap[index] {
5254                    HeapEntry::String(_) => self
5255                        .runtime_slot(self.intrinsics.string_prototype)
5256                        .map_err(EvalFailure::Runtime)?,
5257                    HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5258                        .runtime_slot(self.intrinsics.object_prototype)
5259                        .map_err(EvalFailure::Runtime)?,
5260                    HeapEntry::Symbol { .. } => self
5261                        .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5262                        .map_err(EvalFailure::Runtime)?,
5263                    _ => Some(index),
5264                }
5265            }
5266            None => {
5267                let prototype = match object.decode() {
5268                    Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5269                    Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5270                        self.intrinsics.number_prototype
5271                    }
5272                    _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5273                };
5274                self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5275            }
5276        };
5277        let Some(mut node) = start else {
5278            return Ok(GetOutcome::Value(Value::UNDEFINED));
5279        };
5280        for _ in 0..=self.heap.len() {
5281            if let Some(found) = self.own_get_ascii(node, name) {
5282                return self.found_outcome(found);
5283            }
5284            match self.prototype_index(node)? {
5285                Some(next) => node = next,
5286                None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5287            }
5288        }
5289        Ok(GetOutcome::Value(Value::UNDEFINED))
5290    }
5291
5292    fn found_outcome(&mut self, found: Found) -> Result<GetOutcome, EvalFailure> {
5293        match found {
5294            Found::Value(Value::UNINITIALIZED) => {
5295                let id = self
5296                    .intrinsics
5297                    .builtins
5298                    .id_named("ReferenceError")
5299                    .expect("ReferenceError intrinsic is installed");
5300                match self.throw_error(
5301                    id,
5302                    "Cannot access lexical binding before initialization".into(),
5303                ) {
5304                    EvalFailure::ThrowValue(value) => Err(EvalFailure::ThrowValueOrigin {
5305                        value,
5306                        origin: ThrowOrigin::ReferenceError {
5307                            operation: "lexical binding is uninitialized",
5308                        },
5309                    }),
5310                    failure => Err(failure),
5311                }
5312            }
5313            Found::Value(value) => Ok(GetOutcome::Value(value)),
5314            Found::Text(text) => Ok(GetOutcome::Text(text)),
5315            Found::Getter(getter) => Ok(GetOutcome::Getter(getter)),
5316            Found::Failure(kind) => Err(EvalFailure::Runtime(kind)),
5317            Found::NoGetter => Ok(GetOutcome::Value(Value::UNDEFINED)),
5318        }
5319    }
5320
5321    fn primitive_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5322        if let HeapEntry::String(text) = &self.heap[index]
5323            && let PropertyKey::Named(name) = key
5324        {
5325            if name.eq_ascii("length") {
5326                return Some(Found::Value(number_value(text.len_units() as f64)));
5327            }
5328            if let Some(offset) = array_index(name)
5329                && let Some(unit) = text.unit_at(offset as usize)
5330            {
5331                return Some(Found::Text(EcmaString::from_units(&[unit])));
5332            }
5333        }
5334        None
5335    }
5336    fn own_get_ascii(&self, index: usize, name: &str) -> Option<Found> {
5337        debug_assert!(name.is_ascii());
5338        let slot = |value| self.runtime_slot(value).ok().flatten();
5339        if slot(self.intrinsics.object_prototype) == Some(index) && name == "toString" {
5340            return Some(Found::Value(self.intrinsics.object_to_string()));
5341        }
5342        match &self.heap[index] {
5343            HeapEntry::Object { properties, .. }
5344            | HeapEntry::Generator { properties, .. }
5345            | HeapEntry::Script { properties, .. }
5346            | HeapEntry::NativeFunction { properties, .. }
5347            | HeapEntry::Date { properties, .. }
5348            | HeapEntry::BuiltinIterator { properties, .. }
5349            | HeapEntry::Collection { properties, .. }
5350            | HeapEntry::Promise { properties, .. }
5351            | HeapEntry::Timeout { properties, .. } => property_lookup_ascii(properties, name),
5352            HeapEntry::Array {
5353                elements,
5354                properties,
5355                ..
5356            } => {
5357                if name == "length" {
5358                    return Some(Found::Value(number_value(elements.len() as f64)));
5359                }
5360                if let Some(offset) = array_index_ascii(name)
5361                    && let Some(element) = elements.get(offset as usize)
5362                    && *element != Value::HOLE
5363                {
5364                    return Some(Found::Value(*element));
5365                }
5366                property_lookup_ascii(properties, name)
5367            }
5368            HeapEntry::Function {
5369                module,
5370                function,
5371                properties,
5372                ..
5373            } => {
5374                if let Some(found) = property_lookup_ascii(properties, name) {
5375                    return Some(found);
5376                }
5377                let metadata = &self.module_code(*module).functions()[function.get() as usize];
5378                if name == "length" {
5379                    return Some(Found::Value(
5380                        number_value(metadata.parameter_count() as f64),
5381                    ));
5382                }
5383                if name == "name" {
5384                    return Some(Found::Text(
5385                        metadata
5386                            .name()
5387                            .map(|id| self.constant_text(*module, id).clone())
5388                            .unwrap_or_default(),
5389                    ));
5390                }
5391                None
5392            }
5393            HeapEntry::ModuleNamespace { module } => {
5394                let key = self
5395                    .program_module(*module)
5396                    .exports
5397                    .iter()
5398                    .map(|export| self.constant_text(*module, export.name))
5399                    .find(|candidate| candidate.eq_ascii(name))?
5400                    .clone();
5401                match self.namespace_export(*module, &key) {
5402                    Ok(Some(value)) => Some(Found::Value(value)),
5403                    Ok(None) => None,
5404                    Err(kind) => Some(Found::Failure(kind)),
5405                }
5406            }
5407            HeapEntry::ExternalModuleNamespace { specifier } => {
5408                let export = self.registry.external[specifier]
5409                    .exports
5410                    .iter()
5411                    .find_map(|(candidate, export)| candidate.eq_ascii(name).then_some(export))?;
5412                let cell = export
5413                    .cell
5414                    .expect("external namespace exports link before evaluation");
5415                Some(Found::Value(self.registry.cells[cell.0].value))
5416            }
5417            HeapEntry::RegExp {
5418                pattern,
5419                flags,
5420                properties,
5421                ..
5422            } => {
5423                if let Some(found) = property_lookup_ascii(properties, name) {
5424                    return Some(found);
5425                }
5426                let flag = |unit| {
5427                    Found::Value(Value::boolean(flags.as_units().contains(&u16::from(unit))))
5428                };
5429                match name {
5430                    "source" => Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5431                        pattern,
5432                    ))),
5433                    "flags" => Some(Found::Text(flags.clone())),
5434                    "global" => Some(flag(b'g')),
5435                    "ignoreCase" => Some(flag(b'i')),
5436                    "multiline" => Some(flag(b'm')),
5437                    "sticky" => Some(flag(b'y')),
5438                    "unicode" => Some(flag(b'u')),
5439                    "dotAll" => Some(flag(b's')),
5440                    "lastIndex" => Some(Found::Value(Value::int32(0))),
5441                    _ => None,
5442                }
5443            }
5444            HeapEntry::HashState { update, digest, .. } => match name {
5445                "update" => Some(Found::Value(*update)),
5446                "digest" => Some(Found::Value(*digest)),
5447                _ => None,
5448            },
5449            HeapEntry::ProcessEnv { .. }
5450            | HeapEntry::String(_)
5451            | HeapEntry::BigInt(_)
5452            | HeapEntry::Symbol { .. }
5453            | HeapEntry::PrivateName { .. }
5454            | HeapEntry::Iterator { .. }
5455            | HeapEntry::PromiseResolver { .. }
5456            | HeapEntry::PromiseFinally { .. }
5457            | HeapEntry::PromiseAll { .. }
5458            | HeapEntry::AsyncActivation { .. }
5459            | HeapEntry::PromiseAllElement { .. } => None,
5460        }
5461    }
5462
5463    /// Looks up an own property of the heap entry at `index`, returning `None`
5464    /// when the key is absent so the caller may continue up the prototype chain.
5465    fn own_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5466        if let PropertyKey::Named(name) = key {
5467            let slot = |value| self.runtime_slot(value).ok().flatten();
5468            if slot(self.intrinsics.object_prototype) == Some(index) && name.eq_ascii("toString") {
5469                return Some(Found::Value(self.intrinsics.object_to_string()));
5470            }
5471        }
5472        match &self.heap[index] {
5473            HeapEntry::Object { properties, .. }
5474            | HeapEntry::Generator { properties, .. }
5475            | HeapEntry::Script { properties, .. }
5476            | HeapEntry::Date { properties, .. }
5477            | HeapEntry::BuiltinIterator { properties, .. }
5478            | HeapEntry::Collection { properties, .. }
5479            | HeapEntry::Promise { properties, .. }
5480            | HeapEntry::Timeout { properties, .. } => property_lookup(properties, key),
5481            HeapEntry::Array {
5482                elements,
5483                properties,
5484                ..
5485            } => {
5486                if let PropertyKey::Named(name) = key {
5487                    if name.eq_ascii("length") {
5488                        return Some(Found::Value(number_value(elements.len() as f64)));
5489                    }
5490                    if let Some(offset) = array_index(name)
5491                        && let Some(element) = elements.get(offset as usize)
5492                        && *element != Value::HOLE
5493                    {
5494                        return Some(Found::Value(*element));
5495                    }
5496                }
5497                property_lookup(properties, key)
5498            }
5499            HeapEntry::Function {
5500                module,
5501                function,
5502                properties,
5503                ..
5504            } => {
5505                if let Some(found) = property_lookup(properties, key) {
5506                    return Some(found);
5507                }
5508                if let PropertyKey::Named(name) = key {
5509                    let metadata = &self.module_code(*module).functions()[function.get() as usize];
5510                    if name.eq_ascii("length") {
5511                        return Some(Found::Value(
5512                            number_value(metadata.parameter_count() as f64),
5513                        ));
5514                    }
5515                    if name.eq_ascii("name") {
5516                        return Some(Found::Text(
5517                            metadata
5518                                .name()
5519                                .map(|id| self.constant_text(*module, id).clone())
5520                                .unwrap_or_default(),
5521                        ));
5522                    }
5523                }
5524                None
5525            }
5526            HeapEntry::ModuleNamespace { module } => {
5527                let PropertyKey::Named(name) = key else {
5528                    return None;
5529                };
5530                match self.namespace_export(*module, name) {
5531                    Ok(Some(value)) => Some(Found::Value(value)),
5532                    Ok(None) => None,
5533                    Err(kind) => Some(Found::Failure(kind)),
5534                }
5535            }
5536            HeapEntry::ExternalModuleNamespace { specifier } => {
5537                let PropertyKey::Named(name) = key else {
5538                    return None;
5539                };
5540                let export = self.registry.external[specifier].exports.get(name)?;
5541                Some(Found::Value(export.cell.map_or(export.value, |cell| {
5542                    self.registry.cells[cell.0].value
5543                })))
5544            }
5545            HeapEntry::NativeFunction { properties, .. } => property_lookup(properties, key),
5546            HeapEntry::RegExp {
5547                pattern,
5548                flags,
5549                properties,
5550                ..
5551            } => {
5552                if let Some(found) = property_lookup(properties, key) {
5553                    return Some(found);
5554                }
5555                if let PropertyKey::Named(name) = key {
5556                    let flag = |ascii: &str| {
5557                        Found::Value(Value::boolean(
5558                            flags.as_units().contains(&u16::from(ascii.as_bytes()[0])),
5559                        ))
5560                    };
5561                    if name.eq_ascii("source") {
5562                        return Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5563                            pattern,
5564                        )));
5565                    }
5566                    if name.eq_ascii("flags") {
5567                        return Some(Found::Text(flags.clone()));
5568                    }
5569                    if name.eq_ascii("global") {
5570                        return Some(flag("g"));
5571                    }
5572                    if name.eq_ascii("ignoreCase") {
5573                        return Some(flag("i"));
5574                    }
5575                    if name.eq_ascii("multiline") {
5576                        return Some(flag("m"));
5577                    }
5578                    if name.eq_ascii("sticky") {
5579                        return Some(flag("y"));
5580                    }
5581                    if name.eq_ascii("unicode") {
5582                        return Some(flag("u"));
5583                    }
5584                    if name.eq_ascii("dotAll") {
5585                        return Some(flag("s"));
5586                    }
5587                    if name.eq_ascii("lastIndex") {
5588                        return Some(Found::Value(Value::int32(0)));
5589                    }
5590                }
5591                None
5592            }
5593            HeapEntry::HashState { update, digest, .. } => {
5594                let PropertyKey::Named(name) = key else {
5595                    return None;
5596                };
5597                if name.eq_ascii("update") {
5598                    Some(Found::Value(*update))
5599                } else if name.eq_ascii("digest") {
5600                    Some(Found::Value(*digest))
5601                } else {
5602                    None
5603                }
5604            }
5605            HeapEntry::ProcessEnv { .. }
5606            | HeapEntry::String(_)
5607            | HeapEntry::BigInt(_)
5608            | HeapEntry::Symbol { .. }
5609            | HeapEntry::PrivateName { .. }
5610            | HeapEntry::Iterator { .. }
5611            | HeapEntry::PromiseResolver { .. }
5612            | HeapEntry::PromiseFinally { .. }
5613            | HeapEntry::PromiseAll { .. }
5614            | HeapEntry::AsyncActivation { .. }
5615            | HeapEntry::PromiseAllElement { .. } => None,
5616        }
5617    }
5618
5619    fn namespace_export(
5620        &self,
5621        module: ModuleId,
5622        name: &EcmaString,
5623    ) -> Result<Option<Value>, RuntimeErrorKind> {
5624        if module.get() as usize >= self.dynamic_base {
5625            return Ok(None);
5626        }
5627        match self.program().resolve_export(module, name) {
5628            Some(ResolvedExport::Local { module, binding }) => {
5629                let cell = self.registry.modules[module.get() as usize].binding_cells
5630                    [binding.get() as usize]
5631                    .expect("verified export resolves to a linked cell");
5632                let value = self.registry.cells[cell.0].value;
5633                if value.is_uninitialized() {
5634                    Err(RuntimeErrorKind::TemporalDeadZone { module, binding })
5635                } else {
5636                    Ok(Some(value))
5637                }
5638            }
5639            Some(ResolvedExport::External { module, edge, name }) => {
5640                let Some(specifier) = self.external_specifier(module, edge) else {
5641                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5642                };
5643                let name = self.constant_text(module, name);
5644                let Some(export) = self.registry.external[&specifier].exports.get(name) else {
5645                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5646                };
5647                let Some(cell) = export.cell else {
5648                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5649                };
5650                Ok(Some(self.registry.cells[cell.0].value))
5651            }
5652            None => Ok(None),
5653        }
5654    }
5655
5656    fn own_data_property(&self, index: usize, name: &str) -> Option<Value> {
5657        let properties = match &self.heap[index] {
5658            HeapEntry::Object { properties, .. }
5659            | HeapEntry::Generator { properties, .. }
5660            | HeapEntry::Script { properties, .. }
5661            | HeapEntry::Array { properties, .. }
5662            | HeapEntry::Function { properties, .. }
5663            | HeapEntry::NativeFunction { properties, .. }
5664            | HeapEntry::RegExp { properties, .. }
5665            | HeapEntry::Date { properties, .. }
5666            | HeapEntry::BuiltinIterator { properties, .. }
5667            | HeapEntry::Collection { properties, .. }
5668            | HeapEntry::Promise { properties, .. }
5669            | HeapEntry::Timeout { properties, .. } => properties,
5670            _ => return None,
5671        };
5672        match properties.get_ascii(name) {
5673            Some(Property::Data { value, .. }) => Some(*value),
5674            _ => None,
5675        }
5676    }
5677
5678    fn prototype_index(&self, index: usize) -> Result<Option<usize>, EvalFailure> {
5679        let prototype = match &self.heap[index] {
5680            HeapEntry::Object { prototype, .. }
5681            | HeapEntry::Generator { prototype, .. }
5682            | HeapEntry::Script { prototype, .. }
5683            | HeapEntry::Array { prototype, .. }
5684            | HeapEntry::Function { prototype, .. }
5685            | HeapEntry::RegExp { prototype, .. }
5686            | HeapEntry::Date { prototype, .. }
5687            | HeapEntry::BuiltinIterator { prototype, .. }
5688            | HeapEntry::Collection { prototype, .. }
5689            | HeapEntry::Promise { prototype, .. }
5690            | HeapEntry::Timeout { prototype, .. }
5691            | HeapEntry::ProcessEnv { prototype, .. } => *prototype,
5692            HeapEntry::NativeFunction { .. } => Some(self.intrinsics.function_prototype),
5693            _ => None,
5694        };
5695        match prototype {
5696            Some(value) => self.runtime_slot(value).map_err(EvalFailure::Runtime),
5697            None => Ok(None),
5698        }
5699    }
5700
5701    pub(crate) fn inherits_from_prototype(
5702        &self,
5703        value: Value,
5704        prototype: Value,
5705    ) -> Result<bool, EvalFailure> {
5706        let Some(mut current) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
5707            return Ok(false);
5708        };
5709        let Some(target) = self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? else {
5710            return Ok(false);
5711        };
5712        let mut traversed = 0;
5713        while let Some(next) = self.prototype_index(current)? {
5714            if next == target {
5715                return Ok(true);
5716            }
5717            current = next;
5718            traversed += 1;
5719            if traversed > self.heap.len() {
5720                return Ok(false);
5721            }
5722        }
5723        Ok(false)
5724    }
5725
5726    // ---- property set ------------------------------------------------------
5727
5728    fn resolve_set(
5729        &mut self,
5730        object: Value,
5731        key: PropertyKey,
5732        value: Value,
5733    ) -> Result<SetOutcome, EvalFailure> {
5734        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
5735            Some(index) => {
5736                if matches!(self.heap[index], HeapEntry::ModuleNamespace { .. }) {
5737                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5738                        operation: "assign to module namespace",
5739                    }));
5740                }
5741                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5742                    let PropertyKey::Named(name) = &key else {
5743                        return Ok(SetOutcome::Done);
5744                    };
5745                    let Ok(name) = name.to_utf8_strict() else {
5746                        return Ok(SetOutcome::Done);
5747                    };
5748                    let text = self.to_string(value)?;
5749                    let text = crate::host_objects::env_value_text_lossy(&text);
5750                    self.host.set_env(&name, &text);
5751                    return Ok(SetOutcome::Done);
5752                }
5753                if let Some(setter) = self.find_setter(index, &key)? {
5754                    return Ok(match setter {
5755                        Some(setter) => SetOutcome::Setter(setter),
5756                        None => SetOutcome::Done,
5757                    });
5758                }
5759                self.set_own_data(index, key, value)?;
5760                Ok(SetOutcome::Done)
5761            }
5762            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5763                operation: "set property on primitive",
5764            })),
5765        }
5766    }
5767
5768    fn find_setter(
5769        &self,
5770        index: usize,
5771        key: &PropertyKey,
5772    ) -> Result<Option<Option<Value>>, EvalFailure> {
5773        if self.own_has_non_accessor(index, key) {
5774            return Ok(None);
5775        }
5776        let mut node = index;
5777        let mut guard = 0;
5778        loop {
5779            let accessor = match &self.heap[node] {
5780                HeapEntry::Object { properties, .. }
5781                | HeapEntry::Generator { properties, .. }
5782                | HeapEntry::Script { properties, .. }
5783                | HeapEntry::Array { properties, .. }
5784                | HeapEntry::Function { properties, .. }
5785                | HeapEntry::NativeFunction { properties, .. }
5786                | HeapEntry::RegExp { properties, .. }
5787                | HeapEntry::Date { properties, .. }
5788                | HeapEntry::BuiltinIterator { properties, .. }
5789                | HeapEntry::Collection { properties, .. }
5790                | HeapEntry::Promise { properties, .. }
5791                | HeapEntry::Timeout { properties, .. } => match properties.get(key) {
5792                    Some(Property::Accessor { setter, .. }) => Some(Some(*setter)),
5793                    Some(Property::Data { .. }) => Some(None),
5794                    None => None,
5795                },
5796                _ => None,
5797            };
5798            match accessor {
5799                Some(Some(setter)) => return Ok(Some(setter)),
5800                Some(None) => return Ok(None),
5801                None => {}
5802            }
5803            match self.prototype_index(node)? {
5804                Some(next) => {
5805                    node = next;
5806                    guard += 1;
5807                    if guard > self.heap.len() + 1 {
5808                        return Ok(None);
5809                    }
5810                }
5811                None => return Ok(None),
5812            }
5813        }
5814    }
5815
5816    fn own_has_non_accessor(&self, index: usize, key: &PropertyKey) -> bool {
5817        match &self.heap[index] {
5818            HeapEntry::Array { elements, .. } => {
5819                if let PropertyKey::Named(name) = key {
5820                    if name.eq_ascii("length") {
5821                        return true;
5822                    }
5823                    if let Some(offset) = array_index(name) {
5824                        return elements
5825                            .get(offset as usize)
5826                            .is_some_and(|element| *element != Value::HOLE);
5827                    }
5828                }
5829                false
5830            }
5831            HeapEntry::Function { .. } => {
5832                (key.eq_ascii("length") || key.eq_ascii("name"))
5833                    && match key {
5834                        PropertyKey::Named(name) if name.eq_ascii("length") => {
5835                            self.own_data_property(index, "length").is_none()
5836                        }
5837                        PropertyKey::Named(_) => self.own_data_property(index, "name").is_none(),
5838                        _ => false,
5839                    }
5840            }
5841            _ => false,
5842        }
5843    }
5844
5845    fn set_own_data(
5846        &mut self,
5847        index: usize,
5848        key: PropertyKey,
5849        value: Value,
5850    ) -> Result<(), EvalFailure> {
5851        if matches!(key, PropertyKey::Named(ref name) if name.eq_ascii("length"))
5852            && matches!(self.heap[index], HeapEntry::Array { .. })
5853        {
5854            let HeapEntry::Array {
5855                elements,
5856                properties,
5857                length_writable,
5858                ..
5859            } = &mut self.heap[index]
5860            else {
5861                unreachable!("array checked above");
5862            };
5863            return array_set_length(
5864                elements,
5865                properties,
5866                *length_writable,
5867                value,
5868                "set array length",
5869            );
5870        }
5871        if let HeapEntry::Array {
5872            elements,
5873            length_writable,
5874            ..
5875        } = &self.heap[index]
5876            && let Some(offset) = key.as_string().and_then(array_index)
5877            && offset as usize >= elements.len()
5878            && !*length_writable
5879        {
5880            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5881                operation: "add index beyond non-writable array length",
5882            }));
5883        }
5884        let (properties, extensible, virtual_exists) = match &self.heap[index] {
5885            HeapEntry::Object {
5886                properties,
5887                extensible,
5888                ..
5889            }
5890            | HeapEntry::Generator {
5891                properties,
5892                extensible,
5893                ..
5894            }
5895            | HeapEntry::Script {
5896                properties,
5897                extensible,
5898                ..
5899            }
5900            | HeapEntry::Function {
5901                properties,
5902                extensible,
5903                ..
5904            }
5905            | HeapEntry::NativeFunction {
5906                properties,
5907                extensible,
5908                ..
5909            }
5910            | HeapEntry::RegExp {
5911                properties,
5912                extensible,
5913                ..
5914            }
5915            | HeapEntry::Date {
5916                properties,
5917                extensible,
5918                ..
5919            }
5920            | HeapEntry::BuiltinIterator {
5921                properties,
5922                extensible,
5923                ..
5924            }
5925            | HeapEntry::Collection {
5926                properties,
5927                extensible,
5928                ..
5929            }
5930            | HeapEntry::Promise {
5931                properties,
5932                extensible,
5933                ..
5934            } => (Some(properties), *extensible, false),
5935            HeapEntry::Array {
5936                elements,
5937                properties,
5938                extensible,
5939                ..
5940            } => {
5941                let virtual_exists = key.as_string().is_some_and(|name| {
5942                    name.eq_ascii("length")
5943                        || array_index(name).is_some_and(|offset| {
5944                            elements
5945                                .get(offset as usize)
5946                                .is_some_and(|element| *element != Value::HOLE)
5947                        })
5948                });
5949                (Some(properties), *extensible, virtual_exists)
5950            }
5951            _ => (None, true, false),
5952        };
5953        if let Some(property) = properties.and_then(|properties| properties.get(&key)) {
5954            match property {
5955                Property::Data {
5956                    writable: false, ..
5957                }
5958                | Property::Accessor { .. } => {
5959                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5960                        operation: "assign to read only property",
5961                    }));
5962                }
5963                Property::Data { writable: true, .. } => {}
5964            }
5965        } else if !extensible && !virtual_exists {
5966            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5967                operation: "add property to non-extensible object",
5968            }));
5969        }
5970
5971        let growth = match &self.heap[index] {
5972            HeapEntry::Object { properties, .. }
5973            | HeapEntry::Generator { properties, .. }
5974            | HeapEntry::Script { properties, .. }
5975            | HeapEntry::Function { properties, .. }
5976            | HeapEntry::NativeFunction { properties, .. }
5977            | HeapEntry::RegExp { properties, .. }
5978            | HeapEntry::Date { properties, .. }
5979            | HeapEntry::BuiltinIterator { properties, .. }
5980            | HeapEntry::Collection { properties, .. }
5981            | HeapEntry::Promise { properties, .. }
5982            | HeapEntry::Timeout { properties, .. } => {
5983                usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5984            }
5985            HeapEntry::Array {
5986                elements,
5987                properties,
5988                ..
5989            } => match &key {
5990                PropertyKey::Named(name) if name.eq_ascii("length") => 0,
5991                PropertyKey::Named(name) => {
5992                    if let Some(offset) = array_index(name) {
5993                        (offset as usize + 1).saturating_sub(elements.len()) * 8
5994                    } else {
5995                        usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5996                    }
5997                }
5998                PropertyKey::Symbol(_) | PropertyKey::Private(_) => {
5999                    usize::from(!properties.contains_key(&key)) * key.charge_bytes()
6000                }
6001            },
6002            HeapEntry::String(_) | HeapEntry::BigInt(_) => {
6003                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6004                    operation: "set property on primitive",
6005                }));
6006            }
6007            HeapEntry::Symbol { .. }
6008            | HeapEntry::PrivateName { .. }
6009            | HeapEntry::Iterator { .. }
6010            | HeapEntry::PromiseResolver { .. }
6011            | HeapEntry::PromiseFinally { .. }
6012            | HeapEntry::PromiseAll { .. }
6013            | HeapEntry::AsyncActivation { .. }
6014            | HeapEntry::PromiseAllElement { .. } => {
6015                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6016                    operation: "set property on non-object",
6017                }));
6018            }
6019            HeapEntry::ProcessEnv { .. } => {
6020                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6021                    operation: "set internal process environment",
6022                }));
6023            }
6024            HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6025                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6026                    operation: "assign to module namespace",
6027                }));
6028            }
6029            HeapEntry::HashState { .. } => {
6030                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6031                    operation: "assign to hash state",
6032                }));
6033            }
6034        };
6035        self.charge_heap(growth).map_err(EvalFailure::Runtime)?;
6036        match &mut self.heap[index] {
6037            HeapEntry::Object { properties, .. }
6038            | HeapEntry::Generator { properties, .. }
6039            | HeapEntry::Script { properties, .. }
6040            | HeapEntry::Function { properties, .. }
6041            | HeapEntry::NativeFunction { properties, .. }
6042            | HeapEntry::RegExp { properties, .. }
6043            | HeapEntry::Date { properties, .. }
6044            | HeapEntry::BuiltinIterator { properties, .. }
6045            | HeapEntry::Collection { properties, .. }
6046            | HeapEntry::Promise { properties, .. }
6047            | HeapEntry::Timeout { properties, .. } => {
6048                properties.insert(
6049                    key,
6050                    Property::Data {
6051                        value,
6052                        writable: true,
6053                        enumerable: true,
6054                        configurable: true,
6055                    },
6056                );
6057                Ok(())
6058            }
6059            HeapEntry::Array {
6060                elements,
6061                properties,
6062                length_writable,
6063                ..
6064            } => {
6065                match key {
6066                    PropertyKey::Named(name) => {
6067                        if let Some(offset) = array_index(&name) {
6068                            let offset = offset as usize;
6069                            if elements.len() <= offset {
6070                                array_set_length(
6071                                    elements,
6072                                    properties,
6073                                    *length_writable,
6074                                    number_value((offset + 1) as f64),
6075                                    "set array index",
6076                                )?;
6077                            }
6078                            elements[offset] = value;
6079                        } else {
6080                            properties.insert(
6081                                PropertyKey::Named(name),
6082                                Property::Data {
6083                                    value,
6084                                    writable: true,
6085                                    enumerable: true,
6086                                    configurable: true,
6087                                },
6088                            );
6089                        }
6090                    }
6091                    identity @ (PropertyKey::Symbol(_) | PropertyKey::Private(_)) => {
6092                        properties.insert(
6093                            identity,
6094                            Property::Data {
6095                                value,
6096                                writable: true,
6097                                enumerable: true,
6098                                configurable: true,
6099                            },
6100                        );
6101                    }
6102                }
6103                Ok(())
6104            }
6105            HeapEntry::ProcessEnv { .. } => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6106                operation: "set internal process environment",
6107            })),
6108            _ => unreachable!("primitive and identity entries rejected above"),
6109        }
6110    }
6111
6112    fn define_accessor(
6113        &mut self,
6114        object: Value,
6115        key: PropertyKey,
6116        accessor: Value,
6117        kind: AccessorKind,
6118    ) -> Result<(), EvalFailure> {
6119        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6120            Some(index) => {
6121                self.charge_heap(key.charge_bytes() + 8)
6122                    .map_err(EvalFailure::Runtime)?;
6123                let (properties, extensible) = match &mut self.heap[index] {
6124                    HeapEntry::Object {
6125                        properties,
6126                        extensible,
6127                        ..
6128                    }
6129                    | HeapEntry::Generator {
6130                        properties,
6131                        extensible,
6132                        ..
6133                    }
6134                    | HeapEntry::Script {
6135                        properties,
6136                        extensible,
6137                        ..
6138                    }
6139                    | HeapEntry::Array {
6140                        properties,
6141                        extensible,
6142                        ..
6143                    }
6144                    | HeapEntry::Function {
6145                        properties,
6146                        extensible,
6147                        ..
6148                    }
6149                    | HeapEntry::NativeFunction {
6150                        properties,
6151                        extensible,
6152                        ..
6153                    }
6154                    | HeapEntry::RegExp {
6155                        properties,
6156                        extensible,
6157                        ..
6158                    }
6159                    | HeapEntry::Date {
6160                        properties,
6161                        extensible,
6162                        ..
6163                    }
6164                    | HeapEntry::BuiltinIterator {
6165                        properties,
6166                        extensible,
6167                        ..
6168                    }
6169                    | HeapEntry::Collection {
6170                        properties,
6171                        extensible,
6172                        ..
6173                    }
6174                    | HeapEntry::Promise {
6175                        properties,
6176                        extensible,
6177                        ..
6178                    } => (properties, *extensible),
6179                    _ => {
6180                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6181                            operation: "define accessor on primitive",
6182                        }));
6183                    }
6184                };
6185                if properties
6186                    .get(&key)
6187                    .is_some_and(|property| !property.configurable())
6188                    || (!properties.contains_key(&key) && !extensible)
6189                {
6190                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6191                        operation: "define accessor on non-configurable object",
6192                    }));
6193                }
6194                let property = properties.get_mut(&key);
6195                match property {
6196                    Some(Property::Accessor { getter, setter, .. }) => match kind {
6197                        AccessorKind::Getter => *getter = Some(accessor),
6198                        AccessorKind::Setter => *setter = Some(accessor),
6199                    },
6200                    Some(Property::Data { .. }) | None => {
6201                        let (getter, setter) = match kind {
6202                            AccessorKind::Getter => (Some(accessor), None),
6203                            AccessorKind::Setter => (None, Some(accessor)),
6204                        };
6205                        properties.insert(
6206                            key,
6207                            Property::Accessor {
6208                                getter,
6209                                setter,
6210                                enumerable: true,
6211                                configurable: true,
6212                            },
6213                        );
6214                    }
6215                }
6216                Ok(())
6217            }
6218            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6219                operation: "define accessor on host object",
6220            })),
6221        }
6222    }
6223
6224    fn delete_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6225        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6226            Some(index) => match &mut self.heap[index] {
6227                HeapEntry::Object { properties, .. }
6228                | HeapEntry::Generator { properties, .. }
6229                | HeapEntry::Script { properties, .. }
6230                | HeapEntry::Function { properties, .. }
6231                | HeapEntry::NativeFunction { properties, .. }
6232                | HeapEntry::RegExp { properties, .. }
6233                | HeapEntry::Date { properties, .. }
6234                | HeapEntry::BuiltinIterator { properties, .. }
6235                | HeapEntry::Collection { properties, .. }
6236                | HeapEntry::Promise { properties, .. }
6237                | HeapEntry::Timeout { properties, .. } => {
6238                    if properties
6239                        .get(key)
6240                        .is_some_and(|property| !property.configurable())
6241                    {
6242                        return Ok(false);
6243                    }
6244                    properties.remove(key);
6245                    Ok(true)
6246                }
6247                HeapEntry::Array {
6248                    elements,
6249                    properties,
6250                    ..
6251                } => {
6252                    if properties
6253                        .get(key)
6254                        .is_some_and(|property| !property.configurable())
6255                    {
6256                        return Ok(false);
6257                    }
6258                    if properties.remove(key).is_some() {
6259                        return Ok(true);
6260                    }
6261                    if let PropertyKey::Named(name) = key {
6262                        if name.eq_ascii("length") {
6263                            return Ok(false);
6264                        }
6265                        if let Some(offset) = array_index(name) {
6266                            if let Some(element) = elements.get_mut(offset as usize) {
6267                                *element = Value::HOLE;
6268                            }
6269                            return Ok(true);
6270                        }
6271                    }
6272                    Ok(true)
6273                }
6274                HeapEntry::ProcessEnv { .. } => {
6275                    let PropertyKey::Named(name) = key else {
6276                        return Ok(true);
6277                    };
6278                    Ok(name
6279                        .to_utf8_strict()
6280                        .is_ok_and(|name| self.host.delete_env(&name)))
6281                }
6282                HeapEntry::String(_)
6283                | HeapEntry::BigInt(_)
6284                | HeapEntry::Symbol { .. }
6285                | HeapEntry::PrivateName { .. }
6286                | HeapEntry::Iterator { .. }
6287                | HeapEntry::PromiseResolver { .. }
6288                | HeapEntry::PromiseFinally { .. }
6289                | HeapEntry::PromiseAll { .. }
6290                | HeapEntry::AsyncActivation { .. }
6291                | HeapEntry::PromiseAllElement { .. }
6292                | HeapEntry::HashState { .. } => Ok(true),
6293                HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6294                    Ok(false)
6295                }
6296            },
6297            None => Ok(true),
6298        }
6299    }
6300
6301    fn has_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6302        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6303            Some(index) => {
6304                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
6305                    let PropertyKey::Named(name) = key else {
6306                        return Ok(false);
6307                    };
6308                    return Ok(name
6309                        .to_utf8_strict()
6310                        .is_ok_and(|name| self.host.env(&name).is_some()));
6311                }
6312                if matches!(key, PropertyKey::Private(_)) {
6313                    return Ok(self.own_get(index, key).is_some());
6314                }
6315                let mut node = index;
6316                let mut guard = 0;
6317                loop {
6318                    if self.own_get(node, key).is_some() {
6319                        return Ok(true);
6320                    }
6321                    match self.prototype_index(node)? {
6322                        Some(next) => {
6323                            node = next;
6324                            guard += 1;
6325                            if guard > self.heap.len() + 1 {
6326                                return Ok(false);
6327                            }
6328                        }
6329                        None => return Ok(false),
6330                    }
6331                }
6332            }
6333            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6334                operation: "in",
6335            })),
6336        }
6337    }
6338
6339    // ---- aggregates & prototypes ------------------------------------------
6340
6341    pub(crate) fn array_push(&mut self, array: Value, value: Value) -> Result<(), EvalFailure> {
6342        match self.runtime_slot(array).map_err(EvalFailure::Runtime)? {
6343            Some(index) => {
6344                if !matches!(self.heap[index], HeapEntry::Array { .. }) {
6345                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6346                        operation: "push on non-array",
6347                    }));
6348                }
6349                self.charge_heap(8).map_err(EvalFailure::Runtime)?;
6350                if let HeapEntry::Array {
6351                    elements,
6352                    properties,
6353                    length_writable,
6354                    ..
6355                } = &mut self.heap[index]
6356                {
6357                    let offset = elements.len();
6358                    array_set_length(
6359                        elements,
6360                        properties,
6361                        *length_writable,
6362                        number_value((offset + 1) as f64),
6363                        "push beyond non-writable array length",
6364                    )?;
6365                    elements[offset] = value;
6366                }
6367                Ok(())
6368            }
6369            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6370                operation: "push on non-array",
6371            })),
6372        }
6373    }
6374
6375    fn array_extend(&mut self, array: Value, iterable: Value) -> Result<(), EvalFailure> {
6376        let iterator = self.create_iterator(iterable, IteratorKind::Sync)?;
6377        loop {
6378            let (done, value) = self.iterator_next(iterator)?;
6379            if done {
6380                return Ok(());
6381            }
6382            self.array_push(array, value)?;
6383        }
6384    }
6385
6386    fn object_spread(&mut self, target: Value, source: Value) -> Result<(), EvalFailure> {
6387        let target_index = match self.runtime_slot(target).map_err(EvalFailure::Runtime)? {
6388            Some(index)
6389                if matches!(
6390                    self.heap[index],
6391                    HeapEntry::Object { .. }
6392                        | HeapEntry::Generator { .. }
6393                        | HeapEntry::Script { .. }
6394                        | HeapEntry::Array { .. }
6395                        | HeapEntry::Promise { .. }
6396                ) =>
6397            {
6398                index
6399            }
6400            _ => {
6401                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6402                    operation: "object spread target is not an object",
6403                }));
6404            }
6405        };
6406        let keys = self.own_property_keys(source)?;
6407        for key in keys {
6408            if !self.own_property_is_enumerable(source, &key)? {
6409                continue;
6410            }
6411            let value = self.get_property_key(source, &key)?;
6412            self.set_own_data(target_index, key, value)?;
6413        }
6414        Ok(())
6415    }
6416
6417    fn set_prototype(&mut self, object: Value, prototype: Value) -> Result<(), EvalFailure> {
6418        let prototype = match self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? {
6419            Some(_) => Some(prototype),
6420            None => match prototype.decode() {
6421                Some(Decoded::Null) => None,
6422                Some(Decoded::HeapRef(_)) => Some(prototype),
6423                _ => {
6424                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6425                        operation: "set prototype to non-object",
6426                    }));
6427                }
6428            },
6429        };
6430        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6431            Some(index) => match &mut self.heap[index] {
6432                HeapEntry::Object {
6433                    prototype: slot, ..
6434                }
6435                | HeapEntry::Generator {
6436                    prototype: slot, ..
6437                }
6438                | HeapEntry::Script {
6439                    prototype: slot, ..
6440                }
6441                | HeapEntry::Array {
6442                    prototype: slot, ..
6443                }
6444                | HeapEntry::Function {
6445                    prototype: slot, ..
6446                }
6447                | HeapEntry::RegExp {
6448                    prototype: slot, ..
6449                }
6450                | HeapEntry::Date {
6451                    prototype: slot, ..
6452                }
6453                | HeapEntry::BuiltinIterator {
6454                    prototype: slot, ..
6455                }
6456                | HeapEntry::Collection {
6457                    prototype: slot, ..
6458                }
6459                | HeapEntry::Promise {
6460                    prototype: slot, ..
6461                } => {
6462                    *slot = prototype;
6463                    Ok(())
6464                }
6465                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6466                    operation: "set prototype on primitive",
6467                })),
6468            },
6469            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6470                operation: "set prototype on host object",
6471            })),
6472        }
6473    }
6474
6475    pub(crate) fn create_generator(
6476        &mut self,
6477        start: GeneratorStart,
6478    ) -> Result<Value, RuntimeErrorKind> {
6479        self.allocate(HeapEntry::Generator {
6480            state: GeneratorState::SuspendedStart(start),
6481            properties: PropertyMap::default(),
6482            prototype: Some(self.intrinsics.builtins.generator_prototype()),
6483            extensible: true,
6484        })
6485    }
6486
6487    fn resume_generator(
6488        &mut self,
6489        generator: Value,
6490        resume_value: Value,
6491    ) -> Result<Value, EvalFailure> {
6492        let state = self.take_generator_state(generator)?;
6493        if matches!(&state, GeneratorState::Completed) {
6494            return self.iterator_result(Value::UNDEFINED, true);
6495        }
6496
6497        let stop_depth = self.frames.len();
6498        let return_to = self.frames.last().map(|frame| ReturnTo {
6499            destination: None,
6500            call_pc: frame.pc,
6501            constructed: None,
6502        });
6503        let prepared = match state {
6504            GeneratorState::SuspendedStart(start) => self
6505                .push_frame(
6506                    start.target,
6507                    &start.captures,
6508                    start.this_value,
6509                    start.new_target,
6510                    &start.args,
6511                    return_to,
6512                )
6513                .map_err(|error| EvalFailure::Runtime(error.kind)),
6514            GeneratorState::Suspended(activation) => {
6515                self.push_resumed_generator_frame(activation, resume_value, return_to)
6516            }
6517            GeneratorState::Executing | GeneratorState::Completed => unreachable!(),
6518        };
6519        if let Err(failure) = prepared {
6520            self.settle_generator_completed(generator)?;
6521            return Err(failure);
6522        }
6523
6524        let resumed = self.run_generator_activation(stop_depth);
6525        match resumed {
6526            Ok(GeneratorResume::Yield { value, activation }) => {
6527                self.settle_generator_yield(generator, value, activation)
6528            }
6529            Ok(GeneratorResume::Return(value)) => {
6530                self.settle_generator_completed(generator)?;
6531                self.iterator_result(value, true)
6532            }
6533            Ok(GeneratorResume::Throw { value, origin }) => {
6534                self.settle_generator_completed(generator)?;
6535                Err(EvalFailure::ThrowValueOrigin { value, origin })
6536            }
6537            Err(failure) => {
6538                self.settle_generator_completed(generator)?;
6539                Err(failure)
6540            }
6541        }
6542    }
6543
6544    fn push_resumed_generator_frame(
6545        &mut self,
6546        activation: SuspendedActivation,
6547        resume_value: Value,
6548        return_to: Option<ReturnTo>,
6549    ) -> Result<(), EvalFailure> {
6550        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6551            self.release_suspended_activation_registers(activation.registers.len());
6552            return Err(EvalFailure::Runtime(RuntimeErrorKind::CallDepthExceeded {
6553                limit: self.limits.max_call_depth,
6554            }));
6555        }
6556        let suspend_pc = activation
6557            .resume_token
6558            .checked_sub(1)
6559            .expect("suspended generator token is nonzero") as usize;
6560        let instruction = self.module_code(activation.target.module).functions()
6561            [activation.target.function.get() as usize]
6562            .code()[suspend_pc];
6563        let Instruction::Suspend { dst, resume, .. } = instruction else {
6564            unreachable!("generator resume token names a suspend instruction");
6565        };
6566        let mut frame = Frame {
6567            module: activation.target.module,
6568            function: activation.target.function.get() as usize,
6569            pc: resume.get() as usize,
6570            registers: activation.registers,
6571            return_to,
6572            this_value: activation.this_value,
6573            new_target: activation.new_target,
6574            args: activation.args,
6575            arguments_object: activation.arguments_object,
6576        };
6577        frame.registers[dst.get() as usize] = resume_value;
6578        self.frames.push(frame);
6579        Ok(())
6580    }
6581
6582    fn run_generator_activation(
6583        &mut self,
6584        stop_depth: usize,
6585    ) -> Result<GeneratorResume, EvalFailure> {
6586        self.last_completion = None;
6587        self.pending_generator_resume = None;
6588        self.callback_boundaries.push(stop_depth);
6589        self.generator_boundaries.push(stop_depth);
6590        let result = self.run_loop(stop_depth);
6591        self.generator_boundaries
6592            .pop()
6593            .expect("generator execution owns its suspend boundary");
6594        self.callback_boundaries
6595            .pop()
6596            .expect("generator execution owns its unwind boundary");
6597
6598        match result {
6599            Ok(Some(execution)) => Ok(GeneratorResume::Return(execution.value)),
6600            Ok(None) => {
6601                if let Some(resume) = self.pending_generator_resume.take() {
6602                    return Ok(resume);
6603                }
6604                let value = self.last_completion.take().unwrap_or(Value::UNDEFINED);
6605                Ok(GeneratorResume::Return(value))
6606            }
6607            Err(error) => {
6608                self.unwind_frames_to(stop_depth);
6609                match error.kind {
6610                    RuntimeErrorKind::UncaughtThrow { value, origin } => {
6611                        Ok(GeneratorResume::Throw { value, origin })
6612                    }
6613                    kind => Err(EvalFailure::Runtime(kind)),
6614                }
6615            }
6616        }
6617    }
6618
6619    pub(crate) fn take_generator_state(
6620        &mut self,
6621        generator: Value,
6622    ) -> Result<GeneratorState, EvalFailure> {
6623        let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6624            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6625                operation: "Generator.prototype.next called on incompatible receiver",
6626            }));
6627        };
6628        let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6629            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6630                operation: "Generator.prototype.next called on incompatible receiver",
6631            }));
6632        };
6633        match std::mem::replace(state, GeneratorState::Executing) {
6634            GeneratorState::Executing => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6635                operation: "generator is already running",
6636            })),
6637            GeneratorState::Completed => {
6638                *state = GeneratorState::Completed;
6639                Ok(GeneratorState::Completed)
6640            }
6641            state => Ok(state),
6642        }
6643    }
6644
6645    pub(crate) fn settle_generator_yield(
6646        &mut self,
6647        generator: Value,
6648        value: Value,
6649        activation: SuspendedActivation,
6650    ) -> Result<Value, EvalFailure> {
6651        let register_count = activation.registers.len();
6652        let result = match self.iterator_result(value, false) {
6653            Ok(result) => result,
6654            Err(failure) => {
6655                self.release_suspended_activation_registers(register_count);
6656                self.replace_executing_generator(generator, GeneratorState::Completed)?;
6657                return Err(failure);
6658            }
6659        };
6660        if let Err(failure) =
6661            self.replace_executing_generator(generator, GeneratorState::Suspended(activation))
6662        {
6663            self.release_suspended_activation_registers(register_count);
6664            return Err(failure);
6665        }
6666        Ok(result)
6667    }
6668
6669    pub(crate) fn settle_generator_completed(
6670        &mut self,
6671        generator: Value,
6672    ) -> Result<(), EvalFailure> {
6673        self.replace_executing_generator(generator, GeneratorState::Completed)
6674    }
6675
6676    fn replace_executing_generator(
6677        &mut self,
6678        generator: Value,
6679        next: GeneratorState,
6680    ) -> Result<(), EvalFailure> {
6681        let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6682            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6683                value: generator,
6684            }));
6685        };
6686        let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6687            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6688                value: generator,
6689            }));
6690        };
6691        if !matches!(state, GeneratorState::Executing) {
6692            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6693                value: generator,
6694            }));
6695        }
6696        *state = next;
6697        Ok(())
6698    }
6699
6700    /// Starts an ordinary async function: creates the implicit result Promise,
6701    /// drives the body synchronously to its first `await` or completion under a
6702    /// detached suspend boundary, and returns the Promise. A `return` resolves
6703    /// it and an escaping throw rejects it; a runtime limit failure stays fatal.
6704    pub(crate) fn start_async_call(
6705        &mut self,
6706        target: RuntimeFunction,
6707        captures: &[Value],
6708        this_value: Value,
6709        new_target: Value,
6710        arguments: &[Value],
6711    ) -> Result<Value, EvalFailure> {
6712        let promise = self.create_promise()?;
6713        let record = self.create_async_activation(promise)?;
6714        let stop_depth = self.frames.len();
6715        let return_to = self.frames.last().map(|frame| ReturnTo {
6716            destination: None,
6717            call_pc: frame.pc,
6718            constructed: None,
6719        });
6720        self.push_frame(
6721            target, captures, this_value, new_target, arguments, return_to,
6722        )
6723        .map_err(|error| EvalFailure::Runtime(error.kind))?;
6724        let step = self.drive_async_activation(stop_depth, None);
6725        self.settle_async_step(record, promise, step)?;
6726        Ok(promise)
6727    }
6728
6729    /// Resumes a suspended async activation inside its Promise reaction job. On
6730    /// fulfillment the awaited value is written to `Suspend.dst`; on rejection
6731    /// the reason is thrown at the `Suspend` pc so a covering `try`/`catch`
6732    /// runs. The activation is one-shot; a second resume is a hard error.
6733    fn resume_async(
6734        &mut self,
6735        record: Value,
6736        value: Value,
6737        rejection: Option<ThrowOrigin>,
6738    ) -> Result<(), RuntimeErrorKind> {
6739        let promise = self.async_activation_promise(record)?;
6740        let activation = self.take_async_activation(record)?;
6741        let register_count = activation.registers.len();
6742        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6743            self.release_suspended_activation_registers(register_count);
6744            return Err(RuntimeErrorKind::CallDepthExceeded {
6745                limit: self.limits.max_call_depth,
6746            });
6747        }
6748        let suspend_pc = activation
6749            .resume_token
6750            .checked_sub(1)
6751            .expect("suspended async token is nonzero") as usize;
6752        let instruction = self.module_code(activation.target.module).functions()
6753            [activation.target.function.get() as usize]
6754            .code()[suspend_pc];
6755        let Instruction::Suspend { dst, resume, .. } = instruction else {
6756            unreachable!("async resume token names a suspend instruction");
6757        };
6758        let stop_depth = self.frames.len();
6759        let return_to = self.frames.last().map(|frame| ReturnTo {
6760            destination: None,
6761            call_pc: frame.pc,
6762            constructed: None,
6763        });
6764        let mut frame = Frame {
6765            module: activation.target.module,
6766            function: activation.target.function.get() as usize,
6767            pc: resume.get() as usize,
6768            registers: activation.registers,
6769            return_to,
6770            this_value: activation.this_value,
6771            new_target: activation.new_target,
6772            args: activation.args,
6773            arguments_object: activation.arguments_object,
6774        };
6775        let inject = match rejection {
6776            None => {
6777                frame.registers[dst.get() as usize] = value;
6778                None
6779            }
6780            Some(origin) => Some((value, origin, suspend_pc)),
6781        };
6782        self.frames.push(frame);
6783        let step = self.drive_async_activation(stop_depth, inject);
6784        match self.settle_async_step(record, promise, step) {
6785            Ok(()) => Ok(()),
6786            Err(EvalFailure::Runtime(kind)) => Err(kind),
6787            Err(_) => Err(RuntimeErrorKind::InvalidValue { value: record }),
6788        }
6789    }
6790
6791    /// Runs the interpreter loop for a detached async activation under one
6792    /// suspend and one unwind boundary, optionally injecting a rejection at the
6793    /// resumed `Suspend` pc first. It reports the awaited value on suspension,
6794    /// the returned value on completion, or an uncaught throw; runtime limit
6795    /// failures propagate as fatal `EvalFailure::Runtime`.
6796    fn drive_async_activation(
6797        &mut self,
6798        stop_depth: usize,
6799        inject: Option<(Value, ThrowOrigin, usize)>,
6800    ) -> Result<AsyncStep, EvalFailure> {
6801        self.last_completion = None;
6802        self.pending_async_suspend = None;
6803        self.callback_boundaries.push(stop_depth);
6804        self.async_boundaries.push(stop_depth);
6805        let result = match inject {
6806            None => self.run_loop(stop_depth),
6807            Some((value, origin, faulting_pc)) => match self.throw(value, origin, faulting_pc) {
6808                Ok(()) => self.run_loop(stop_depth),
6809                Err(error) => Err(error),
6810            },
6811        };
6812        self.async_boundaries
6813            .pop()
6814            .expect("async execution owns its suspend boundary");
6815        self.callback_boundaries
6816            .pop()
6817            .expect("async execution owns its unwind boundary");
6818        match result {
6819            Ok(Some(execution)) => Ok(AsyncStep::Return(execution.value)),
6820            Ok(None) => {
6821                if let Some((awaited, activation)) = self.pending_async_suspend.take() {
6822                    Ok(AsyncStep::Suspend {
6823                        awaited,
6824                        activation,
6825                    })
6826                } else {
6827                    Ok(AsyncStep::Return(
6828                        self.last_completion.take().unwrap_or(Value::UNDEFINED),
6829                    ))
6830                }
6831            }
6832            Err(error) => {
6833                self.unwind_frames_to(stop_depth);
6834                match error.kind {
6835                    RuntimeErrorKind::UncaughtThrow { value, origin } => {
6836                        Ok(AsyncStep::Throw { value, origin })
6837                    }
6838                    kind => Err(EvalFailure::Runtime(kind)),
6839                }
6840            }
6841        }
6842    }
6843
6844    /// Settles the result Promise (or arms the next await) for one async step.
6845    fn settle_async_step(
6846        &mut self,
6847        record: Value,
6848        promise: Value,
6849        step: Result<AsyncStep, EvalFailure>,
6850    ) -> Result<(), EvalFailure> {
6851        match step {
6852            Ok(AsyncStep::Suspend {
6853                awaited,
6854                activation,
6855            }) => {
6856                let register_count = activation.registers.len();
6857                let result = self
6858                    .store_async_activation(record, activation)
6859                    .and_then(|()| self.await_promise(awaited, record));
6860                if result.is_err() {
6861                    let released = self
6862                        .take_async_activation(record)
6863                        .map_or(register_count, |stored| stored.registers.len());
6864                    self.release_suspended_activation_registers(released);
6865                }
6866                result
6867            }
6868            Ok(AsyncStep::Return(value)) => self
6869                .resolve_promise(promise, value)
6870                .map_err(EvalFailure::Runtime),
6871            Ok(AsyncStep::Throw { value, origin }) => self
6872                .reject_promise(promise, value, origin)
6873                .map_err(EvalFailure::Runtime),
6874            Err(failure) => Err(failure),
6875        }
6876    }
6877
6878    /// Resolves the awaited value through Promise resolution and attaches the
6879    /// two direct resume reactions that point only at the activation record. An
6880    /// already-settled Promise costs exactly one microtask tick.
6881    fn await_promise(&mut self, awaited: Value, record: Value) -> Result<(), EvalFailure> {
6882        let promise = self.promise_resolve(awaited)?;
6883        let index = self
6884            .runtime_slot(promise)
6885            .map_err(EvalFailure::Runtime)?
6886            .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6887                value: promise,
6888            }))?;
6889        let settled = match &self.heap[index] {
6890            HeapEntry::Promise {
6891                state: PromiseState::Pending { .. },
6892                ..
6893            } => None,
6894            HeapEntry::Promise {
6895                state: PromiseState::Fulfilled { value },
6896                ..
6897            } => Some((true, *value, ThrowOrigin::Bytecode)),
6898            HeapEntry::Promise {
6899                state: PromiseState::Rejected { reason, origin },
6900                ..
6901            } => Some((false, *reason, *origin)),
6902            _ => {
6903                return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6904                    value: promise,
6905                }));
6906            }
6907        };
6908        if let Some((fulfilled, value, origin)) = settled {
6909            self.ensure_microtask_capacity(1)
6910                .map_err(EvalFailure::Runtime)?;
6911            let reaction = if fulfilled {
6912                PromiseReaction::AsyncFulfill { activation: record }
6913            } else {
6914                PromiseReaction::AsyncReject { activation: record }
6915            };
6916            self.microtasks.push_back(MicrotaskJob::Reaction {
6917                reaction,
6918                value,
6919                origin,
6920            });
6921            return Ok(());
6922        }
6923        self.charge_promise_reactions(2)?;
6924        let HeapEntry::Promise {
6925            state:
6926                PromiseState::Pending {
6927                    fulfill_reactions,
6928                    reject_reactions,
6929                },
6930            ..
6931        } = &mut self.heap[index]
6932        else {
6933            unreachable!("pending Promise state was checked before reaction registration");
6934        };
6935        fulfill_reactions.push(PromiseReaction::AsyncFulfill { activation: record });
6936        reject_reactions.push(PromiseReaction::AsyncReject { activation: record });
6937        Ok(())
6938    }
6939
6940    fn create_async_activation(&mut self, promise: Value) -> Result<Value, EvalFailure> {
6941        self.allocate(HeapEntry::AsyncActivation {
6942            activation: None,
6943            promise,
6944        })
6945        .map_err(EvalFailure::Runtime)
6946    }
6947
6948    fn store_async_activation(
6949        &mut self,
6950        record: Value,
6951        activation: SuspendedActivation,
6952    ) -> Result<(), EvalFailure> {
6953        let index = self
6954            .runtime_slot(record)
6955            .map_err(EvalFailure::Runtime)?
6956            .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6957                value: record,
6958            }))?;
6959        let HeapEntry::AsyncActivation {
6960            activation: slot, ..
6961        } = &mut self.heap[index]
6962        else {
6963            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6964                value: record,
6965            }));
6966        };
6967        *slot = Some(activation);
6968        Ok(())
6969    }
6970
6971    /// Takes the one suspended activation out of the record, making resume
6972    /// one-shot. A second take (a second resume) is a hard invalid-state error.
6973    fn take_async_activation(
6974        &mut self,
6975        record: Value,
6976    ) -> Result<SuspendedActivation, RuntimeErrorKind> {
6977        let index = self
6978            .runtime_slot(record)?
6979            .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
6980        let HeapEntry::AsyncActivation {
6981            activation: slot, ..
6982        } = &mut self.heap[index]
6983        else {
6984            return Err(RuntimeErrorKind::InvalidValue { value: record });
6985        };
6986        slot.take()
6987            .ok_or(RuntimeErrorKind::InvalidValue { value: record })
6988    }
6989
6990    fn async_activation_promise(&self, record: Value) -> Result<Value, RuntimeErrorKind> {
6991        let index = self
6992            .runtime_slot(record)?
6993            .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
6994        let HeapEntry::AsyncActivation { promise, .. } = &self.heap[index] else {
6995            return Err(RuntimeErrorKind::InvalidValue { value: record });
6996        };
6997        Ok(*promise)
6998    }
6999
7000    pub(crate) fn iterator_result(
7001        &mut self,
7002        value: Value,
7003        done: bool,
7004    ) -> Result<Value, EvalFailure> {
7005        let result = self
7006            .allocate(HeapEntry::Object {
7007                properties: PropertyMap::default(),
7008                prototype: Some(self.intrinsics.object_prototype),
7009                boxed_primitive: None,
7010                extensible: true,
7011            })
7012            .map_err(EvalFailure::Runtime)?;
7013        self.set_data_property(result, "value", value)?;
7014        self.set_data_property(result, "done", Value::boolean(done))?;
7015        Ok(result)
7016    }
7017
7018    // ---- iterators ---------------------------------------------------------
7019
7020    fn create_iterator(&mut self, src: Value, kind: IteratorKind) -> Result<Value, EvalFailure> {
7021        if kind == IteratorKind::Keys {
7022            let keys = self.enumerable_keys(src)?;
7023            return self
7024                .allocate(HeapEntry::Iterator {
7025                    state: IteratorState::Keys { index: 0, keys },
7026                })
7027                .map_err(EvalFailure::Runtime);
7028        }
7029
7030        let iterator_symbol = self.intrinsics.builtins.symbol_iterator();
7031        let iterator_key = self.to_property_key(iterator_symbol)?;
7032        let method = self.get_property_key(src, &iterator_key)?;
7033        if !self.is_callable(method)? {
7034            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7035                operation: "value is not iterable",
7036            }));
7037        }
7038        let iterator = self.call_value(method, src, &[])?;
7039        if !self.is_object(iterator) {
7040            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7041                operation: "iterator method returned a non-object",
7042            }));
7043        }
7044        let next = self.get_named_property(iterator, "next")?;
7045        self.create_protocol_iterator(iterator, next)
7046    }
7047
7048    pub(crate) fn create_protocol_iterator(
7049        &mut self,
7050        iterator: Value,
7051        next: Value,
7052    ) -> Result<Value, EvalFailure> {
7053        self.allocate(HeapEntry::Iterator {
7054            state: IteratorState::Protocol { iterator, next },
7055        })
7056        .map_err(EvalFailure::Runtime)
7057    }
7058
7059    fn own_property_keys(&self, src: Value) -> Result<Vec<PropertyKey>, EvalFailure> {
7060        match self.runtime_slot(src).map_err(EvalFailure::Runtime)? {
7061            Some(index) => match &self.heap[index] {
7062                HeapEntry::Object { properties, .. }
7063                | HeapEntry::Generator { properties, .. }
7064                | HeapEntry::Script { properties, .. }
7065                | HeapEntry::Function { properties, .. }
7066                | HeapEntry::NativeFunction { properties, .. }
7067                | HeapEntry::RegExp { properties, .. }
7068                | HeapEntry::Date { properties, .. }
7069                | HeapEntry::BuiltinIterator { properties, .. }
7070                | HeapEntry::Collection { properties, .. }
7071                | HeapEntry::Promise { properties, .. }
7072                | HeapEntry::Timeout { properties, .. } => Ok(ordered_property_keys(properties)),
7073                HeapEntry::Array {
7074                    elements,
7075                    properties,
7076                    ..
7077                } => {
7078                    let mut indices: Vec<(usize, PropertyKey)> = elements
7079                        .iter()
7080                        .enumerate()
7081                        .filter(|(_, element)| **element != Value::HOLE)
7082                        .map(|(offset, _)| {
7083                            (
7084                                offset,
7085                                PropertyKey::Named(EcmaString::from_utf8(&offset.to_string())),
7086                            )
7087                        })
7088                        .collect();
7089                    let mut suffix = Vec::new();
7090                    for key in ordered_property_keys(properties) {
7091                        let Some(offset) = key.as_string().and_then(array_index) else {
7092                            suffix.push(key);
7093                            continue;
7094                        };
7095                        let offset = offset as usize;
7096                        if elements
7097                            .get(offset)
7098                            .is_some_and(|element| *element != Value::HOLE)
7099                        {
7100                            continue;
7101                        }
7102                        indices.push((offset, key));
7103                    }
7104                    indices.sort_unstable_by_key(|(offset, _)| *offset);
7105                    Ok(indices
7106                        .into_iter()
7107                        .map(|(_, key)| key)
7108                        .chain(suffix)
7109                        .collect())
7110                }
7111                HeapEntry::String(text) => Ok((0..text.len_units())
7112                    .map(|index| PropertyKey::Named(EcmaString::from_utf8(&index.to_string())))
7113                    .collect()),
7114                HeapEntry::ModuleNamespace { module } => {
7115                    let mut names: Vec<EcmaString> = self
7116                        .program_module(*module)
7117                        .exports
7118                        .iter()
7119                        .map(|export| self.constant_text(*module, export.name).clone())
7120                        .collect();
7121                    names.sort();
7122                    Ok(names.into_iter().map(PropertyKey::Named).collect())
7123                }
7124                HeapEntry::ExternalModuleNamespace { specifier } => Ok(self.registry.external
7125                    [specifier]
7126                    .exports
7127                    .keys()
7128                    .cloned()
7129                    .map(PropertyKey::Named)
7130                    .collect()),
7131                HeapEntry::ProcessEnv { .. }
7132                | HeapEntry::BigInt(_)
7133                | HeapEntry::Symbol { .. }
7134                | HeapEntry::PrivateName { .. }
7135                | HeapEntry::HashState { .. }
7136                | HeapEntry::Iterator { .. }
7137                | HeapEntry::PromiseResolver { .. }
7138                | HeapEntry::PromiseFinally { .. }
7139                | HeapEntry::PromiseAll { .. }
7140                | HeapEntry::AsyncActivation { .. }
7141                | HeapEntry::PromiseAllElement { .. } => Ok(Vec::new()),
7142            },
7143            None => Ok(Vec::new()),
7144        }
7145    }
7146
7147    fn own_property_is_enumerable(
7148        &self,
7149        src: Value,
7150        key: &PropertyKey,
7151    ) -> Result<bool, EvalFailure> {
7152        let Some(index) = self.runtime_slot(src).map_err(EvalFailure::Runtime)? else {
7153            return Ok(false);
7154        };
7155        Ok(match &self.heap[index] {
7156            HeapEntry::Array {
7157                elements,
7158                properties,
7159                ..
7160            } => properties.get(key).map_or_else(
7161                || {
7162                    key.as_string().is_some_and(|name| {
7163                        array_index(name).is_some_and(|offset| {
7164                            elements
7165                                .get(offset as usize)
7166                                .is_some_and(|element| *element != Value::HOLE)
7167                        })
7168                    })
7169                },
7170                Property::enumerable,
7171            ),
7172            HeapEntry::String(text) => key.as_string().is_some_and(|name| {
7173                array_index(name).is_some_and(|offset| (offset as usize) < text.len_units())
7174            }),
7175            HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
7176                matches!(key, PropertyKey::Named(_))
7177            }
7178            HeapEntry::Object { properties, .. }
7179            | HeapEntry::Generator { properties, .. }
7180            | HeapEntry::Script { properties, .. }
7181            | HeapEntry::Function { properties, .. }
7182            | HeapEntry::NativeFunction { properties, .. }
7183            | HeapEntry::RegExp { properties, .. }
7184            | HeapEntry::Date { properties, .. }
7185            | HeapEntry::BuiltinIterator { properties, .. }
7186            | HeapEntry::Collection { properties, .. }
7187            | HeapEntry::Promise { properties, .. }
7188            | HeapEntry::Timeout { properties, .. } => {
7189                properties.get(key).is_some_and(Property::enumerable)
7190            }
7191            _ => false,
7192        })
7193    }
7194
7195    fn enumerable_keys(&self, src: Value) -> Result<Vec<EcmaString>, EvalFailure> {
7196        let mut names = Vec::new();
7197        for key in self.own_property_keys(src)? {
7198            if !self.own_property_is_enumerable(src, &key)? {
7199                continue;
7200            }
7201            if let PropertyKey::Named(name) = key {
7202                names.push(name);
7203            }
7204        }
7205        Ok(names)
7206    }
7207
7208    fn iterator_next(&mut self, iterator: Value) -> Result<(bool, Value), EvalFailure> {
7209        let (callee, this_value) = match self.prepare_iterator_next(iterator)? {
7210            IteratorNextPrepared::Ready { done, value } => return Ok((done, value)),
7211            IteratorNextPrepared::Call { callee, this_value } => (callee, this_value),
7212        };
7213
7214        let result = self.call_value(callee, this_value, &[])?;
7215        if !self.is_object(result) {
7216            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7217                operation: "iterator next returned a non-object",
7218            }));
7219        }
7220        let done = self.get_named_property(result, "done")?;
7221        if self.truthy(done) {
7222            return Ok((true, Value::UNDEFINED));
7223        }
7224        let value = self.get_named_property(result, "value")?;
7225        Ok((false, value))
7226    }
7227
7228    pub(crate) fn prepare_iterator_next(
7229        &mut self,
7230        iterator: Value,
7231    ) -> Result<IteratorNextPrepared, EvalFailure> {
7232        let iterator_index = self
7233            .runtime_slot(iterator)
7234            .map_err(EvalFailure::Runtime)?
7235            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
7236                operation: "iterator next on non-iterator",
7237            }))?;
7238        match &self.heap[iterator_index] {
7239            HeapEntry::Iterator {
7240                state: IteratorState::Keys { index, keys },
7241            } => {
7242                let Some(text) = keys.get(*index).cloned() else {
7243                    return Ok(IteratorNextPrepared::Ready {
7244                        done: true,
7245                        value: Value::UNDEFINED,
7246                    });
7247                };
7248                let value = self
7249                    .allocate(HeapEntry::String(text))
7250                    .map_err(EvalFailure::Runtime)?;
7251                self.advance_iterator(iterator_index);
7252                Ok(IteratorNextPrepared::Ready { done: false, value })
7253            }
7254            HeapEntry::Iterator {
7255                state: IteratorState::Protocol { iterator, next },
7256            } => Ok(IteratorNextPrepared::Call {
7257                callee: *next,
7258                this_value: *iterator,
7259            }),
7260            _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7261                operation: "iterator next on non-iterator",
7262            })),
7263        }
7264    }
7265
7266    pub(crate) fn iterable_values(&mut self, source: Value) -> Result<Vec<Value>, EvalFailure> {
7267        let iterator = self.create_iterator(source, IteratorKind::Sync)?;
7268        let mut values = Vec::new();
7269        loop {
7270            let (done, value) = self.iterator_next(iterator)?;
7271            if done {
7272                return Ok(values);
7273            }
7274            let bytes = values
7275                .len()
7276                .checked_add(1)
7277                .and_then(|length| length.checked_mul(std::mem::size_of::<Value>()))
7278                .ok_or(EvalFailure::Runtime(
7279                    RuntimeErrorKind::HeapByteLimitExceeded {
7280                        limit: self.limits.max_heap_bytes,
7281                    },
7282                ))?;
7283            self.ensure_allocation_capacity(1, bytes)
7284                .map_err(EvalFailure::Runtime)?;
7285            values.push(value);
7286        }
7287    }
7288
7289    fn advance_iterator(&mut self, iterator_index: usize) {
7290        if let HeapEntry::Iterator {
7291            state: IteratorState::Keys { index, .. },
7292        } = &mut self.heap[iterator_index]
7293        {
7294            *index += 1;
7295        }
7296    }
7297
7298    // ---- operators & coercions --------------------------------------------
7299
7300    fn eval_unary(&mut self, op: UnaryOp, operand: Value) -> Result<Value, EvalFailure> {
7301        match op {
7302            UnaryOp::Void => Ok(Value::UNDEFINED),
7303            UnaryOp::TypeOf => {
7304                let text = EcmaString::from_utf8(self.type_of(operand));
7305                self.allocate(HeapEntry::String(text))
7306                    .map_err(EvalFailure::Runtime)
7307            }
7308            UnaryOp::Plus => self.to_number(operand),
7309            UnaryOp::Negate => {
7310                if let Some(text) = self.bigint_text(operand) {
7311                    let negated = if text == "0" {
7312                        "0".to_owned()
7313                    } else if let Some(positive) = text.strip_prefix('-') {
7314                        positive.to_owned()
7315                    } else {
7316                        format!("-{text}")
7317                    };
7318                    return self
7319                        .allocate(HeapEntry::BigInt(negated))
7320                        .map_err(EvalFailure::Runtime);
7321                }
7322                let number =
7323                    numeric_f64(self.to_number(operand)?).expect("ToNumber returns numeric");
7324                Ok(number_value(-number))
7325            }
7326            UnaryOp::BitwiseNot => {
7327                if let Some(text) = self.bigint_text(operand) {
7328                    let value = text.parse::<i128>().map_err(|_| {
7329                        EvalFailure::Throw(ThrowOrigin::RangeError {
7330                            operation: "bigint bitwise not",
7331                        })
7332                    })?;
7333                    return self
7334                        .allocate(HeapEntry::BigInt((!value).to_string()))
7335                        .map_err(EvalFailure::Runtime);
7336                }
7337                Ok(Value::int32(
7338                    (!to_int32(numeric_f64(self.to_number(operand)?).unwrap())) as u32,
7339                ))
7340            }
7341            UnaryOp::LogicalNot => Ok(Value::boolean(!self.truthy(operand))),
7342        }
7343    }
7344
7345    fn eval_binary(
7346        &mut self,
7347        op: BinaryOp,
7348        left: Value,
7349        right: Value,
7350    ) -> Result<Value, EvalFailure> {
7351        match op {
7352            BinaryOp::StrictEqual => Ok(Value::boolean(self.strict_equal(left, right))),
7353            BinaryOp::StrictNotEqual => Ok(Value::boolean(!self.strict_equal(left, right))),
7354            BinaryOp::Equal | BinaryOp::NotEqual => {
7355                let equal = self.abstract_equal(left, right)?;
7356                Ok(Value::boolean(if op == BinaryOp::Equal {
7357                    equal
7358                } else {
7359                    !equal
7360                }))
7361            }
7362            BinaryOp::LessThan
7363            | BinaryOp::LessThanOrEqual
7364            | BinaryOp::GreaterThan
7365            | BinaryOp::GreaterThanOrEqual => {
7366                let ordering = self.relational_compare(left, right)?;
7367                let result = match (op, ordering) {
7368                    (_, None) => false,
7369                    (BinaryOp::LessThan, Some(order)) => order == Ordering::Less,
7370                    (BinaryOp::LessThanOrEqual, Some(order)) => order != Ordering::Greater,
7371                    (BinaryOp::GreaterThan, Some(order)) => order == Ordering::Greater,
7372                    (BinaryOp::GreaterThanOrEqual, Some(order)) => order != Ordering::Less,
7373                    _ => unreachable!(),
7374                };
7375                Ok(Value::boolean(result))
7376            }
7377            BinaryOp::InstanceOf => self.instance_of(left, right).map(Value::boolean),
7378            BinaryOp::In => {
7379                let key = self.to_property_key(left)?;
7380                self.has_property(right, &key).map(Value::boolean)
7381            }
7382            BinaryOp::Add => self.add(left, right),
7383            BinaryOp::Subtract
7384            | BinaryOp::Multiply
7385            | BinaryOp::Divide
7386            | BinaryOp::Remainder
7387            | BinaryOp::Exponent
7388            | BinaryOp::BitAnd
7389            | BinaryOp::BitOr
7390            | BinaryOp::BitXor
7391            | BinaryOp::ShiftLeft
7392            | BinaryOp::ShiftRight
7393            | BinaryOp::UnsignedShiftRight => self.numeric_binary(op, left, right),
7394        }
7395    }
7396
7397    fn add(&mut self, left: Value, right: Value) -> Result<Value, EvalFailure> {
7398        let left = self.to_primitive_default(left)?;
7399        let right = self.to_primitive_default(right)?;
7400        let left_string = self.string_text(left).cloned();
7401        let right_string = self.string_text(right).cloned();
7402        if left_string.is_some() || right_string.is_some() {
7403            let left = match left_string {
7404                Some(text) => text,
7405                None => self.to_string(left)?,
7406            };
7407            let right = match right_string {
7408                Some(text) => text,
7409                None => self.to_string(right)?,
7410            };
7411            let mut builder = EcmaStringBuilder::with_capacity(
7412                left.len_units().saturating_add(right.len_units()),
7413            );
7414            for &unit in left.as_units() {
7415                builder.push_unit(unit);
7416            }
7417            for &unit in right.as_units() {
7418                builder.push_unit(unit);
7419            }
7420            return self
7421                .allocate(HeapEntry::String(builder.finish()))
7422                .map_err(EvalFailure::Runtime);
7423        }
7424        let left_bigint = self.bigint_text(left).map(str::to_owned);
7425        let right_bigint = self.bigint_text(right).map(str::to_owned);
7426        match (left_bigint, right_bigint) {
7427            (Some(left), Some(right)) => {
7428                let sum = bigint_i128(&left)?
7429                    .checked_add(bigint_i128(&right)?)
7430                    .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError {
7431                        operation: "bigint add overflow",
7432                    }))?;
7433                return self
7434                    .allocate(HeapEntry::BigInt(sum.to_string()))
7435                    .map_err(EvalFailure::Runtime);
7436            }
7437            (Some(_), None) | (None, Some(_)) => {
7438                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7439                    operation: "add bigint and number",
7440                }));
7441            }
7442            (None, None) => {}
7443        }
7444        let left = numeric_f64(self.to_number(left)?).unwrap();
7445        let right = numeric_f64(self.to_number(right)?).unwrap();
7446        Ok(number_value(left + right))
7447    }
7448
7449    fn numeric_binary(
7450        &mut self,
7451        op: BinaryOp,
7452        left: Value,
7453        right: Value,
7454    ) -> Result<Value, EvalFailure> {
7455        let left_bigint = self.bigint_text(left).map(str::to_owned);
7456        let right_bigint = self.bigint_text(right).map(str::to_owned);
7457        if left_bigint.is_some() || right_bigint.is_some() {
7458            let (Some(left), Some(right)) = (left_bigint, right_bigint) else {
7459                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7460                    operation: "mix bigint and number",
7461                }));
7462            };
7463            let result = bigint_binary(op, &left, &right)?;
7464            return self
7465                .allocate(HeapEntry::BigInt(result))
7466                .map_err(EvalFailure::Runtime);
7467        }
7468        let left = numeric_f64(self.to_number(left)?).unwrap();
7469        let right = numeric_f64(self.to_number(right)?).unwrap();
7470        let value = match op {
7471            BinaryOp::Subtract => number_value(left - right),
7472            BinaryOp::Multiply => number_value(left * right),
7473            BinaryOp::Divide => Value::number(left / right),
7474            BinaryOp::Remainder => Value::number(left % right),
7475            BinaryOp::Exponent => Value::number(left.powf(right)),
7476            BinaryOp::BitAnd => Value::int32((to_int32(left) & to_int32(right)) as u32),
7477            BinaryOp::BitOr => Value::int32((to_int32(left) | to_int32(right)) as u32),
7478            BinaryOp::BitXor => Value::int32((to_int32(left) ^ to_int32(right)) as u32),
7479            BinaryOp::ShiftLeft => {
7480                Value::int32(to_int32(left).wrapping_shl(to_uint32(right) & 31) as u32)
7481            }
7482            BinaryOp::ShiftRight => {
7483                Value::int32((to_int32(left) >> (to_uint32(right) & 31)) as u32)
7484            }
7485            BinaryOp::UnsignedShiftRight => {
7486                number_value((to_uint32(left) >> (to_uint32(right) & 31)) as f64)
7487            }
7488            _ => unreachable!("numeric binary operator partition"),
7489        };
7490        Ok(value)
7491    }
7492
7493    fn coercion_is_primitive(&self, value: Value) -> Result<bool, EvalFailure> {
7494        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
7495            return Ok(true);
7496        };
7497        Ok(matches!(
7498            self.heap[index],
7499            HeapEntry::String(_)
7500                | HeapEntry::BigInt(_)
7501                | HeapEntry::Symbol { .. }
7502                | HeapEntry::PrivateName { .. }
7503        ))
7504    }
7505
7506    fn to_primitive_default(&mut self, value: Value) -> Result<Value, EvalFailure> {
7507        let prefer_string = self
7508            .runtime_slot(value)
7509            .map_err(EvalFailure::Runtime)?
7510            .is_some_and(|index| matches!(self.heap[index], HeapEntry::Date { .. }));
7511        self.to_primitive_observable(value, prefer_string)
7512    }
7513
7514    pub(crate) fn to_primitive_observable(
7515        &mut self,
7516        value: Value,
7517        prefer_string: bool,
7518    ) -> Result<Value, EvalFailure> {
7519        if self.coercion_is_primitive(value)? {
7520            return Ok(value);
7521        }
7522        let methods = if prefer_string {
7523            ["toString", "valueOf"]
7524        } else {
7525            ["valueOf", "toString"]
7526        };
7527        for name in methods {
7528            let method = self.get_named_property(value, name)?;
7529            if !self.is_callable(method)? {
7530                continue;
7531            }
7532            let primitive = self.call_value(method, value, &[])?;
7533            if self.coercion_is_primitive(primitive)? {
7534                return Ok(primitive);
7535            }
7536        }
7537        Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7538            operation: "cannot convert object to primitive",
7539        }))
7540    }
7541
7542    pub(crate) fn to_string_observable(&mut self, value: Value) -> Result<EcmaString, EvalFailure> {
7543        let primitive = self.to_primitive_observable(value, true)?;
7544        self.to_string(primitive)
7545    }
7546
7547    pub(crate) fn to_number_observable(&mut self, value: Value) -> Result<Value, EvalFailure> {
7548        let primitive = self.to_primitive_observable(value, false)?;
7549        self.to_number(primitive)
7550    }
7551
7552    fn to_number(&self, value: Value) -> Result<Value, EvalFailure> {
7553        match value.decode() {
7554            Some(Decoded::Number(_)) | Some(Decoded::Int32(_)) => self.to_primitive(value),
7555            Some(Decoded::Undefined) => Ok(Value::number(f64::NAN)),
7556            Some(Decoded::Null) => Ok(Value::int32(0)),
7557            Some(Decoded::Boolean(value)) => Ok(Value::int32(u32::from(value))),
7558            Some(Decoded::Hole) | Some(Decoded::Uninitialized) => Ok(Value::number(f64::NAN)),
7559            Some(Decoded::HeapRef(_)) => {
7560                match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7561                    Some(index) => match &self.heap[index] {
7562                        HeapEntry::String(text) => Ok(number_value(parse_number(text))),
7563                        HeapEntry::BigInt(_) => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7564                            operation: "convert bigint to number",
7565                        })),
7566                        HeapEntry::Array { elements, .. } if elements.is_empty() => {
7567                            Ok(Value::int32(0))
7568                        }
7569                        HeapEntry::Array { elements, .. } if elements.len() == 1 => {
7570                            self.to_number(elements[0])
7571                        }
7572                        HeapEntry::Symbol { .. } | HeapEntry::PrivateName { .. } => {
7573                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7574                                operation: "convert symbol to number",
7575                            }))
7576                        }
7577                        HeapEntry::Object { .. }
7578                        | HeapEntry::Generator { .. }
7579                        | HeapEntry::Script { .. }
7580                        | HeapEntry::Array { .. }
7581                        | HeapEntry::Function { .. }
7582                        | HeapEntry::ModuleNamespace { .. }
7583                        | HeapEntry::ExternalModuleNamespace { .. }
7584                        | HeapEntry::HashState { .. }
7585                        | HeapEntry::NativeFunction { .. }
7586                        | HeapEntry::RegExp { .. }
7587                        | HeapEntry::Date { .. }
7588                        | HeapEntry::BuiltinIterator { .. }
7589                        | HeapEntry::Collection { .. }
7590                        | HeapEntry::Promise { .. }
7591                        | HeapEntry::PromiseResolver { .. }
7592                        | HeapEntry::PromiseFinally { .. }
7593                        | HeapEntry::PromiseAll { .. }
7594                        | HeapEntry::AsyncActivation { .. }
7595                        | HeapEntry::PromiseAllElement { .. }
7596                        | HeapEntry::ProcessEnv { .. }
7597                        | HeapEntry::Iterator { .. }
7598                        | HeapEntry::Timeout { .. } => Ok(Value::number(f64::NAN)),
7599                    },
7600                    None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7601                        operation: "coerce host object to number",
7602                    })),
7603                }
7604            }
7605            None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7606                value,
7607            })),
7608        }
7609    }
7610
7611    fn truthy(&self, value: Value) -> bool {
7612        match value.decode() {
7613            Some(Decoded::Number(number)) => number != 0.0 && !number.is_nan(),
7614            Some(Decoded::Int32(value)) => value != 0,
7615            Some(Decoded::Undefined | Decoded::Null | Decoded::Hole | Decoded::Uninitialized)
7616            | None => false,
7617            Some(Decoded::Boolean(value)) => value,
7618            Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7619                Ok(Some(index)) => match &self.heap[index] {
7620                    HeapEntry::String(text) => !text.is_empty(),
7621                    HeapEntry::BigInt(text) => text != "0",
7622                    HeapEntry::Object { .. }
7623                    | HeapEntry::Generator { .. }
7624                    | HeapEntry::Script { .. }
7625                    | HeapEntry::Array { .. }
7626                    | HeapEntry::Function { .. }
7627                    | HeapEntry::ModuleNamespace { .. }
7628                    | HeapEntry::ExternalModuleNamespace { .. }
7629                    | HeapEntry::HashState { .. }
7630                    | HeapEntry::NativeFunction { .. }
7631                    | HeapEntry::Symbol { .. }
7632                    | HeapEntry::PrivateName { .. }
7633                    | HeapEntry::RegExp { .. }
7634                    | HeapEntry::Date { .. }
7635                    | HeapEntry::BuiltinIterator { .. }
7636                    | HeapEntry::Collection { .. }
7637                    | HeapEntry::Promise { .. }
7638                    | HeapEntry::PromiseResolver { .. }
7639                    | HeapEntry::PromiseFinally { .. }
7640                    | HeapEntry::PromiseAll { .. }
7641                    | HeapEntry::AsyncActivation { .. }
7642                    | HeapEntry::PromiseAllElement { .. }
7643                    | HeapEntry::ProcessEnv { .. }
7644                    | HeapEntry::Iterator { .. }
7645                    | HeapEntry::Timeout { .. } => true,
7646                },
7647                Ok(None) => true,
7648                Err(_) => false,
7649            },
7650        }
7651    }
7652
7653    fn type_of(&self, value: Value) -> &'static str {
7654        match value.decode() {
7655            Some(Decoded::Undefined | Decoded::Hole | Decoded::Uninitialized) | None => "undefined",
7656            Some(Decoded::Number(_) | Decoded::Int32(_)) => "number",
7657            Some(Decoded::Null) => "object",
7658            Some(Decoded::Boolean(_)) => "boolean",
7659            Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7660                Ok(Some(index)) => match &self.heap[index] {
7661                    HeapEntry::String(_) => "string",
7662                    HeapEntry::BigInt(_) => "bigint",
7663                    HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. } => "function",
7664                    HeapEntry::Symbol { .. } => "symbol",
7665                    HeapEntry::PrivateName { .. } => "object",
7666                    HeapEntry::Object { .. }
7667                    | HeapEntry::Generator { .. }
7668                    | HeapEntry::Script { .. }
7669                    | HeapEntry::Array { .. }
7670                    | HeapEntry::ModuleNamespace { .. }
7671                    | HeapEntry::ExternalModuleNamespace { .. }
7672                    | HeapEntry::HashState { .. }
7673                    | HeapEntry::RegExp { .. }
7674                    | HeapEntry::Date { .. }
7675                    | HeapEntry::BuiltinIterator { .. }
7676                    | HeapEntry::Collection { .. }
7677                    | HeapEntry::Promise { .. }
7678                    | HeapEntry::PromiseResolver { .. }
7679                    | HeapEntry::PromiseFinally { .. }
7680                    | HeapEntry::PromiseAll { .. }
7681                    | HeapEntry::AsyncActivation { .. }
7682                    | HeapEntry::PromiseAllElement { .. }
7683                    | HeapEntry::ProcessEnv { .. }
7684                    | HeapEntry::Iterator { .. }
7685                    | HeapEntry::Timeout { .. } => "object",
7686                },
7687                _ => "object",
7688            },
7689        }
7690    }
7691
7692    fn strict_equal(&self, left: Value, right: Value) -> bool {
7693        match (left.decode(), right.decode()) {
7694            (Some(Decoded::Number(a)), Some(Decoded::Number(b))) => a == b,
7695            (Some(Decoded::Number(a)), Some(Decoded::Int32(b)))
7696            | (Some(Decoded::Int32(b)), Some(Decoded::Number(a))) => a == f64::from(b as i32),
7697            (Some(Decoded::Int32(a)), Some(Decoded::Int32(b))) => a == b,
7698            (Some(Decoded::HeapRef(_)), Some(Decoded::HeapRef(_))) => {
7699                match (self.runtime_slot(left), self.runtime_slot(right)) {
7700                    (Ok(Some(a)), Ok(Some(b))) => match (&self.heap[a], &self.heap[b]) {
7701                        (HeapEntry::String(a), HeapEntry::String(b)) => a == b,
7702                        (HeapEntry::BigInt(a), HeapEntry::BigInt(b)) => a == b,
7703                        _ => left == right,
7704                    },
7705                    _ => left == right,
7706                }
7707            }
7708            _ => left == right,
7709        }
7710    }
7711
7712    fn abstract_equal(&self, left: Value, right: Value) -> Result<bool, EvalFailure> {
7713        if self.strict_equal(left, right) {
7714            return Ok(true);
7715        }
7716        if matches!(
7717            (left.decode(), right.decode()),
7718            (Some(Decoded::Null), Some(Decoded::Undefined))
7719                | (Some(Decoded::Undefined), Some(Decoded::Null))
7720        ) {
7721            return Ok(true);
7722        }
7723        let left_number = self.to_number(left);
7724        let right_number = self.to_number(right);
7725        match (left_number, right_number) {
7726            (Ok(left), Ok(right)) => Ok(numeric_f64(left).unwrap() == numeric_f64(right).unwrap()),
7727            _ => Ok(false),
7728        }
7729    }
7730
7731    fn relational_compare(
7732        &self,
7733        left: Value,
7734        right: Value,
7735    ) -> Result<Option<Ordering>, EvalFailure> {
7736        if let (Some(left), Some(right)) = (self.string_text(left), self.string_text(right)) {
7737            return Ok(Some(left.cmp(right)));
7738        }
7739        if let (Some(left), Some(right)) = (self.bigint_text(left), self.bigint_text(right)) {
7740            return Ok(Some(bigint_i128(left)?.cmp(&bigint_i128(right)?)));
7741        }
7742        let left = numeric_f64(self.to_number(left)?).unwrap();
7743        let right = numeric_f64(self.to_number(right)?).unwrap();
7744        Ok(left.partial_cmp(&right))
7745    }
7746
7747    /// `value instanceof constructor`: walks `value`'s prototype chain for the
7748    /// constructor's own `prototype` object, matching by heap identity.
7749    fn instance_of(&mut self, value: Value, constructor: Value) -> Result<bool, EvalFailure> {
7750        let constructor = self
7751            .bound_target(constructor)
7752            .map_err(EvalFailure::Runtime)?;
7753        match self
7754            .runtime_slot(constructor)
7755            .map_err(EvalFailure::Runtime)?
7756        {
7757            Some(index) => {
7758                if !matches!(
7759                    self.heap[index],
7760                    HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
7761                ) {
7762                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7763                        operation: "instanceof",
7764                    }));
7765                }
7766                let target = match self.own_get_ascii(index, "prototype") {
7767                    Some(Found::Value(value)) if self.is_object(value) => value,
7768                    _ => {
7769                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7770                            operation: "instanceof prototype is not an object",
7771                        }));
7772                    }
7773                };
7774                let target_slot = self.runtime_slot(target).map_err(EvalFailure::Runtime)?;
7775                let mut node = match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7776                    Some(node) => node,
7777                    None => return Ok(false),
7778                };
7779                let mut guard = 0;
7780                loop {
7781                    if Some(node) == target_slot {
7782                        return Ok(true);
7783                    }
7784                    match self.prototype_index(node)? {
7785                        Some(next) => {
7786                            node = next;
7787                            guard += 1;
7788                            if guard > self.heap.len() + 1 {
7789                                return Ok(false);
7790                            }
7791                        }
7792                        None => return Ok(false),
7793                    }
7794                }
7795            }
7796            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7797                operation: "instanceof",
7798            })),
7799        }
7800    }
7801
7802    fn value_to_string(&self, value: Value, depth: usize) -> Result<EcmaString, EvalFailure> {
7803        if depth >= 32 {
7804            return Ok(EcmaString::default());
7805        }
7806        let ascii = |text: String| EcmaString::from_utf8(&text);
7807        match value.decode() {
7808            Some(Decoded::Number(number)) => Ok(ascii(Self::ordinary_number_to_string(number))),
7809            Some(Decoded::Int32(raw)) => Ok(ascii((raw as i32).to_string())),
7810            Some(Decoded::Undefined | Decoded::Uninitialized) => {
7811                Ok(EcmaString::from_utf8("undefined"))
7812            }
7813            Some(Decoded::Null) => Ok(EcmaString::from_utf8("null")),
7814            Some(Decoded::Boolean(value)) => {
7815                Ok(EcmaString::from_utf8(if value { "true" } else { "false" }))
7816            }
7817            Some(Decoded::Hole) => Ok(EcmaString::default()),
7818            Some(Decoded::HeapRef(_)) => {
7819                match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7820                    Some(index) => match &self.heap[index] {
7821                        HeapEntry::String(text) => Ok(text.clone()),
7822                        HeapEntry::BigInt(text) => Ok(EcmaString::from_utf8(text)),
7823                        HeapEntry::Object { .. }
7824                        | HeapEntry::Generator { .. }
7825                        | HeapEntry::Script { .. }
7826                        | HeapEntry::Date { .. }
7827                        | HeapEntry::BuiltinIterator { .. }
7828                        | HeapEntry::Collection { .. }
7829                        | HeapEntry::Promise { .. }
7830                        | HeapEntry::PromiseResolver { .. }
7831                        | HeapEntry::PromiseFinally { .. }
7832                        | HeapEntry::PromiseAll { .. }
7833                        | HeapEntry::AsyncActivation { .. }
7834                        | HeapEntry::PromiseAllElement { .. }
7835                        | HeapEntry::ModuleNamespace { .. }
7836                        | HeapEntry::ExternalModuleNamespace { .. }
7837                        | HeapEntry::ProcessEnv { .. }
7838                        | HeapEntry::Iterator { .. }
7839                        | HeapEntry::Timeout { .. }
7840                        | HeapEntry::HashState { .. } => {
7841                            Ok(EcmaString::from_utf8("[object Object]"))
7842                        }
7843                        HeapEntry::RegExp { pattern, flags, .. } => {
7844                            let mut builder = EcmaStringBuilder::with_capacity(
7845                                pattern
7846                                    .len_units()
7847                                    .saturating_add(flags.len_units())
7848                                    .saturating_add(2),
7849                            );
7850                            builder.push_unit(u16::from(b'/'));
7851                            for &unit in pattern.as_units() {
7852                                builder.push_unit(unit);
7853                            }
7854                            builder.push_unit(u16::from(b'/'));
7855                            for &unit in flags.as_units() {
7856                                builder.push_unit(unit);
7857                            }
7858                            Ok(builder.finish())
7859                        }
7860                        HeapEntry::Symbol { .. } => {
7861                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7862                                operation: "convert symbol to string",
7863                            }))
7864                        }
7865                        HeapEntry::PrivateName { .. } => {
7866                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7867                                operation: "convert private name to string",
7868                            }))
7869                        }
7870                        HeapEntry::Function {
7871                            module, function, ..
7872                        } => {
7873                            let flags = self.module_code(*module).functions()
7874                                [function.get() as usize]
7875                                .flags();
7876                            Ok(EcmaString::from_utf8(
7877                                match (flags.is_async, flags.is_generator) {
7878                                    (true, true) => "async function* () { [bytecode] }",
7879                                    (true, false) => "async function () { [bytecode] }",
7880                                    (false, true) => "function* () { [bytecode] }",
7881                                    (false, false) => "function () { [bytecode] }",
7882                                },
7883                            ))
7884                        }
7885                        HeapEntry::NativeFunction { .. } => {
7886                            Ok(EcmaString::from_utf8("function () { [native code] }"))
7887                        }
7888                        HeapEntry::Array { elements, .. } => {
7889                            let mut text = EcmaStringBuilder::new();
7890                            for (index, element) in elements.iter().copied().enumerate() {
7891                                if index != 0 {
7892                                    text.push_unit(u16::from(b','));
7893                                }
7894                                if element != Value::HOLE
7895                                    && element != Value::NULL
7896                                    && element != Value::UNDEFINED
7897                                {
7898                                    for &unit in
7899                                        self.value_to_string(element, depth + 1)?.as_units()
7900                                    {
7901                                        text.push_unit(unit);
7902                                    }
7903                                }
7904                            }
7905                            Ok(text.finish())
7906                        }
7907                    },
7908                    None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7909                        operation: "coerce host object to string",
7910                    })),
7911                }
7912            }
7913            None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7914                value,
7915            })),
7916        }
7917    }
7918
7919    fn string_text(&self, value: Value) -> Option<&EcmaString> {
7920        let index = self.runtime_slot(value).ok()??;
7921        match &self.heap[index] {
7922            HeapEntry::String(text) => Some(text),
7923            _ => None,
7924        }
7925    }
7926
7927    fn bigint_text(&self, value: Value) -> Option<&str> {
7928        let index = self.runtime_slot(value).ok()??;
7929        match &self.heap[index] {
7930            HeapEntry::BigInt(text) => Some(text),
7931            _ => None,
7932        }
7933    }
7934
7935    fn is_object(&self, value: Value) -> bool {
7936        match self.runtime_slot(value) {
7937            Ok(Some(index)) => !matches!(
7938                self.heap[index],
7939                HeapEntry::String(_)
7940                    | HeapEntry::BigInt(_)
7941                    | HeapEntry::PromiseResolver { .. }
7942                    | HeapEntry::PromiseFinally { .. }
7943                    | HeapEntry::PromiseAll { .. }
7944                    | HeapEntry::AsyncActivation { .. }
7945                    | HeapEntry::PromiseAllElement { .. }
7946            ),
7947            Ok(None) => matches!(value.decode(), Some(Decoded::HeapRef(_))),
7948            Err(_) => false,
7949        }
7950    }
7951}
7952
7953fn ordered_property_keys(properties: &PropertyMap) -> Vec<PropertyKey> {
7954    let mut indices = Vec::new();
7955    let mut strings = Vec::new();
7956    let mut symbols = Vec::new();
7957    for (key, _) in properties.iter() {
7958        match key {
7959            PropertyKey::Named(name) => match array_index(name) {
7960                Some(index) => indices.push((index, key.clone())),
7961                None => strings.push(key.clone()),
7962            },
7963            PropertyKey::Symbol(_) => symbols.push(key.clone()),
7964            PropertyKey::Private(_) => {}
7965        }
7966    }
7967    indices.sort_unstable_by_key(|(index, _)| *index);
7968    indices
7969        .into_iter()
7970        .map(|(_, key)| key)
7971        .chain(strings)
7972        .chain(symbols)
7973        .collect()
7974}
7975
7976fn property_lookup(properties: &PropertyMap, key: &PropertyKey) -> Option<Found> {
7977    match properties.get(key) {
7978        Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
7979        Some(Property::Accessor { getter, .. }) => Some(match getter {
7980            Some(getter) => Found::Getter(*getter),
7981            None => Found::NoGetter,
7982        }),
7983        None => None,
7984    }
7985}
7986
7987fn property_lookup_ascii(properties: &PropertyMap, name: &str) -> Option<Found> {
7988    match properties.get_ascii(name) {
7989        Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
7990        Some(Property::Accessor { getter, .. }) => Some(match getter {
7991            Some(getter) => Found::Getter(*getter),
7992            None => Found::NoGetter,
7993        }),
7994        None => None,
7995    }
7996}
7997
7998fn innermost_handler(function: &Function, pc: usize) -> Option<bamts_bytecode::ExceptionHandler> {
7999    function
8000        .handlers()
8001        .iter()
8002        .copied()
8003        .filter(|handler| handler.start.get() as usize <= pc && pc < handler.end.get() as usize)
8004        .max_by(|left, right| {
8005            left.start
8006                .get()
8007                .cmp(&right.start.get())
8008                .then_with(|| right.end.get().cmp(&left.end.get()))
8009        })
8010}
8011
8012fn numeric_f64(value: Value) -> Option<f64> {
8013    match value.decode()? {
8014        Decoded::Number(number) => Some(number),
8015        Decoded::Int32(raw) => Some(f64::from(raw as i32)),
8016        _ => None,
8017    }
8018}
8019
8020fn number_value(number: f64) -> Value {
8021    if number.is_finite()
8022        && number.fract() == 0.0
8023        && number >= f64::from(i32::MIN)
8024        && number <= f64::from(i32::MAX)
8025    {
8026        Value::int32(number as i32 as u32)
8027    } else {
8028        Value::number(number)
8029    }
8030}
8031
8032fn parse_number(text: &EcmaString) -> f64 {
8033    let Ok(text) = text.to_utf8_strict() else {
8034        return f64::NAN;
8035    };
8036    parse_number_utf8(&text)
8037}
8038
8039fn parse_number_utf8(text: &str) -> f64 {
8040    let trimmed = text.trim();
8041    if trimmed.is_empty() {
8042        0.0
8043    } else {
8044        trimmed.parse::<f64>().unwrap_or(f64::NAN)
8045    }
8046}
8047
8048fn format_number(number: f64) -> String {
8049    if number.is_nan() {
8050        return "NaN".to_owned();
8051    }
8052    if number == f64::INFINITY {
8053        return "Infinity".to_owned();
8054    }
8055    if number == f64::NEG_INFINITY {
8056        return "-Infinity".to_owned();
8057    }
8058    if number == 0.0 {
8059        return "0".to_owned();
8060    }
8061
8062    let negative = number.is_sign_negative();
8063    let raw = number.abs().to_string();
8064    let (mantissa, explicit_exponent) = match raw.split_once(['e', 'E']) {
8065        Some((mantissa, exponent)) => (
8066            mantissa,
8067            exponent
8068                .parse::<i32>()
8069                .expect("Rust formats finite f64 exponents as i32"),
8070        ),
8071        None => (raw.as_str(), 0),
8072    };
8073    let decimal = mantissa.find('.').unwrap_or(mantissa.len());
8074    let untrimmed: String = mantissa.chars().filter(|ch| *ch != '.').collect();
8075    let first = untrimmed
8076        .find(|ch| ch != '0')
8077        .expect("a nonzero number has a nonzero decimal digit");
8078    let digits = untrimmed[first..].trim_end_matches('0');
8079    let exponent = explicit_exponent + decimal as i32 - first as i32 - 1;
8080
8081    let mut result = String::new();
8082    if negative {
8083        result.push('-');
8084    }
8085    if !(-6..21).contains(&exponent) {
8086        result.push(digits.as_bytes()[0] as char);
8087        if digits.len() > 1 {
8088            result.push('.');
8089            result.push_str(&digits[1..]);
8090        }
8091        result.push('e');
8092        if exponent >= 0 {
8093            result.push('+');
8094        }
8095        result.push_str(&exponent.to_string());
8096    } else if exponent >= 0 {
8097        let integer_digits = exponent as usize + 1;
8098        if digits.len() <= integer_digits {
8099            result.push_str(digits);
8100            result.extend(std::iter::repeat_n('0', integer_digits - digits.len()));
8101        } else {
8102            result.push_str(&digits[..integer_digits]);
8103            result.push('.');
8104            result.push_str(&digits[integer_digits..]);
8105        }
8106    } else {
8107        result.push_str("0.");
8108        result.extend(std::iter::repeat_n('0', (-exponent - 1) as usize));
8109        result.push_str(digits);
8110    }
8111    result
8112}
8113
8114fn to_uint32(number: f64) -> u32 {
8115    if !number.is_finite() || number == 0.0 {
8116        0
8117    } else {
8118        number.trunc().rem_euclid(4_294_967_296.0) as u32
8119    }
8120}
8121
8122fn to_int32(number: f64) -> i32 {
8123    to_uint32(number) as i32
8124}
8125
8126fn array_index_ascii(key: &str) -> Option<u32> {
8127    if !key.is_ascii() || key.is_empty() || (key.len() > 1 && key.as_bytes()[0] == b'0') {
8128        return None;
8129    }
8130    let mut index = 0_u32;
8131    for byte in key.bytes() {
8132        if !byte.is_ascii_digit() {
8133            return None;
8134        }
8135        index = index.checked_mul(10)?.checked_add(u32::from(byte - b'0'))?;
8136    }
8137    (index != u32::MAX).then_some(index)
8138}
8139
8140fn array_index(key: &EcmaString) -> Option<u32> {
8141    let units = key.as_units();
8142    if units.is_empty() || (units.len() > 1 && units[0] == u16::from(b'0')) {
8143        return None;
8144    }
8145    let mut index = 0_u32;
8146    for &unit in units {
8147        if !(u16::from(b'0')..=u16::from(b'9')).contains(&unit) {
8148            return None;
8149        }
8150        index = index
8151            .checked_mul(10)?
8152            .checked_add(u32::from(unit - u16::from(b'0')))?;
8153    }
8154    (index != u32::MAX).then_some(index)
8155}
8156
8157fn exact_array_length(value: Value) -> Option<usize> {
8158    let number = numeric_f64(value)?;
8159    if number.is_finite() && number >= 0.0 && number.fract() == 0.0 && number <= u32::MAX as f64 {
8160        Some(number as usize)
8161    } else {
8162        None
8163    }
8164}
8165
8166pub(crate) fn apply_array_length(
8167    elements: &mut Vec<Value>,
8168    properties: &mut PropertyMap,
8169    length: usize,
8170    operation: &'static str,
8171) -> Result<(), EvalFailure> {
8172    if length >= elements.len() {
8173        elements.resize(length, Value::HOLE);
8174        return Ok(());
8175    }
8176    let blocked = properties
8177        .iter()
8178        .filter_map(|(key, property)| {
8179            (!property.configurable())
8180                .then(|| key.as_string().and_then(array_index))
8181                .flatten()
8182        })
8183        .map(|offset| offset as usize)
8184        .filter(|offset| *offset >= length)
8185        .max();
8186    let effective_length = blocked.map_or(length, |offset| offset + 1);
8187    properties.0.retain(|(key, _)| {
8188        key.as_string()
8189            .and_then(array_index)
8190            .is_none_or(|offset| (offset as usize) < effective_length)
8191    });
8192    elements.resize(effective_length, Value::HOLE);
8193    if blocked.is_some() {
8194        return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8195    }
8196    Ok(())
8197}
8198
8199pub(crate) fn array_set_length(
8200    elements: &mut Vec<Value>,
8201    properties: &mut PropertyMap,
8202    length_writable: bool,
8203    value: Value,
8204    operation: &'static str,
8205) -> Result<(), EvalFailure> {
8206    let length = exact_array_length(value)
8207        .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError { operation }))?;
8208    if !length_writable {
8209        return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8210    }
8211    apply_array_length(elements, properties, length, operation)
8212}
8213
8214fn bigint_i128(text: &str) -> Result<i128, EvalFailure> {
8215    text.parse::<i128>().map_err(|_| {
8216        EvalFailure::Throw(ThrowOrigin::RangeError {
8217            operation: "bigint magnitude exceeds runtime width",
8218        })
8219    })
8220}
8221
8222fn bigint_binary(op: BinaryOp, left: &str, right: &str) -> Result<String, EvalFailure> {
8223    let left = bigint_i128(left)?;
8224    let right = bigint_i128(right)?;
8225    let overflow =
8226        |operation: &'static str| EvalFailure::Throw(ThrowOrigin::RangeError { operation });
8227    let result = match op {
8228        BinaryOp::Subtract => left
8229            .checked_sub(right)
8230            .ok_or_else(|| overflow("bigint subtract overflow"))?,
8231        BinaryOp::Multiply => left
8232            .checked_mul(right)
8233            .ok_or_else(|| overflow("bigint multiply overflow"))?,
8234        BinaryOp::Divide => {
8235            if right == 0 {
8236                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8237                    operation: "bigint division by zero",
8238                }));
8239            }
8240            left.checked_div(right)
8241                .ok_or_else(|| overflow("bigint divide overflow"))?
8242        }
8243        BinaryOp::Remainder => {
8244            if right == 0 {
8245                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8246                    operation: "bigint remainder by zero",
8247                }));
8248            }
8249            left.checked_rem(right)
8250                .ok_or_else(|| overflow("bigint remainder overflow"))?
8251        }
8252        BinaryOp::Exponent => {
8253            if right < 0 {
8254                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8255                    operation: "bigint negative exponent",
8256                }));
8257            }
8258            let exponent =
8259                u32::try_from(right).map_err(|_| overflow("bigint exponent overflow"))?;
8260            left.checked_pow(exponent)
8261                .ok_or_else(|| overflow("bigint exponent overflow"))?
8262        }
8263        BinaryOp::BitAnd => left & right,
8264        BinaryOp::BitOr => left | right,
8265        BinaryOp::BitXor => left ^ right,
8266        BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
8267            let left_shift = (op == BinaryOp::ShiftLeft) == (right >= 0);
8268            let amount =
8269                u32::try_from(right.unsigned_abs()).map_err(|_| overflow("bigint shift width"))?;
8270            let shifted = if left_shift {
8271                left.checked_shl(amount)
8272            } else {
8273                left.checked_shr(amount)
8274            };
8275            shifted.ok_or_else(|| overflow("bigint shift overflow"))?
8276        }
8277        BinaryOp::UnsignedShiftRight => {
8278            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
8279                operation: "unsigned shift on bigint",
8280            }));
8281        }
8282        _ => unreachable!("bigint arithmetic partition"),
8283    };
8284    Ok(result.to_string())
8285}
8286
8287pub(crate) fn unary_from_selector(op: u32) -> Option<UnaryOp> {
8288    match op {
8289        0 => Some(UnaryOp::Void),
8290        1 => Some(UnaryOp::TypeOf),
8291        2 => Some(UnaryOp::Plus),
8292        3 => Some(UnaryOp::Negate),
8293        4 => Some(UnaryOp::BitwiseNot),
8294        5 => Some(UnaryOp::LogicalNot),
8295        _ => None,
8296    }
8297}
8298
8299pub(crate) fn binary_from_selector(op: u32) -> Option<BinaryOp> {
8300    match op {
8301        0 => Some(BinaryOp::Add),
8302        1 => Some(BinaryOp::Subtract),
8303        2 => Some(BinaryOp::Multiply),
8304        3 => Some(BinaryOp::Divide),
8305        4 => Some(BinaryOp::Remainder),
8306        5 => Some(BinaryOp::Exponent),
8307        6 => Some(BinaryOp::BitAnd),
8308        7 => Some(BinaryOp::BitOr),
8309        8 => Some(BinaryOp::BitXor),
8310        9 => Some(BinaryOp::ShiftLeft),
8311        10 => Some(BinaryOp::ShiftRight),
8312        11 => Some(BinaryOp::UnsignedShiftRight),
8313        12 => Some(BinaryOp::Equal),
8314        13 => Some(BinaryOp::NotEqual),
8315        14 => Some(BinaryOp::StrictEqual),
8316        15 => Some(BinaryOp::StrictNotEqual),
8317        16 => Some(BinaryOp::LessThan),
8318        17 => Some(BinaryOp::LessThanOrEqual),
8319        18 => Some(BinaryOp::GreaterThan),
8320        19 => Some(BinaryOp::GreaterThanOrEqual),
8321        20 => Some(BinaryOp::InstanceOf),
8322        21 => Some(BinaryOp::In),
8323        _ => None,
8324    }
8325}
8326
8327pub(crate) fn iterator_kind_from_selector(kind: u32) -> Option<IteratorKind> {
8328    match kind {
8329        0 => Some(IteratorKind::Sync),
8330        1 => Some(IteratorKind::Async),
8331        2 => Some(IteratorKind::Keys),
8332        _ => None,
8333    }
8334}
8335
8336pub(crate) fn accessor_from_selector(kind: u32) -> Option<AccessorKind> {
8337    match kind {
8338        0 => Some(AccessorKind::Getter),
8339        1 => Some(AccessorKind::Setter),
8340        _ => None,
8341    }
8342}
8343
8344#[cfg(test)]
8345mod tests {
8346    use std::sync::Arc;
8347
8348    use super::*;
8349    use crate::intrinsics::BuiltinOutcome;
8350    use bamts_bytecode::{
8351        Binding, Edge, EdgeKind, ExceptionHandler, Export, ExportSource, FunctionFlags, NumberBits,
8352        ProgramModule, Register,
8353    };
8354
8355    fn reg(raw: u32) -> Register {
8356        Register::new(raw)
8357    }
8358    fn pc(raw: u32) -> Pc {
8359        Pc::new(raw)
8360    }
8361    fn cid(raw: u32) -> ConstantId {
8362        ConstantId::new(raw)
8363    }
8364
8365    /// A function with no captures.
8366    fn function(
8367        parameters: u32,
8368        registers: u32,
8369        code: Vec<Instruction>,
8370        handlers: Vec<ExceptionHandler>,
8371    ) -> Function {
8372        Function::new(
8373            None,
8374            0,
8375            parameters,
8376            registers,
8377            FunctionFlags::default(),
8378            code,
8379            handlers,
8380        )
8381    }
8382
8383    fn generator_function(
8384        parameters: u32,
8385        registers: u32,
8386        code: Vec<Instruction>,
8387        handlers: Vec<ExceptionHandler>,
8388    ) -> Function {
8389        Function::new(
8390            None,
8391            0,
8392            parameters,
8393            registers,
8394            FunctionFlags {
8395                is_async: false,
8396                is_generator: true,
8397            },
8398            code,
8399            handlers,
8400        )
8401    }
8402
8403    fn async_function(
8404        parameters: u32,
8405        registers: u32,
8406        code: Vec<Instruction>,
8407        handlers: Vec<ExceptionHandler>,
8408    ) -> Function {
8409        Function::new(
8410            None,
8411            0,
8412            parameters,
8413            registers,
8414            FunctionFlags {
8415                is_async: true,
8416                is_generator: false,
8417            },
8418            code,
8419            handlers,
8420        )
8421    }
8422
8423    /// A function with `captures` leading capture registers.
8424    fn closure_function(
8425        captures: u32,
8426        parameters: u32,
8427        registers: u32,
8428        code: Vec<Instruction>,
8429    ) -> Function {
8430        Function::new(
8431            None,
8432            captures,
8433            parameters,
8434            registers,
8435            FunctionFlags::default(),
8436            code,
8437            Vec::new(),
8438        )
8439    }
8440
8441    fn verified(mut constants: Vec<Constant>, functions: Vec<Function>) -> Program<Verified> {
8442        let name = ConstantId::new(constants.len() as u32);
8443        constants.push(Constant::String(EcmaString::from_utf8("<test>")));
8444        let code = Module::new(constants, functions, FunctionId::new(0))
8445            .verify()
8446            .expect("valid test bytecode");
8447        Program::link(
8448            vec![ProgramModule {
8449                name,
8450                code,
8451                edges: Vec::new(),
8452                bindings: Vec::new(),
8453                exports: Vec::new(),
8454            }],
8455            ModuleId::new(0),
8456        )
8457        .expect("valid one-module test program")
8458    }
8459    fn program_module(
8460        name: &str,
8461        mut constants: Vec<Constant>,
8462        functions: Vec<Function>,
8463        edges: Vec<Edge>,
8464        bindings: Vec<Binding>,
8465        exports: Vec<Export>,
8466    ) -> ProgramModule<Verified> {
8467        constants.insert(0, Constant::String(EcmaString::from_utf8(name)));
8468        let code = Module::new(constants, functions, FunctionId::new(0))
8469            .verify()
8470            .expect("valid test bytecode");
8471        ProgramModule {
8472            name: ConstantId::new(0),
8473            code,
8474            edges,
8475            bindings,
8476            exports,
8477        }
8478    }
8479
8480    fn linked(modules: Vec<ProgramModule<Verified>>, entry: u32) -> Program<Verified> {
8481        Program::link(modules, ModuleId::new(entry)).expect("valid linked test program")
8482    }
8483
8484    fn namespace_descriptor_entry() -> Function {
8485        function(
8486            0,
8487            7,
8488            vec![
8489                Instruction::LoadGlobal {
8490                    dst: reg(0),
8491                    name: cid(1),
8492                },
8493                Instruction::LoadGlobal {
8494                    dst: reg(1),
8495                    name: cid(3),
8496                },
8497                Instruction::LoadConst {
8498                    dst: reg(2),
8499                    constant: cid(4),
8500                },
8501                Instruction::GetProperty {
8502                    dst: reg(3),
8503                    object: reg(1),
8504                    key: reg(2),
8505                },
8506                Instruction::CreateArray { dst: reg(4) },
8507                Instruction::ArrayPush {
8508                    array: reg(4),
8509                    value: reg(0),
8510                },
8511                Instruction::LoadConst {
8512                    dst: reg(5),
8513                    constant: cid(5),
8514                },
8515                Instruction::ArrayPush {
8516                    array: reg(4),
8517                    value: reg(5),
8518                },
8519                Instruction::Call {
8520                    dst: reg(6),
8521                    callee: reg(3),
8522                    this_value: reg(4),
8523                    arguments: reg(4),
8524                },
8525                Instruction::Return { value: reg(6) },
8526            ],
8527            Vec::new(),
8528        )
8529    }
8530
8531    #[derive(Default)]
8532    struct TestHost;
8533    impl Host for TestHost {}
8534
8535    #[test]
8536    fn async_await_setup_failure_releases_suspended_registers() {
8537        let program = verified(
8538            vec![Constant::Undefined],
8539            vec![
8540                function(0, 1, vec![Instruction::Halt], Vec::new()),
8541                async_function(
8542                    0,
8543                    2,
8544                    vec![
8545                        Instruction::LoadConst {
8546                            dst: reg(0),
8547                            constant: cid(0),
8548                        },
8549                        Instruction::Suspend {
8550                            dst: reg(1),
8551                            src: reg(0),
8552                            resume: pc(2),
8553                        },
8554                        Instruction::Return { value: reg(1) },
8555                    ],
8556                    Vec::new(),
8557                ),
8558            ],
8559        );
8560        let mut host = TestHost;
8561        let limits = Limits {
8562            max_microtasks: 0,
8563            ..Limits::default()
8564        };
8565        let mut machine = Machine::new(&program, &mut host, limits);
8566        machine.frames.clear();
8567        machine.live_registers = 0;
8568        let callable = generator_callable(&mut machine, 1);
8569
8570        assert!(matches!(
8571            machine.call_value(callable, Value::UNDEFINED, &[]),
8572            Err(EvalFailure::Runtime(
8573                RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 0 }
8574            ))
8575        ));
8576        assert_eq!(machine.live_registers, 0);
8577    }
8578
8579    fn run_ok(program: &Program<Verified>) -> Execution {
8580        let mut host = TestHost;
8581        Machine::new(program, &mut host, Limits::default())
8582            .run()
8583            .unwrap()
8584    }
8585
8586    fn generator_callable<H: Host>(machine: &mut Machine<'_, H>, function: u32) -> Value {
8587        machine
8588            .allocate(HeapEntry::Function {
8589                module: ModuleId::new(0),
8590                function: FunctionId::new(function),
8591                captures: Vec::new(),
8592                properties: PropertyMap::default(),
8593                prototype: Some(machine.intrinsics.function_prototype),
8594                extensible: true,
8595            })
8596            .unwrap()
8597    }
8598
8599    fn generator_next<H: Host>(
8600        machine: &mut Machine<'_, H>,
8601        generator: Value,
8602        resume_value: Value,
8603    ) -> Result<(Value, bool), EvalFailure> {
8604        let next = machine.get_named_property(generator, "next")?;
8605        let result = machine.call_value(next, generator, &[resume_value])?;
8606        let done = machine.get_named_property(result, "done")?;
8607        let value = machine.get_named_property(result, "value")?;
8608        Ok((value, machine.truthy(done)))
8609    }
8610
8611    #[test]
8612    fn sync_generator_is_lazy_resumes_registers_and_stays_completed() {
8613        let program = verified(
8614            vec![Constant::Int32(10)],
8615            vec![
8616                function(0, 1, vec![Instruction::Halt], Vec::new()),
8617                generator_function(
8618                    0,
8619                    3,
8620                    vec![
8621                        Instruction::LoadConst {
8622                            dst: reg(0),
8623                            constant: cid(0),
8624                        },
8625                        Instruction::Suspend {
8626                            dst: reg(1),
8627                            src: reg(0),
8628                            resume: pc(2),
8629                        },
8630                        Instruction::Binary {
8631                            dst: reg(2),
8632                            op: BinaryOp::Add,
8633                            left: reg(0),
8634                            right: reg(1),
8635                        },
8636                        Instruction::Return { value: reg(2) },
8637                    ],
8638                    Vec::new(),
8639                ),
8640            ],
8641        );
8642        let mut host = TestHost;
8643        let mut machine = Machine::new(&program, &mut host, Limits::default());
8644        machine.frames.clear();
8645        machine.live_registers = 0;
8646        let callable = generator_callable(&mut machine, 1);
8647        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8648        assert_eq!(machine.live_registers, 0, "calling must not start the body");
8649        machine
8650            .set_data_property(generator, "visible", Value::int32(1))
8651            .unwrap();
8652        assert_eq!(
8653            machine.get_named_property(generator, "visible").unwrap(),
8654            Value::int32(1),
8655        );
8656        assert_eq!(
8657            machine.own_property_keys(generator).unwrap(),
8658            vec![PropertyKey::Named(EcmaString::from_utf8("visible"))],
8659        );
8660        assert!(
8661            machine
8662                .inherits_from_prototype(
8663                    generator,
8664                    machine.intrinsics.builtins.generator_prototype(),
8665                )
8666                .unwrap()
8667        );
8668
8669        assert_eq!(
8670            generator_next(&mut machine, generator, Value::int32(99)).unwrap(),
8671            (Value::int32(10), false),
8672        );
8673        assert_eq!(machine.live_registers, 3);
8674        assert_eq!(
8675            generator_next(&mut machine, generator, Value::int32(5)).unwrap(),
8676            (Value::int32(15), true),
8677        );
8678        assert_eq!(machine.live_registers, 0);
8679        assert_eq!(
8680            generator_next(&mut machine, generator, Value::int32(8)).unwrap(),
8681            (Value::UNDEFINED, true),
8682        );
8683    }
8684
8685    #[test]
8686    fn sync_generator_reentrant_next_is_a_type_error() {
8687        let program = verified(
8688            Vec::new(),
8689            vec![
8690                function(0, 1, vec![Instruction::Halt], Vec::new()),
8691                generator_function(0, 1, vec![Instruction::Halt], Vec::new()),
8692            ],
8693        );
8694        let mut host = TestHost;
8695        let mut machine = Machine::new(&program, &mut host, Limits::default());
8696        machine.frames.clear();
8697        machine.live_registers = 0;
8698        let callable = generator_callable(&mut machine, 1);
8699        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8700        let _ = machine.take_generator_state(generator).unwrap();
8701
8702        assert!(matches!(
8703            generator_next(&mut machine, generator, Value::UNDEFINED),
8704            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
8705        ));
8706    }
8707
8708    #[test]
8709    fn sync_generator_uncaught_throw_preserves_origin_and_completes() {
8710        let program = verified(
8711            vec![Constant::Int32(7)],
8712            vec![
8713                function(0, 1, vec![Instruction::Halt], Vec::new()),
8714                generator_function(
8715                    0,
8716                    1,
8717                    vec![
8718                        Instruction::LoadConst {
8719                            dst: reg(0),
8720                            constant: cid(0),
8721                        },
8722                        Instruction::Throw { value: reg(0) },
8723                    ],
8724                    Vec::new(),
8725                ),
8726            ],
8727        );
8728        let mut host = TestHost;
8729        let mut machine = Machine::new(&program, &mut host, Limits::default());
8730        machine.frames.clear();
8731        machine.live_registers = 0;
8732        let callable = generator_callable(&mut machine, 1);
8733        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8734
8735        assert!(matches!(
8736            generator_next(&mut machine, generator, Value::UNDEFINED),
8737            Err(EvalFailure::ThrowValueOrigin {
8738                value,
8739                origin: ThrowOrigin::Bytecode,
8740            }) if value == Value::int32(7)
8741        ));
8742        assert_eq!(
8743            generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8744            (Value::UNDEFINED, true),
8745        );
8746        assert_eq!(machine.live_registers, 0);
8747    }
8748
8749    #[test]
8750    fn outer_compiled_handler_catches_generator_throw_value() {
8751        let program = verified(
8752            vec![
8753                Constant::Int32(7),
8754                Constant::Undefined,
8755                Constant::String(EcmaString::from_utf8("next")),
8756            ],
8757            vec![
8758                function(
8759                    0,
8760                    8,
8761                    vec![
8762                        Instruction::CreateArray { dst: reg(0) },
8763                        Instruction::CreateClosure {
8764                            dst: reg(1),
8765                            function: FunctionId::new(1),
8766                            captures: reg(0),
8767                        },
8768                        Instruction::CreateArray { dst: reg(2) },
8769                        Instruction::LoadConst {
8770                            dst: reg(3),
8771                            constant: cid(1),
8772                        },
8773                        Instruction::Call {
8774                            dst: reg(4),
8775                            callee: reg(1),
8776                            this_value: reg(3),
8777                            arguments: reg(2),
8778                        },
8779                        Instruction::LoadConst {
8780                            dst: reg(5),
8781                            constant: cid(2),
8782                        },
8783                        Instruction::GetProperty {
8784                            dst: reg(6),
8785                            object: reg(4),
8786                            key: reg(5),
8787                        },
8788                        Instruction::Call {
8789                            dst: reg(7),
8790                            callee: reg(6),
8791                            this_value: reg(4),
8792                            arguments: reg(2),
8793                        },
8794                        Instruction::Return { value: reg(3) },
8795                        Instruction::Return { value: reg(7) },
8796                    ],
8797                    vec![ExceptionHandler {
8798                        start: pc(7),
8799                        end: pc(8),
8800                        handler: pc(9),
8801                        catch_register: reg(7),
8802                    }],
8803                ),
8804                generator_function(
8805                    0,
8806                    1,
8807                    vec![
8808                        Instruction::LoadConst {
8809                            dst: reg(0),
8810                            constant: cid(0),
8811                        },
8812                        Instruction::Throw { value: reg(0) },
8813                    ],
8814                    Vec::new(),
8815                ),
8816            ],
8817        );
8818
8819        assert_eq!(run_ok(&program).value, Value::int32(7));
8820    }
8821
8822    #[test]
8823    fn sync_generator_catches_body_throw_before_suspending() {
8824        let program = verified(
8825            vec![Constant::Int32(7)],
8826            vec![
8827                function(0, 1, vec![Instruction::Halt], Vec::new()),
8828                generator_function(
8829                    0,
8830                    3,
8831                    vec![
8832                        Instruction::LoadConst {
8833                            dst: reg(0),
8834                            constant: cid(0),
8835                        },
8836                        Instruction::Throw { value: reg(0) },
8837                        Instruction::Suspend {
8838                            dst: reg(2),
8839                            src: reg(1),
8840                            resume: pc(3),
8841                        },
8842                        Instruction::Return { value: reg(2) },
8843                    ],
8844                    vec![ExceptionHandler {
8845                        start: pc(1),
8846                        end: pc(2),
8847                        handler: pc(2),
8848                        catch_register: reg(1),
8849                    }],
8850                ),
8851            ],
8852        );
8853        let mut host = TestHost;
8854        let mut machine = Machine::new(&program, &mut host, Limits::default());
8855        machine.frames.clear();
8856        machine.live_registers = 0;
8857        let callable = generator_callable(&mut machine, 1);
8858        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8859
8860        assert_eq!(
8861            generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8862            (Value::int32(7), false),
8863        );
8864        assert_eq!(
8865            generator_next(&mut machine, generator, Value::int32(9)).unwrap(),
8866            (Value::int32(9), true),
8867        );
8868    }
8869
8870    #[test]
8871    fn suspended_generator_registers_remain_charged() {
8872        let program = verified(
8873            vec![Constant::Int32(1)],
8874            vec![
8875                function(0, 1, vec![Instruction::Halt], Vec::new()),
8876                generator_function(
8877                    0,
8878                    3,
8879                    vec![
8880                        Instruction::LoadConst {
8881                            dst: reg(0),
8882                            constant: cid(0),
8883                        },
8884                        Instruction::Suspend {
8885                            dst: reg(1),
8886                            src: reg(0),
8887                            resume: pc(2),
8888                        },
8889                        Instruction::Return { value: reg(1) },
8890                    ],
8891                    Vec::new(),
8892                ),
8893            ],
8894        );
8895        let mut host = TestHost;
8896        let mut machine = Machine::new(
8897            &program,
8898            &mut host,
8899            Limits {
8900                max_total_registers: 3,
8901                ..Limits::default()
8902            },
8903        );
8904        machine.frames.clear();
8905        machine.live_registers = 0;
8906        let callable = generator_callable(&mut machine, 1);
8907        let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8908        let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8909        assert_eq!(
8910            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8911            (Value::int32(1), false),
8912        );
8913        assert!(matches!(
8914            generator_next(&mut machine, second, Value::UNDEFINED),
8915            Err(EvalFailure::Runtime(
8916                RuntimeErrorKind::RegisterLimitExceeded { .. }
8917            ))
8918        ));
8919        assert_eq!(machine.live_registers, 3);
8920        assert_eq!(
8921            generator_next(&mut machine, first, Value::int32(4)).unwrap(),
8922            (Value::int32(4), true),
8923        );
8924        assert_eq!(machine.live_registers, 0);
8925    }
8926
8927    #[test]
8928    fn resumed_generator_call_depth_failure_releases_registers() {
8929        let program = verified(
8930            vec![Constant::Int32(1)],
8931            vec![
8932                function(0, 1, vec![Instruction::Halt], Vec::new()),
8933                generator_function(
8934                    0,
8935                    2,
8936                    vec![
8937                        Instruction::LoadConst {
8938                            dst: reg(0),
8939                            constant: cid(0),
8940                        },
8941                        Instruction::Suspend {
8942                            dst: reg(1),
8943                            src: reg(0),
8944                            resume: pc(2),
8945                        },
8946                        Instruction::Return { value: reg(1) },
8947                    ],
8948                    Vec::new(),
8949                ),
8950            ],
8951        );
8952        let mut host = TestHost;
8953        let mut machine = Machine::new(
8954            &program,
8955            &mut host,
8956            Limits {
8957                max_total_registers: 2,
8958                ..Limits::default()
8959            },
8960        );
8961        machine.frames.clear();
8962        machine.live_registers = 0;
8963
8964        let callable = generator_callable(&mut machine, 1);
8965        let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8966
8967        // Start and suspend the first generator, charging its two registers.
8968        assert_eq!(
8969            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8970            (Value::int32(1), false),
8971        );
8972        assert_eq!(machine.live_registers, 2);
8973
8974        // Fill the compiled call depth to the exact limit so the next resume
8975        // fails in push_resumed_generator_frame before it can take ownership.
8976        machine.frames.push(Frame {
8977            module: ModuleId::new(0),
8978            function: 0,
8979            pc: 0,
8980            registers: Vec::new(),
8981            return_to: None,
8982            this_value: Value::UNDEFINED,
8983            new_target: Value::UNDEFINED,
8984            args: Vec::new(),
8985            arguments_object: None,
8986        });
8987        machine.limits.max_call_depth = machine.frames.len();
8988
8989        assert!(matches!(
8990            generator_next(&mut machine, first, Value::int32(7)),
8991            Err(EvalFailure::Runtime(
8992                RuntimeErrorKind::CallDepthExceeded { .. }
8993            ))
8994        ));
8995        assert_eq!(machine.live_registers, 0);
8996
8997        // The generator is now sticky Completed.
8998        assert_eq!(
8999            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
9000            (Value::UNDEFINED, true),
9001        );
9002
9003        // Remove the artificial depth and make room for another activation.
9004        machine.frames.pop();
9005        machine.limits.max_call_depth = Limits::default().max_call_depth;
9006
9007        // A second generator can suspend again only if the first's charge was released.
9008        let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9009        assert_eq!(
9010            generator_next(&mut machine, second, Value::UNDEFINED).unwrap(),
9011            (Value::int32(1), false),
9012        );
9013        assert_eq!(machine.live_registers, 2);
9014        assert_eq!(
9015            generator_next(&mut machine, second, Value::int32(9)).unwrap(),
9016            (Value::int32(9), true),
9017        );
9018        assert_eq!(machine.live_registers, 0);
9019    }
9020    #[test]
9021    fn array_extend_consumes_generator_through_sync_iterator_protocol() {
9022        let program = verified(
9023            vec![Constant::Int32(1), Constant::Int32(2)],
9024            vec![
9025                function(0, 1, vec![Instruction::Halt], Vec::new()),
9026                generator_function(
9027                    0,
9028                    3,
9029                    vec![
9030                        Instruction::LoadConst {
9031                            dst: reg(0),
9032                            constant: cid(0),
9033                        },
9034                        Instruction::Suspend {
9035                            dst: reg(2),
9036                            src: reg(0),
9037                            resume: pc(2),
9038                        },
9039                        Instruction::LoadConst {
9040                            dst: reg(1),
9041                            constant: cid(1),
9042                        },
9043                        Instruction::Suspend {
9044                            dst: reg(2),
9045                            src: reg(1),
9046                            resume: pc(4),
9047                        },
9048                        Instruction::Return { value: reg(2) },
9049                    ],
9050                    Vec::new(),
9051                ),
9052            ],
9053        );
9054        let mut host = TestHost;
9055        let mut machine = Machine::new(&program, &mut host, Limits::default());
9056        machine.frames.clear();
9057        machine.live_registers = 0;
9058        let callable = generator_callable(&mut machine, 1);
9059        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9060        let array = machine
9061            .allocate(HeapEntry::Array {
9062                elements: Vec::new(),
9063                properties: PropertyMap::default(),
9064                prototype: Some(machine.intrinsics.array_prototype),
9065                extensible: true,
9066                length_writable: true,
9067            })
9068            .unwrap();
9069
9070        machine.array_extend(array, generator).unwrap();
9071        assert_eq!(
9072            machine.array_elements(array).unwrap(),
9073            Some(vec![Value::int32(1), Value::int32(2)]),
9074        );
9075        assert_eq!(machine.live_registers, 0);
9076    }
9077
9078    #[test]
9079    fn runtime_callback_without_interpreter_caller_propagates_throw() {
9080        let program = verified(
9081            Vec::new(),
9082            vec![
9083                function(0, 1, vec![Instruction::Halt], Vec::new()),
9084                function(1, 1, vec![Instruction::Throw { value: reg(0) }], Vec::new()),
9085            ],
9086        );
9087        let mut host = TestHost;
9088        let mut machine = Machine::new(&program, &mut host, Limits::default());
9089        machine.frames.clear();
9090        machine.live_registers = 0;
9091        let callee = machine
9092            .allocate(HeapEntry::Function {
9093                module: ModuleId::new(0),
9094                function: FunctionId::new(1),
9095                captures: Vec::new(),
9096                properties: PropertyMap::default(),
9097                prototype: Some(machine.intrinsics.function_prototype),
9098                extensible: true,
9099            })
9100            .unwrap();
9101        let thrown = Value::int32(7);
9102
9103        assert!(matches!(
9104            machine.call_value(callee, Value::UNDEFINED, &[thrown]),
9105            Err(EvalFailure::ThrowValue(value)) if value == thrown
9106        ));
9107    }
9108
9109    #[test]
9110    fn runtime_callback_failure_releases_root_frame() {
9111        let program = verified(
9112            Vec::new(),
9113            vec![
9114                function(0, 1, vec![Instruction::Halt], Vec::new()),
9115                function(
9116                    1,
9117                    1,
9118                    vec![Instruction::Return { value: reg(0) }],
9119                    Vec::new(),
9120                ),
9121            ],
9122        );
9123        let mut host = TestHost;
9124        let mut machine = Machine::new(&program, &mut host, Limits::default());
9125        machine.frames.clear();
9126        machine.live_registers = 0;
9127        let callee = machine
9128            .allocate(HeapEntry::Function {
9129                module: ModuleId::new(0),
9130                function: FunctionId::new(1),
9131                captures: Vec::new(),
9132                properties: PropertyMap::default(),
9133                prototype: Some(machine.intrinsics.function_prototype),
9134                extensible: true,
9135            })
9136            .unwrap();
9137        machine.fuel = 0;
9138
9139        assert!(matches!(
9140            machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9141            Err(EvalFailure::Runtime(RuntimeErrorKind::FuelExhausted { .. }))
9142        ));
9143        assert!(machine.frames.is_empty());
9144        assert_eq!(machine.live_registers, 0);
9145
9146        machine.fuel = 1;
9147        assert!(matches!(
9148            machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9149            Ok(value) if value == Value::int32(7)
9150        ));
9151    }
9152
9153    #[test]
9154    fn object_values_have_stable_distinct_heap_identity() {
9155        let module = verified(
9156            vec![],
9157            vec![function(
9158                0,
9159                5,
9160                vec![
9161                    Instruction::CreateObject { dst: reg(0) },
9162                    Instruction::CreateObject { dst: reg(1) },
9163                    Instruction::Binary {
9164                        dst: reg(2),
9165                        op: BinaryOp::StrictEqual,
9166                        left: reg(0),
9167                        right: reg(1),
9168                    },
9169                    Instruction::Move {
9170                        dst: reg(3),
9171                        src: reg(0),
9172                    },
9173                    Instruction::Binary {
9174                        dst: reg(4),
9175                        op: BinaryOp::StrictEqual,
9176                        left: reg(0),
9177                        right: reg(3),
9178                    },
9179                    Instruction::Return { value: reg(4) },
9180                ],
9181                vec![],
9182            )],
9183        );
9184        let execution = run_ok(&module);
9185        assert_eq!(execution.entry_registers[2], Value::FALSE);
9186        assert_eq!(execution.value, Value::TRUE);
9187    }
9188
9189    #[test]
9190    fn addition_coerces_objects_left_to_right_and_interpolates_errors() {
9191        let module = verified(
9192            vec![
9193                Constant::String(EcmaString::from_utf8("L")),
9194                Constant::String(EcmaString::from_utf8("additionOrder")),
9195                Constant::String(EcmaString::from_utf8("message")),
9196            ],
9197            vec![
9198                function(0, 1, vec![Instruction::Halt], Vec::new()),
9199                function(
9200                    0,
9201                    1,
9202                    vec![
9203                        Instruction::LoadConst {
9204                            dst: reg(0),
9205                            constant: cid(0),
9206                        },
9207                        Instruction::StoreGlobal {
9208                            name: cid(1),
9209                            value: reg(0),
9210                        },
9211                        Instruction::Return { value: reg(0) },
9212                    ],
9213                    Vec::new(),
9214                ),
9215                function(
9216                    0,
9217                    1,
9218                    vec![
9219                        Instruction::LoadGlobal {
9220                            dst: reg(0),
9221                            name: cid(1),
9222                        },
9223                        Instruction::Return { value: reg(0) },
9224                    ],
9225                    Vec::new(),
9226                ),
9227            ],
9228        );
9229        let mut host = TestHost;
9230        let mut machine = Machine::new(&module, &mut host, Limits::default());
9231        machine.frames.clear();
9232        machine.live_registers = 0;
9233        let left = machine
9234            .allocate(HeapEntry::Object {
9235                properties: PropertyMap::default(),
9236                prototype: Some(machine.intrinsics.object_prototype),
9237                extensible: true,
9238                boxed_primitive: None,
9239            })
9240            .unwrap();
9241        let right = machine
9242            .allocate(HeapEntry::Object {
9243                properties: PropertyMap::default(),
9244                prototype: Some(machine.intrinsics.object_prototype),
9245                extensible: true,
9246                boxed_primitive: None,
9247            })
9248            .unwrap();
9249        let left_value_of = machine
9250            .allocate(HeapEntry::Function {
9251                module: ModuleId::new(0),
9252                function: FunctionId::new(1),
9253                captures: Vec::new(),
9254                properties: PropertyMap::default(),
9255                prototype: Some(machine.intrinsics.function_prototype),
9256                extensible: true,
9257            })
9258            .unwrap();
9259        let right_value_of = machine
9260            .allocate(HeapEntry::Function {
9261                module: ModuleId::new(0),
9262                function: FunctionId::new(2),
9263                captures: Vec::new(),
9264                properties: PropertyMap::default(),
9265                prototype: Some(machine.intrinsics.function_prototype),
9266                extensible: true,
9267            })
9268            .unwrap();
9269        machine
9270            .set_data_property(left, "valueOf", left_value_of)
9271            .unwrap();
9272        machine
9273            .set_data_property(right, "valueOf", right_value_of)
9274            .unwrap();
9275        let coerced = machine.add(left, right).unwrap();
9276        assert!(
9277            machine
9278                .string_value(coerced)
9279                .is_some_and(|text| text.eq_ascii("LL"))
9280        );
9281
9282        let error_constructor = machine.intrinsics.global("Error").unwrap();
9283        let message = machine
9284            .allocate(HeapEntry::String(EcmaString::from_utf8("message")))
9285            .unwrap();
9286        let error = machine
9287            .call_value(error_constructor, Value::UNDEFINED, &[message])
9288            .unwrap();
9289        let empty = machine
9290            .allocate(HeapEntry::String(EcmaString::default()))
9291            .unwrap();
9292        let interpolated = machine.add(empty, error).unwrap();
9293        assert!(
9294            machine
9295                .string_value(interpolated)
9296                .is_some_and(|text| text.eq_ascii("Error: message"))
9297        );
9298
9299        let date_constructor = machine.intrinsics.global("Date").unwrap();
9300        let date_prototype = machine
9301            .get_named_property(date_constructor, "prototype")
9302            .unwrap();
9303        let date = machine
9304            .allocate(HeapEntry::Date {
9305                time: 0.0,
9306                properties: PropertyMap::default(),
9307                prototype: Some(date_prototype),
9308                extensible: true,
9309            })
9310            .unwrap();
9311        machine
9312            .set_data_property(date, "toString", left_value_of)
9313            .unwrap();
9314        let date_text = machine.add(date, empty).unwrap();
9315        assert!(
9316            machine
9317                .string_value(date_text)
9318                .is_some_and(|text| text.eq_ascii("L"))
9319        );
9320    }
9321
9322    #[test]
9323    fn computed_member_access_uses_dynamic_register_key() {
9324        // key = "a" + "b"; obj[key] = 7; return obj[key].
9325        let module = verified(
9326            vec![
9327                Constant::String(EcmaString::from_utf8("a")),
9328                Constant::String(EcmaString::from_utf8("b")),
9329                Constant::Int32(7),
9330            ],
9331            vec![function(
9332                0,
9333                6,
9334                vec![
9335                    Instruction::LoadConst {
9336                        dst: reg(1),
9337                        constant: cid(0),
9338                    },
9339                    Instruction::LoadConst {
9340                        dst: reg(2),
9341                        constant: cid(1),
9342                    },
9343                    Instruction::Binary {
9344                        dst: reg(3),
9345                        op: BinaryOp::Add,
9346                        left: reg(1),
9347                        right: reg(2),
9348                    },
9349                    Instruction::CreateObject { dst: reg(0) },
9350                    Instruction::LoadConst {
9351                        dst: reg(4),
9352                        constant: cid(2),
9353                    },
9354                    Instruction::SetProperty {
9355                        object: reg(0),
9356                        key: reg(3),
9357                        value: reg(4),
9358                    },
9359                    Instruction::GetProperty {
9360                        dst: reg(5),
9361                        object: reg(0),
9362                        key: reg(3),
9363                    },
9364                    Instruction::Return { value: reg(5) },
9365                ],
9366                vec![],
9367            )],
9368        );
9369        assert_eq!(run_ok(&module).value, Value::int32(7));
9370    }
9371
9372    #[test]
9373    fn property_delete_and_array_holes_are_real_mutations() {
9374        let module = verified(
9375            vec![
9376                Constant::String(EcmaString::from_utf8("0")),
9377                Constant::Int32(5),
9378            ],
9379            vec![function(
9380                0,
9381                5,
9382                vec![
9383                    Instruction::CreateArray { dst: reg(0) },
9384                    Instruction::LoadConst {
9385                        dst: reg(1),
9386                        constant: cid(0),
9387                    },
9388                    Instruction::LoadConst {
9389                        dst: reg(4),
9390                        constant: cid(1),
9391                    },
9392                    Instruction::SetProperty {
9393                        object: reg(0),
9394                        key: reg(1),
9395                        value: reg(4),
9396                    },
9397                    Instruction::GetProperty {
9398                        dst: reg(2),
9399                        object: reg(0),
9400                        key: reg(1),
9401                    },
9402                    Instruction::DeleteProperty {
9403                        dst: reg(3),
9404                        object: reg(0),
9405                        key: reg(1),
9406                    },
9407                    Instruction::GetProperty {
9408                        dst: reg(4),
9409                        object: reg(0),
9410                        key: reg(1),
9411                    },
9412                    Instruction::Return { value: reg(3) },
9413                ],
9414                vec![],
9415            )],
9416        );
9417        let execution = run_ok(&module);
9418        assert_eq!(execution.entry_registers[2], Value::int32(5));
9419        assert_eq!(execution.entry_registers[4], Value::UNDEFINED);
9420        assert_eq!(execution.value, Value::TRUE);
9421    }
9422
9423    #[test]
9424    fn closure_captures_seed_leading_registers_before_parameters() {
9425        // captures = [42]; fn1(7) => capture(r0) + param(r1) = 49.
9426        let entry = function(
9427            0,
9428            3,
9429            vec![
9430                Instruction::CreateArray { dst: reg(0) },
9431                Instruction::LoadConst {
9432                    dst: reg(1),
9433                    constant: cid(0),
9434                },
9435                Instruction::ArrayPush {
9436                    array: reg(0),
9437                    value: reg(1),
9438                },
9439                Instruction::CreateClosure {
9440                    dst: reg(2),
9441                    function: FunctionId::new(1),
9442                    captures: reg(0),
9443                },
9444                // arguments array [7]
9445                Instruction::CreateArray { dst: reg(0) },
9446                Instruction::LoadConst {
9447                    dst: reg(1),
9448                    constant: cid(1),
9449                },
9450                Instruction::ArrayPush {
9451                    array: reg(0),
9452                    value: reg(1),
9453                },
9454                Instruction::LoadConst {
9455                    dst: reg(1),
9456                    constant: cid(2),
9457                },
9458                Instruction::Call {
9459                    dst: reg(1),
9460                    callee: reg(2),
9461                    this_value: reg(1),
9462                    arguments: reg(0),
9463                },
9464                Instruction::Return { value: reg(1) },
9465            ],
9466            vec![],
9467        );
9468        // capture_count = 1, parameter_count = 1: r0 = capture, r1 = param.
9469        let callee = closure_function(
9470            1,
9471            1,
9472            3,
9473            vec![
9474                Instruction::Binary {
9475                    dst: reg(2),
9476                    op: BinaryOp::Add,
9477                    left: reg(0),
9478                    right: reg(1),
9479                },
9480                Instruction::Return { value: reg(2) },
9481            ],
9482        );
9483        let module = verified(
9484            vec![Constant::Int32(42), Constant::Int32(7), Constant::Undefined],
9485            vec![entry, callee],
9486        );
9487        assert_eq!(run_ok(&module).value, Value::int32(49));
9488    }
9489
9490    #[test]
9491    fn calls_scale_past_fixed_window_via_arguments_array() {
9492        // Build a 500-element arguments array and call a callee returning
9493        // arguments.length — impossible under a 127 fixed window.
9494        let mut code = vec![Instruction::CreateArray { dst: reg(0) }];
9495        code.push(Instruction::LoadConst {
9496            dst: reg(1),
9497            constant: cid(0),
9498        });
9499        for _ in 0..500 {
9500            code.push(Instruction::ArrayPush {
9501                array: reg(0),
9502                value: reg(1),
9503            });
9504        }
9505        code.push(Instruction::CreateClosure {
9506            dst: reg(2),
9507            function: FunctionId::new(1),
9508            captures: reg(3),
9509        });
9510        // captures array for a zero-capture function
9511        // (reg(3) must be an empty array)
9512        // Insert its creation before CreateClosure:
9513        let mut prelude = vec![Instruction::CreateArray { dst: reg(3) }];
9514        prelude.append(&mut code);
9515        let mut code = prelude;
9516        code.push(Instruction::LoadConst {
9517            dst: reg(1),
9518            constant: cid(1),
9519        });
9520        code.push(Instruction::Call {
9521            dst: reg(1),
9522            callee: reg(2),
9523            this_value: reg(1),
9524            arguments: reg(0),
9525        });
9526        code.push(Instruction::Return { value: reg(1) });
9527
9528        let entry = function(0, 4, code, vec![]);
9529        let callee = function(
9530            0,
9531            2,
9532            vec![
9533                Instruction::LoadArguments { dst: reg(0) },
9534                Instruction::LoadConst {
9535                    dst: reg(1),
9536                    constant: cid(2),
9537                },
9538                Instruction::GetProperty {
9539                    dst: reg(0),
9540                    object: reg(0),
9541                    key: reg(1),
9542                },
9543                Instruction::Return { value: reg(0) },
9544            ],
9545            vec![],
9546        );
9547        let module = verified(
9548            vec![
9549                Constant::Int32(1),
9550                Constant::Undefined,
9551                Constant::String(EcmaString::from_utf8("length")),
9552            ],
9553            vec![entry, callee],
9554        );
9555        assert_eq!(run_ok(&module).value, Value::int32(500));
9556    }
9557
9558    #[test]
9559    fn array_extend_spreads_iterable_elements() {
9560        // dst = []; dst.push(1); dst.extend([2,3]); return dst.length == 3.
9561        let entry = function(
9562            0,
9563            4,
9564            vec![
9565                Instruction::CreateArray { dst: reg(0) },
9566                Instruction::LoadConst {
9567                    dst: reg(1),
9568                    constant: cid(0),
9569                },
9570                Instruction::ArrayPush {
9571                    array: reg(0),
9572                    value: reg(1),
9573                },
9574                // source [2,3]
9575                Instruction::CreateArray { dst: reg(2) },
9576                Instruction::LoadConst {
9577                    dst: reg(1),
9578                    constant: cid(1),
9579                },
9580                Instruction::ArrayPush {
9581                    array: reg(2),
9582                    value: reg(1),
9583                },
9584                Instruction::LoadConst {
9585                    dst: reg(1),
9586                    constant: cid(2),
9587                },
9588                Instruction::ArrayPush {
9589                    array: reg(2),
9590                    value: reg(1),
9591                },
9592                Instruction::ArrayExtend {
9593                    array: reg(0),
9594                    iterable: reg(2),
9595                },
9596                Instruction::LoadConst {
9597                    dst: reg(3),
9598                    constant: cid(3),
9599                },
9600                Instruction::GetProperty {
9601                    dst: reg(0),
9602                    object: reg(0),
9603                    key: reg(3),
9604                },
9605                Instruction::Return { value: reg(0) },
9606            ],
9607            vec![],
9608        );
9609        let module = verified(
9610            vec![
9611                Constant::Int32(1),
9612                Constant::Int32(2),
9613                Constant::Int32(3),
9614                Constant::String(EcmaString::from_utf8("length")),
9615            ],
9616            vec![entry],
9617        );
9618        assert_eq!(run_ok(&module).value, Value::int32(3));
9619    }
9620
9621    #[test]
9622    fn array_extend_uses_sync_protocol_for_set_and_rejects_plain_object() {
9623        let module = verified(
9624            Vec::new(),
9625            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9626        );
9627        let mut host = TestHost;
9628        let mut machine = Machine::new(&module, &mut host, Limits::default());
9629        let set_constructor = machine.intrinsics.global("Set").unwrap();
9630        let set_prototype = machine
9631            .get_named_property(set_constructor, "prototype")
9632            .unwrap();
9633        let set = machine
9634            .allocate(HeapEntry::Collection {
9635                entries: vec![CollectionEntry {
9636                    order: 0,
9637                    key: Value::int32(7),
9638                    value: Value::int32(7),
9639                }],
9640                next_order: 1,
9641                properties: PropertyMap::default(),
9642                prototype: Some(set_prototype),
9643                extensible: true,
9644            })
9645            .unwrap();
9646        let target = machine
9647            .allocate(HeapEntry::Array {
9648                elements: Vec::new(),
9649                properties: PropertyMap::default(),
9650                prototype: Some(machine.intrinsics.array_prototype),
9651                extensible: true,
9652                length_writable: true,
9653            })
9654            .unwrap();
9655
9656        machine.array_extend(target, set).unwrap();
9657        assert_eq!(
9658            machine.array_elements(target).unwrap(),
9659            Some(vec![Value::int32(7)])
9660        );
9661
9662        let plain_object = machine
9663            .allocate(HeapEntry::Object {
9664                properties: PropertyMap::default(),
9665                prototype: Some(machine.intrinsics.object_prototype),
9666                boxed_primitive: None,
9667                extensible: true,
9668            })
9669            .unwrap();
9670        assert!(matches!(
9671            machine.array_extend(target, plain_object),
9672            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9673                operation: "value is not iterable"
9674            }))
9675        ));
9676    }
9677
9678    #[test]
9679    fn sync_iterator_uses_symbol_method_and_caches_next() {
9680        fn iterator_identity<H: Host>(
9681            _machine: &mut Machine<'_, H>,
9682            this: Value,
9683            _args: &[Value],
9684            _constructing: bool,
9685        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9686            Ok(intrinsics::BuiltinOutcome::Value(this))
9687        }
9688
9689        fn next_getter<H: Host>(
9690            machine: &mut Machine<'_, H>,
9691            this: Value,
9692            _args: &[Value],
9693            _constructing: bool,
9694        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9695            let reads = machine.get_named_property(this, "nextReads")?;
9696            let reads = if reads == Value::int32(0) { 1 } else { 2 };
9697            machine.set_data_property(this, "nextReads", Value::int32(reads))?;
9698            Ok(intrinsics::BuiltinOutcome::Value(
9699                machine.get_named_property(this, "nextFunction")?,
9700            ))
9701        }
9702
9703        fn next_result<H: Host>(
9704            machine: &mut Machine<'_, H>,
9705            this: Value,
9706            _args: &[Value],
9707            _constructing: bool,
9708        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9709            Ok(intrinsics::BuiltinOutcome::Value(
9710                machine.get_named_property(this, "result")?,
9711            ))
9712        }
9713
9714        fn done_getter<H: Host>(
9715            machine: &mut Machine<'_, H>,
9716            this: Value,
9717            _args: &[Value],
9718            _constructing: bool,
9719        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9720            machine.set_data_property(this, "order", Value::int32(1))?;
9721            Ok(intrinsics::BuiltinOutcome::Value(Value::FALSE))
9722        }
9723
9724        fn value_getter<H: Host>(
9725            machine: &mut Machine<'_, H>,
9726            this: Value,
9727            _args: &[Value],
9728            _constructing: bool,
9729        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9730            if machine.get_named_property(this, "order")? != Value::int32(1) {
9731                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9732                    operation: "iterator value read before done",
9733                }));
9734            }
9735            Ok(intrinsics::BuiltinOutcome::Value(Value::int32(42)))
9736        }
9737
9738        let module = verified(
9739            Vec::new(),
9740            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9741        );
9742        let mut host = TestHost;
9743        let mut machine = Machine::new(&module, &mut host, Limits::default());
9744        let mut install = |name, handler| {
9745            let id = machine
9746                .intrinsics
9747                .builtins
9748                .register(intrinsics::BuiltinDef {
9749                    name,
9750                    length: 0,
9751                    handler,
9752                });
9753            intrinsics::native_function(&mut machine.heap, id, name, 0)
9754        };
9755        let iterator_identity = install(
9756            "[Symbol.iterator]",
9757            iterator_identity::<TestHost> as intrinsics::BuiltinHandler<TestHost>,
9758        );
9759        let next_getter = install("get next", next_getter::<TestHost>);
9760        let next_result = install("next", next_result::<TestHost>);
9761        let done_getter = install("get done", done_getter::<TestHost>);
9762        let value_getter = install("get value", value_getter::<TestHost>);
9763        let object_prototype = machine.intrinsics.object_prototype;
9764        let result = machine
9765            .allocate(HeapEntry::Object {
9766                properties: {
9767                    let mut properties = PropertyMap::default();
9768                    for (key, property) in [
9769                        (
9770                            PropertyKey::Named(EcmaString::from_utf8("order")),
9771                            Property::Data {
9772                                value: Value::int32(0),
9773                                writable: true,
9774                                enumerable: true,
9775                                configurable: true,
9776                            },
9777                        ),
9778                        (
9779                            PropertyKey::Named(EcmaString::from_utf8("done")),
9780                            Property::Accessor {
9781                                getter: Some(done_getter),
9782                                setter: None,
9783                                enumerable: true,
9784                                configurable: true,
9785                            },
9786                        ),
9787                        (
9788                            PropertyKey::Named(EcmaString::from_utf8("value")),
9789                            Property::Accessor {
9790                                getter: Some(value_getter),
9791                                setter: None,
9792                                enumerable: true,
9793                                configurable: true,
9794                            },
9795                        ),
9796                    ] {
9797                        properties.insert(key, property);
9798                    }
9799                    properties
9800                },
9801                prototype: Some(object_prototype),
9802                boxed_primitive: None,
9803                extensible: true,
9804            })
9805            .unwrap();
9806        let iterator_symbol = machine.intrinsics.builtins.symbol_iterator();
9807        let iterator_key = machine.to_property_key(iterator_symbol).unwrap();
9808        let source = machine
9809            .allocate(HeapEntry::Object {
9810                properties: {
9811                    let mut properties = PropertyMap::default();
9812                    for (key, property) in [
9813                        (
9814                            iterator_key,
9815                            Property::Data {
9816                                value: iterator_identity,
9817                                writable: true,
9818                                enumerable: false,
9819                                configurable: true,
9820                            },
9821                        ),
9822                        (
9823                            PropertyKey::Named(EcmaString::from_utf8("next")),
9824                            Property::Accessor {
9825                                getter: Some(next_getter),
9826                                setter: None,
9827                                enumerable: false,
9828                                configurable: true,
9829                            },
9830                        ),
9831                        (
9832                            PropertyKey::Named(EcmaString::from_utf8("nextReads")),
9833                            Property::Data {
9834                                value: Value::int32(0),
9835                                writable: true,
9836                                enumerable: true,
9837                                configurable: true,
9838                            },
9839                        ),
9840                        (
9841                            PropertyKey::Named(EcmaString::from_utf8("nextFunction")),
9842                            Property::Data {
9843                                value: next_result,
9844                                writable: true,
9845                                enumerable: true,
9846                                configurable: true,
9847                            },
9848                        ),
9849                        (
9850                            PropertyKey::Named(EcmaString::from_utf8("result")),
9851                            Property::Data {
9852                                value: result,
9853                                writable: true,
9854                                enumerable: true,
9855                                configurable: true,
9856                            },
9857                        ),
9858                    ] {
9859                        properties.insert(key, property);
9860                    }
9861                    properties
9862                },
9863                prototype: Some(object_prototype),
9864                boxed_primitive: None,
9865                extensible: true,
9866            })
9867            .unwrap();
9868
9869        let iterator = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9870        assert_eq!(
9871            machine.iterator_next(iterator).unwrap(),
9872            (false, Value::int32(42))
9873        );
9874        assert_eq!(
9875            machine.iterator_next(iterator).unwrap(),
9876            (false, Value::int32(42))
9877        );
9878        assert_eq!(
9879            machine.get_named_property(source, "nextReads").unwrap(),
9880            Value::int32(1)
9881        );
9882
9883        let mut completed_properties = PropertyMap::default();
9884        completed_properties.insert(
9885            PropertyKey::Named(EcmaString::from_utf8("done")),
9886            Property::Data {
9887                value: Value::TRUE,
9888                writable: true,
9889                enumerable: true,
9890                configurable: true,
9891            },
9892        );
9893        completed_properties.insert(
9894            PropertyKey::Named(EcmaString::from_utf8("value")),
9895            Property::Accessor {
9896                getter: Some(value_getter),
9897                setter: None,
9898                enumerable: true,
9899                configurable: true,
9900            },
9901        );
9902        let completed = machine
9903            .allocate(HeapEntry::Object {
9904                properties: completed_properties,
9905                prototype: Some(object_prototype),
9906                boxed_primitive: None,
9907                extensible: true,
9908            })
9909            .unwrap();
9910        machine
9911            .set_data_property(source, "result", completed)
9912            .unwrap();
9913        assert_eq!(
9914            machine.iterator_next(iterator).unwrap(),
9915            (true, Value::UNDEFINED)
9916        );
9917
9918        machine
9919            .delete_property(source, &PropertyKey::Named(EcmaString::from_utf8("next")))
9920            .unwrap();
9921        machine
9922            .set_data_property(source, "next", Value::int32(1))
9923            .unwrap();
9924        let invalid_next = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9925        assert!(matches!(
9926            machine.iterator_next(invalid_next),
9927            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
9928        ));
9929    }
9930
9931    #[test]
9932    fn object_spread_copies_own_properties() {
9933        // src = {}; src.x = 9; target = {}; { ...src }; return target.x.
9934        let key = |c: u32| Instruction::LoadConst {
9935            dst: reg(3),
9936            constant: cid(c),
9937        };
9938        let module = verified(
9939            vec![
9940                Constant::String(EcmaString::from_utf8("x")),
9941                Constant::Int32(9),
9942            ],
9943            vec![function(
9944                0,
9945                4,
9946                vec![
9947                    Instruction::CreateObject { dst: reg(0) },
9948                    key(0),
9949                    Instruction::LoadConst {
9950                        dst: reg(2),
9951                        constant: cid(1),
9952                    },
9953                    Instruction::SetProperty {
9954                        object: reg(0),
9955                        key: reg(3),
9956                        value: reg(2),
9957                    },
9958                    Instruction::CreateObject { dst: reg(1) },
9959                    Instruction::ObjectSpread {
9960                        target: reg(1),
9961                        source: reg(0),
9962                    },
9963                    key(0),
9964                    Instruction::GetProperty {
9965                        dst: reg(2),
9966                        object: reg(1),
9967                        key: reg(3),
9968                    },
9969                    Instruction::Return { value: reg(2) },
9970                ],
9971                vec![],
9972            )],
9973        );
9974        assert_eq!(run_ok(&module).value, Value::int32(9));
9975    }
9976
9977    #[test]
9978    fn object_spread_copies_enumerable_symbol_properties() {
9979        let module = verified(
9980            Vec::new(),
9981            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9982        );
9983        let mut host = TestHost;
9984        let mut machine = Machine::new(&module, &mut host, Limits::default());
9985        let prototype = machine.intrinsics.object_prototype;
9986        let object = |machine: &mut Machine<'_, TestHost>| {
9987            machine
9988                .allocate(HeapEntry::Object {
9989                    properties: PropertyMap::default(),
9990                    prototype: Some(prototype),
9991                    boxed_primitive: None,
9992                    extensible: true,
9993                })
9994                .unwrap()
9995        };
9996        let source = object(&mut machine);
9997        let target = object(&mut machine);
9998        let symbol = machine
9999            .allocate(HeapEntry::Symbol {
10000                description: EcmaString::from_utf8("key"),
10001            })
10002            .unwrap();
10003        let key = machine.to_property_key(symbol).unwrap();
10004        machine
10005            .set_data_property_key(source, key.clone(), Value::int32(42))
10006            .unwrap();
10007
10008        machine.object_spread(target, source).unwrap();
10009
10010        assert_eq!(
10011            machine.get_property_key(target, &key).unwrap(),
10012            Value::int32(42)
10013        );
10014    }
10015
10016    #[test]
10017    fn object_spread_rechecks_descriptors_after_getters() {
10018        fn delete_next<H: Host>(
10019            machine: &mut Machine<'_, H>,
10020            this: Value,
10021            _args: &[Value],
10022            _constructing: bool,
10023        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
10024            machine.delete_property(this, &PropertyKey::Named(EcmaString::from_utf8("next")))?;
10025            Ok(intrinsics::BuiltinOutcome::Value(Value::int32(1)))
10026        }
10027
10028        let module = verified(
10029            Vec::new(),
10030            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
10031        );
10032        let mut host = TestHost;
10033        let mut machine = Machine::new(&module, &mut host, Limits::default());
10034        let getter_id = machine
10035            .intrinsics
10036            .builtins
10037            .register(intrinsics::BuiltinDef {
10038                name: "delete next",
10039                length: 0,
10040                handler: delete_next::<TestHost>,
10041            });
10042        let getter = intrinsics::native_function(&mut machine.heap, getter_id, "delete next", 0);
10043        let first = PropertyKey::Named(EcmaString::from_utf8("first"));
10044        let next = PropertyKey::Named(EcmaString::from_utf8("next"));
10045        let mut source_properties = PropertyMap::default();
10046        source_properties.insert(
10047            first.clone(),
10048            Property::Accessor {
10049                getter: Some(getter),
10050                setter: None,
10051                enumerable: true,
10052                configurable: true,
10053            },
10054        );
10055        source_properties.insert(
10056            next.clone(),
10057            Property::Data {
10058                value: Value::int32(2),
10059                writable: true,
10060                enumerable: true,
10061                configurable: true,
10062            },
10063        );
10064        let prototype = machine.intrinsics.object_prototype;
10065        let source = machine
10066            .allocate(HeapEntry::Object {
10067                properties: source_properties,
10068                prototype: Some(prototype),
10069                boxed_primitive: None,
10070                extensible: true,
10071            })
10072            .unwrap();
10073        let target = machine
10074            .allocate(HeapEntry::Object {
10075                properties: PropertyMap::default(),
10076                prototype: Some(prototype),
10077                boxed_primitive: None,
10078                extensible: true,
10079            })
10080            .unwrap();
10081
10082        machine.object_spread(target, source).unwrap();
10083
10084        assert_eq!(
10085            machine.get_property_key(target, &first).unwrap(),
10086            Value::int32(1)
10087        );
10088        assert!(!machine.has_own_property_key(target, &next).unwrap());
10089    }
10090
10091    #[test]
10092    fn private_names_have_distinct_identity_and_are_gettable() {
10093        // Two private names with the same description are distinct keys.
10094        let module = verified(
10095            vec![
10096                Constant::String(EcmaString::from_utf8("x")),
10097                Constant::Int32(1),
10098                Constant::Int32(2),
10099            ],
10100            vec![function(
10101                0,
10102                6,
10103                vec![
10104                    Instruction::CreateObject { dst: reg(0) },
10105                    Instruction::CreatePrivateName {
10106                        dst: reg(1),
10107                        description: cid(0),
10108                    },
10109                    Instruction::CreatePrivateName {
10110                        dst: reg(2),
10111                        description: cid(0),
10112                    },
10113                    Instruction::LoadConst {
10114                        dst: reg(3),
10115                        constant: cid(1),
10116                    },
10117                    Instruction::SetProperty {
10118                        object: reg(0),
10119                        key: reg(1),
10120                        value: reg(3),
10121                    },
10122                    Instruction::LoadConst {
10123                        dst: reg(3),
10124                        constant: cid(2),
10125                    },
10126                    Instruction::SetProperty {
10127                        object: reg(0),
10128                        key: reg(2),
10129                        value: reg(3),
10130                    },
10131                    // r4 = obj[#1] (1), r5 = obj[#2] (2)
10132                    Instruction::GetProperty {
10133                        dst: reg(4),
10134                        object: reg(0),
10135                        key: reg(1),
10136                    },
10137                    Instruction::GetProperty {
10138                        dst: reg(5),
10139                        object: reg(0),
10140                        key: reg(2),
10141                    },
10142                    // distinctness: #1 !== #2
10143                    Instruction::Binary {
10144                        dst: reg(3),
10145                        op: BinaryOp::StrictEqual,
10146                        left: reg(1),
10147                        right: reg(2),
10148                    },
10149                    Instruction::Return { value: reg(4) },
10150                ],
10151                vec![],
10152            )],
10153        );
10154        let execution = run_ok(&module);
10155        assert_eq!(execution.value, Value::int32(1));
10156        assert_eq!(execution.entry_registers[5], Value::int32(2));
10157        assert_eq!(execution.entry_registers[3], Value::FALSE);
10158    }
10159
10160    #[test]
10161    fn accessor_getter_is_invoked_on_property_read() {
10162        // Define a getter returning 99, then read the property.
10163        let entry = function(
10164            0,
10165            4,
10166            vec![
10167                Instruction::CreateObject { dst: reg(0) },
10168                Instruction::CreateArray { dst: reg(3) },
10169                Instruction::CreateClosure {
10170                    dst: reg(1),
10171                    function: FunctionId::new(1),
10172                    captures: reg(3),
10173                },
10174                Instruction::LoadConst {
10175                    dst: reg(2),
10176                    constant: cid(0),
10177                },
10178                Instruction::DefineAccessor {
10179                    object: reg(0),
10180                    key: reg(2),
10181                    accessor: reg(1),
10182                    kind: AccessorKind::Getter,
10183                },
10184                Instruction::GetProperty {
10185                    dst: reg(1),
10186                    object: reg(0),
10187                    key: reg(2),
10188                },
10189                Instruction::Return { value: reg(1) },
10190            ],
10191            vec![],
10192        );
10193        let getter = function(
10194            0,
10195            1,
10196            vec![
10197                Instruction::LoadConst {
10198                    dst: reg(0),
10199                    constant: cid(1),
10200                },
10201                Instruction::Return { value: reg(0) },
10202            ],
10203            vec![],
10204        );
10205        let module = verified(
10206            vec![
10207                Constant::String(EcmaString::from_utf8("g")),
10208                Constant::Int32(99),
10209            ],
10210            vec![entry, getter],
10211        );
10212        assert_eq!(run_ok(&module).value, Value::int32(99));
10213    }
10214
10215    #[test]
10216    fn prototype_chain_lookup_and_instanceof() {
10217        // proto = {}; proto.m = 5; ctor.prototype = proto; obj = new ctor();
10218        // return (obj.m == 5) && (obj instanceof ctor).
10219        let entry = function(
10220            0,
10221            6,
10222            vec![
10223                // proto object with m = 5
10224                Instruction::CreateObject { dst: reg(0) },
10225                Instruction::LoadConst {
10226                    dst: reg(1),
10227                    constant: cid(0),
10228                },
10229                Instruction::LoadConst {
10230                    dst: reg(2),
10231                    constant: cid(1),
10232                },
10233                Instruction::SetProperty {
10234                    object: reg(0),
10235                    key: reg(1),
10236                    value: reg(2),
10237                },
10238                // ctor closure
10239                Instruction::CreateArray { dst: reg(4) },
10240                Instruction::CreateClosure {
10241                    dst: reg(3),
10242                    function: FunctionId::new(1),
10243                    captures: reg(4),
10244                },
10245                // ctor.prototype = proto
10246                Instruction::LoadConst {
10247                    dst: reg(1),
10248                    constant: cid(2),
10249                },
10250                Instruction::SetProperty {
10251                    object: reg(3),
10252                    key: reg(1),
10253                    value: reg(0),
10254                },
10255                // obj = new ctor()  (empty args)
10256                Instruction::CreateArray { dst: reg(4) },
10257                Instruction::Construct {
10258                    dst: reg(0),
10259                    callee: reg(3),
10260                    arguments: reg(4),
10261                },
10262                // obj.m via prototype chain
10263                Instruction::LoadConst {
10264                    dst: reg(1),
10265                    constant: cid(0),
10266                },
10267                Instruction::GetProperty {
10268                    dst: reg(2),
10269                    object: reg(0),
10270                    key: reg(1),
10271                },
10272                // obj instanceof ctor
10273                Instruction::Binary {
10274                    dst: reg(5),
10275                    op: BinaryOp::InstanceOf,
10276                    left: reg(0),
10277                    right: reg(3),
10278                },
10279                Instruction::Return { value: reg(2) },
10280            ],
10281            vec![],
10282        );
10283        let ctor = function(0, 1, vec![Instruction::Halt], vec![]);
10284        let module = verified(
10285            vec![
10286                Constant::String(EcmaString::from_utf8("m")),
10287                Constant::Int32(5),
10288                Constant::String(EcmaString::from_utf8("prototype")),
10289            ],
10290            vec![entry, ctor],
10291        );
10292        let execution = run_ok(&module);
10293        assert_eq!(execution.value, Value::int32(5));
10294        assert_eq!(execution.entry_registers[5], Value::TRUE);
10295    }
10296
10297    #[test]
10298    fn sync_iterator_walks_array_elements() {
10299        // Sum [10,20] via GetIterator/IteratorNext loop.
10300        let entry = function(
10301            0,
10302            6,
10303            vec![
10304                Instruction::CreateArray { dst: reg(0) },
10305                Instruction::LoadConst {
10306                    dst: reg(1),
10307                    constant: cid(0),
10308                },
10309                Instruction::ArrayPush {
10310                    array: reg(0),
10311                    value: reg(1),
10312                },
10313                Instruction::LoadConst {
10314                    dst: reg(1),
10315                    constant: cid(1),
10316                },
10317                Instruction::ArrayPush {
10318                    array: reg(0),
10319                    value: reg(1),
10320                },
10321                // acc = 0
10322                Instruction::LoadConst {
10323                    dst: reg(2),
10324                    constant: cid(2),
10325                },
10326                Instruction::GetIterator {
10327                    dst: reg(3),
10328                    src: reg(0),
10329                    kind: IteratorKind::Sync,
10330                },
10331                // loop head @7: next
10332                Instruction::IteratorNext {
10333                    done: reg(4),
10334                    value: reg(5),
10335                    iterator: reg(3),
10336                },
10337                Instruction::JumpIfTrue {
10338                    condition: reg(4),
10339                    target: pc(11),
10340                },
10341                Instruction::Binary {
10342                    dst: reg(2),
10343                    op: BinaryOp::Add,
10344                    left: reg(2),
10345                    right: reg(5),
10346                },
10347                Instruction::Jump { target: pc(7) },
10348                // @11 done
10349                Instruction::Return { value: reg(2) },
10350            ],
10351            vec![],
10352        );
10353        let module = verified(
10354            vec![Constant::Int32(10), Constant::Int32(20), Constant::Int32(0)],
10355            vec![entry],
10356        );
10357        assert_eq!(run_ok(&module).value, Value::int32(30));
10358    }
10359
10360    #[test]
10361    fn keys_iterator_enumerates_own_object_keys() {
10362        // obj = {a:1}; for-in yields "a".
10363        let entry = function(
10364            0,
10365            6,
10366            vec![
10367                Instruction::CreateObject { dst: reg(0) },
10368                Instruction::LoadConst {
10369                    dst: reg(1),
10370                    constant: cid(0),
10371                },
10372                Instruction::LoadConst {
10373                    dst: reg(2),
10374                    constant: cid(1),
10375                },
10376                Instruction::SetProperty {
10377                    object: reg(0),
10378                    key: reg(1),
10379                    value: reg(2),
10380                },
10381                Instruction::GetIterator {
10382                    dst: reg(3),
10383                    src: reg(0),
10384                    kind: IteratorKind::Keys,
10385                },
10386                Instruction::IteratorNext {
10387                    done: reg(4),
10388                    value: reg(5),
10389                    iterator: reg(3),
10390                },
10391                Instruction::Return { value: reg(5) },
10392            ],
10393            vec![],
10394        );
10395        let module = verified(
10396            vec![
10397                Constant::String(EcmaString::from_utf8("a")),
10398                Constant::Int32(1),
10399            ],
10400            vec![entry],
10401        );
10402        let execution = run_ok(&module);
10403        // The produced key must equal a fresh "a" string.
10404        let key = execution.value;
10405        // Compare via a second machine's constant is awkward; instead assert it
10406        // is a heap string by checking done flag was false.
10407        assert_eq!(execution.entry_registers[4], Value::FALSE);
10408        assert_ne!(key, Value::UNDEFINED);
10409    }
10410
10411    #[test]
10412    fn async_iterator_steps_like_sync() {
10413        let entry = function(
10414            0,
10415            5,
10416            vec![
10417                Instruction::CreateArray { dst: reg(0) },
10418                Instruction::LoadConst {
10419                    dst: reg(1),
10420                    constant: cid(0),
10421                },
10422                Instruction::ArrayPush {
10423                    array: reg(0),
10424                    value: reg(1),
10425                },
10426                Instruction::GetIterator {
10427                    dst: reg(2),
10428                    src: reg(0),
10429                    kind: IteratorKind::Async,
10430                },
10431                Instruction::IteratorNext {
10432                    done: reg(3),
10433                    value: reg(4),
10434                    iterator: reg(2),
10435                },
10436                Instruction::Return { value: reg(4) },
10437            ],
10438            vec![],
10439        );
10440        let module = verified(vec![Constant::Int32(8)], vec![entry]);
10441        let execution = run_ok(&module);
10442        assert_eq!(execution.value, Value::int32(8));
10443        assert_eq!(execution.entry_registers[3], Value::FALSE);
10444    }
10445
10446    #[test]
10447    fn globals_store_load_and_typeof_undeclared() {
10448        // StoreGlobal x=5; TypeOfGlobal y (undeclared) -> "undefined";
10449        // TypeOfGlobal x -> "number"; return LoadGlobal x.
10450        let entry = function(
10451            0,
10452            3,
10453            vec![
10454                Instruction::LoadConst {
10455                    dst: reg(0),
10456                    constant: cid(2),
10457                },
10458                Instruction::StoreGlobal {
10459                    name: cid(0),
10460                    value: reg(0),
10461                },
10462                Instruction::TypeOfGlobal {
10463                    dst: reg(1),
10464                    name: cid(1),
10465                },
10466                Instruction::TypeOfGlobal {
10467                    dst: reg(2),
10468                    name: cid(0),
10469                },
10470                Instruction::LoadGlobal {
10471                    dst: reg(0),
10472                    name: cid(0),
10473                },
10474                Instruction::Return { value: reg(0) },
10475            ],
10476            vec![],
10477        );
10478        let module = verified(
10479            vec![
10480                Constant::String(EcmaString::from_utf8("x")),
10481                Constant::String(EcmaString::from_utf8("y")),
10482                Constant::Int32(5),
10483            ],
10484            vec![entry],
10485        );
10486        assert_eq!(run_ok(&module).value, Value::int32(5));
10487    }
10488
10489    #[test]
10490    fn create_cell_throws_reference_error_before_initialization() {
10491        let module = verified(
10492            vec![Constant::Int32(0)],
10493            vec![function(
10494                0,
10495                3,
10496                vec![
10497                    Instruction::CreateCell { dst: reg(0) },
10498                    Instruction::LoadConst {
10499                        dst: reg(1),
10500                        constant: cid(0),
10501                    },
10502                    Instruction::GetProperty {
10503                        dst: reg(2),
10504                        object: reg(0),
10505                        key: reg(1),
10506                    },
10507                    Instruction::Return { value: reg(2) },
10508                ],
10509                vec![],
10510            )],
10511        );
10512        let mut host = TestHost;
10513        let error = Machine::new(&module, &mut host, Limits::default())
10514            .run()
10515            .expect_err("uninitialized cell read throws");
10516        assert!(matches!(
10517            error.kind,
10518            RuntimeErrorKind::UncaughtThrow {
10519                origin: ThrowOrigin::ReferenceError { .. },
10520                ..
10521            }
10522        ));
10523    }
10524
10525    #[test]
10526    fn create_cell_can_be_initialized_to_undefined() {
10527        let module = verified(
10528            vec![Constant::Int32(0), Constant::Undefined],
10529            vec![function(
10530                0,
10531                4,
10532                vec![
10533                    Instruction::CreateCell { dst: reg(0) },
10534                    Instruction::LoadConst {
10535                        dst: reg(1),
10536                        constant: cid(0),
10537                    },
10538                    Instruction::LoadConst {
10539                        dst: reg(2),
10540                        constant: cid(1),
10541                    },
10542                    Instruction::SetProperty {
10543                        object: reg(0),
10544                        key: reg(1),
10545                        value: reg(2),
10546                    },
10547                    Instruction::GetProperty {
10548                        dst: reg(3),
10549                        object: reg(0),
10550                        key: reg(1),
10551                    },
10552                    Instruction::Return { value: reg(3) },
10553                ],
10554                vec![],
10555            )],
10556        );
10557        let mut host = TestHost;
10558        let execution = Machine::new(&module, &mut host, Limits::default())
10559            .run()
10560            .expect("explicit undefined initializes the cell");
10561        assert_eq!(execution.value, Value::UNDEFINED);
10562    }
10563
10564    #[test]
10565    fn load_undeclared_global_throws_reference_error() {
10566        let module = verified(
10567            vec![Constant::String(EcmaString::from_utf8("missing"))],
10568            vec![function(
10569                0,
10570                2,
10571                vec![
10572                    Instruction::LoadGlobal {
10573                        dst: reg(0),
10574                        name: cid(0),
10575                    },
10576                    Instruction::Halt,
10577                    Instruction::Return { value: reg(1) },
10578                ],
10579                vec![ExceptionHandler {
10580                    start: pc(0),
10581                    end: pc(1),
10582                    handler: pc(2),
10583                    catch_register: reg(1),
10584                }],
10585            )],
10586        );
10587        let mut host = TestHost;
10588        // No handler at top level path would raise; here it is caught, and the
10589        // caught value is undefined (the ReferenceError marker value).
10590        let execution = Machine::new(&module, &mut host, Limits::default())
10591            .run()
10592            .unwrap();
10593        assert_eq!(execution.value, Value::UNDEFINED);
10594    }
10595
10596    #[test]
10597    fn uncaught_reference_error_reports_origin() {
10598        let module = verified(
10599            vec![Constant::String(EcmaString::from_utf8("missing"))],
10600            vec![function(
10601                0,
10602                1,
10603                vec![
10604                    Instruction::LoadGlobal {
10605                        dst: reg(0),
10606                        name: cid(0),
10607                    },
10608                    Instruction::Return { value: reg(0) },
10609                ],
10610                vec![],
10611            )],
10612        );
10613        let mut host = TestHost;
10614        let error = Machine::new(&module, &mut host, Limits::default())
10615            .run()
10616            .unwrap_err();
10617        assert_eq!(error.pc, pc(0));
10618        assert!(matches!(
10619            error.kind,
10620            RuntimeErrorKind::UncaughtThrow {
10621                origin: ThrowOrigin::ReferenceError { .. },
10622                ..
10623            }
10624        ));
10625    }
10626
10627    fn assert_uri_error(global: &str, argument: EcmaString) {
10628        let module = verified(
10629            vec![
10630                Constant::String(EcmaString::from_utf8(global)),
10631                Constant::String(argument),
10632                Constant::Undefined,
10633            ],
10634            vec![function(
10635                0,
10636                5,
10637                vec![
10638                    Instruction::LoadGlobal {
10639                        dst: reg(0),
10640                        name: cid(0),
10641                    },
10642                    Instruction::LoadConst {
10643                        dst: reg(1),
10644                        constant: cid(1),
10645                    },
10646                    Instruction::LoadConst {
10647                        dst: reg(2),
10648                        constant: cid(2),
10649                    },
10650                    Instruction::CreateArray { dst: reg(3) },
10651                    Instruction::ArrayPush {
10652                        array: reg(3),
10653                        value: reg(1),
10654                    },
10655                    Instruction::Call {
10656                        dst: reg(4),
10657                        callee: reg(0),
10658                        this_value: reg(2),
10659                        arguments: reg(3),
10660                    },
10661                    Instruction::Return { value: reg(4) },
10662                ],
10663                Vec::new(),
10664            )],
10665        );
10666        let mut host = TestHost;
10667        let error = Machine::new(&module, &mut host, Limits::default())
10668            .run()
10669            .unwrap_err();
10670        assert_eq!(error.pc, pc(5));
10671        assert!(matches!(
10672            error.kind,
10673            RuntimeErrorKind::UncaughtThrow {
10674                origin: ThrowOrigin::UriError {
10675                    operation: "URI malformed"
10676                },
10677                ..
10678            }
10679        ));
10680    }
10681
10682    #[test]
10683    fn uri_builtins_report_uri_error() {
10684        for (global, argument) in [
10685            ("encodeURIComponent", EcmaString::from_units(&[0xd800])),
10686            ("decodeURIComponent", EcmaString::from_utf8("%")),
10687            ("decodeURIComponent", EcmaString::from_utf8("%GG")),
10688            ("decodeURIComponent", EcmaString::from_utf8("%FF")),
10689            ("decodeURIComponent", EcmaString::from_utf8("%80")),
10690            ("decodeURIComponent", EcmaString::from_utf8("%C0%80")),
10691            ("decodeURIComponent", EcmaString::from_utf8("%E2%82")),
10692            ("decodeURIComponent", EcmaString::from_utf8("%ED%A0%80")),
10693            ("decodeURIComponent", EcmaString::from_utf8("%F4%90%80%80")),
10694            (
10695                "decodeURIComponent",
10696                EcmaString::from_utf8("%F8%80%80%80%80"),
10697            ),
10698        ] {
10699            assert_uri_error(global, argument);
10700        }
10701    }
10702
10703    fn assert_uri_decode(argument: EcmaString, expected: EcmaString) {
10704        let module = verified(
10705            vec![
10706                Constant::String(EcmaString::from_utf8("decodeURIComponent")),
10707                Constant::String(argument),
10708                Constant::Undefined,
10709                Constant::String(expected),
10710            ],
10711            vec![function(
10712                0,
10713                7,
10714                vec![
10715                    Instruction::LoadGlobal {
10716                        dst: reg(0),
10717                        name: cid(0),
10718                    },
10719                    Instruction::LoadConst {
10720                        dst: reg(1),
10721                        constant: cid(1),
10722                    },
10723                    Instruction::LoadConst {
10724                        dst: reg(2),
10725                        constant: cid(2),
10726                    },
10727                    Instruction::CreateArray { dst: reg(3) },
10728                    Instruction::ArrayPush {
10729                        array: reg(3),
10730                        value: reg(1),
10731                    },
10732                    Instruction::Call {
10733                        dst: reg(4),
10734                        callee: reg(0),
10735                        this_value: reg(2),
10736                        arguments: reg(3),
10737                    },
10738                    Instruction::LoadConst {
10739                        dst: reg(5),
10740                        constant: cid(3),
10741                    },
10742                    Instruction::Binary {
10743                        dst: reg(6),
10744                        op: BinaryOp::StrictEqual,
10745                        left: reg(4),
10746                        right: reg(5),
10747                    },
10748                    Instruction::Return { value: reg(6) },
10749                ],
10750                Vec::new(),
10751            )],
10752        );
10753        let mut host = TestHost;
10754        let execution = Machine::new(&module, &mut host, Limits::default())
10755            .run()
10756            .unwrap();
10757        assert_eq!(execution.value, Value::TRUE);
10758    }
10759
10760    #[test]
10761    fn decode_uri_component_preserves_units_and_decodes_utf8() {
10762        let exact = EcmaString::from_units(&[0xd800, 0x61, 0xdfff]);
10763        for (argument, expected) in [
10764            (exact.clone(), exact),
10765            (EcmaString::from_utf8("%2F"), EcmaString::from_utf8("/")),
10766            (
10767                EcmaString::from_utf8("%F0%9F%98%80"),
10768                EcmaString::from_utf8("😀"),
10769            ),
10770            (
10771                EcmaString::from_utf8("%E4%B8%ADA"),
10772                EcmaString::from_utf8("中A"),
10773            ),
10774            (EcmaString::from_utf8("%00"), EcmaString::from_units(&[0])),
10775        ] {
10776            assert_uri_decode(argument, expected);
10777        }
10778    }
10779
10780    #[test]
10781    fn regexp_is_object_with_source_and_flags() {
10782        // typeof re === "object" is not directly returnable; return re.source.
10783        let module = verified(
10784            vec![
10785                Constant::String(EcmaString::from_utf8("ab")),
10786                Constant::String(EcmaString::from_utf8("gi")),
10787                Constant::String(EcmaString::from_utf8("source")),
10788                Constant::String(EcmaString::from_utf8("global")),
10789            ],
10790            vec![function(
10791                0,
10792                4,
10793                vec![
10794                    Instruction::CreateRegExp {
10795                        dst: reg(0),
10796                        pattern: cid(0),
10797                        flags: cid(1),
10798                    },
10799                    Instruction::LoadConst {
10800                        dst: reg(1),
10801                        constant: cid(3),
10802                    },
10803                    Instruction::GetProperty {
10804                        dst: reg(2),
10805                        object: reg(0),
10806                        key: reg(1),
10807                    },
10808                    Instruction::Unary {
10809                        dst: reg(3),
10810                        op: UnaryOp::TypeOf,
10811                        operand: reg(0),
10812                    },
10813                    Instruction::Return { value: reg(2) },
10814                ],
10815                vec![],
10816            )],
10817        );
10818        let execution = run_ok(&module);
10819        // re.global -> true
10820        assert_eq!(execution.value, Value::TRUE);
10821    }
10822
10823    #[test]
10824    fn this_and_new_target_are_frame_owned() {
10825        // Call passes this; new.target is undefined in a plain call.
10826        let entry = function(
10827            0,
10828            4,
10829            vec![
10830                Instruction::CreateObject { dst: reg(0) },
10831                Instruction::CreateArray { dst: reg(3) },
10832                Instruction::CreateClosure {
10833                    dst: reg(1),
10834                    function: FunctionId::new(1),
10835                    captures: reg(3),
10836                },
10837                Instruction::CreateArray { dst: reg(2) },
10838                Instruction::Call {
10839                    dst: reg(0),
10840                    callee: reg(1),
10841                    this_value: reg(0),
10842                    arguments: reg(2),
10843                },
10844                Instruction::Return { value: reg(0) },
10845            ],
10846            vec![],
10847        );
10848        // returns (this === passed) is hard cross-frame; instead return typeof
10849        // new.target which is "undefined" for a plain call.
10850        let callee = function(
10851            0,
10852            2,
10853            vec![
10854                Instruction::LoadNewTarget { dst: reg(0) },
10855                Instruction::Unary {
10856                    dst: reg(1),
10857                    op: UnaryOp::TypeOf,
10858                    operand: reg(0),
10859                },
10860                Instruction::Return { value: reg(1) },
10861            ],
10862            vec![],
10863        );
10864        let module = verified(vec![], vec![entry, callee]);
10865        let execution = run_ok(&module);
10866        // typeof undefined is a heap "undefined" string; strict-compare against
10867        // typeof of a known-undefined value is awkward, so assert non-undefined
10868        // heap string was produced and the call completed.
10869        assert_ne!(execution.value, Value::UNDEFINED);
10870    }
10871
10872    #[test]
10873    fn new_target_is_constructor_during_construct() {
10874        // In a constructor, new.target === callee; verify via instanceof-style
10875        // check: store new.target on this, then read back after construct.
10876        let entry = function(
10877            0,
10878            4,
10879            vec![
10880                Instruction::CreateArray { dst: reg(3) },
10881                Instruction::CreateClosure {
10882                    dst: reg(0),
10883                    function: FunctionId::new(1),
10884                    captures: reg(3),
10885                },
10886                // ctor.prototype = {}
10887                Instruction::CreateObject { dst: reg(1) },
10888                Instruction::LoadConst {
10889                    dst: reg(2),
10890                    constant: cid(0),
10891                },
10892                Instruction::SetProperty {
10893                    object: reg(0),
10894                    key: reg(2),
10895                    value: reg(1),
10896                },
10897                Instruction::CreateArray { dst: reg(3) },
10898                Instruction::Construct {
10899                    dst: reg(1),
10900                    callee: reg(0),
10901                    arguments: reg(3),
10902                },
10903                // read back this.nt === ctor
10904                Instruction::LoadConst {
10905                    dst: reg(2),
10906                    constant: cid(1),
10907                },
10908                Instruction::GetProperty {
10909                    dst: reg(3),
10910                    object: reg(1),
10911                    key: reg(2),
10912                },
10913                Instruction::Binary {
10914                    dst: reg(3),
10915                    op: BinaryOp::StrictEqual,
10916                    left: reg(3),
10917                    right: reg(0),
10918                },
10919                Instruction::Return { value: reg(3) },
10920            ],
10921            vec![],
10922        );
10923        let ctor = function(
10924            0,
10925            3,
10926            vec![
10927                Instruction::LoadNewTarget { dst: reg(0) },
10928                Instruction::LoadThis { dst: reg(1) },
10929                Instruction::LoadConst {
10930                    dst: reg(2),
10931                    constant: cid(1),
10932                },
10933                Instruction::SetProperty {
10934                    object: reg(1),
10935                    key: reg(2),
10936                    value: reg(0),
10937                },
10938                Instruction::Halt,
10939            ],
10940            vec![],
10941        );
10942        let module = verified(
10943            vec![
10944                Constant::String(EcmaString::from_utf8("prototype")),
10945                Constant::String(EcmaString::from_utf8("nt")),
10946            ],
10947            vec![entry, ctor],
10948        );
10949        assert_eq!(run_ok(&module).value, Value::TRUE);
10950    }
10951
10952    #[test]
10953    fn arguments_object_reflects_passed_values() {
10954        // callee returns arguments[0].
10955        let entry = function(
10956            0,
10957            4,
10958            vec![
10959                Instruction::CreateArray { dst: reg(3) },
10960                Instruction::CreateClosure {
10961                    dst: reg(0),
10962                    function: FunctionId::new(1),
10963                    captures: reg(3),
10964                },
10965                // args = [42]
10966                Instruction::CreateArray { dst: reg(2) },
10967                Instruction::LoadConst {
10968                    dst: reg(1),
10969                    constant: cid(0),
10970                },
10971                Instruction::ArrayPush {
10972                    array: reg(2),
10973                    value: reg(1),
10974                },
10975                Instruction::Call {
10976                    dst: reg(0),
10977                    callee: reg(0),
10978                    this_value: reg(1),
10979                    arguments: reg(2),
10980                },
10981                Instruction::Return { value: reg(0) },
10982            ],
10983            vec![],
10984        );
10985        let callee = function(
10986            0,
10987            2,
10988            vec![
10989                Instruction::LoadArguments { dst: reg(0) },
10990                Instruction::LoadConst {
10991                    dst: reg(1),
10992                    constant: cid(1),
10993                },
10994                Instruction::GetProperty {
10995                    dst: reg(0),
10996                    object: reg(0),
10997                    key: reg(1),
10998                },
10999                Instruction::Return { value: reg(0) },
11000            ],
11001            vec![],
11002        );
11003        let module = verified(
11004            vec![
11005                Constant::Int32(42),
11006                Constant::String(EcmaString::from_utf8("0")),
11007            ],
11008            vec![entry, callee],
11009        );
11010        assert_eq!(run_ok(&module).value, Value::int32(42));
11011    }
11012
11013    #[test]
11014    fn catch_register_receives_exact_thrown_value() {
11015        let module = verified(
11016            vec![Constant::Int32(9)],
11017            vec![function(
11018                0,
11019                2,
11020                vec![
11021                    Instruction::LoadConst {
11022                        dst: reg(0),
11023                        constant: cid(0),
11024                    },
11025                    Instruction::Throw { value: reg(0) },
11026                    Instruction::Return { value: reg(1) },
11027                ],
11028                vec![ExceptionHandler {
11029                    start: pc(1),
11030                    end: pc(2),
11031                    handler: pc(2),
11032                    catch_register: reg(1),
11033                }],
11034            )],
11035        );
11036        assert_eq!(run_ok(&module).value, Value::int32(9));
11037    }
11038
11039    #[test]
11040    fn native_callback_throw_is_caught_at_outer_call_site() {
11041        let entry = function(
11042            0,
11043            9,
11044            vec![
11045                Instruction::CreateArray { dst: reg(0) },
11046                Instruction::LoadConst {
11047                    dst: reg(1),
11048                    constant: cid(0),
11049                },
11050                Instruction::ArrayPush {
11051                    array: reg(0),
11052                    value: reg(1),
11053                },
11054                Instruction::CreateArray { dst: reg(2) },
11055                Instruction::CreateClosure {
11056                    dst: reg(3),
11057                    function: FunctionId::new(1),
11058                    captures: reg(2),
11059                },
11060                Instruction::LoadConst {
11061                    dst: reg(4),
11062                    constant: cid(1),
11063                },
11064                Instruction::GetProperty {
11065                    dst: reg(5),
11066                    object: reg(0),
11067                    key: reg(4),
11068                },
11069                Instruction::CreateArray { dst: reg(6) },
11070                Instruction::ArrayPush {
11071                    array: reg(6),
11072                    value: reg(3),
11073                },
11074                Instruction::Call {
11075                    dst: reg(7),
11076                    callee: reg(5),
11077                    this_value: reg(0),
11078                    arguments: reg(6),
11079                },
11080                Instruction::Halt,
11081                Instruction::Return { value: reg(8) },
11082            ],
11083            vec![ExceptionHandler {
11084                start: pc(9),
11085                end: pc(10),
11086                handler: pc(11),
11087                catch_register: reg(8),
11088            }],
11089        );
11090        let callback = closure_function(
11091            0,
11092            0,
11093            1,
11094            vec![
11095                Instruction::LoadConst {
11096                    dst: reg(0),
11097                    constant: cid(0),
11098                },
11099                Instruction::Throw { value: reg(0) },
11100            ],
11101        );
11102        let module = verified(
11103            vec![
11104                Constant::Int32(7),
11105                Constant::String(EcmaString::from_utf8("map")),
11106            ],
11107            vec![entry, callback],
11108        );
11109
11110        assert_eq!(run_ok(&module).value, Value::int32(7));
11111    }
11112
11113    #[test]
11114    fn native_callback_throw_uncaught_at_outer_call_site() {
11115        let entry = function(
11116            0,
11117            9,
11118            vec![
11119                Instruction::CreateArray { dst: reg(0) },
11120                Instruction::LoadConst {
11121                    dst: reg(1),
11122                    constant: cid(0),
11123                },
11124                Instruction::ArrayPush {
11125                    array: reg(0),
11126                    value: reg(1),
11127                },
11128                Instruction::CreateArray { dst: reg(2) },
11129                Instruction::CreateClosure {
11130                    dst: reg(3),
11131                    function: FunctionId::new(1),
11132                    captures: reg(2),
11133                },
11134                Instruction::LoadConst {
11135                    dst: reg(4),
11136                    constant: cid(1),
11137                },
11138                Instruction::GetProperty {
11139                    dst: reg(5),
11140                    object: reg(0),
11141                    key: reg(4),
11142                },
11143                Instruction::CreateArray { dst: reg(6) },
11144                Instruction::ArrayPush {
11145                    array: reg(6),
11146                    value: reg(3),
11147                },
11148                Instruction::Call {
11149                    dst: reg(7),
11150                    callee: reg(5),
11151                    this_value: reg(0),
11152                    arguments: reg(6),
11153                },
11154                Instruction::Halt,
11155            ],
11156            Vec::new(),
11157        );
11158        let callback = closure_function(
11159            0,
11160            0,
11161            1,
11162            vec![
11163                Instruction::LoadConst {
11164                    dst: reg(0),
11165                    constant: cid(0),
11166                },
11167                Instruction::Throw { value: reg(0) },
11168            ],
11169        );
11170        let simple = closure_function(
11171            0,
11172            0,
11173            1,
11174            vec![
11175                Instruction::LoadConst {
11176                    dst: reg(0),
11177                    constant: cid(2),
11178                },
11179                Instruction::Return { value: reg(0) },
11180            ],
11181        );
11182        let module = verified(
11183            vec![
11184                Constant::Int32(7),
11185                Constant::String(EcmaString::from_utf8("map")),
11186                Constant::Int32(42),
11187            ],
11188            vec![entry, callback, simple],
11189        );
11190
11191        let mut host = TestHost;
11192        let mut machine = Machine::new(&module, &mut host, Limits::default());
11193        let error = machine.run_loop(0).unwrap_err();
11194        assert_eq!(
11195            error.kind,
11196            RuntimeErrorKind::UncaughtThrow {
11197                value: Value::int32(7),
11198                origin: ThrowOrigin::Bytecode,
11199            }
11200        );
11201        assert!(machine.callback_boundaries.is_empty());
11202        assert!(machine.frames.is_empty());
11203        assert_eq!(machine.live_registers, 0);
11204
11205        let callee = machine
11206            .allocate(HeapEntry::Function {
11207                module: ModuleId::new(0),
11208                function: FunctionId::new(2),
11209                captures: Vec::new(),
11210                properties: PropertyMap::default(),
11211                prototype: Some(machine.intrinsics.function_prototype),
11212                extensible: true,
11213            })
11214            .unwrap();
11215        assert_eq!(
11216            machine.call_value(callee, Value::UNDEFINED, &[]).unwrap(),
11217            Value::int32(42)
11218        );
11219    }
11220
11221    #[test]
11222    fn callee_throw_unwinds_to_call_site_handler() {
11223        let entry = function(
11224            0,
11225            4,
11226            vec![
11227                Instruction::CreateArray { dst: reg(3) },
11228                Instruction::CreateClosure {
11229                    dst: reg(0),
11230                    function: FunctionId::new(1),
11231                    captures: reg(3),
11232                },
11233                Instruction::CreateArray { dst: reg(1) },
11234                Instruction::Call {
11235                    dst: reg(2),
11236                    callee: reg(0),
11237                    this_value: reg(1),
11238                    arguments: reg(1),
11239                },
11240                Instruction::Halt,
11241                Instruction::Return { value: reg(3) },
11242            ],
11243            vec![ExceptionHandler {
11244                start: pc(3),
11245                end: pc(4),
11246                handler: pc(5),
11247                catch_register: reg(3),
11248            }],
11249        );
11250        let callee = function(
11251            0,
11252            1,
11253            vec![
11254                Instruction::LoadConst {
11255                    dst: reg(0),
11256                    constant: cid(0),
11257                },
11258                Instruction::Throw { value: reg(0) },
11259            ],
11260            vec![],
11261        );
11262        let module = verified(vec![Constant::Int32(7)], vec![entry, callee]);
11263        assert_eq!(run_ok(&module).value, Value::int32(7));
11264    }
11265
11266    #[test]
11267    fn heap_and_register_limits_fail_before_unbounded_growth() {
11268        let module = verified(
11269            vec![],
11270            vec![function(
11271                0,
11272                2,
11273                vec![
11274                    Instruction::CreateObject { dst: reg(0) },
11275                    Instruction::CreateObject { dst: reg(1) },
11276                    Instruction::Halt,
11277                ],
11278                vec![],
11279            )],
11280        );
11281        let mut host = TestHost;
11282        let error = Machine::new(
11283            &module,
11284            &mut host,
11285            Limits {
11286                max_heap_slots: 1,
11287                ..Limits::default()
11288            },
11289        )
11290        .run()
11291        .unwrap_err();
11292        assert_eq!(error.pc, pc(1));
11293        assert_eq!(
11294            error.kind,
11295            RuntimeErrorKind::HeapSlotLimitExceeded { limit: 1 }
11296        );
11297
11298        let mut host = TestHost;
11299        let error = Machine::new(
11300            &module,
11301            &mut host,
11302            Limits {
11303                max_total_registers: 1,
11304                ..Limits::default()
11305            },
11306        )
11307        .run()
11308        .unwrap_err();
11309        assert_eq!(
11310            error.kind,
11311            RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
11312        );
11313    }
11314
11315    #[test]
11316    fn argument_array_length_limit_is_enforced() {
11317        let entry = function(
11318            0,
11319            4,
11320            vec![
11321                Instruction::CreateArray { dst: reg(3) },
11322                Instruction::CreateClosure {
11323                    dst: reg(0),
11324                    function: FunctionId::new(1),
11325                    captures: reg(3),
11326                },
11327                Instruction::CreateArray { dst: reg(2) },
11328                Instruction::LoadConst {
11329                    dst: reg(1),
11330                    constant: cid(0),
11331                },
11332                Instruction::ArrayPush {
11333                    array: reg(2),
11334                    value: reg(1),
11335                },
11336                Instruction::Call {
11337                    dst: reg(0),
11338                    callee: reg(0),
11339                    this_value: reg(1),
11340                    arguments: reg(2),
11341                },
11342                Instruction::Halt,
11343            ],
11344            vec![],
11345        );
11346        let callee = function(1, 1, vec![Instruction::Return { value: reg(0) }], vec![]);
11347        let module = verified(vec![Constant::Int32(1)], vec![entry, callee]);
11348        let mut host = TestHost;
11349        let error = Machine::new(
11350            &module,
11351            &mut host,
11352            Limits {
11353                max_argument_count: 0,
11354                ..Limits::default()
11355            },
11356        )
11357        .run()
11358        .unwrap_err();
11359        assert_eq!(
11360            error.kind,
11361            RuntimeErrorKind::ArgumentLimitExceeded {
11362                limit: 0,
11363                requested: 1
11364            }
11365        );
11366    }
11367
11368    #[test]
11369    fn u32_registers_and_instruction_pcs_do_not_truncate_at_127() {
11370        let mut code = vec![Instruction::LoadConst {
11371            dst: reg(0),
11372            constant: cid(0),
11373        }];
11374        for register in 1..=199 {
11375            code.push(Instruction::Move {
11376                dst: reg(register),
11377                src: reg(register - 1),
11378            });
11379        }
11380        code.push(Instruction::Return { value: reg(199) });
11381        let module = verified(
11382            vec![Constant::Number(NumberBits::from_f64(3.5))],
11383            vec![function(0, 200, code, vec![])],
11384        );
11385        let execution = run_ok(&module);
11386        assert_eq!(execution.value, Value::number(3.5));
11387        assert_eq!(execution.entry_registers[199], Value::number(3.5));
11388    }
11389
11390    #[test]
11391    fn construct_returned_object_overrides_default_instance() {
11392        // A constructor returning its own object overrides the default instance.
11393        let entry = function(
11394            0,
11395            3,
11396            vec![
11397                Instruction::CreateArray { dst: reg(2) },
11398                Instruction::CreateClosure {
11399                    dst: reg(0),
11400                    function: FunctionId::new(1),
11401                    captures: reg(2),
11402                },
11403                Instruction::CreateArray { dst: reg(2) },
11404                Instruction::Construct {
11405                    dst: reg(1),
11406                    callee: reg(0),
11407                    arguments: reg(2),
11408                },
11409                // returned object has marker property set to 5
11410                Instruction::LoadConst {
11411                    dst: reg(0),
11412                    constant: cid(0),
11413                },
11414                Instruction::GetProperty {
11415                    dst: reg(2),
11416                    object: reg(1),
11417                    key: reg(0),
11418                },
11419                Instruction::Return { value: reg(2) },
11420            ],
11421            vec![],
11422        );
11423        let returns_object = function(
11424            0,
11425            3,
11426            vec![
11427                Instruction::CreateObject { dst: reg(0) },
11428                Instruction::LoadConst {
11429                    dst: reg(1),
11430                    constant: cid(0),
11431                },
11432                Instruction::LoadConst {
11433                    dst: reg(2),
11434                    constant: cid(1),
11435                },
11436                Instruction::SetProperty {
11437                    object: reg(0),
11438                    key: reg(1),
11439                    value: reg(2),
11440                },
11441                Instruction::Return { value: reg(0) },
11442            ],
11443            vec![],
11444        );
11445        let module = verified(
11446            vec![
11447                Constant::String(EcmaString::from_utf8("marker")),
11448                Constant::Int32(5),
11449            ],
11450            vec![entry, returns_object],
11451        );
11452        assert_eq!(run_ok(&module).value, Value::int32(5));
11453    }
11454
11455    #[test]
11456    fn ecmascript_number_formatting_is_shortest_round_trip() {
11457        let cases = [
11458            (0.1 + 0.2, "0.30000000000000004"),
11459            (1e21, "1e+21"),
11460            (-0.0, "0"),
11461            (1.0 / 3.0, "0.3333333333333333"),
11462            (1e-6, "0.000001"),
11463            (1e-7, "1e-7"),
11464        ];
11465        for (number, expected) in cases {
11466            assert_eq!(
11467                Machine::<TestHost>::ordinary_number_to_string(number),
11468                expected
11469            );
11470        }
11471    }
11472
11473    #[test]
11474    fn own_keys_put_indices_before_insertion_ordered_strings() {
11475        let module = verified(
11476            Vec::new(),
11477            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11478        );
11479        let mut host = TestHost;
11480        let mut machine = Machine::new(&module, &mut host, Limits::default());
11481        let object = machine
11482            .allocate(HeapEntry::Object {
11483                properties: PropertyMap::default(),
11484                prototype: Some(machine.intrinsics.object_prototype),
11485                boxed_primitive: None,
11486                extensible: true,
11487            })
11488            .unwrap();
11489        let index = machine.runtime_slot(object).unwrap().unwrap();
11490        for (key, value) in [("b", 1), ("2", 2), ("a", 3), ("1", 4)] {
11491            machine
11492                .set_own_data(
11493                    index,
11494                    PropertyKey::Named(EcmaString::from_utf8(key)),
11495                    Value::int32(value),
11496                )
11497                .unwrap();
11498        }
11499        assert_eq!(
11500            machine.enumerable_keys(object).unwrap(),
11501            ["1", "2", "b", "a"].map(EcmaString::from_utf8)
11502        );
11503    }
11504
11505    #[test]
11506    fn object_prototype_to_string_uses_realm_tags() {
11507        let module = verified(
11508            Vec::new(),
11509            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11510        );
11511        let mut host = TestHost;
11512        let mut machine = Machine::new(&module, &mut host, Limits::default());
11513        let array = machine
11514            .allocate(HeapEntry::Array {
11515                elements: Vec::new(),
11516                properties: PropertyMap::default(),
11517                prototype: Some(machine.intrinsics.array_prototype),
11518                extensible: true,
11519                length_writable: true,
11520            })
11521            .unwrap();
11522        let object = machine
11523            .allocate(HeapEntry::Object {
11524                properties: PropertyMap::default(),
11525                prototype: Some(machine.intrinsics.object_prototype),
11526                boxed_primitive: None,
11527                extensible: true,
11528            })
11529            .unwrap();
11530        let function = machine.intrinsics.global("Object").unwrap();
11531        let to_string = machine.intrinsics.object_to_string();
11532        for (value, expected) in [
11533            (Value::UNDEFINED, "[object Undefined]"),
11534            (Value::NULL, "[object Null]"),
11535            (Value::TRUE, "[object Boolean]"),
11536            (array, "[object Array]"),
11537            (object, "[object Object]"),
11538            (function, "[object Function]"),
11539        ] {
11540            let tag = machine.call_value(to_string, value, &[]).unwrap();
11541            assert!(
11542                machine
11543                    .string_text(tag)
11544                    .is_some_and(|text| text.eq_ascii(expected))
11545            );
11546        }
11547    }
11548
11549    #[derive(Default)]
11550    struct CapabilityHost {
11551        stdout: Vec<u8>,
11552        stderr: Vec<u8>,
11553        env: BTreeMap<String, String>,
11554    }
11555
11556    impl Host for CapabilityHost {
11557        fn write_stdout(&mut self, bytes: &[u8]) {
11558            self.stdout.extend_from_slice(bytes);
11559        }
11560
11561        fn write_stderr(&mut self, bytes: &[u8]) {
11562            self.stderr.extend_from_slice(bytes);
11563        }
11564
11565        fn env(&self, name: &str) -> Option<&str> {
11566            self.env.get(name).map(String::as_str)
11567        }
11568
11569        fn set_env(&mut self, name: &str, value: &str) {
11570            self.env.insert(name.to_owned(), value.to_owned());
11571        }
11572
11573        fn delete_env(&mut self, name: &str) -> bool {
11574            self.env.remove(name).is_some()
11575        }
11576    }
11577
11578    #[test]
11579    fn console_formats_node_value_shapes_byte_exactly() {
11580        let module = verified(
11581            Vec::new(),
11582            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11583        );
11584        let mut host = CapabilityHost::default();
11585        {
11586            let mut machine = Machine::new(&module, &mut host, Limits::default());
11587            let console = machine.intrinsics.global("console").unwrap();
11588            let log = machine.get_named_property(console, "log").unwrap();
11589            let string = machine
11590                .allocate(HeapEntry::String(EcmaString::from_utf8("hello")))
11591                .unwrap();
11592            let array_string = machine
11593                .allocate(HeapEntry::String(EcmaString::from_utf8("x")))
11594                .unwrap();
11595            let array = machine
11596                .allocate(HeapEntry::Array {
11597                    elements: vec![Value::int32(1), array_string],
11598                    properties: PropertyMap::default(),
11599                    prototype: Some(machine.intrinsics.array_prototype),
11600                    extensible: true,
11601                    length_writable: true,
11602                })
11603                .unwrap();
11604            let mut inner_properties = PropertyMap::default();
11605            inner_properties.insert(
11606                PropertyKey::Named(EcmaString::from_utf8("answer")),
11607                Property::Data {
11608                    value: Value::int32(42),
11609                    writable: true,
11610                    enumerable: true,
11611                    configurable: true,
11612                },
11613            );
11614            let inner = machine
11615                .allocate(HeapEntry::Object {
11616                    properties: inner_properties,
11617                    prototype: Some(machine.intrinsics.object_prototype),
11618                    boxed_primitive: None,
11619                    extensible: true,
11620                })
11621                .unwrap();
11622            let mut outer_properties = PropertyMap::default();
11623            outer_properties.insert(
11624                PropertyKey::Named(EcmaString::from_utf8("nested")),
11625                Property::Data {
11626                    value: inner,
11627                    writable: true,
11628                    enumerable: true,
11629                    configurable: true,
11630                },
11631            );
11632            let outer = machine
11633                .allocate(HeapEntry::Object {
11634                    properties: outer_properties,
11635                    prototype: Some(machine.intrinsics.object_prototype),
11636                    boxed_primitive: None,
11637                    extensible: true,
11638                })
11639                .unwrap();
11640            let symbol = machine
11641                .allocate(HeapEntry::Symbol {
11642                    description: EcmaString::from_utf8("token"),
11643                })
11644                .unwrap();
11645            for value in [
11646                string,
11647                Value::int32(42),
11648                array,
11649                outer,
11650                Value::UNDEFINED,
11651                Value::NULL,
11652                symbol,
11653            ] {
11654                machine.call_value(log, console, &[value]).unwrap();
11655            }
11656        }
11657        assert_eq!(
11658            host.stdout,
11659            b"hello\n42\n[ 1, 'x' ]\n{ nested: { answer: 42 } }\nundefined\nnull\nSymbol(token)\n"
11660        );
11661        assert!(host.stderr.is_empty());
11662    }
11663
11664    #[test]
11665    fn console_and_process_properties_are_reassignable_and_env_is_live() {
11666        let module = verified(
11667            Vec::new(),
11668            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11669        );
11670        let mut host = CapabilityHost::default();
11671        {
11672            let mut machine = Machine::new(&module, &mut host, Limits::default());
11673            let console = machine.intrinsics.global("console").unwrap();
11674            let warn = machine.get_named_property(console, "warn").unwrap();
11675            machine
11676                .set_data_property(console, "warn", Value::int32(91))
11677                .unwrap();
11678            assert_eq!(
11679                machine.get_named_property(console, "warn").unwrap(),
11680                Value::int32(91)
11681            );
11682            machine.set_data_property(console, "warn", warn).unwrap();
11683
11684            let process = machine.intrinsics.global("process").unwrap();
11685            let env = machine.get_named_property(process, "env").unwrap();
11686            machine
11687                .set_data_property(env, "BAMTS_MODE", Value::int32(7))
11688                .unwrap();
11689            let value = machine.get_named_property(env, "BAMTS_MODE").unwrap();
11690            assert!(
11691                machine
11692                    .string_text(value)
11693                    .is_some_and(|text| text.eq_ascii("7"))
11694            );
11695            assert!(
11696                machine
11697                    .delete_property(
11698                        env,
11699                        &PropertyKey::Named(EcmaString::from_utf8("BAMTS_MODE"))
11700                    )
11701                    .unwrap()
11702            );
11703            assert_eq!(
11704                machine.get_named_property(env, "BAMTS_MODE").unwrap(),
11705                Value::UNDEFINED
11706            );
11707        }
11708        assert_eq!(host.env("BAMTS_MODE"), None);
11709    }
11710
11711    #[test]
11712    fn independent_modules_keep_same_name_globals_isolated() {
11713        let dependency = |name: &str, value: i32| {
11714            program_module(
11715                name,
11716                vec![
11717                    Constant::String(EcmaString::from_utf8("x")),
11718                    Constant::Int32(value),
11719                ],
11720                vec![function(
11721                    0,
11722                    1,
11723                    vec![
11724                        Instruction::LoadConst {
11725                            dst: reg(0),
11726                            constant: cid(2),
11727                        },
11728                        Instruction::StoreGlobal {
11729                            name: cid(1),
11730                            value: reg(0),
11731                        },
11732                        Instruction::Return { value: reg(0) },
11733                    ],
11734                    Vec::new(),
11735                )],
11736                Vec::new(),
11737                vec![Binding {
11738                    name: cid(1),
11739                    kind: BindingKind::Hoisted,
11740                }],
11741                vec![Export {
11742                    name: cid(1),
11743                    source: ExportSource::Local(BindingId::new(0)),
11744                }],
11745            )
11746        };
11747        let root = program_module(
11748            "root",
11749            vec![
11750                Constant::String(EcmaString::from_utf8("left")),
11751                Constant::String(EcmaString::from_utf8("right")),
11752                Constant::String(EcmaString::from_utf8("x")),
11753            ],
11754            vec![function(
11755                0,
11756                5,
11757                vec![
11758                    Instruction::LoadGlobal {
11759                        dst: reg(0),
11760                        name: cid(1),
11761                    },
11762                    Instruction::LoadGlobal {
11763                        dst: reg(1),
11764                        name: cid(2),
11765                    },
11766                    Instruction::LoadConst {
11767                        dst: reg(2),
11768                        constant: cid(3),
11769                    },
11770                    Instruction::GetProperty {
11771                        dst: reg(3),
11772                        object: reg(0),
11773                        key: reg(2),
11774                    },
11775                    Instruction::GetProperty {
11776                        dst: reg(4),
11777                        object: reg(1),
11778                        key: reg(2),
11779                    },
11780                    Instruction::Binary {
11781                        dst: reg(0),
11782                        op: BinaryOp::Add,
11783                        left: reg(3),
11784                        right: reg(4),
11785                    },
11786                    Instruction::Return { value: reg(0) },
11787                ],
11788                Vec::new(),
11789            )],
11790            vec![
11791                Edge {
11792                    specifier: cid(1),
11793                    target: EdgeTarget::Local(ModuleId::new(0)),
11794                    kind: EdgeKind::Static,
11795                },
11796                Edge {
11797                    specifier: cid(2),
11798                    target: EdgeTarget::Local(ModuleId::new(1)),
11799                    kind: EdgeKind::Static,
11800                },
11801            ],
11802            vec![
11803                Binding {
11804                    name: cid(1),
11805                    kind: BindingKind::Namespace {
11806                        edge: EdgeId::new(0),
11807                    },
11808                },
11809                Binding {
11810                    name: cid(2),
11811                    kind: BindingKind::Namespace {
11812                        edge: EdgeId::new(1),
11813                    },
11814                },
11815            ],
11816            Vec::new(),
11817        );
11818        let program = linked(vec![dependency("left", 1), dependency("right", 2), root], 2);
11819        assert_eq!(run_ok(&program).value, Value::int32(3));
11820    }
11821
11822    #[test]
11823    fn imported_binding_observes_post_link_mutation_live() {
11824        let dependency = program_module(
11825            "dependency",
11826            vec![
11827                Constant::String(EcmaString::from_utf8("x")),
11828                Constant::Int32(1),
11829                Constant::Int32(2),
11830                Constant::String(EcmaString::from_utf8("set")),
11831            ],
11832            vec![
11833                function(
11834                    0,
11835                    3,
11836                    vec![
11837                        Instruction::LoadConst {
11838                            dst: reg(0),
11839                            constant: cid(2),
11840                        },
11841                        Instruction::StoreGlobal {
11842                            name: cid(1),
11843                            value: reg(0),
11844                        },
11845                        Instruction::CreateArray { dst: reg(1) },
11846                        Instruction::CreateClosure {
11847                            dst: reg(2),
11848                            function: FunctionId::new(1),
11849                            captures: reg(1),
11850                        },
11851                        Instruction::StoreGlobal {
11852                            name: cid(4),
11853                            value: reg(2),
11854                        },
11855                        Instruction::Return { value: reg(0) },
11856                    ],
11857                    Vec::new(),
11858                ),
11859                function(
11860                    0,
11861                    1,
11862                    vec![
11863                        Instruction::LoadConst {
11864                            dst: reg(0),
11865                            constant: cid(3),
11866                        },
11867                        Instruction::StoreGlobal {
11868                            name: cid(1),
11869                            value: reg(0),
11870                        },
11871                        Instruction::Return { value: reg(0) },
11872                    ],
11873                    Vec::new(),
11874                ),
11875            ],
11876            Vec::new(),
11877            vec![
11878                Binding {
11879                    name: cid(1),
11880                    kind: BindingKind::Hoisted,
11881                },
11882                Binding {
11883                    name: cid(4),
11884                    kind: BindingKind::Hoisted,
11885                },
11886            ],
11887            vec![
11888                Export {
11889                    name: cid(1),
11890                    source: ExportSource::Local(BindingId::new(0)),
11891                },
11892                Export {
11893                    name: cid(4),
11894                    source: ExportSource::Local(BindingId::new(1)),
11895                },
11896            ],
11897        );
11898        let root = program_module(
11899            "root",
11900            vec![
11901                Constant::String(EcmaString::from_utf8("x")),
11902                Constant::String(EcmaString::from_utf8("set")),
11903                Constant::String(EcmaString::from_utf8("dep")),
11904            ],
11905            vec![function(
11906                0,
11907                3,
11908                vec![
11909                    Instruction::LoadGlobal {
11910                        dst: reg(0),
11911                        name: cid(2),
11912                    },
11913                    Instruction::CreateArray { dst: reg(1) },
11914                    Instruction::Call {
11915                        dst: reg(2),
11916                        callee: reg(0),
11917                        this_value: reg(1),
11918                        arguments: reg(1),
11919                    },
11920                    Instruction::LoadGlobal {
11921                        dst: reg(0),
11922                        name: cid(1),
11923                    },
11924                    Instruction::Return { value: reg(0) },
11925                ],
11926                Vec::new(),
11927            )],
11928            vec![Edge {
11929                specifier: cid(3),
11930                target: EdgeTarget::Local(ModuleId::new(0)),
11931                kind: EdgeKind::Static,
11932            }],
11933            vec![
11934                Binding {
11935                    name: cid(1),
11936                    kind: BindingKind::Imported {
11937                        edge: EdgeId::new(0),
11938                        name: cid(1),
11939                    },
11940                },
11941                Binding {
11942                    name: cid(2),
11943                    kind: BindingKind::Imported {
11944                        edge: EdgeId::new(0),
11945                        name: cid(2),
11946                    },
11947                },
11948            ],
11949            Vec::new(),
11950        );
11951        assert_eq!(
11952            run_ok(&linked(vec![dependency, root], 1)).value,
11953            Value::int32(2)
11954        );
11955    }
11956
11957    #[test]
11958    fn closure_globals_resolve_in_the_defining_module() {
11959        let dependency = program_module(
11960            "dependency",
11961            vec![
11962                Constant::String(EcmaString::from_utf8("x")),
11963                Constant::Int32(10),
11964                Constant::String(EcmaString::from_utf8("read")),
11965            ],
11966            vec![
11967                function(
11968                    0,
11969                    3,
11970                    vec![
11971                        Instruction::LoadConst {
11972                            dst: reg(0),
11973                            constant: cid(2),
11974                        },
11975                        Instruction::StoreGlobal {
11976                            name: cid(1),
11977                            value: reg(0),
11978                        },
11979                        Instruction::CreateArray { dst: reg(1) },
11980                        Instruction::CreateClosure {
11981                            dst: reg(2),
11982                            function: FunctionId::new(1),
11983                            captures: reg(1),
11984                        },
11985                        Instruction::StoreGlobal {
11986                            name: cid(3),
11987                            value: reg(2),
11988                        },
11989                        Instruction::Return { value: reg(0) },
11990                    ],
11991                    Vec::new(),
11992                ),
11993                function(
11994                    0,
11995                    1,
11996                    vec![
11997                        Instruction::LoadGlobal {
11998                            dst: reg(0),
11999                            name: cid(1),
12000                        },
12001                        Instruction::Return { value: reg(0) },
12002                    ],
12003                    Vec::new(),
12004                ),
12005            ],
12006            Vec::new(),
12007            vec![
12008                Binding {
12009                    name: cid(1),
12010                    kind: BindingKind::Hoisted,
12011                },
12012                Binding {
12013                    name: cid(3),
12014                    kind: BindingKind::Hoisted,
12015                },
12016            ],
12017            vec![Export {
12018                name: cid(3),
12019                source: ExportSource::Local(BindingId::new(1)),
12020            }],
12021        );
12022        let root = program_module(
12023            "root",
12024            vec![
12025                Constant::String(EcmaString::from_utf8("x")),
12026                Constant::Int32(20),
12027                Constant::String(EcmaString::from_utf8("read")),
12028                Constant::String(EcmaString::from_utf8("dep")),
12029            ],
12030            vec![function(
12031                0,
12032                4,
12033                vec![
12034                    Instruction::LoadConst {
12035                        dst: reg(0),
12036                        constant: cid(2),
12037                    },
12038                    Instruction::StoreGlobal {
12039                        name: cid(1),
12040                        value: reg(0),
12041                    },
12042                    Instruction::LoadGlobal {
12043                        dst: reg(1),
12044                        name: cid(3),
12045                    },
12046                    Instruction::CreateArray { dst: reg(2) },
12047                    Instruction::Call {
12048                        dst: reg(3),
12049                        callee: reg(1),
12050                        this_value: reg(2),
12051                        arguments: reg(2),
12052                    },
12053                    Instruction::Return { value: reg(3) },
12054                ],
12055                Vec::new(),
12056            )],
12057            vec![Edge {
12058                specifier: cid(4),
12059                target: EdgeTarget::Local(ModuleId::new(0)),
12060                kind: EdgeKind::Static,
12061            }],
12062            vec![
12063                Binding {
12064                    name: cid(1),
12065                    kind: BindingKind::Hoisted,
12066                },
12067                Binding {
12068                    name: cid(3),
12069                    kind: BindingKind::Imported {
12070                        edge: EdgeId::new(0),
12071                        name: cid(3),
12072                    },
12073                },
12074            ],
12075            Vec::new(),
12076        );
12077        assert_eq!(
12078            run_ok(&linked(vec![dependency, root], 1)).value,
12079            Value::int32(10)
12080        );
12081    }
12082
12083    #[test]
12084    fn cycle_traps_a_lexical_read_before_initialization() {
12085        let first = program_module(
12086            "first",
12087            vec![
12088                Constant::String(EcmaString::from_utf8("a")),
12089                Constant::Int32(1),
12090                Constant::String(EcmaString::from_utf8("second")),
12091            ],
12092            vec![function(
12093                0,
12094                1,
12095                vec![
12096                    Instruction::LoadConst {
12097                        dst: reg(0),
12098                        constant: cid(2),
12099                    },
12100                    Instruction::StoreGlobal {
12101                        name: cid(1),
12102                        value: reg(0),
12103                    },
12104                    Instruction::Return { value: reg(0) },
12105                ],
12106                Vec::new(),
12107            )],
12108            vec![Edge {
12109                specifier: cid(3),
12110                target: EdgeTarget::Local(ModuleId::new(1)),
12111                kind: EdgeKind::Static,
12112            }],
12113            vec![Binding {
12114                name: cid(1),
12115                kind: BindingKind::Lexical,
12116            }],
12117            vec![Export {
12118                name: cid(1),
12119                source: ExportSource::Local(BindingId::new(0)),
12120            }],
12121        );
12122        let second = program_module(
12123            "second",
12124            vec![
12125                Constant::String(EcmaString::from_utf8("a")),
12126                Constant::String(EcmaString::from_utf8("first")),
12127            ],
12128            vec![function(
12129                0,
12130                1,
12131                vec![
12132                    Instruction::LoadGlobal {
12133                        dst: reg(0),
12134                        name: cid(1),
12135                    },
12136                    Instruction::Return { value: reg(0) },
12137                ],
12138                Vec::new(),
12139            )],
12140            vec![Edge {
12141                specifier: cid(2),
12142                target: EdgeTarget::Local(ModuleId::new(0)),
12143                kind: EdgeKind::Static,
12144            }],
12145            vec![Binding {
12146                name: cid(1),
12147                kind: BindingKind::Imported {
12148                    edge: EdgeId::new(0),
12149                    name: cid(1),
12150                },
12151            }],
12152            Vec::new(),
12153        );
12154        let program = linked(vec![first, second], 0);
12155        let mut host = TestHost;
12156        let error = Machine::new(&program, &mut host, Limits::default())
12157            .run()
12158            .unwrap_err();
12159        assert!(matches!(
12160            error.kind,
12161            RuntimeErrorKind::TemporalDeadZone { module, binding }
12162                if module == ModuleId::new(1) && binding == BindingId::new(0)
12163        ));
12164    }
12165
12166    #[test]
12167    fn cycle_reentry_with_a_hoisted_binding_completes() {
12168        let first = program_module(
12169            "first",
12170            vec![
12171                Constant::String(EcmaString::from_utf8("a")),
12172                Constant::Int32(1),
12173                Constant::String(EcmaString::from_utf8("second")),
12174            ],
12175            vec![function(
12176                0,
12177                1,
12178                vec![
12179                    Instruction::LoadConst {
12180                        dst: reg(0),
12181                        constant: cid(2),
12182                    },
12183                    Instruction::StoreGlobal {
12184                        name: cid(1),
12185                        value: reg(0),
12186                    },
12187                    Instruction::Return { value: reg(0) },
12188                ],
12189                Vec::new(),
12190            )],
12191            vec![Edge {
12192                specifier: cid(3),
12193                target: EdgeTarget::Local(ModuleId::new(1)),
12194                kind: EdgeKind::Static,
12195            }],
12196            vec![Binding {
12197                name: cid(1),
12198                kind: BindingKind::Hoisted,
12199            }],
12200            vec![Export {
12201                name: cid(1),
12202                source: ExportSource::Local(BindingId::new(0)),
12203            }],
12204        );
12205        let second = program_module(
12206            "second",
12207            vec![
12208                Constant::String(EcmaString::from_utf8("a")),
12209                Constant::String(EcmaString::from_utf8("first")),
12210            ],
12211            vec![function(
12212                0,
12213                1,
12214                vec![
12215                    Instruction::LoadGlobal {
12216                        dst: reg(0),
12217                        name: cid(1),
12218                    },
12219                    Instruction::Return { value: reg(0) },
12220                ],
12221                Vec::new(),
12222            )],
12223            vec![Edge {
12224                specifier: cid(2),
12225                target: EdgeTarget::Local(ModuleId::new(0)),
12226                kind: EdgeKind::Static,
12227            }],
12228            vec![Binding {
12229                name: cid(1),
12230                kind: BindingKind::Imported {
12231                    edge: EdgeId::new(0),
12232                    name: cid(1),
12233                },
12234            }],
12235            Vec::new(),
12236        );
12237        assert_eq!(
12238            run_ok(&linked(vec![first, second], 0)).value,
12239            Value::int32(1)
12240        );
12241    }
12242
12243    #[test]
12244    fn namespace_identity_reads_live_cells_and_enumerates_sorted_keys() {
12245        let dependency = program_module(
12246            "dependency",
12247            vec![
12248                Constant::String(EcmaString::from_utf8("z")),
12249                Constant::String(EcmaString::from_utf8("a")),
12250                Constant::String(EcmaString::from_utf8("mutate")),
12251                Constant::Int32(1),
12252                Constant::Int32(2),
12253                Constant::Int32(3),
12254            ],
12255            vec![
12256                function(
12257                    0,
12258                    4,
12259                    vec![
12260                        Instruction::LoadConst {
12261                            dst: reg(0),
12262                            constant: cid(4),
12263                        },
12264                        Instruction::StoreGlobal {
12265                            name: cid(1),
12266                            value: reg(0),
12267                        },
12268                        Instruction::LoadConst {
12269                            dst: reg(0),
12270                            constant: cid(5),
12271                        },
12272                        Instruction::StoreGlobal {
12273                            name: cid(2),
12274                            value: reg(0),
12275                        },
12276                        Instruction::CreateArray { dst: reg(1) },
12277                        Instruction::CreateClosure {
12278                            dst: reg(2),
12279                            function: FunctionId::new(1),
12280                            captures: reg(1),
12281                        },
12282                        Instruction::StoreGlobal {
12283                            name: cid(3),
12284                            value: reg(2),
12285                        },
12286                        Instruction::Return { value: reg(0) },
12287                    ],
12288                    Vec::new(),
12289                ),
12290                function(
12291                    0,
12292                    1,
12293                    vec![
12294                        Instruction::LoadConst {
12295                            dst: reg(0),
12296                            constant: cid(6),
12297                        },
12298                        Instruction::StoreGlobal {
12299                            name: cid(1),
12300                            value: reg(0),
12301                        },
12302                        Instruction::Return { value: reg(0) },
12303                    ],
12304                    Vec::new(),
12305                ),
12306            ],
12307            Vec::new(),
12308            vec![
12309                Binding {
12310                    name: cid(1),
12311                    kind: BindingKind::Hoisted,
12312                },
12313                Binding {
12314                    name: cid(2),
12315                    kind: BindingKind::Hoisted,
12316                },
12317                Binding {
12318                    name: cid(3),
12319                    kind: BindingKind::Hoisted,
12320                },
12321            ],
12322            vec![
12323                Export {
12324                    name: cid(1),
12325                    source: ExportSource::Local(BindingId::new(0)),
12326                },
12327                Export {
12328                    name: cid(2),
12329                    source: ExportSource::Local(BindingId::new(1)),
12330                },
12331                Export {
12332                    name: cid(3),
12333                    source: ExportSource::Local(BindingId::new(2)),
12334                },
12335            ],
12336        );
12337        let root = program_module(
12338            "root",
12339            vec![
12340                Constant::String(EcmaString::from_utf8("ns1")),
12341                Constant::String(EcmaString::from_utf8("ns2")),
12342                Constant::String(EcmaString::from_utf8("mutate")),
12343                Constant::String(EcmaString::from_utf8("z")),
12344                Constant::String(EcmaString::from_utf8("a")),
12345                Constant::String(EcmaString::from_utf8("dep")),
12346                Constant::String(EcmaString::from_utf8("Object")),
12347                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
12348                Constant::String(EcmaString::from_utf8("value")),
12349                Constant::String(EcmaString::from_utf8("writable")),
12350                Constant::String(EcmaString::from_utf8("enumerable")),
12351                Constant::String(EcmaString::from_utf8("configurable")),
12352                Constant::String(EcmaString::from_utf8("missing")),
12353            ],
12354            vec![function(
12355                0,
12356                31,
12357                vec![
12358                    Instruction::LoadGlobal {
12359                        dst: reg(0),
12360                        name: cid(1),
12361                    },
12362                    Instruction::LoadGlobal {
12363                        dst: reg(1),
12364                        name: cid(2),
12365                    },
12366                    Instruction::Binary {
12367                        dst: reg(2),
12368                        op: BinaryOp::StrictEqual,
12369                        left: reg(0),
12370                        right: reg(1),
12371                    },
12372                    Instruction::LoadGlobal {
12373                        dst: reg(3),
12374                        name: cid(3),
12375                    },
12376                    Instruction::CreateArray { dst: reg(4) },
12377                    Instruction::Call {
12378                        dst: reg(5),
12379                        callee: reg(3),
12380                        this_value: reg(4),
12381                        arguments: reg(4),
12382                    },
12383                    Instruction::LoadConst {
12384                        dst: reg(6),
12385                        constant: cid(4),
12386                    },
12387                    Instruction::GetProperty {
12388                        dst: reg(7),
12389                        object: reg(0),
12390                        key: reg(6),
12391                    },
12392                    Instruction::GetIterator {
12393                        dst: reg(8),
12394                        src: reg(0),
12395                        kind: IteratorKind::Keys,
12396                    },
12397                    Instruction::IteratorNext {
12398                        done: reg(9),
12399                        value: reg(10),
12400                        iterator: reg(8),
12401                    },
12402                    Instruction::LoadConst {
12403                        dst: reg(11),
12404                        constant: cid(5),
12405                    },
12406                    Instruction::Binary {
12407                        dst: reg(12),
12408                        op: BinaryOp::StrictEqual,
12409                        left: reg(10),
12410                        right: reg(11),
12411                    },
12412                    Instruction::IteratorNext {
12413                        done: reg(9),
12414                        value: reg(10),
12415                        iterator: reg(8),
12416                    },
12417                    Instruction::LoadConst {
12418                        dst: reg(13),
12419                        constant: cid(3),
12420                    },
12421                    Instruction::Binary {
12422                        dst: reg(5),
12423                        op: BinaryOp::StrictEqual,
12424                        left: reg(10),
12425                        right: reg(13),
12426                    },
12427                    Instruction::IteratorNext {
12428                        done: reg(9),
12429                        value: reg(10),
12430                        iterator: reg(8),
12431                    },
12432                    Instruction::Binary {
12433                        dst: reg(14),
12434                        op: BinaryOp::StrictEqual,
12435                        left: reg(10),
12436                        right: reg(6),
12437                    },
12438                    Instruction::LoadGlobal {
12439                        dst: reg(15),
12440                        name: cid(7),
12441                    },
12442                    Instruction::LoadConst {
12443                        dst: reg(16),
12444                        constant: cid(8),
12445                    },
12446                    Instruction::GetProperty {
12447                        dst: reg(17),
12448                        object: reg(15),
12449                        key: reg(16),
12450                    },
12451                    Instruction::CreateArray { dst: reg(18) },
12452                    Instruction::ArrayPush {
12453                        array: reg(18),
12454                        value: reg(0),
12455                    },
12456                    Instruction::ArrayPush {
12457                        array: reg(18),
12458                        value: reg(6),
12459                    },
12460                    Instruction::Call {
12461                        dst: reg(19),
12462                        callee: reg(17),
12463                        this_value: reg(18),
12464                        arguments: reg(18),
12465                    },
12466                    Instruction::LoadConst {
12467                        dst: reg(20),
12468                        constant: cid(9),
12469                    },
12470                    Instruction::GetProperty {
12471                        dst: reg(21),
12472                        object: reg(19),
12473                        key: reg(20),
12474                    },
12475                    Instruction::LoadConst {
12476                        dst: reg(22),
12477                        constant: cid(10),
12478                    },
12479                    Instruction::GetProperty {
12480                        dst: reg(23),
12481                        object: reg(19),
12482                        key: reg(22),
12483                    },
12484                    Instruction::LoadConst {
12485                        dst: reg(24),
12486                        constant: cid(11),
12487                    },
12488                    Instruction::GetProperty {
12489                        dst: reg(25),
12490                        object: reg(19),
12491                        key: reg(24),
12492                    },
12493                    Instruction::LoadConst {
12494                        dst: reg(26),
12495                        constant: cid(12),
12496                    },
12497                    Instruction::GetProperty {
12498                        dst: reg(27),
12499                        object: reg(19),
12500                        key: reg(26),
12501                    },
12502                    Instruction::CreateArray { dst: reg(28) },
12503                    Instruction::LoadConst {
12504                        dst: reg(29),
12505                        constant: cid(13),
12506                    },
12507                    Instruction::ArrayPush {
12508                        array: reg(28),
12509                        value: reg(0),
12510                    },
12511                    Instruction::ArrayPush {
12512                        array: reg(28),
12513                        value: reg(29),
12514                    },
12515                    Instruction::Call {
12516                        dst: reg(30),
12517                        callee: reg(17),
12518                        this_value: reg(28),
12519                        arguments: reg(28),
12520                    },
12521                    Instruction::Return { value: reg(21) },
12522                ],
12523                Vec::new(),
12524            )],
12525            vec![Edge {
12526                specifier: cid(6),
12527                target: EdgeTarget::Local(ModuleId::new(0)),
12528                kind: EdgeKind::Static,
12529            }],
12530            vec![
12531                Binding {
12532                    name: cid(1),
12533                    kind: BindingKind::Namespace {
12534                        edge: EdgeId::new(0),
12535                    },
12536                },
12537                Binding {
12538                    name: cid(2),
12539                    kind: BindingKind::Namespace {
12540                        edge: EdgeId::new(0),
12541                    },
12542                },
12543                Binding {
12544                    name: cid(3),
12545                    kind: BindingKind::Imported {
12546                        edge: EdgeId::new(0),
12547                        name: cid(3),
12548                    },
12549                },
12550            ],
12551            Vec::new(),
12552        );
12553        let execution = run_ok(&linked(vec![dependency, root], 1));
12554        assert_eq!(execution.value, Value::int32(3));
12555        assert_eq!(execution.entry_registers[2], Value::TRUE);
12556        assert_eq!(execution.entry_registers[5], Value::TRUE);
12557        assert_eq!(execution.entry_registers[12], Value::TRUE);
12558        assert_eq!(execution.entry_registers[14], Value::TRUE);
12559        assert_eq!(execution.entry_registers[23], Value::TRUE);
12560        assert_eq!(execution.entry_registers[25], Value::TRUE);
12561        assert_eq!(execution.entry_registers[27], Value::FALSE);
12562        assert_eq!(execution.entry_registers[30], Value::UNDEFINED);
12563    }
12564
12565    #[test]
12566    fn side_effect_module_runs_once_with_single_or_duplicate_static_edges() {
12567        for duplicate in [false, true] {
12568            let dependency = program_module(
12569                "dependency",
12570                vec![
12571                    Constant::String(EcmaString::from_utf8("count")),
12572                    Constant::Int32(0),
12573                    Constant::Int32(1),
12574                ],
12575                vec![function(
12576                    0,
12577                    2,
12578                    vec![
12579                        Instruction::LoadGlobal {
12580                            dst: reg(0),
12581                            name: cid(1),
12582                        },
12583                        Instruction::JumpIfFalse {
12584                            condition: reg(0),
12585                            target: pc(3),
12586                        },
12587                        Instruction::Jump { target: pc(5) },
12588                        Instruction::LoadConst {
12589                            dst: reg(0),
12590                            constant: cid(2),
12591                        },
12592                        Instruction::StoreGlobal {
12593                            name: cid(1),
12594                            value: reg(0),
12595                        },
12596                        Instruction::LoadConst {
12597                            dst: reg(1),
12598                            constant: cid(3),
12599                        },
12600                        Instruction::Binary {
12601                            dst: reg(0),
12602                            op: BinaryOp::Add,
12603                            left: reg(0),
12604                            right: reg(1),
12605                        },
12606                        Instruction::StoreGlobal {
12607                            name: cid(1),
12608                            value: reg(0),
12609                        },
12610                        Instruction::Return { value: reg(0) },
12611                    ],
12612                    Vec::new(),
12613                )],
12614                Vec::new(),
12615                vec![Binding {
12616                    name: cid(1),
12617                    kind: BindingKind::Hoisted,
12618                }],
12619                vec![Export {
12620                    name: cid(1),
12621                    source: ExportSource::Local(BindingId::new(0)),
12622                }],
12623            );
12624            let mut edges = vec![Edge {
12625                specifier: cid(2),
12626                target: EdgeTarget::Local(ModuleId::new(0)),
12627                kind: EdgeKind::Static,
12628            }];
12629            if duplicate {
12630                edges.push(Edge {
12631                    specifier: cid(3),
12632                    target: EdgeTarget::Local(ModuleId::new(0)),
12633                    kind: EdgeKind::Static,
12634                });
12635            }
12636            let root = program_module(
12637                "root",
12638                vec![
12639                    Constant::String(EcmaString::from_utf8("count")),
12640                    Constant::String(EcmaString::from_utf8("dep-one")),
12641                    Constant::String(EcmaString::from_utf8("dep-two")),
12642                ],
12643                vec![function(
12644                    0,
12645                    1,
12646                    vec![
12647                        Instruction::LoadGlobal {
12648                            dst: reg(0),
12649                            name: cid(1),
12650                        },
12651                        Instruction::Return { value: reg(0) },
12652                    ],
12653                    Vec::new(),
12654                )],
12655                edges,
12656                vec![Binding {
12657                    name: cid(1),
12658                    kind: BindingKind::Imported {
12659                        edge: EdgeId::new(0),
12660                        name: cid(1),
12661                    },
12662                }],
12663                Vec::new(),
12664            );
12665            assert_eq!(
12666                run_ok(&linked(vec![dependency, root], 1)).value,
12667                Value::int32(1)
12668            );
12669        }
12670    }
12671
12672    #[test]
12673    fn failed_module_rethrows_the_identical_stored_value() {
12674        let module = program_module(
12675            "throws",
12676            Vec::new(),
12677            vec![function(
12678                0,
12679                1,
12680                vec![
12681                    Instruction::CreateObject { dst: reg(0) },
12682                    Instruction::Throw { value: reg(0) },
12683                ],
12684                Vec::new(),
12685            )],
12686            Vec::new(),
12687            Vec::new(),
12688            Vec::new(),
12689        );
12690        let program = linked(vec![module], 0);
12691        let mut host = TestHost;
12692        let mut machine = Machine::new(&program, &mut host, Limits::default());
12693        machine.frames.clear();
12694        machine.live_registers = 0;
12695        machine.instantiate_modules().unwrap();
12696        let first = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12697        let second = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12698        let RuntimeErrorKind::UncaughtThrow { value: first, .. } = first.kind else {
12699            panic!("module must fail by throwing");
12700        };
12701        let RuntimeErrorKind::UncaughtThrow { value: second, .. } = second.kind else {
12702            panic!("stored failure must remain a throw");
12703        };
12704        assert_eq!(first, second);
12705        assert!(first.as_heap_ref().is_some());
12706    }
12707
12708    #[test]
12709    fn external_static_edge_is_a_typed_runtime_error() {
12710        let module = program_module(
12711            "root",
12712            vec![Constant::String(EcmaString::from_utf8("external"))],
12713            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12714            vec![Edge {
12715                specifier: cid(1),
12716                target: EdgeTarget::External,
12717                kind: EdgeKind::Static,
12718            }],
12719            Vec::new(),
12720            Vec::new(),
12721        );
12722        let program = linked(vec![module], 0);
12723        let mut host = TestHost;
12724        let error = Machine::new(&program, &mut host, Limits::default())
12725            .run()
12726            .unwrap_err();
12727        assert!(matches!(
12728            error.kind,
12729            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
12730                if module == ModuleId::new(0) && edge == EdgeId::new(0)
12731        ));
12732    }
12733
12734    #[test]
12735    fn external_module_and_export_names_preserve_unicode() {
12736        for (specifier, export) in [("módulo", "value"), ("external", "café")] {
12737            let module = program_module(
12738                "root",
12739                vec![
12740                    Constant::String(EcmaString::from_utf8(export)),
12741                    Constant::String(EcmaString::from_utf8(specifier)),
12742                ],
12743                vec![function(
12744                    0,
12745                    1,
12746                    vec![
12747                        Instruction::LoadGlobal {
12748                            dst: reg(0),
12749                            name: cid(1),
12750                        },
12751                        Instruction::Return { value: reg(0) },
12752                    ],
12753                    Vec::new(),
12754                )],
12755                vec![Edge {
12756                    specifier: cid(2),
12757                    target: EdgeTarget::External,
12758                    kind: EdgeKind::Static,
12759                }],
12760                vec![Binding {
12761                    name: cid(1),
12762                    kind: BindingKind::Imported {
12763                        edge: EdgeId::new(0),
12764                        name: cid(1),
12765                    },
12766                }],
12767                Vec::new(),
12768            );
12769            let program = linked(vec![module], 0);
12770            let mut host = TestHost;
12771            let mut machine = Machine::new(&program, &mut host, Limits::default());
12772            machine.registry.external.insert(
12773                EcmaString::from_utf8(specifier),
12774                ExternalModuleInstance {
12775                    namespace: Value::UNDEFINED,
12776                    exports: BTreeMap::from([(
12777                        EcmaString::from_utf8(export),
12778                        ExternalExport {
12779                            value: Value::int32(7),
12780                            cell: None,
12781                        },
12782                    )]),
12783                    internals: BTreeMap::new(),
12784                },
12785            );
12786
12787            assert_eq!(machine.run().unwrap().value, Value::int32(7));
12788        }
12789    }
12790
12791    #[test]
12792    fn dynamic_import_preserves_cycles_identity_and_single_evaluation() {
12793        let root = program_module(
12794            "root",
12795            vec![
12796                Constant::String(EcmaString::from_utf8("./dependency")),
12797                Constant::String(EcmaString::from_utf8("count")),
12798                Constant::Int32(0),
12799                Constant::String(EcmaString::from_utf8("value")),
12800            ],
12801            vec![function(
12802                0,
12803                7,
12804                vec![
12805                    Instruction::LoadConst {
12806                        dst: reg(0),
12807                        constant: cid(3),
12808                    },
12809                    Instruction::StoreGlobal {
12810                        name: cid(2),
12811                        value: reg(0),
12812                    },
12813                    Instruction::Import {
12814                        dst: reg(1),
12815                        specifier: cid(1),
12816                    },
12817                    Instruction::Import {
12818                        dst: reg(2),
12819                        specifier: cid(1),
12820                    },
12821                    Instruction::Binary {
12822                        dst: reg(3),
12823                        op: BinaryOp::StrictEqual,
12824                        left: reg(1),
12825                        right: reg(2),
12826                    },
12827                    Instruction::LoadConst {
12828                        dst: reg(4),
12829                        constant: cid(4),
12830                    },
12831                    Instruction::GetProperty {
12832                        dst: reg(5),
12833                        object: reg(2),
12834                        key: reg(4),
12835                    },
12836                    Instruction::LoadGlobal {
12837                        dst: reg(6),
12838                        name: cid(2),
12839                    },
12840                    Instruction::Return { value: reg(5) },
12841                ],
12842                Vec::new(),
12843            )],
12844            vec![Edge {
12845                specifier: cid(1),
12846                target: EdgeTarget::Local(ModuleId::new(1)),
12847                kind: EdgeKind::Dynamic,
12848            }],
12849            Vec::new(),
12850            Vec::new(),
12851        );
12852        let dependency = program_module(
12853            "dependency",
12854            vec![
12855                Constant::String(EcmaString::from_utf8("./root")),
12856                Constant::String(EcmaString::from_utf8("count")),
12857                Constant::Int32(1),
12858                Constant::Int32(7),
12859                Constant::String(EcmaString::from_utf8("value")),
12860            ],
12861            vec![function(
12862                0,
12863                3,
12864                vec![
12865                    Instruction::LoadGlobal {
12866                        dst: reg(0),
12867                        name: cid(2),
12868                    },
12869                    Instruction::LoadConst {
12870                        dst: reg(1),
12871                        constant: cid(3),
12872                    },
12873                    Instruction::Binary {
12874                        dst: reg(2),
12875                        op: BinaryOp::Add,
12876                        left: reg(0),
12877                        right: reg(1),
12878                    },
12879                    Instruction::StoreGlobal {
12880                        name: cid(2),
12881                        value: reg(2),
12882                    },
12883                    Instruction::LoadConst {
12884                        dst: reg(0),
12885                        constant: cid(4),
12886                    },
12887                    Instruction::StoreGlobal {
12888                        name: cid(5),
12889                        value: reg(0),
12890                    },
12891                    Instruction::Return { value: reg(0) },
12892                ],
12893                Vec::new(),
12894            )],
12895            vec![Edge {
12896                specifier: cid(1),
12897                target: EdgeTarget::Local(ModuleId::new(0)),
12898                kind: EdgeKind::Static,
12899            }],
12900            vec![Binding {
12901                name: cid(5),
12902                kind: BindingKind::Hoisted,
12903            }],
12904            vec![Export {
12905                name: cid(5),
12906                source: ExportSource::Local(BindingId::new(0)),
12907            }],
12908        );
12909
12910        let execution = run_ok(&linked(vec![root, dependency], 0));
12911        assert_eq!(execution.value, Value::int32(7));
12912        assert_eq!(execution.entry_registers[1], execution.entry_registers[2]);
12913        assert_eq!(execution.entry_registers[3], Value::TRUE);
12914        assert_eq!(execution.entry_registers[6], Value::int32(1));
12915    }
12916
12917    #[test]
12918    fn dynamic_import_counts_live_registers_and_retries_engine_failures() {
12919        let target = program_module(
12920            "target",
12921            Vec::new(),
12922            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12923            Vec::new(),
12924            Vec::new(),
12925            Vec::new(),
12926        );
12927        let root = program_module(
12928            "root",
12929            vec![Constant::String(EcmaString::from_utf8("./target"))],
12930            vec![function(
12931                0,
12932                1,
12933                vec![
12934                    Instruction::Import {
12935                        dst: reg(0),
12936                        specifier: cid(1),
12937                    },
12938                    Instruction::Return { value: reg(0) },
12939                ],
12940                Vec::new(),
12941            )],
12942            vec![Edge {
12943                specifier: cid(1),
12944                target: EdgeTarget::Local(ModuleId::new(1)),
12945                kind: EdgeKind::Dynamic,
12946            }],
12947            Vec::new(),
12948            Vec::new(),
12949        );
12950        let program = linked(vec![root, target], 0);
12951        let mut host = TestHost;
12952        let mut machine = Machine::new(
12953            &program,
12954            &mut host,
12955            Limits {
12956                max_total_registers: 1,
12957                ..Limits::default()
12958            },
12959        );
12960        machine.frames.clear();
12961        machine.live_registers = 0;
12962        machine.instantiate_modules().unwrap();
12963
12964        let error = machine.evaluate_import(ModuleId::new(0)).unwrap_err();
12965        assert!(matches!(
12966            error.kind,
12967            RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
12968        ));
12969        assert_eq!(machine.frames.len(), 0);
12970        assert_eq!(machine.live_registers, 0);
12971
12972        machine.limits.max_total_registers = 2;
12973        machine.evaluate_import(ModuleId::new(0)).unwrap();
12974    }
12975
12976    #[test]
12977    fn dynamic_import_rethrows_one_stored_failure_at_each_import_site() {
12978        let root = program_module(
12979            "root",
12980            vec![
12981                Constant::String(EcmaString::from_utf8("./target")),
12982                Constant::String(EcmaString::from_utf8("count")),
12983                Constant::Int32(0),
12984            ],
12985            vec![function(
12986                0,
12987                4,
12988                vec![
12989                    Instruction::LoadConst {
12990                        dst: reg(0),
12991                        constant: cid(3),
12992                    },
12993                    Instruction::StoreGlobal {
12994                        name: cid(2),
12995                        value: reg(0),
12996                    },
12997                    Instruction::Import {
12998                        dst: reg(0),
12999                        specifier: cid(1),
13000                    },
13001                    Instruction::Halt,
13002                    Instruction::Import {
13003                        dst: reg(0),
13004                        specifier: cid(1),
13005                    },
13006                    Instruction::Halt,
13007                    Instruction::LoadGlobal {
13008                        dst: reg(3),
13009                        name: cid(2),
13010                    },
13011                    Instruction::Return { value: reg(2) },
13012                ],
13013                vec![
13014                    ExceptionHandler {
13015                        start: pc(2),
13016                        end: pc(3),
13017                        handler: pc(4),
13018                        catch_register: reg(1),
13019                    },
13020                    ExceptionHandler {
13021                        start: pc(4),
13022                        end: pc(5),
13023                        handler: pc(6),
13024                        catch_register: reg(2),
13025                    },
13026                ],
13027            )],
13028            vec![Edge {
13029                specifier: cid(1),
13030                target: EdgeTarget::Local(ModuleId::new(1)),
13031                kind: EdgeKind::Dynamic,
13032            }],
13033            Vec::new(),
13034            Vec::new(),
13035        );
13036        let target = program_module(
13037            "target",
13038            vec![
13039                Constant::String(EcmaString::from_utf8("count")),
13040                Constant::Int32(1),
13041                Constant::Int32(9),
13042            ],
13043            vec![function(
13044                0,
13045                3,
13046                vec![
13047                    Instruction::LoadGlobal {
13048                        dst: reg(0),
13049                        name: cid(1),
13050                    },
13051                    Instruction::LoadConst {
13052                        dst: reg(1),
13053                        constant: cid(2),
13054                    },
13055                    Instruction::Binary {
13056                        dst: reg(2),
13057                        op: BinaryOp::Add,
13058                        left: reg(0),
13059                        right: reg(1),
13060                    },
13061                    Instruction::StoreGlobal {
13062                        name: cid(1),
13063                        value: reg(2),
13064                    },
13065                    Instruction::LoadConst {
13066                        dst: reg(0),
13067                        constant: cid(3),
13068                    },
13069                    Instruction::Throw { value: reg(0) },
13070                ],
13071                Vec::new(),
13072            )],
13073            Vec::new(),
13074            Vec::new(),
13075            Vec::new(),
13076        );
13077
13078        let execution = run_ok(&linked(vec![root, target], 0));
13079        assert_eq!(execution.value, Value::int32(9));
13080        assert_eq!(execution.entry_registers[1], Value::int32(9));
13081        assert_eq!(execution.entry_registers[2], Value::int32(9));
13082        assert_eq!(execution.entry_registers[3], Value::int32(1));
13083    }
13084
13085    #[test]
13086    fn dynamic_import_returns_the_registered_external_namespace() {
13087        let module = program_module(
13088            "root",
13089            vec![Constant::String(EcmaString::from_utf8("external"))],
13090            vec![function(
13091                0,
13092                3,
13093                vec![
13094                    Instruction::Import {
13095                        dst: reg(0),
13096                        specifier: cid(1),
13097                    },
13098                    Instruction::Import {
13099                        dst: reg(1),
13100                        specifier: cid(1),
13101                    },
13102                    Instruction::Binary {
13103                        dst: reg(2),
13104                        op: BinaryOp::StrictEqual,
13105                        left: reg(0),
13106                        right: reg(1),
13107                    },
13108                    Instruction::Return { value: reg(2) },
13109                ],
13110                Vec::new(),
13111            )],
13112            vec![Edge {
13113                specifier: cid(1),
13114                target: EdgeTarget::External,
13115                kind: EdgeKind::Dynamic,
13116            }],
13117            Vec::new(),
13118            Vec::new(),
13119        );
13120        let program = linked(vec![module], 0);
13121        let mut host = TestHost;
13122        let mut machine = Machine::new(&program, &mut host, Limits::default());
13123        let namespace = machine
13124            .allocate(HeapEntry::Object {
13125                properties: PropertyMap::default(),
13126                prototype: Some(machine.intrinsics.object_prototype),
13127                boxed_primitive: None,
13128                extensible: true,
13129            })
13130            .unwrap();
13131        machine.registry.external.insert(
13132            EcmaString::from_utf8("external"),
13133            ExternalModuleInstance {
13134                namespace,
13135                exports: BTreeMap::new(),
13136                internals: BTreeMap::new(),
13137            },
13138        );
13139
13140        let execution = machine.run().unwrap();
13141        assert_eq!(execution.value, Value::TRUE);
13142        assert_eq!(execution.entry_registers[0], namespace);
13143        assert_eq!(execution.entry_registers[1], namespace);
13144    }
13145
13146    #[test]
13147    fn dynamic_import_resolution_is_requester_scoped() {
13148        let requester = |name, target| {
13149            program_module(
13150                name,
13151                vec![Constant::String(EcmaString::from_utf8("./target"))],
13152                vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13153                vec![Edge {
13154                    specifier: cid(1),
13155                    target: EdgeTarget::Local(ModuleId::new(target)),
13156                    kind: EdgeKind::Dynamic,
13157                }],
13158                Vec::new(),
13159                Vec::new(),
13160            )
13161        };
13162        let target = |name| {
13163            program_module(
13164                name,
13165                Vec::new(),
13166                vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13167                Vec::new(),
13168                Vec::new(),
13169                Vec::new(),
13170            )
13171        };
13172        let program = linked(
13173            vec![
13174                requester("first", 2),
13175                requester("second", 3),
13176                target("first-target"),
13177                target("second-target"),
13178            ],
13179            0,
13180        );
13181        let mut host = TestHost;
13182        let machine = Machine::new(&program, &mut host, Limits::default());
13183
13184        assert_eq!(
13185            machine.resolve_import(ModuleId::new(0), cid(1)),
13186            Ok(ImportTarget::Local(ModuleId::new(2)))
13187        );
13188        assert_eq!(
13189            machine.resolve_import(ModuleId::new(1), cid(1)),
13190            Ok(ImportTarget::Local(ModuleId::new(3)))
13191        );
13192    }
13193
13194    #[test]
13195    fn dynamic_import_of_a_missing_external_is_a_runtime_error() {
13196        let module = program_module(
13197            "root",
13198            vec![Constant::String(EcmaString::from_utf8("dynamic"))],
13199            vec![function(
13200                0,
13201                1,
13202                vec![
13203                    Instruction::Import {
13204                        dst: reg(0),
13205                        specifier: cid(1),
13206                    },
13207                    Instruction::Return { value: reg(0) },
13208                ],
13209                Vec::new(),
13210            )],
13211            vec![Edge {
13212                specifier: cid(1),
13213                target: EdgeTarget::External,
13214                kind: EdgeKind::Dynamic,
13215            }],
13216            Vec::new(),
13217            Vec::new(),
13218        );
13219        let program = linked(vec![module], 0);
13220        let mut host = TestHost;
13221        let error = Machine::new(&program, &mut host, Limits::default())
13222            .run()
13223            .unwrap_err();
13224        assert!(matches!(
13225            error.kind,
13226            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13227                if module == ModuleId::new(0) && edge == EdgeId::new(0)
13228        ));
13229    }
13230
13231    #[test]
13232    fn unbound_global_names_fall_back_to_the_realm_global_map() {
13233        let program = verified(
13234            vec![
13235                Constant::String(EcmaString::from_utf8("realmOnly")),
13236                Constant::Int32(7),
13237            ],
13238            vec![function(
13239                0,
13240                1,
13241                vec![
13242                    Instruction::LoadConst {
13243                        dst: reg(0),
13244                        constant: cid(1),
13245                    },
13246                    Instruction::StoreGlobal {
13247                        name: cid(0),
13248                        value: reg(0),
13249                    },
13250                    Instruction::LoadGlobal {
13251                        dst: reg(0),
13252                        name: cid(0),
13253                    },
13254                    Instruction::Return { value: reg(0) },
13255                ],
13256                Vec::new(),
13257            )],
13258        );
13259        assert_eq!(run_ok(&program).value, Value::int32(7));
13260    }
13261
13262    #[test]
13263    fn module_cell_limit_is_enforced_before_evaluation() {
13264        let module = program_module(
13265            "root",
13266            vec![Constant::String(EcmaString::from_utf8("x"))],
13267            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13268            Vec::new(),
13269            vec![Binding {
13270                name: cid(1),
13271                kind: BindingKind::Hoisted,
13272            }],
13273            Vec::new(),
13274        );
13275        let program = linked(vec![module], 0);
13276        let mut host = TestHost;
13277        let error = Machine::new(
13278            &program,
13279            &mut host,
13280            Limits {
13281                max_module_cells: 0,
13282                ..Limits::default()
13283            },
13284        )
13285        .run()
13286        .unwrap_err();
13287        assert!(matches!(
13288            error.kind,
13289            RuntimeErrorKind::ModuleCellLimitExceeded { limit: 0 }
13290        ));
13291    }
13292    #[test]
13293    fn imported_binding_store_throws_without_mutating_the_exporter() {
13294        let dependency = program_module(
13295            "dependency",
13296            vec![
13297                Constant::String(EcmaString::from_utf8("x")),
13298                Constant::Int32(1),
13299            ],
13300            vec![function(
13301                0,
13302                1,
13303                vec![
13304                    Instruction::LoadConst {
13305                        dst: reg(0),
13306                        constant: cid(2),
13307                    },
13308                    Instruction::StoreGlobal {
13309                        name: cid(1),
13310                        value: reg(0),
13311                    },
13312                    Instruction::Return { value: reg(0) },
13313                ],
13314                Vec::new(),
13315            )],
13316            Vec::new(),
13317            vec![Binding {
13318                name: cid(1),
13319                kind: BindingKind::Hoisted,
13320            }],
13321            vec![Export {
13322                name: cid(1),
13323                source: ExportSource::Local(BindingId::new(0)),
13324            }],
13325        );
13326        let root = program_module(
13327            "root",
13328            vec![
13329                Constant::String(EcmaString::from_utf8("x")),
13330                Constant::Int32(2),
13331                Constant::String(EcmaString::from_utf8("dep")),
13332            ],
13333            vec![function(
13334                0,
13335                1,
13336                vec![
13337                    Instruction::LoadConst {
13338                        dst: reg(0),
13339                        constant: cid(2),
13340                    },
13341                    Instruction::StoreGlobal {
13342                        name: cid(1),
13343                        value: reg(0),
13344                    },
13345                    Instruction::Return { value: reg(0) },
13346                ],
13347                Vec::new(),
13348            )],
13349            vec![Edge {
13350                specifier: cid(3),
13351                target: EdgeTarget::Local(ModuleId::new(0)),
13352                kind: EdgeKind::Static,
13353            }],
13354            vec![Binding {
13355                name: cid(1),
13356                kind: BindingKind::Imported {
13357                    edge: EdgeId::new(0),
13358                    name: cid(1),
13359                },
13360            }],
13361            Vec::new(),
13362        );
13363        let program = linked(vec![dependency, root], 1);
13364        let mut host = TestHost;
13365        let mut machine = Machine::new(&program, &mut host, Limits::default());
13366        machine.frames.clear();
13367        machine.live_registers = 0;
13368        machine.instantiate_modules().unwrap();
13369        assert!(machine.evaluate_module(ModuleId::new(1)).is_err());
13370        let exporter = machine.registry.modules[0].binding_cells[0].unwrap();
13371        assert_eq!(machine.registry.cells[exporter.0].value, Value::int32(1));
13372    }
13373
13374    #[test]
13375    fn namespace_descriptor_propagates_temporal_dead_zone() {
13376        let root = program_module(
13377            "root",
13378            vec![
13379                Constant::String(EcmaString::from_utf8("x")),
13380                Constant::Int32(1),
13381                Constant::String(EcmaString::from_utf8("dependency")),
13382            ],
13383            vec![function(
13384                0,
13385                1,
13386                vec![
13387                    Instruction::LoadConst {
13388                        dst: reg(0),
13389                        constant: cid(2),
13390                    },
13391                    Instruction::StoreGlobal {
13392                        name: cid(1),
13393                        value: reg(0),
13394                    },
13395                    Instruction::Return { value: reg(0) },
13396                ],
13397                Vec::new(),
13398            )],
13399            vec![Edge {
13400                specifier: cid(3),
13401                target: EdgeTarget::Local(ModuleId::new(1)),
13402                kind: EdgeKind::Static,
13403            }],
13404            vec![Binding {
13405                name: cid(1),
13406                kind: BindingKind::Lexical,
13407            }],
13408            vec![Export {
13409                name: cid(1),
13410                source: ExportSource::Local(BindingId::new(0)),
13411            }],
13412        );
13413        let dependency = program_module(
13414            "dependency",
13415            vec![
13416                Constant::String(EcmaString::from_utf8("ns")),
13417                Constant::String(EcmaString::from_utf8("root")),
13418                Constant::String(EcmaString::from_utf8("Object")),
13419                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13420                Constant::String(EcmaString::from_utf8("x")),
13421            ],
13422            vec![namespace_descriptor_entry()],
13423            vec![Edge {
13424                specifier: cid(2),
13425                target: EdgeTarget::Local(ModuleId::new(0)),
13426                kind: EdgeKind::Static,
13427            }],
13428            vec![Binding {
13429                name: cid(1),
13430                kind: BindingKind::Namespace {
13431                    edge: EdgeId::new(0),
13432                },
13433            }],
13434            Vec::new(),
13435        );
13436        let program = linked(vec![root, dependency], 0);
13437        let mut host = TestHost;
13438        let error = Machine::new(&program, &mut host, Limits::default())
13439            .run()
13440            .expect_err("descriptor reads uninitialized namespace export");
13441        assert!(matches!(
13442            error.kind,
13443            RuntimeErrorKind::TemporalDeadZone { module, binding }
13444                if module == ModuleId::new(0) && binding == BindingId::new(0)
13445        ));
13446    }
13447
13448    #[test]
13449    fn namespace_descriptor_propagates_external_linkage_error() {
13450        let exported = program_module(
13451            "exported",
13452            vec![
13453                Constant::String(EcmaString::from_utf8("x")),
13454                Constant::String(EcmaString::from_utf8("external")),
13455            ],
13456            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13457            vec![Edge {
13458                specifier: cid(2),
13459                target: EdgeTarget::External,
13460                kind: EdgeKind::Dynamic,
13461            }],
13462            Vec::new(),
13463            vec![Export {
13464                name: cid(1),
13465                source: ExportSource::Indirect {
13466                    edge: EdgeId::new(0),
13467                    name: cid(1),
13468                },
13469            }],
13470        );
13471        let importer = program_module(
13472            "importer",
13473            vec![
13474                Constant::String(EcmaString::from_utf8("ns")),
13475                Constant::String(EcmaString::from_utf8("exported")),
13476                Constant::String(EcmaString::from_utf8("Object")),
13477                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13478                Constant::String(EcmaString::from_utf8("x")),
13479            ],
13480            vec![namespace_descriptor_entry()],
13481            vec![Edge {
13482                specifier: cid(2),
13483                target: EdgeTarget::Local(ModuleId::new(0)),
13484                kind: EdgeKind::Static,
13485            }],
13486            vec![Binding {
13487                name: cid(1),
13488                kind: BindingKind::Namespace {
13489                    edge: EdgeId::new(0),
13490                },
13491            }],
13492            Vec::new(),
13493        );
13494        let program = linked(vec![exported, importer], 1);
13495        let mut host = TestHost;
13496        let error = Machine::new(&program, &mut host, Limits::default())
13497            .run()
13498            .expect_err("descriptor resolves external namespace export");
13499        assert!(matches!(
13500            error.kind,
13501            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13502                if module == ModuleId::new(0) && edge == EdgeId::new(0)
13503        ));
13504    }
13505
13506    #[test]
13507    fn installed_script_uses_machine_wide_id_and_keeps_its_code() {
13508        let root = verified(
13509            Vec::new(),
13510            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13511        );
13512        let script = Arc::new(verified(
13513            vec![Constant::Int32(42)],
13514            vec![function(
13515                0,
13516                1,
13517                vec![
13518                    Instruction::LoadConst {
13519                        dst: reg(0),
13520                        constant: cid(0),
13521                    },
13522                    Instruction::Return { value: reg(0) },
13523                ],
13524                Vec::new(),
13525            )],
13526        ));
13527        let mut host = TestHost;
13528        let mut machine = Machine::new(&root, &mut host, Limits::default());
13529        machine.instantiate_modules().unwrap();
13530        let module = machine.install_script_reserving(script, 0, 0).unwrap();
13531
13532        assert_eq!(module, ModuleId::new(root.modules().len() as u32));
13533        assert!(machine.program().module(module).is_none());
13534        assert_eq!(
13535            machine.module_code(module).constants()[0],
13536            Constant::Int32(42)
13537        );
13538
13539        let closure = machine
13540            .allocate(HeapEntry::Function {
13541                module,
13542                function: FunctionId::new(0),
13543                captures: Vec::new(),
13544                properties: PropertyMap::default(),
13545                prototype: Some(machine.intrinsics.function_prototype),
13546                extensible: true,
13547            })
13548            .unwrap();
13549        assert!(matches!(
13550            machine.call_value(closure, Value::UNDEFINED, &[]),
13551            Ok(value) if value == Value::int32(42)
13552        ));
13553    }
13554
13555    #[test]
13556    fn installed_script_rejects_non_classic_programs_and_enforces_limit() {
13557        let root = verified(
13558            Vec::new(),
13559            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13560        );
13561        let two_modules = Arc::new(linked(
13562            vec![
13563                program_module(
13564                    "first",
13565                    Vec::new(),
13566                    vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13567                    Vec::new(),
13568                    Vec::new(),
13569                    Vec::new(),
13570                ),
13571                program_module(
13572                    "second",
13573                    Vec::new(),
13574                    vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13575                    Vec::new(),
13576                    Vec::new(),
13577                    Vec::new(),
13578                ),
13579            ],
13580            0,
13581        ));
13582        let script = Arc::new(verified(
13583            Vec::new(),
13584            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13585        ));
13586        let mut host = TestHost;
13587        let mut machine = Machine::new(
13588            &root,
13589            &mut host,
13590            Limits {
13591                max_dynamic_modules: 1,
13592                ..Limits::default()
13593            },
13594        );
13595        machine.instantiate_modules().unwrap();
13596
13597        assert!(matches!(
13598            machine.install_script_reserving(two_modules, 0, 0),
13599            Err(RuntimeErrorKind::InvalidDynamicScript { .. })
13600        ));
13601        machine
13602            .install_script_reserving(script.clone(), 0, 0)
13603            .unwrap();
13604        assert!(matches!(
13605            machine.install_script_reserving(script, 0, 0),
13606            Err(RuntimeErrorKind::DynamicModuleLimitExceeded { limit: 1 })
13607        ));
13608    }
13609
13610    #[test]
13611    fn script_heap_cost_counts_scalar_constant_slots() {
13612        let entry = || vec![function(0, 1, vec![Instruction::Halt], Vec::new())];
13613        let empty = verified(Vec::new(), entry());
13614        let constants = vec![Constant::Int32(0); 128];
13615        let scalars = verified(constants.clone(), entry());
13616
13617        let added = Machine::<TestHost>::script_heap_cost(&scalars)
13618            - Machine::<TestHost>::script_heap_cost(&empty);
13619
13620        assert!(added >= constants.len() * std::mem::size_of::<Constant>());
13621    }
13622
13623    #[test]
13624    fn script_heap_cost_includes_verification_storage() {
13625        let small = verified(
13626            Vec::new(),
13627            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13628        );
13629        let large = verified(
13630            Vec::new(),
13631            vec![function(0, 130, vec![Instruction::Halt], Vec::new())],
13632        );
13633        let small_verification = small.modules()[0].code.verification_bytes();
13634        let large_verification = large.modules()[0].code.verification_bytes();
13635
13636        assert_eq!(
13637            Machine::<TestHost>::script_heap_cost(&large)
13638                - Machine::<TestHost>::script_heap_cost(&small),
13639            large_verification - small_verification
13640        );
13641    }
13642    #[test]
13643    fn promise_resolver_settles_once_and_reactions_wait_for_drain() {
13644        let program = verified(
13645            vec![
13646                Constant::String(EcmaString::from_utf8("resolve")),
13647                Constant::String(EcmaString::from_utf8("reject")),
13648                Constant::String(EcmaString::from_utf8("observed")),
13649            ],
13650            vec![
13651                function(0, 1, vec![Instruction::Halt], Vec::new()),
13652                function(
13653                    2,
13654                    2,
13655                    vec![
13656                        Instruction::StoreGlobal {
13657                            name: cid(0),
13658                            value: reg(0),
13659                        },
13660                        Instruction::StoreGlobal {
13661                            name: cid(1),
13662                            value: reg(1),
13663                        },
13664                        Instruction::Return { value: reg(0) },
13665                    ],
13666                    Vec::new(),
13667                ),
13668                function(
13669                    1,
13670                    1,
13671                    vec![
13672                        Instruction::StoreGlobal {
13673                            name: cid(2),
13674                            value: reg(0),
13675                        },
13676                        Instruction::Return { value: reg(0) },
13677                    ],
13678                    Vec::new(),
13679                ),
13680            ],
13681        );
13682        let mut host = TestHost;
13683        let mut machine = Machine::new(&program, &mut host, Limits::default());
13684        machine.frames.clear();
13685        machine.live_registers = 0;
13686        let executor = machine
13687            .allocate(HeapEntry::Function {
13688                module: ModuleId::new(0),
13689                function: FunctionId::new(1),
13690                captures: Vec::new(),
13691                properties: PropertyMap::default(),
13692                prototype: Some(machine.intrinsics.function_prototype),
13693                extensible: true,
13694            })
13695            .unwrap();
13696        let observer = machine
13697            .allocate(HeapEntry::Function {
13698                module: ModuleId::new(0),
13699                function: FunctionId::new(2),
13700                captures: Vec::new(),
13701                properties: PropertyMap::default(),
13702                prototype: Some(machine.intrinsics.function_prototype),
13703                extensible: true,
13704            })
13705            .unwrap();
13706        let constructor = machine.intrinsics.global("Promise").unwrap();
13707        let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13708        let HeapEntry::NativeFunction {
13709            callable: NativeCallable::Builtin(constructor_id),
13710            ..
13711        } = machine.heap[constructor_index]
13712        else {
13713            panic!("Promise must be a native constructor");
13714        };
13715        let BuiltinOutcome::Value(promise) = machine
13716            .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13717            .unwrap()
13718        else {
13719            panic!("Promise construction returns a Promise");
13720        };
13721        let then = machine.get_named_property(promise, "then").unwrap();
13722        machine
13723            .call_value(then, promise, &[observer])
13724            .expect("then returns a derived Promise");
13725        let resolve = machine
13726            .globals
13727            .get(&EcmaString::from_utf8("resolve"))
13728            .copied()
13729            .unwrap();
13730        let reject = machine
13731            .globals
13732            .get(&EcmaString::from_utf8("reject"))
13733            .copied()
13734            .unwrap();
13735        assert_eq!(
13736            machine
13737                .call_value(resolve, Value::UNDEFINED, &[Value::int32(1)])
13738                .unwrap(),
13739            Value::UNDEFINED
13740        );
13741        assert_eq!(
13742            machine
13743                .call_value(reject, Value::UNDEFINED, &[Value::int32(2)])
13744                .unwrap(),
13745            Value::UNDEFINED
13746        );
13747        assert!(
13748            !machine
13749                .globals
13750                .contains_key(&EcmaString::from_utf8("observed"))
13751        );
13752
13753        let drain = machine.drain_microtasks().unwrap();
13754        assert_eq!(drain.executed, 1);
13755        assert!(drain.uncaught.is_empty());
13756        assert_eq!(
13757            machine
13758                .globals
13759                .get(&EcmaString::from_utf8("observed"))
13760                .copied(),
13761            Some(Value::int32(1))
13762        );
13763    }
13764
13765    #[test]
13766    fn promise_resolution_adopts_thenables_with_a_fresh_resolver() {
13767        let program = verified(
13768            vec![
13769                Constant::String(EcmaString::from_utf8("resolve")),
13770                Constant::String(EcmaString::from_utf8("reject")),
13771                Constant::String(EcmaString::from_utf8("observed")),
13772                Constant::Int32(7),
13773                Constant::Int32(8),
13774                Constant::Int32(9),
13775                Constant::Undefined,
13776            ],
13777            vec![
13778                function(0, 1, vec![Instruction::Halt], Vec::new()),
13779                function(
13780                    2,
13781                    2,
13782                    vec![
13783                        Instruction::StoreGlobal {
13784                            name: cid(0),
13785                            value: reg(0),
13786                        },
13787                        Instruction::StoreGlobal {
13788                            name: cid(1),
13789                            value: reg(1),
13790                        },
13791                        Instruction::Return { value: reg(0) },
13792                    ],
13793                    Vec::new(),
13794                ),
13795                function(
13796                    1,
13797                    1,
13798                    vec![
13799                        Instruction::StoreGlobal {
13800                            name: cid(2),
13801                            value: reg(0),
13802                        },
13803                        Instruction::Return { value: reg(0) },
13804                    ],
13805                    Vec::new(),
13806                ),
13807                function(
13808                    2,
13809                    6,
13810                    vec![
13811                        Instruction::LoadConst {
13812                            dst: reg(2),
13813                            constant: cid(3),
13814                        },
13815                        Instruction::CreateArray { dst: reg(3) },
13816                        Instruction::ArrayPush {
13817                            array: reg(3),
13818                            value: reg(2),
13819                        },
13820                        Instruction::LoadConst {
13821                            dst: reg(4),
13822                            constant: cid(6),
13823                        },
13824                        Instruction::Call {
13825                            dst: reg(5),
13826                            callee: reg(0),
13827                            this_value: reg(4),
13828                            arguments: reg(3),
13829                        },
13830                        Instruction::LoadConst {
13831                            dst: reg(2),
13832                            constant: cid(4),
13833                        },
13834                        Instruction::CreateArray { dst: reg(3) },
13835                        Instruction::ArrayPush {
13836                            array: reg(3),
13837                            value: reg(2),
13838                        },
13839                        Instruction::Call {
13840                            dst: reg(5),
13841                            callee: reg(1),
13842                            this_value: reg(4),
13843                            arguments: reg(3),
13844                        },
13845                        Instruction::LoadConst {
13846                            dst: reg(2),
13847                            constant: cid(5),
13848                        },
13849                        Instruction::Throw { value: reg(2) },
13850                    ],
13851                    Vec::new(),
13852                ),
13853            ],
13854        );
13855        let mut host = TestHost;
13856        let mut machine = Machine::new(&program, &mut host, Limits::default());
13857        machine.frames.clear();
13858        machine.live_registers = 0;
13859        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
13860            machine
13861                .allocate(HeapEntry::Function {
13862                    module: ModuleId::new(0),
13863                    function: FunctionId::new(function),
13864                    captures: Vec::new(),
13865                    properties: PropertyMap::default(),
13866                    prototype: Some(machine.intrinsics.function_prototype),
13867                    extensible: true,
13868                })
13869                .unwrap()
13870        };
13871        let executor = runtime_function(&mut machine, 1);
13872        let observer = runtime_function(&mut machine, 2);
13873        let then_callback = runtime_function(&mut machine, 3);
13874        let thenable = machine
13875            .allocate(HeapEntry::Object {
13876                properties: PropertyMap::default(),
13877                prototype: Some(machine.intrinsics.object_prototype),
13878                boxed_primitive: None,
13879                extensible: true,
13880            })
13881            .unwrap();
13882        machine
13883            .set_data_property(thenable, "then", then_callback)
13884            .unwrap();
13885
13886        let constructor = machine.intrinsics.global("Promise").unwrap();
13887        let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13888        let HeapEntry::NativeFunction {
13889            callable: NativeCallable::Builtin(constructor_id),
13890            ..
13891        } = machine.heap[constructor_index]
13892        else {
13893            panic!("Promise must be a native constructor");
13894        };
13895        let BuiltinOutcome::Value(promise) = machine
13896            .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13897            .unwrap()
13898        else {
13899            panic!("Promise construction returns a Promise");
13900        };
13901        let resolve = machine
13902            .globals
13903            .get(&EcmaString::from_utf8("resolve"))
13904            .copied()
13905            .unwrap();
13906        let reject = machine
13907            .globals
13908            .get(&EcmaString::from_utf8("reject"))
13909            .copied()
13910            .unwrap();
13911        machine
13912            .call_value(resolve, Value::UNDEFINED, &[thenable])
13913            .unwrap();
13914        let then = machine.get_named_property(promise, "then").unwrap();
13915        machine.call_value(then, promise, &[observer]).unwrap();
13916        machine
13917            .call_value(reject, Value::UNDEFINED, &[Value::int32(9)])
13918            .unwrap();
13919        assert!(
13920            !machine
13921                .globals
13922                .contains_key(&EcmaString::from_utf8("observed"))
13923        );
13924
13925        let drain = machine.drain_microtasks().unwrap();
13926        assert_eq!(drain.executed, 2);
13927        assert!(drain.uncaught.is_empty());
13928        assert_eq!(
13929            machine
13930                .globals
13931                .get(&EcmaString::from_utf8("observed"))
13932                .copied(),
13933            Some(Value::int32(7))
13934        );
13935    }
13936
13937    #[test]
13938    fn queue_microtask_drains_fifo_including_jobs_added_during_drain() {
13939        let program = verified(
13940            vec![
13941                Constant::String(EcmaString::from_utf8("order")),
13942                Constant::String(EcmaString::from_utf8("queueMicrotask")),
13943                Constant::String(EcmaString::from_utf8("third")),
13944                Constant::Int32(1),
13945                Constant::Int32(2),
13946                Constant::Int32(3),
13947                Constant::Undefined,
13948            ],
13949            vec![
13950                function(0, 1, vec![Instruction::Halt], Vec::new()),
13951                function(
13952                    0,
13953                    7,
13954                    vec![
13955                        Instruction::LoadGlobal {
13956                            dst: reg(0),
13957                            name: cid(0),
13958                        },
13959                        Instruction::LoadConst {
13960                            dst: reg(1),
13961                            constant: cid(3),
13962                        },
13963                        Instruction::ArrayPush {
13964                            array: reg(0),
13965                            value: reg(1),
13966                        },
13967                        Instruction::LoadGlobal {
13968                            dst: reg(2),
13969                            name: cid(1),
13970                        },
13971                        Instruction::LoadGlobal {
13972                            dst: reg(3),
13973                            name: cid(2),
13974                        },
13975                        Instruction::CreateArray { dst: reg(4) },
13976                        Instruction::ArrayPush {
13977                            array: reg(4),
13978                            value: reg(3),
13979                        },
13980                        Instruction::LoadConst {
13981                            dst: reg(5),
13982                            constant: cid(6),
13983                        },
13984                        Instruction::Call {
13985                            dst: reg(6),
13986                            callee: reg(2),
13987                            this_value: reg(5),
13988                            arguments: reg(4),
13989                        },
13990                        Instruction::Return { value: reg(1) },
13991                    ],
13992                    Vec::new(),
13993                ),
13994                function(
13995                    0,
13996                    2,
13997                    vec![
13998                        Instruction::LoadGlobal {
13999                            dst: reg(0),
14000                            name: cid(0),
14001                        },
14002                        Instruction::LoadConst {
14003                            dst: reg(1),
14004                            constant: cid(4),
14005                        },
14006                        Instruction::ArrayPush {
14007                            array: reg(0),
14008                            value: reg(1),
14009                        },
14010                        Instruction::Return { value: reg(1) },
14011                    ],
14012                    Vec::new(),
14013                ),
14014                function(
14015                    0,
14016                    2,
14017                    vec![
14018                        Instruction::LoadGlobal {
14019                            dst: reg(0),
14020                            name: cid(0),
14021                        },
14022                        Instruction::LoadConst {
14023                            dst: reg(1),
14024                            constant: cid(5),
14025                        },
14026                        Instruction::ArrayPush {
14027                            array: reg(0),
14028                            value: reg(1),
14029                        },
14030                        Instruction::Return { value: reg(1) },
14031                    ],
14032                    Vec::new(),
14033                ),
14034            ],
14035        );
14036        let mut host = TestHost;
14037        let mut machine = Machine::new(&program, &mut host, Limits::default());
14038        machine.frames.clear();
14039        machine.live_registers = 0;
14040        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14041            machine
14042                .allocate(HeapEntry::Function {
14043                    module: ModuleId::new(0),
14044                    function: FunctionId::new(function),
14045                    captures: Vec::new(),
14046                    properties: PropertyMap::default(),
14047                    prototype: Some(machine.intrinsics.function_prototype),
14048                    extensible: true,
14049                })
14050                .unwrap()
14051        };
14052        let first = runtime_function(&mut machine, 1);
14053        let second = runtime_function(&mut machine, 2);
14054        let third = runtime_function(&mut machine, 3);
14055        let order = machine
14056            .allocate(HeapEntry::Array {
14057                elements: Vec::new(),
14058                properties: PropertyMap::default(),
14059                prototype: Some(machine.intrinsics.array_prototype),
14060                extensible: true,
14061                length_writable: true,
14062            })
14063            .unwrap();
14064        machine
14065            .globals
14066            .insert(EcmaString::from_utf8("order"), order);
14067        machine
14068            .globals
14069            .insert(EcmaString::from_utf8("third"), third);
14070        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14071        machine
14072            .call_value(queue, Value::UNDEFINED, &[first])
14073            .unwrap();
14074        machine
14075            .call_value(queue, Value::UNDEFINED, &[second])
14076            .unwrap();
14077
14078        let drain = machine.drain_microtasks().unwrap();
14079        assert_eq!(drain.executed, 3);
14080        assert!(drain.uncaught.is_empty());
14081        let index = machine.runtime_slot(order).unwrap().unwrap();
14082        let HeapEntry::Array { elements, .. } = &machine.heap[index] else {
14083            panic!("order remains an array");
14084        };
14085        assert_eq!(
14086            elements,
14087            &[Value::int32(1), Value::int32(2), Value::int32(3)]
14088        );
14089    }
14090
14091    #[test]
14092    fn queue_microtask_reports_callback_throws_and_continues() {
14093        let program = verified(
14094            vec![
14095                Constant::Int32(7),
14096                Constant::Int32(1),
14097                Constant::String(EcmaString::from_utf8("observed")),
14098            ],
14099            vec![
14100                function(0, 1, vec![Instruction::Halt], Vec::new()),
14101                function(
14102                    0,
14103                    1,
14104                    vec![
14105                        Instruction::LoadConst {
14106                            dst: reg(0),
14107                            constant: cid(0),
14108                        },
14109                        Instruction::Throw { value: reg(0) },
14110                    ],
14111                    Vec::new(),
14112                ),
14113                function(
14114                    0,
14115                    1,
14116                    vec![
14117                        Instruction::LoadConst {
14118                            dst: reg(0),
14119                            constant: cid(1),
14120                        },
14121                        Instruction::StoreGlobal {
14122                            name: cid(2),
14123                            value: reg(0),
14124                        },
14125                        Instruction::Return { value: reg(0) },
14126                    ],
14127                    Vec::new(),
14128                ),
14129            ],
14130        );
14131        let mut host = TestHost;
14132        let mut machine = Machine::new(&program, &mut host, Limits::default());
14133        machine.frames.clear();
14134        machine.live_registers = 0;
14135        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14136            machine
14137                .allocate(HeapEntry::Function {
14138                    module: ModuleId::new(0),
14139                    function: FunctionId::new(function),
14140                    captures: Vec::new(),
14141                    properties: PropertyMap::default(),
14142                    prototype: Some(machine.intrinsics.function_prototype),
14143                    extensible: true,
14144                })
14145                .unwrap()
14146        };
14147        let throwing = runtime_function(&mut machine, 1);
14148        let observer = runtime_function(&mut machine, 2);
14149        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14150        machine
14151            .call_value(queue, Value::UNDEFINED, &[throwing])
14152            .unwrap();
14153        machine
14154            .call_value(queue, Value::UNDEFINED, &[observer])
14155            .unwrap();
14156
14157        let drain = machine.drain_microtasks().unwrap();
14158        assert_eq!(drain.executed, 2);
14159        assert_eq!(
14160            drain.uncaught,
14161            vec![CallbackException {
14162                value: Value::int32(7),
14163                origin: ThrowOrigin::Bytecode,
14164            }]
14165        );
14166        assert_eq!(
14167            machine
14168                .globals
14169                .get(&EcmaString::from_utf8("observed"))
14170                .copied(),
14171            Some(Value::int32(1))
14172        );
14173    }
14174
14175    #[test]
14176    fn microtask_boundaries_preserve_the_queued_head() {
14177        let program = verified(
14178            vec![Constant::Undefined],
14179            vec![
14180                function(0, 1, vec![Instruction::Halt], Vec::new()),
14181                function(
14182                    0,
14183                    1,
14184                    vec![
14185                        Instruction::LoadConst {
14186                            dst: reg(0),
14187                            constant: cid(0),
14188                        },
14189                        Instruction::Return { value: reg(0) },
14190                    ],
14191                    Vec::new(),
14192                ),
14193            ],
14194        );
14195        let mut host = TestHost;
14196        let mut machine = Machine::new(
14197            &program,
14198            &mut host,
14199            Limits {
14200                max_microtasks: 1,
14201                ..Limits::default()
14202            },
14203        );
14204        machine.frames.clear();
14205        machine.live_registers = 0;
14206        let callback = machine
14207            .allocate(HeapEntry::Function {
14208                module: ModuleId::new(0),
14209                function: FunctionId::new(1),
14210                captures: Vec::new(),
14211                properties: PropertyMap::default(),
14212                prototype: Some(machine.intrinsics.function_prototype),
14213                extensible: true,
14214            })
14215            .unwrap();
14216        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14217        assert!(matches!(
14218            machine.call_value(queue, Value::UNDEFINED, &[Value::int32(1)]),
14219            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
14220        ));
14221        machine
14222            .call_value(queue, Value::UNDEFINED, &[callback])
14223            .unwrap();
14224        assert!(matches!(
14225            machine.call_value(queue, Value::UNDEFINED, &[callback]),
14226            Err(EvalFailure::Runtime(
14227                RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 1 }
14228            ))
14229        ));
14230
14231        let fuel = machine.fuel;
14232        machine.microtask_drain_active = true;
14233        let reentry = machine.drain_microtasks().unwrap_err();
14234        assert!(matches!(
14235            reentry.kind,
14236            RuntimeErrorKind::MicrotaskDrainReentry
14237        ));
14238        assert_eq!(machine.fuel, fuel);
14239        assert_eq!(machine.microtasks.len(), 1);
14240        machine.microtask_drain_active = false;
14241
14242        machine.fuel = 0;
14243        let exhausted = machine.drain_microtasks().unwrap_err();
14244        assert!(matches!(
14245            exhausted.kind,
14246            RuntimeErrorKind::FuelExhausted { .. }
14247        ));
14248        assert!(!machine.microtask_drain_active);
14249        assert_eq!(machine.microtasks.len(), 1);
14250
14251        machine.fuel = 100;
14252        let drain = machine.drain_microtasks().unwrap();
14253        assert_eq!(drain.executed, 1);
14254        assert!(machine.microtasks.is_empty());
14255    }
14256
14257    // ---- timers -----------------------------------------------------------
14258
14259    #[derive(Default)]
14260    struct ManualTimerState {
14261        live: std::collections::BTreeMap<u64, u64>,
14262        reports: std::collections::VecDeque<TimerWakeup>,
14263        scheduled: Vec<(u64, u32)>,
14264        cancelled: Vec<u64>,
14265        fail_schedule: bool,
14266        fail_poll: bool,
14267    }
14268
14269    #[derive(Clone, Default)]
14270    struct ManualTimerProvider {
14271        state: std::rc::Rc<std::cell::RefCell<ManualTimerState>>,
14272    }
14273
14274    impl TimerProvider for ManualTimerProvider {
14275        fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError> {
14276            let mut state = self.state.borrow_mut();
14277            state.scheduled.push((id, delay_ms));
14278            if state.fail_schedule {
14279                return Err(TimerError::new("manual schedule failure"));
14280            }
14281            let deadline = u64::from(delay_ms);
14282            state.live.insert(id, deadline);
14283            Ok(deadline)
14284        }
14285
14286        fn cancel(&mut self, id: u64) -> Result<bool, TimerError> {
14287            let mut state = self.state.borrow_mut();
14288            state.cancelled.push(id);
14289            Ok(state.live.remove(&id).is_some())
14290        }
14291
14292        fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError> {
14293            let mut state = self.state.borrow_mut();
14294            if state.fail_poll {
14295                return Err(TimerError::new("manual poll failure"));
14296            }
14297            output.extend(state.reports.drain(..));
14298            Ok(())
14299        }
14300
14301        fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError> {
14302            Ok(self.state.borrow_mut().reports.pop_front())
14303        }
14304
14305        fn has_pending(&self) -> bool {
14306            !self.state.borrow().live.is_empty()
14307        }
14308    }
14309
14310    #[derive(Default)]
14311    struct TimerTestHost {
14312        provider: ManualTimerProvider,
14313    }
14314
14315    impl Host for TimerTestHost {
14316        fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
14317            Some(&mut self.provider)
14318        }
14319    }
14320
14321    fn timer_program() -> Program<Verified> {
14322        verified(
14323            vec![
14324                Constant::String(EcmaString::from_utf8("a")),
14325                Constant::String(EcmaString::from_utf8("b")),
14326                Constant::String(EcmaString::from_utf8("this_seen")),
14327                Constant::String(EcmaString::from_utf8("arg_seen")),
14328                Constant::Int32(1),
14329                Constant::Int32(7),
14330            ],
14331            vec![
14332                function(0, 1, vec![Instruction::Halt], Vec::new()),
14333                function(
14334                    0,
14335                    1,
14336                    vec![
14337                        Instruction::LoadConst {
14338                            dst: reg(0),
14339                            constant: cid(4),
14340                        },
14341                        Instruction::StoreGlobal {
14342                            name: cid(0),
14343                            value: reg(0),
14344                        },
14345                        Instruction::Return { value: reg(0) },
14346                    ],
14347                    Vec::new(),
14348                ),
14349                function(
14350                    0,
14351                    1,
14352                    vec![
14353                        Instruction::LoadConst {
14354                            dst: reg(0),
14355                            constant: cid(4),
14356                        },
14357                        Instruction::StoreGlobal {
14358                            name: cid(1),
14359                            value: reg(0),
14360                        },
14361                        Instruction::Return { value: reg(0) },
14362                    ],
14363                    Vec::new(),
14364                ),
14365                function(
14366                    1,
14367                    2,
14368                    vec![
14369                        Instruction::LoadThis { dst: reg(1) },
14370                        Instruction::StoreGlobal {
14371                            name: cid(2),
14372                            value: reg(1),
14373                        },
14374                        Instruction::StoreGlobal {
14375                            name: cid(3),
14376                            value: reg(0),
14377                        },
14378                        Instruction::Return { value: reg(0) },
14379                    ],
14380                    Vec::new(),
14381                ),
14382                function(
14383                    0,
14384                    1,
14385                    vec![
14386                        Instruction::LoadConst {
14387                            dst: reg(0),
14388                            constant: cid(5),
14389                        },
14390                        Instruction::Throw { value: reg(0) },
14391                    ],
14392                    Vec::new(),
14393                ),
14394            ],
14395        )
14396    }
14397
14398    fn timer_fn(machine: &mut Machine<'_, TimerTestHost>, index: u32) -> Value {
14399        machine
14400            .allocate(HeapEntry::Function {
14401                module: ModuleId::new(0),
14402                function: FunctionId::new(index),
14403                captures: Vec::new(),
14404                properties: PropertyMap::default(),
14405                prototype: Some(machine.intrinsics.function_prototype),
14406                extensible: true,
14407            })
14408            .unwrap()
14409    }
14410
14411    fn read_global(machine: &Machine<'_, TimerTestHost>, name: &str) -> Option<Value> {
14412        machine.globals.get(&EcmaString::from_utf8(name)).copied()
14413    }
14414
14415    fn set_timeout_global(machine: &Machine<'_, TimerTestHost>) -> Value {
14416        machine
14417            .intrinsics
14418            .global("setTimeout")
14419            .expect("setTimeout is installed")
14420    }
14421
14422    fn schedule_nested_timer(
14423        machine: &mut Machine<'_, TimerTestHost>,
14424        _this: Value,
14425        _args: &[Value],
14426        _constructing: bool,
14427    ) -> Result<BuiltinOutcome, EvalFailure> {
14428        let callback = machine
14429            .globals
14430            .get(&EcmaString::from_utf8("nestedCallback"))
14431            .copied()
14432            .expect("test installs nested callback");
14433        let set_timeout = set_timeout_global(machine);
14434        machine.call_value(set_timeout, Value::UNDEFINED, &[callback, Value::int32(1)])?;
14435        Ok(BuiltinOutcome::Value(Value::UNDEFINED))
14436    }
14437
14438    fn timer_native(
14439        machine: &mut Machine<'_, TimerTestHost>,
14440        name: &'static str,
14441        handler: crate::intrinsics::BuiltinHandler<TimerTestHost>,
14442    ) -> Value {
14443        let id = machine
14444            .intrinsics
14445            .builtins
14446            .register(crate::intrinsics::BuiltinDef {
14447                name,
14448                length: 0,
14449                handler,
14450            });
14451        crate::intrinsics::native_function(&mut machine.heap, id, name, 0)
14452    }
14453
14454    #[test]
14455    fn timers_are_absent_without_the_capability() {
14456        let program = timer_program();
14457        let mut host = TestHost;
14458        let mut machine = Machine::new(&program, &mut host, Limits::default());
14459        machine.frames.clear();
14460        machine.live_registers = 0;
14461        assert!(machine.intrinsics.global("setTimeout").is_none());
14462        assert!(machine.intrinsics.global("clearTimeout").is_none());
14463        assert!(!machine.has_pending_timers());
14464        assert_eq!(
14465            machine.run_one_expired_timer().unwrap(),
14466            TimerRun::default()
14467        );
14468        assert!(!machine.wait_for_timer_expiry().unwrap());
14469    }
14470
14471    #[test]
14472    fn set_timeout_rejects_a_non_callable_callback_before_coercion() {
14473        let program = timer_program();
14474        let mut host = TimerTestHost::default();
14475        let shared = host.provider.state.clone();
14476        let mut machine = Machine::new(&program, &mut host, Limits::default());
14477        machine.frames.clear();
14478        machine.live_registers = 0;
14479        let set_timeout = set_timeout_global(&machine);
14480        let failure = machine
14481            .call_value(
14482                set_timeout,
14483                Value::UNDEFINED,
14484                &[Value::int32(3), Value::int32(5)],
14485            )
14486            .unwrap_err();
14487        assert!(matches!(
14488            failure,
14489            EvalFailure::Throw(ThrowOrigin::TypeError { .. })
14490        ));
14491        // Nothing was armed, so no delay coercion or provider call happened.
14492        assert!(shared.borrow().scheduled.is_empty());
14493        assert!(!machine.has_pending_timers());
14494    }
14495
14496    #[test]
14497    fn set_timeout_clamps_and_truncates_like_node() {
14498        let program = timer_program();
14499        let mut host = TimerTestHost::default();
14500        let shared = host.provider.state.clone();
14501        let mut machine = Machine::new(&program, &mut host, Limits::default());
14502        machine.frames.clear();
14503        machine.live_registers = 0;
14504        let set_timeout = set_timeout_global(&machine);
14505        let callback = timer_fn(&mut machine, 1);
14506        for delay in [
14507            Value::int32(0),
14508            Value::number(-5.0),
14509            Value::number(f64::NAN),
14510            Value::number(2_147_483_648.0),
14511            Value::int32(2_147_483_647),
14512            Value::number(3.9),
14513        ] {
14514            machine
14515                .call_value(set_timeout, Value::UNDEFINED, &[callback, delay])
14516                .unwrap();
14517        }
14518        let delays: Vec<u32> = shared.borrow().scheduled.iter().map(|(_, d)| *d).collect();
14519        assert_eq!(delays, vec![1, 1, 1, 1, 2_147_483_647, 3]);
14520        // Ids are minted monotonically from 1 and never reused.
14521        let ids: Vec<u64> = shared
14522            .borrow()
14523            .scheduled
14524            .iter()
14525            .map(|(id, _)| *id)
14526            .collect();
14527        assert_eq!(ids, vec![1, 2, 3, 4, 5, 6]);
14528    }
14529
14530    #[test]
14531    fn same_deadline_timers_run_in_registration_order_despite_reverse_reports() {
14532        let program = timer_program();
14533        let mut host = TimerTestHost::default();
14534        let shared = host.provider.state.clone();
14535        let mut machine = Machine::new(&program, &mut host, Limits::default());
14536        machine.frames.clear();
14537        machine.live_registers = 0;
14538        let set_timeout = set_timeout_global(&machine);
14539        let a = timer_fn(&mut machine, 1);
14540        let b = timer_fn(&mut machine, 2);
14541        machine
14542            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14543            .unwrap();
14544        machine
14545            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(5)])
14546            .unwrap();
14547        // Host reports the later registration first and in split batches.
14548        shared.borrow_mut().reports.push_back(TimerWakeup {
14549            id: 2,
14550            deadline_ms: 5,
14551        });
14552        let first = machine.run_one_expired_timer().unwrap();
14553        assert_eq!(first.executed, 1);
14554        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14555        assert_eq!(read_global(&machine, "b"), None);
14556        let second = machine.run_one_expired_timer().unwrap();
14557        assert_eq!(second.executed, 1);
14558        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14559        assert!(!machine.has_pending_timers());
14560    }
14561
14562    #[test]
14563    fn a_shorter_deadline_beats_an_older_sequence() {
14564        let program = timer_program();
14565        let mut host = TimerTestHost::default();
14566        let shared = host.provider.state.clone();
14567        let mut machine = Machine::new(&program, &mut host, Limits::default());
14568        machine.frames.clear();
14569        machine.live_registers = 0;
14570        let set_timeout = set_timeout_global(&machine);
14571        let a = timer_fn(&mut machine, 1);
14572        let b = timer_fn(&mut machine, 2);
14573        machine
14574            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14575            .unwrap();
14576        machine
14577            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14578            .unwrap();
14579        shared.borrow_mut().reports.push_back(TimerWakeup {
14580            id: 1,
14581            deadline_ms: 5,
14582        });
14583        machine.run_one_expired_timer().unwrap();
14584        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14585        assert_eq!(read_global(&machine, "a"), None);
14586    }
14587
14588    #[test]
14589    fn clear_timeout_prevents_a_ready_timer_and_ignores_stale_ids() {
14590        let program = timer_program();
14591        let mut host = TimerTestHost::default();
14592        let shared = host.provider.state.clone();
14593        let mut machine = Machine::new(&program, &mut host, Limits::default());
14594        machine.frames.clear();
14595        machine.live_registers = 0;
14596        let set_timeout = set_timeout_global(&machine);
14597        let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14598        let a = timer_fn(&mut machine, 1);
14599        let b = timer_fn(&mut machine, 2);
14600        let handle_a = machine
14601            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14602            .unwrap();
14603        machine
14604            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14605            .unwrap();
14606        // Clear the first timer even though the host already reported it.
14607        shared.borrow_mut().reports.push_back(TimerWakeup {
14608            id: 1,
14609            deadline_ms: 3,
14610        });
14611        machine
14612            .call_value(clear_timeout, Value::UNDEFINED, &[handle_a])
14613            .unwrap();
14614        assert!(shared.borrow().cancelled.contains(&1));
14615        // A stale positive-integer id must not cancel the surviving timer.
14616        machine
14617            .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14618            .unwrap();
14619        shared.borrow_mut().reports.push_back(TimerWakeup {
14620            id: 2,
14621            deadline_ms: 3,
14622        });
14623        let run = machine.run_one_expired_timer().unwrap();
14624        assert_eq!(run.executed, 1);
14625        assert_eq!(read_global(&machine, "a"), None);
14626        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14627    }
14628
14629    #[test]
14630    fn clear_timeout_accepts_a_direct_positive_integer_id() {
14631        let program = timer_program();
14632        let mut host = TimerTestHost::default();
14633        let shared = host.provider.state.clone();
14634        let mut machine = Machine::new(&program, &mut host, Limits::default());
14635        machine.frames.clear();
14636        machine.live_registers = 0;
14637        let set_timeout = set_timeout_global(&machine);
14638        let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14639        let a = timer_fn(&mut machine, 1);
14640        machine
14641            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14642            .unwrap();
14643        machine
14644            .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14645            .unwrap();
14646        assert!(!machine.has_pending_timers());
14647        shared.borrow_mut().reports.push_back(TimerWakeup {
14648            id: 1,
14649            deadline_ms: 3,
14650        });
14651        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 0);
14652
14653        machine.next_timer_id = Some(u64::MAX);
14654        let handle = machine
14655            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14656            .unwrap();
14657        machine
14658            .call_value(
14659                clear_timeout,
14660                Value::UNDEFINED,
14661                &[Value::number(u64::MAX as f64)],
14662            )
14663            .unwrap();
14664        assert!(machine.has_pending_timers());
14665        machine
14666            .call_value(clear_timeout, Value::UNDEFINED, &[handle])
14667            .unwrap();
14668        assert!(!machine.has_pending_timers());
14669        // A no-op clear of an unrelated value never coerces or errors.
14670        machine
14671            .call_value(clear_timeout, Value::UNDEFINED, &[Value::UNDEFINED])
14672            .unwrap();
14673    }
14674
14675    #[test]
14676    fn timer_callback_receives_trailing_args_and_the_handle_as_this() {
14677        let program = timer_program();
14678        let mut host = TimerTestHost::default();
14679        let shared = host.provider.state.clone();
14680        let mut machine = Machine::new(&program, &mut host, Limits::default());
14681        machine.frames.clear();
14682        machine.live_registers = 0;
14683        let set_timeout = set_timeout_global(&machine);
14684        let callback = timer_fn(&mut machine, 3);
14685        let handle = machine
14686            .call_value(
14687                set_timeout,
14688                Value::UNDEFINED,
14689                &[callback, Value::int32(1), Value::int32(42)],
14690            )
14691            .unwrap();
14692        shared.borrow_mut().reports.push_back(TimerWakeup {
14693            id: 1,
14694            deadline_ms: 1,
14695        });
14696        machine.run_one_expired_timer().unwrap();
14697        assert_eq!(read_global(&machine, "this_seen"), Some(handle));
14698        assert_eq!(read_global(&machine, "arg_seen"), Some(Value::int32(42)));
14699    }
14700
14701    #[test]
14702    fn a_callback_created_timer_waits_for_a_later_checkpoint() {
14703        let program = timer_program();
14704        let mut host = TimerTestHost::default();
14705        let shared = host.provider.state.clone();
14706        let mut machine = Machine::new(&program, &mut host, Limits::default());
14707        machine.frames.clear();
14708        machine.live_registers = 0;
14709        let set_timeout = set_timeout_global(&machine);
14710        let nested = timer_fn(&mut machine, 2);
14711        machine
14712            .globals
14713            .insert(EcmaString::from_utf8("nestedCallback"), nested);
14714        let creator = timer_native(&mut machine, "schedule nested", schedule_nested_timer);
14715        machine
14716            .call_value(set_timeout, Value::UNDEFINED, &[creator, Value::int32(1)])
14717            .unwrap();
14718        shared.borrow_mut().reports.push_back(TimerWakeup {
14719            id: 1,
14720            deadline_ms: 1,
14721        });
14722        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14723        assert_eq!(read_global(&machine, "b"), None);
14724        assert!(machine.has_pending_timers());
14725        // Even if the provider can report it immediately, it runs only in a
14726        // later explicit timer checkpoint.
14727        shared.borrow_mut().reports.push_back(TimerWakeup {
14728            id: 2,
14729            deadline_ms: 1,
14730        });
14731        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14732        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14733    }
14734
14735    #[test]
14736    fn timer_callback_throw_is_reported_and_a_runtime_failure_propagates() {
14737        let program = timer_program();
14738        let mut host = TimerTestHost::default();
14739        let shared = host.provider.state.clone();
14740        let mut machine = Machine::new(&program, &mut host, Limits::default());
14741        machine.frames.clear();
14742        machine.live_registers = 0;
14743        let set_timeout = set_timeout_global(&machine);
14744        let thrower = timer_fn(&mut machine, 4);
14745        machine
14746            .call_value(set_timeout, Value::UNDEFINED, &[thrower, Value::int32(1)])
14747            .unwrap();
14748        shared.borrow_mut().reports.push_back(TimerWakeup {
14749            id: 1,
14750            deadline_ms: 1,
14751        });
14752        let run = machine.run_one_expired_timer().unwrap();
14753        assert_eq!(run.executed, 1);
14754        assert_eq!(
14755            run.uncaught,
14756            vec![CallbackException {
14757                value: Value::int32(7),
14758                origin: ThrowOrigin::Bytecode
14759            }]
14760        );
14761
14762        // A runtime failure inside the callback stops the checkpoint.
14763        let another = timer_fn(&mut machine, 1);
14764        machine
14765            .call_value(set_timeout, Value::UNDEFINED, &[another, Value::int32(1)])
14766            .unwrap();
14767        shared.borrow_mut().reports.push_back(TimerWakeup {
14768            id: 2,
14769            deadline_ms: 1,
14770        });
14771        machine.fuel = 1;
14772        let error = machine.run_one_expired_timer().unwrap_err();
14773        assert!(matches!(error.kind, RuntimeErrorKind::FuelExhausted { .. }));
14774    }
14775
14776    #[test]
14777    fn a_timer_checkpoint_never_drains_microtasks() {
14778        let program = timer_program();
14779        let mut host = TimerTestHost::default();
14780        let shared = host.provider.state.clone();
14781        let mut machine = Machine::new(&program, &mut host, Limits::default());
14782        machine.frames.clear();
14783        machine.live_registers = 0;
14784        let set_timeout = set_timeout_global(&machine);
14785        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14786        let a = timer_fn(&mut machine, 1);
14787        let b = timer_fn(&mut machine, 2);
14788        machine
14789            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14790            .unwrap();
14791        machine.call_value(queue, Value::UNDEFINED, &[b]).unwrap();
14792        shared.borrow_mut().reports.push_back(TimerWakeup {
14793            id: 1,
14794            deadline_ms: 1,
14795        });
14796        let run = machine.run_one_expired_timer().unwrap();
14797        assert_eq!(run.executed, 1);
14798        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14799        assert_eq!(read_global(&machine, "b"), None);
14800        assert_eq!(machine.microtasks.len(), 1);
14801        machine.drain_microtasks().unwrap();
14802        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14803    }
14804
14805    #[test]
14806    fn timer_reentry_capacity_and_fuel_preserve_state() {
14807        let program = timer_program();
14808        let mut host = TimerTestHost::default();
14809        let shared = host.provider.state.clone();
14810        let mut machine = Machine::new(
14811            &program,
14812            &mut host,
14813            Limits {
14814                max_timers: 1,
14815                ..Limits::default()
14816            },
14817        );
14818        machine.frames.clear();
14819        machine.live_registers = 0;
14820        let set_timeout = set_timeout_global(&machine);
14821        let a = timer_fn(&mut machine, 1);
14822        let b = timer_fn(&mut machine, 2);
14823        machine
14824            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14825            .unwrap();
14826        // Capacity is enforced before any provider or table mutation.
14827        let capacity = machine
14828            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(1)])
14829            .unwrap_err();
14830        assert!(matches!(
14831            capacity,
14832            EvalFailure::Runtime(RuntimeErrorKind::TimerCapacityExceeded { limit: 1 })
14833        ));
14834        assert_eq!(shared.borrow().scheduled.len(), 1);
14835
14836        // Reentry fails without consuming fuel or touching the ready timer.
14837        shared.borrow_mut().reports.push_back(TimerWakeup {
14838            id: 1,
14839            deadline_ms: 1,
14840        });
14841        machine.timer_checkpoint_active = true;
14842        let fuel = machine.fuel;
14843        let reentry = machine.run_one_expired_timer().unwrap_err();
14844        assert!(matches!(
14845            reentry.kind,
14846            RuntimeErrorKind::TimerCheckpointReentry
14847        ));
14848        assert_eq!(machine.fuel, fuel);
14849        machine.timer_checkpoint_active = false;
14850
14851        // Fuel is charged before the live record is removed.
14852        machine.fuel = 0;
14853        let exhausted = machine.run_one_expired_timer().unwrap_err();
14854        assert!(matches!(
14855            exhausted.kind,
14856            RuntimeErrorKind::FuelExhausted { .. }
14857        ));
14858        assert!(machine.has_pending_timers());
14859        machine.fuel = 100;
14860        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14861        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14862    }
14863
14864    #[test]
14865    fn a_failed_schedule_never_reuses_its_timer_id() {
14866        let program = timer_program();
14867        let mut host = TimerTestHost::default();
14868        let shared = host.provider.state.clone();
14869        let mut machine = Machine::new(&program, &mut host, Limits::default());
14870        machine.frames.clear();
14871        machine.live_registers = 0;
14872        let set_timeout = set_timeout_global(&machine);
14873        let a = timer_fn(&mut machine, 1);
14874        shared.borrow_mut().fail_schedule = true;
14875        let failure = machine
14876            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14877            .unwrap_err();
14878        assert!(matches!(
14879            failure,
14880            EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure { .. })
14881        ));
14882        shared.borrow_mut().fail_schedule = false;
14883        machine
14884            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14885            .unwrap();
14886        let ids: Vec<u64> = shared
14887            .borrow()
14888            .scheduled
14889            .iter()
14890            .map(|(id, _)| *id)
14891            .collect();
14892        assert_eq!(ids, vec![1, 2]);
14893    }
14894
14895    #[test]
14896    fn wait_for_timer_expiry_promotes_a_reported_timer() {
14897        let program = timer_program();
14898        let mut host = TimerTestHost::default();
14899        let shared = host.provider.state.clone();
14900        let mut machine = Machine::new(&program, &mut host, Limits::default());
14901        machine.frames.clear();
14902        machine.live_registers = 0;
14903        assert!(!machine.wait_for_timer_expiry().unwrap());
14904        let set_timeout = set_timeout_global(&machine);
14905        let a = timer_fn(&mut machine, 1);
14906        machine
14907            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14908            .unwrap();
14909        shared.borrow_mut().reports.push_back(TimerWakeup {
14910            id: 1,
14911            deadline_ms: 1,
14912        });
14913        assert!(machine.wait_for_timer_expiry().unwrap());
14914        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14915        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14916    }
14917}