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    /// Evaluates the program, drives the automatic event loop to quiescence,
1397    /// and returns the synchronous [`Execution`] snapshot taken right after
1398    /// evaluation. Callbacks may mutate globals and append host output, but
1399    /// the returned value and entry register snapshot stay those of the
1400    /// synchronous evaluation.
1401    pub fn run(mut self) -> Result<Execution, RuntimeError> {
1402        let execution = self.evaluate()?;
1403        self.run_to_quiescence()?;
1404        Ok(execution)
1405    }
1406
1407    /// Evaluates the program without draining queued microtasks.
1408    ///
1409    /// The machine remains available for an explicit [`Self::drain_microtasks`]
1410    /// checkpoint.
1411    pub fn evaluate(&mut self) -> Result<Execution, RuntimeError> {
1412        if let Some(program) = self.program {
1413            let entry = program.entry();
1414            self.frames.clear();
1415            self.live_registers = 0;
1416            self.instantiate_modules()?;
1417            return self.evaluate_module(entry)?.ok_or_else(|| {
1418                self.program_error(
1419                    entry,
1420                    RuntimeErrorKind::InvalidVerifiedProgram {
1421                        module: entry,
1422                        instruction: Instruction::Halt,
1423                    },
1424                )
1425            });
1426        }
1427        Ok(self
1428            .run_loop(0)?
1429            .expect("the entry frame completes before the run loop stops"))
1430    }
1431
1432    /// Runs at most one expired live timer callback.
1433    ///
1434    /// This explicit checkpoint never drains microtasks. Expiry reports only
1435    /// advance a monotonic watermark; visible delivery is ordered by the
1436    /// machine-owned `(deadline, sequence)` key.
1437    pub fn run_one_expired_timer(&mut self) -> Result<TimerRun, RuntimeError> {
1438        if self.timer_checkpoint_active {
1439            return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1440        }
1441        self.timer_checkpoint_active = true;
1442        let result = (|| {
1443            self.poll_timer_expiries()
1444                .map_err(|kind| self.checkpoint_error(kind))?;
1445            let Some(order) = self.ready_timers.first().copied() else {
1446                return Ok(TimerRun::default());
1447            };
1448            let Some(id) = self.timers.iter().find_map(|(id, timer)| {
1449                ((timer.deadline_ms, timer.sequence) == order).then_some(*id)
1450            }) else {
1451                return Err(self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1452                    value: Value::UNDEFINED,
1453                }));
1454            };
1455            // Preserve both the ready key and live record when fuel is empty.
1456            self.consume_fuel(1)
1457                .map_err(|kind| self.checkpoint_error(kind))?;
1458            self.ready_timers.remove(&order);
1459            let timer = self
1460                .timers
1461                .remove(&id)
1462                .expect("ready timer remains live until after fuel charging");
1463            let mut report = TimerRun {
1464                executed: 1,
1465                uncaught: Vec::new(),
1466            };
1467            match self.call_value(timer.callback, timer.handle, &timer.arguments) {
1468                Ok(_) => {}
1469                Err(EvalFailure::Runtime(kind)) => {
1470                    return Err(self.checkpoint_error(kind));
1471                }
1472                Err(failure) => {
1473                    let (value, origin) =
1474                        self.promise_rejection_value(failure)
1475                            .map_err(|failure| match failure {
1476                                EvalFailure::Runtime(kind) => self.checkpoint_error(kind),
1477                                _ => self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1478                                    value: timer.callback,
1479                                }),
1480                            })?;
1481                    report.uncaught.try_reserve(1).map_err(|_| {
1482                        self.checkpoint_error(RuntimeErrorKind::HeapByteLimitExceeded {
1483                            limit: self.limits.max_heap_bytes,
1484                        })
1485                    })?;
1486                    report.uncaught.push(CallbackException { value, origin });
1487                }
1488            }
1489            Ok(report)
1490        })();
1491        self.timer_checkpoint_active = false;
1492        result
1493    }
1494
1495    /// Blocks in the host provider until an expiry report is available.
1496    /// Returns whether at least one live timer became ready.
1497    pub fn wait_for_timer_expiry(&mut self) -> Result<bool, RuntimeError> {
1498        if self.timer_checkpoint_active {
1499            return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1500        }
1501        if !self.ready_timers.is_empty() {
1502            return Ok(true);
1503        }
1504        self.timer_checkpoint_active = true;
1505        let result = (|| {
1506            let wakeup = match self.host.timers() {
1507                Some(provider) => provider.wait_expired(),
1508                None => return Ok(false),
1509            }
1510            .map_err(|error| {
1511                self.checkpoint_error(RuntimeErrorKind::TimerProviderFailure {
1512                    message: error.to_string(),
1513                })
1514            })?;
1515            if let Some(wakeup) = wakeup {
1516                self.promote_timer_wakeup(wakeup);
1517            }
1518            Ok(!self.ready_timers.is_empty())
1519        })();
1520        self.timer_checkpoint_active = false;
1521        result
1522    }
1523
1524    /// Returns whether any machine-owned live timer remains.
1525    #[must_use]
1526    pub fn has_pending_timers(&self) -> bool {
1527        !self.timers.is_empty()
1528    }
1529
1530    /// Drives the automatic event loop to quiescence after synchronous
1531    /// evaluation.
1532    ///
1533    /// Drains every queued microtask first. While a live machine timer
1534    /// remains, waits for one deadline to become ready, runs exactly one
1535    /// timer callback, and drains microtasks again. A timer created by a
1536    /// callback therefore waits for a later timer turn. The first uncaught
1537    /// `queueMicrotask` or timer callback exception stops the loop and becomes
1538    /// [`RuntimeErrorKind::UncaughtThrow`]; a timer exception is converted
1539    /// before any later drain, so its queued microtasks and later timers stay
1540    /// pending. Promise handler throws settle their derived promises and never
1541    /// abort the loop. Fatal runtime failures propagate unchanged.
1542    ///
1543    /// A false wait retries only while the host still exposes the timer
1544    /// capability and the provider reports an armed timer; otherwise the loop
1545    /// fails with [`RuntimeErrorKind::TimerProviderFailure`] instead of
1546    /// spinning. Existing fuel is the only work bound: recursive jobs or
1547    /// timers eventually fail with [`RuntimeErrorKind::FuelExhausted`].
1548    pub fn run_to_quiescence(&mut self) -> Result<(), RuntimeError> {
1549        self.drain_microtasks_automatic()?;
1550        while self.has_pending_timers() {
1551            if !self.wait_for_timer_expiry()? {
1552                let retry = self
1553                    .host
1554                    .timers()
1555                    .is_some_and(|provider| provider.has_pending());
1556                if !retry {
1557                    return Err(self.checkpoint_error(RuntimeErrorKind::TimerProviderFailure {
1558                        message: "the timer provider lost a live machine timer".to_owned(),
1559                    }));
1560                }
1561                continue;
1562            }
1563            let run = self.run_one_expired_timer()?;
1564            if let Some(exception) = run.uncaught.into_iter().next() {
1565                return Err(self.checkpoint_error(RuntimeErrorKind::UncaughtThrow {
1566                    value: exception.value,
1567                    origin: exception.origin,
1568                }));
1569            }
1570            self.drain_microtasks_automatic()?;
1571        }
1572        Ok(())
1573    }
1574
1575    pub(crate) fn schedule_timeout(
1576        &mut self,
1577        callback: Value,
1578        delay_ms: u32,
1579        arguments: Vec<Value>,
1580    ) -> Result<Value, EvalFailure> {
1581        if self.timers.len() >= self.limits.max_timers {
1582            return Err(EvalFailure::Runtime(
1583                RuntimeErrorKind::TimerCapacityExceeded {
1584                    limit: self.limits.max_timers,
1585                },
1586            ));
1587        }
1588        let id = self.next_timer_id.take().ok_or(EvalFailure::Runtime(
1589            RuntimeErrorKind::TimerCapacityExceeded {
1590                limit: self.limits.max_timers,
1591            },
1592        ))?;
1593        self.next_timer_id = id.checked_add(1);
1594        let sequence = self.next_timer_sequence.take().ok_or(EvalFailure::Runtime(
1595            RuntimeErrorKind::TimerCapacityExceeded {
1596                limit: self.limits.max_timers,
1597            },
1598        ))?;
1599        self.next_timer_sequence = sequence.checked_add(1);
1600        let deadline_ms = self
1601            .host
1602            .timers()
1603            .ok_or(EvalFailure::Runtime(
1604                RuntimeErrorKind::TimerProviderFailure {
1605                    message: "timer capability is unavailable".to_owned(),
1606                },
1607            ))?
1608            .schedule(id, delay_ms)
1609            .map_err(|error| {
1610                EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1611                    message: error.to_string(),
1612                })
1613            })?;
1614        let handle = match self.allocate(HeapEntry::Timeout {
1615            id,
1616            properties: PropertyMap::default(),
1617            prototype: Some(self.intrinsics.object_prototype),
1618            extensible: true,
1619        }) {
1620            Ok(handle) => handle,
1621            Err(kind) => {
1622                if let Some(provider) = self.host.timers() {
1623                    let _ = provider.cancel(id);
1624                }
1625                return Err(EvalFailure::Runtime(kind));
1626            }
1627        };
1628        self.timers.insert(
1629            id,
1630            TimerRecord {
1631                callback,
1632                arguments,
1633                handle,
1634                deadline_ms,
1635                sequence,
1636            },
1637        );
1638        Ok(handle)
1639    }
1640
1641    pub(crate) fn clear_timeout(&mut self, handle: Value) -> Result<(), EvalFailure> {
1642        let id = match handle.decode() {
1643            Some(Decoded::Int32(raw)) if (raw as i32) > 0 => Some(u64::from(raw)),
1644            Some(Decoded::Number(number))
1645                if number.is_finite()
1646                    && number > 0.0
1647                    && number.fract() == 0.0
1648                    && number < u64::MAX as f64 =>
1649            {
1650                Some(number as u64)
1651            }
1652            Some(Decoded::HeapRef(_)) => {
1653                self.runtime_slot(handle)
1654                    .ok()
1655                    .flatten()
1656                    .and_then(|index| match &self.heap[index] {
1657                        HeapEntry::Timeout { id, .. } => Some(*id),
1658                        _ => None,
1659                    })
1660            }
1661            _ => None,
1662        };
1663        let Some(id) = id else {
1664            return Ok(());
1665        };
1666        let Some(timer) = self.timers.remove(&id) else {
1667            return Ok(());
1668        };
1669        self.ready_timers
1670            .remove(&(timer.deadline_ms, timer.sequence));
1671        if let Some(provider) = self.host.timers() {
1672            provider.cancel(id).map_err(|error| {
1673                EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1674                    message: error.to_string(),
1675                })
1676            })?;
1677        }
1678        Ok(())
1679    }
1680
1681    fn poll_timer_expiries(&mut self) -> Result<(), RuntimeErrorKind> {
1682        let mut wakeups = Vec::new();
1683        let Some(provider) = self.host.timers() else {
1684            return Ok(());
1685        };
1686        provider.poll_expired(&mut wakeups).map_err(|error| {
1687            RuntimeErrorKind::TimerProviderFailure {
1688                message: error.to_string(),
1689            }
1690        })?;
1691        // One live report authorizes every earlier deadline. Collapse a host
1692        // batch to one watermark instead of rescanning all live timers per report.
1693        if let Some(wakeup) = wakeups
1694            .into_iter()
1695            .filter(|wakeup| self.timers.contains_key(&wakeup.id))
1696            .max_by_key(|wakeup| wakeup.deadline_ms)
1697        {
1698            self.promote_timer_wakeup(wakeup);
1699        }
1700        Ok(())
1701    }
1702
1703    fn promote_timer_wakeup(&mut self, wakeup: TimerWakeup) {
1704        // Unknown IDs are stale cancellation races and carry no authority to
1705        // advance the watermark.
1706        if !self.timers.contains_key(&wakeup.id) {
1707            return;
1708        }
1709        let watermark = self.timer_watermark.map_or(wakeup.deadline_ms, |current| {
1710            current.max(wakeup.deadline_ms)
1711        });
1712        self.timer_watermark = Some(watermark);
1713        for timer in self.timers.values() {
1714            if timer.deadline_ms <= watermark {
1715                self.ready_timers
1716                    .insert((timer.deadline_ms, timer.sequence));
1717            }
1718        }
1719    }
1720
1721    /// Runs queued microtasks in FIFO order until the queue is empty.
1722    ///
1723    /// Jobs queued by another job run in the same checkpoint. Promise callback
1724    /// throws settle their derived promises. Throws from `queueMicrotask`
1725    /// callbacks are returned in [`MicrotaskDrain::uncaught`].
1726    pub fn drain_microtasks(&mut self) -> Result<MicrotaskDrain, RuntimeError> {
1727        self.drain_microtasks_core(true)
1728    }
1729
1730    /// Automatic drain for the event loop: the first uncaught `queueMicrotask`
1731    /// callback exception stops the checkpoint and becomes
1732    /// [`RuntimeErrorKind::UncaughtThrow`], leaving later jobs queued. All
1733    /// other semantics match the public manual checkpoint.
1734    fn drain_microtasks_automatic(&mut self) -> Result<(), RuntimeError> {
1735        let report = self.drain_microtasks_core(false)?;
1736        let Some(exception) = report.uncaught.into_iter().next() else {
1737            return Ok(());
1738        };
1739        Err(self.checkpoint_error(RuntimeErrorKind::UncaughtThrow {
1740            value: exception.value,
1741            origin: exception.origin,
1742        }))
1743    }
1744
1745    /// Shared microtask checkpoint. Both drains share the reentry guard,
1746    /// fuel-before-pop charging, FIFO execution, Promise reaction semantics,
1747    /// fatal-error propagation, and flag cleanup. With `collect_uncaught`
1748    /// every callback exception is collected and later jobs still run;
1749    /// without it the first exception ends the checkpoint with the rest of
1750    /// the queue untouched. The exceptionless path performs no allocation.
1751    fn drain_microtasks_core(
1752        &mut self,
1753        collect_uncaught: bool,
1754    ) -> Result<MicrotaskDrain, RuntimeError> {
1755        if self.microtask_drain_active {
1756            return Err(self.checkpoint_error(RuntimeErrorKind::MicrotaskDrainReentry));
1757        }
1758        self.microtask_drain_active = true;
1759        let result = (|| {
1760            let mut report = MicrotaskDrain::default();
1761            while self.microtasks.front().is_some() {
1762                self.consume_fuel(1)
1763                    .map_err(|kind| self.checkpoint_error(kind))?;
1764                let job = self
1765                    .microtasks
1766                    .pop_front()
1767                    .expect("the queued microtask remains present after fuel charging");
1768                report.executed = report.executed.saturating_add(1);
1769                let Some(exception) = self
1770                    .execute_microtask_job(job)
1771                    .map_err(|kind| self.checkpoint_error(kind))?
1772                else {
1773                    continue;
1774                };
1775                report.uncaught.try_reserve(1).map_err(|_| {
1776                    self.checkpoint_error(RuntimeErrorKind::HeapByteLimitExceeded {
1777                        limit: self.limits.max_heap_bytes,
1778                    })
1779                })?;
1780                report.uncaught.push(exception);
1781                if !collect_uncaught {
1782                    break;
1783                }
1784            }
1785            Ok(report)
1786        })();
1787        self.microtask_drain_active = false;
1788        result
1789    }
1790
1791    fn checkpoint_error(&self, kind: RuntimeErrorKind) -> RuntimeError {
1792        let function = self.module.entry();
1793        let instruction = self.module.functions()[function.get() as usize]
1794            .code()
1795            .first()
1796            .copied()
1797            .unwrap_or(Instruction::Halt);
1798        RuntimeError {
1799            kind,
1800            function,
1801            pc: Pc::new(0),
1802            source: RuntimeSource {
1803                function_name: None,
1804                instruction,
1805            },
1806        }
1807    }
1808
1809    /// Runs one popped microtask job. Promise reaction and thenable jobs
1810    /// settle their derived promises and never surface a callback exception;
1811    /// a `queueMicrotask` callback throw is classified and returned for the
1812    /// checkpoint to collect or stop on.
1813    fn execute_microtask_job(
1814        &mut self,
1815        job: MicrotaskJob,
1816    ) -> Result<Option<CallbackException>, RuntimeErrorKind> {
1817        match job {
1818            MicrotaskJob::Reaction {
1819                reaction,
1820                value,
1821                origin,
1822            } => self
1823                .execute_promise_reaction(reaction, value, origin)
1824                .map(|()| None),
1825            MicrotaskJob::Thenable {
1826                promise,
1827                thenable,
1828                then,
1829            } => self
1830                .execute_thenable_job(promise, thenable, then)
1831                .map(|()| None),
1832            MicrotaskJob::Callback { callback } => self.execute_callback_microtask(callback),
1833        }
1834    }
1835
1836    fn execute_callback_microtask(
1837        &mut self,
1838        callback: Value,
1839    ) -> Result<Option<CallbackException>, RuntimeErrorKind> {
1840        match self.call_value(callback, Value::UNDEFINED, &[]) {
1841            Ok(_) => Ok(None),
1842            Err(EvalFailure::Runtime(kind)) => Err(kind),
1843            Err(failure) => {
1844                let (value, origin) =
1845                    self.promise_rejection_value(failure)
1846                        .map_err(|failure| match failure {
1847                            EvalFailure::Runtime(kind) => kind,
1848                            _ => RuntimeErrorKind::InvalidValue { value: callback },
1849                        })?;
1850                Ok(Some(CallbackException { value, origin }))
1851            }
1852        }
1853    }
1854
1855    fn execute_thenable_job(
1856        &mut self,
1857        promise: Value,
1858        thenable: Value,
1859        then: Value,
1860    ) -> Result<(), RuntimeErrorKind> {
1861        let record = self
1862            .create_promise_resolver(promise)
1863            .map_err(|failure| match failure {
1864                EvalFailure::Runtime(kind) => kind,
1865                _ => RuntimeErrorKind::InvalidValue { value: promise },
1866            })?;
1867        let (resolve_target, reject_target) = self.intrinsics.builtins.promise_resolver_targets();
1868        let resolve = self
1869            .create_promise_resolver_function(resolve_target, record)
1870            .map_err(|failure| match failure {
1871                EvalFailure::Runtime(kind) => kind,
1872                _ => RuntimeErrorKind::InvalidValue { value: record },
1873            })?;
1874        let reject = self
1875            .create_promise_resolver_function(reject_target, record)
1876            .map_err(|failure| match failure {
1877                EvalFailure::Runtime(kind) => kind,
1878                _ => RuntimeErrorKind::InvalidValue { value: record },
1879            })?;
1880        match self.call_value(then, thenable, &[resolve, reject]) {
1881            Ok(_) => Ok(()),
1882            Err(EvalFailure::Runtime(kind)) => Err(kind),
1883            Err(failure) => self
1884                .reject_promise_resolver_failure(record, failure)
1885                .map_err(|failure| match failure {
1886                    EvalFailure::Runtime(kind) => kind,
1887                    _ => RuntimeErrorKind::InvalidValue { value: record },
1888                }),
1889        }
1890    }
1891
1892    fn execute_promise_reaction(
1893        &mut self,
1894        reaction: PromiseReaction,
1895        value: Value,
1896        origin: ThrowOrigin,
1897    ) -> Result<(), RuntimeErrorKind> {
1898        match reaction {
1899            PromiseReaction::Fulfilled { handler, derived } => self.execute_promise_handler(
1900                handler,
1901                derived,
1902                value,
1903                origin,
1904                PromiseCompletion::Fulfilled,
1905            ),
1906            PromiseReaction::Rejected { handler, derived } => self.execute_promise_handler(
1907                handler,
1908                derived,
1909                value,
1910                origin,
1911                PromiseCompletion::Rejected,
1912            ),
1913            PromiseReaction::Finally {
1914                handler,
1915                derived,
1916                completion,
1917            } => self.execute_promise_finally(handler, derived, value, origin, completion),
1918            PromiseReaction::AsyncFulfill { activation } => {
1919                self.resume_async(activation, value, None)
1920            }
1921            PromiseReaction::AsyncReject { activation } => {
1922                self.resume_async(activation, value, Some(origin))
1923            }
1924        }
1925    }
1926
1927    fn execute_promise_handler(
1928        &mut self,
1929        handler: Value,
1930        derived: Value,
1931        value: Value,
1932        origin: ThrowOrigin,
1933        completion: PromiseCompletion,
1934    ) -> Result<(), RuntimeErrorKind> {
1935        if !self.is_callable(handler).map_err(|failure| match failure {
1936            EvalFailure::Runtime(kind) => kind,
1937            _ => RuntimeErrorKind::InvalidValue { value: handler },
1938        })? {
1939            return match completion {
1940                PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1941                PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1942            };
1943        }
1944        match self.call_value(handler, Value::UNDEFINED, &[value]) {
1945            Ok(result) => self.resolve_promise(derived, result),
1946            Err(EvalFailure::Runtime(kind)) => Err(kind),
1947            Err(failure) => self
1948                .reject_promise_failure(derived, failure)
1949                .map_err(|failure| match failure {
1950                    EvalFailure::Runtime(kind) => kind,
1951                    _ => RuntimeErrorKind::InvalidValue { value: derived },
1952                }),
1953        }
1954    }
1955
1956    fn execute_promise_finally(
1957        &mut self,
1958        handler: Value,
1959        derived: Value,
1960        value: Value,
1961        origin: ThrowOrigin,
1962        completion: PromiseCompletion,
1963    ) -> Result<(), RuntimeErrorKind> {
1964        if !self.is_callable(handler).map_err(|failure| match failure {
1965            EvalFailure::Runtime(kind) => kind,
1966            _ => RuntimeErrorKind::InvalidValue { value: handler },
1967        })? {
1968            return match completion {
1969                PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1970                PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1971            };
1972        }
1973        let cleanup = self.create_promise().map_err(|failure| match failure {
1974            EvalFailure::Runtime(kind) => kind,
1975            _ => RuntimeErrorKind::InvalidValue { value: derived },
1976        })?;
1977        let record = self
1978            .create_promise_finally(derived, value, origin, completion)
1979            .map_err(|failure| match failure {
1980                EvalFailure::Runtime(kind) => kind,
1981                _ => RuntimeErrorKind::InvalidValue { value: derived },
1982            })?;
1983        let (on_fulfilled, on_rejected) = self.intrinsics.builtins.promise_finally_targets();
1984        let on_fulfilled = self
1985            .create_promise_resolver_function(on_fulfilled, record)
1986            .map_err(|failure| match failure {
1987                EvalFailure::Runtime(kind) => kind,
1988                _ => RuntimeErrorKind::InvalidValue { value: record },
1989            })?;
1990        let on_rejected = self
1991            .create_promise_resolver_function(on_rejected, record)
1992            .map_err(|failure| match failure {
1993                EvalFailure::Runtime(kind) => kind,
1994                _ => RuntimeErrorKind::InvalidValue { value: record },
1995            })?;
1996        self.promise_then(cleanup, on_fulfilled, on_rejected)
1997            .map_err(|failure| match failure {
1998                EvalFailure::Runtime(kind) => kind,
1999                _ => RuntimeErrorKind::InvalidValue { value: cleanup },
2000            })?;
2001        match self.call_value(handler, Value::UNDEFINED, &[]) {
2002            Ok(result) => self.resolve_promise(cleanup, result),
2003            Err(EvalFailure::Runtime(kind)) => Err(kind),
2004            Err(failure) => self
2005                .reject_promise_failure(cleanup, failure)
2006                .map_err(|failure| match failure {
2007                    EvalFailure::Runtime(kind) => kind,
2008                    _ => RuntimeErrorKind::InvalidValue { value: cleanup },
2009                }),
2010        }
2011    }
2012
2013    pub(crate) fn enqueue_microtask_callback(
2014        &mut self,
2015        callback: Value,
2016    ) -> Result<(), EvalFailure> {
2017        self.ensure_microtask_capacity(1)
2018            .map_err(EvalFailure::Runtime)?;
2019        self.microtasks
2020            .push_back(MicrotaskJob::Callback { callback });
2021        Ok(())
2022    }
2023
2024    fn ensure_microtask_capacity(&mut self, additional: usize) -> Result<(), RuntimeErrorKind> {
2025        if self
2026            .microtasks
2027            .len()
2028            .checked_add(additional)
2029            .is_none_or(|length| length > self.limits.max_microtasks)
2030        {
2031            return Err(RuntimeErrorKind::MicrotaskQueueLimitExceeded {
2032                limit: self.limits.max_microtasks,
2033            });
2034        }
2035        self.microtasks.try_reserve(additional).map_err(|_| {
2036            RuntimeErrorKind::HeapByteLimitExceeded {
2037                limit: self.limits.max_heap_bytes,
2038            }
2039        })
2040    }
2041
2042    pub(crate) fn create_promise(&mut self) -> Result<Value, EvalFailure> {
2043        self.allocate(HeapEntry::Promise {
2044            state: PromiseState::Pending {
2045                fulfill_reactions: Vec::new(),
2046                reject_reactions: Vec::new(),
2047            },
2048            properties: PropertyMap::default(),
2049            prototype: Some(self.intrinsics.builtins.promise_prototype()),
2050            extensible: true,
2051        })
2052        .map_err(EvalFailure::Runtime)
2053    }
2054
2055    pub(crate) fn create_promise_resolver(&mut self, promise: Value) -> Result<Value, EvalFailure> {
2056        self.allocate(HeapEntry::PromiseResolver {
2057            promise,
2058            used: false,
2059        })
2060        .map_err(EvalFailure::Runtime)
2061    }
2062
2063    pub(crate) fn create_promise_resolver_function(
2064        &mut self,
2065        target: Value,
2066        record: Value,
2067    ) -> Result<Value, EvalFailure> {
2068        self.allocate(HeapEntry::NativeFunction {
2069            callable: NativeCallable::Bound(Box::new(BoundCallable {
2070                target,
2071                this_value: Value::UNDEFINED,
2072                arguments: vec![record],
2073            })),
2074            properties: PropertyMap::default(),
2075            extensible: true,
2076        })
2077        .map_err(EvalFailure::Runtime)
2078    }
2079
2080    pub(crate) fn resolve_promise_resolver(
2081        &mut self,
2082        record: Value,
2083        value: Value,
2084    ) -> Result<(), EvalFailure> {
2085        if let Some(promise) = self.use_promise_resolver(record)? {
2086            self.resolve_promise(promise, value)
2087                .map_err(EvalFailure::Runtime)?;
2088        }
2089        Ok(())
2090    }
2091
2092    pub(crate) fn reject_promise_resolver(
2093        &mut self,
2094        record: Value,
2095        reason: Value,
2096    ) -> Result<(), EvalFailure> {
2097        if let Some(promise) = self.use_promise_resolver(record)? {
2098            self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2099                .map_err(EvalFailure::Runtime)?;
2100        }
2101        Ok(())
2102    }
2103
2104    pub(crate) fn reject_promise_resolver_failure(
2105        &mut self,
2106        record: Value,
2107        failure: EvalFailure,
2108    ) -> Result<(), EvalFailure> {
2109        if let Some(promise) = self.use_promise_resolver(record)? {
2110            self.reject_promise_failure(promise, failure)?;
2111        }
2112        Ok(())
2113    }
2114
2115    fn use_promise_resolver(&mut self, record: Value) -> Result<Option<Value>, EvalFailure> {
2116        let index = self
2117            .runtime_slot(record)
2118            .map_err(EvalFailure::Runtime)?
2119            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2120                operation: "Promise resolver",
2121            }))?;
2122        let HeapEntry::PromiseResolver { promise, used } = &mut self.heap[index] else {
2123            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2124                operation: "Promise resolver",
2125            }));
2126        };
2127        if *used {
2128            return Ok(None);
2129        }
2130        *used = true;
2131        Ok(Some(*promise))
2132    }
2133
2134    fn charge_promise_reactions(&mut self, count: usize) -> Result<(), EvalFailure> {
2135        let bytes = std::mem::size_of::<PromiseReaction>()
2136            .checked_mul(count)
2137            .ok_or(EvalFailure::Runtime(
2138                RuntimeErrorKind::HeapByteLimitExceeded {
2139                    limit: self.limits.max_heap_bytes,
2140                },
2141            ))?;
2142        self.charge_heap(bytes).map_err(EvalFailure::Runtime)
2143    }
2144
2145    pub(crate) fn promise_then(
2146        &mut self,
2147        promise: Value,
2148        on_fulfilled: Value,
2149        on_rejected: Value,
2150    ) -> Result<Value, EvalFailure> {
2151        let index = self
2152            .runtime_slot(promise)
2153            .map_err(EvalFailure::Runtime)?
2154            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2155                operation: "Promise.prototype.then",
2156            }))?;
2157        let settled = match &self.heap[index] {
2158            HeapEntry::Promise {
2159                state: PromiseState::Pending { .. },
2160                ..
2161            } => None,
2162            HeapEntry::Promise {
2163                state: PromiseState::Fulfilled { value },
2164                ..
2165            } => Some((true, *value, ThrowOrigin::Bytecode)),
2166            HeapEntry::Promise {
2167                state: PromiseState::Rejected { reason, origin },
2168                ..
2169            } => Some((false, *reason, *origin)),
2170            _ => {
2171                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2172                    operation: "Promise.prototype.then",
2173                }));
2174            }
2175        };
2176        let derived = self.create_promise()?;
2177        if let Some((fulfilled, value, origin)) = settled {
2178            self.ensure_microtask_capacity(1)
2179                .map_err(EvalFailure::Runtime)?;
2180            let reaction = if fulfilled {
2181                PromiseReaction::Fulfilled {
2182                    handler: on_fulfilled,
2183                    derived,
2184                }
2185            } else {
2186                PromiseReaction::Rejected {
2187                    handler: on_rejected,
2188                    derived,
2189                }
2190            };
2191            self.microtasks.push_back(MicrotaskJob::Reaction {
2192                reaction,
2193                value,
2194                origin,
2195            });
2196            return Ok(derived);
2197        }
2198        self.charge_promise_reactions(2)?;
2199        let HeapEntry::Promise {
2200            state:
2201                PromiseState::Pending {
2202                    fulfill_reactions,
2203                    reject_reactions,
2204                },
2205            ..
2206        } = &mut self.heap[index]
2207        else {
2208            unreachable!("pending Promise state was checked before derived allocation");
2209        };
2210        fulfill_reactions.push(PromiseReaction::Fulfilled {
2211            handler: on_fulfilled,
2212            derived,
2213        });
2214        reject_reactions.push(PromiseReaction::Rejected {
2215            handler: on_rejected,
2216            derived,
2217        });
2218        Ok(derived)
2219    }
2220
2221    pub(crate) fn promise_finally(
2222        &mut self,
2223        promise: Value,
2224        handler: Value,
2225    ) -> Result<Value, EvalFailure> {
2226        let index = self
2227            .runtime_slot(promise)
2228            .map_err(EvalFailure::Runtime)?
2229            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2230                operation: "Promise.prototype.finally",
2231            }))?;
2232        let settled = match &self.heap[index] {
2233            HeapEntry::Promise {
2234                state: PromiseState::Pending { .. },
2235                ..
2236            } => None,
2237            HeapEntry::Promise {
2238                state: PromiseState::Fulfilled { value },
2239                ..
2240            } => Some((true, *value, ThrowOrigin::Bytecode)),
2241            HeapEntry::Promise {
2242                state: PromiseState::Rejected { reason, origin },
2243                ..
2244            } => Some((false, *reason, *origin)),
2245            _ => {
2246                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2247                    operation: "Promise.prototype.finally",
2248                }));
2249            }
2250        };
2251        let derived = self.create_promise()?;
2252        let reaction = |completion| PromiseReaction::Finally {
2253            handler,
2254            derived,
2255            completion,
2256        };
2257        if let Some((fulfilled, value, origin)) = settled {
2258            self.ensure_microtask_capacity(1)
2259                .map_err(EvalFailure::Runtime)?;
2260            self.microtasks.push_back(MicrotaskJob::Reaction {
2261                reaction: reaction(if fulfilled {
2262                    PromiseCompletion::Fulfilled
2263                } else {
2264                    PromiseCompletion::Rejected
2265                }),
2266                value,
2267                origin,
2268            });
2269            return Ok(derived);
2270        }
2271        self.charge_promise_reactions(2)?;
2272        let HeapEntry::Promise {
2273            state:
2274                PromiseState::Pending {
2275                    fulfill_reactions,
2276                    reject_reactions,
2277                },
2278            ..
2279        } = &mut self.heap[index]
2280        else {
2281            unreachable!("pending Promise state was checked before derived allocation");
2282        };
2283        fulfill_reactions.push(reaction(PromiseCompletion::Fulfilled));
2284        reject_reactions.push(reaction(PromiseCompletion::Rejected));
2285        Ok(derived)
2286    }
2287
2288    pub(crate) fn create_promise_finally(
2289        &mut self,
2290        derived: Value,
2291        value: Value,
2292        origin: ThrowOrigin,
2293        completion: PromiseCompletion,
2294    ) -> Result<Value, EvalFailure> {
2295        self.allocate(HeapEntry::PromiseFinally {
2296            derived,
2297            value,
2298            origin,
2299            completion,
2300        })
2301        .map_err(EvalFailure::Runtime)
2302    }
2303
2304    pub(crate) fn fulfill_promise_finally(&mut self, record: Value) -> Result<(), EvalFailure> {
2305        let (derived, value, origin, completion) = self.promise_finally_record(record)?;
2306        match completion {
2307            PromiseCompletion::Fulfilled => self
2308                .resolve_promise(derived, value)
2309                .map_err(EvalFailure::Runtime),
2310            PromiseCompletion::Rejected => self
2311                .reject_promise(derived, value, origin)
2312                .map_err(EvalFailure::Runtime),
2313        }
2314    }
2315
2316    pub(crate) fn reject_promise_finally(
2317        &mut self,
2318        record: Value,
2319        reason: Value,
2320    ) -> Result<(), EvalFailure> {
2321        let (derived, _, _, _) = self.promise_finally_record(record)?;
2322        self.reject_promise(derived, reason, ThrowOrigin::Bytecode)
2323            .map_err(EvalFailure::Runtime)
2324    }
2325
2326    fn promise_finally_record(
2327        &mut self,
2328        record: Value,
2329    ) -> Result<(Value, Value, ThrowOrigin, PromiseCompletion), EvalFailure> {
2330        let index = self
2331            .runtime_slot(record)
2332            .map_err(EvalFailure::Runtime)?
2333            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2334                operation: "Promise finally target",
2335            }))?;
2336        let HeapEntry::PromiseFinally {
2337            derived,
2338            value,
2339            origin,
2340            completion,
2341        } = &self.heap[index]
2342        else {
2343            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2344                operation: "Promise finally target",
2345            }));
2346        };
2347        Ok((*derived, *value, *origin, *completion))
2348    }
2349
2350    pub(crate) fn promise_resolve(&mut self, value: Value) -> Result<Value, EvalFailure> {
2351        if matches!(self.runtime_slot(value).map_err(EvalFailure::Runtime)?, Some(index) if matches!(self.heap[index], HeapEntry::Promise { .. }))
2352        {
2353            return Ok(value);
2354        }
2355        let promise = self.create_promise()?;
2356        self.resolve_promise(promise, value)
2357            .map_err(EvalFailure::Runtime)?;
2358        Ok(promise)
2359    }
2360
2361    pub(crate) fn promise_reject(&mut self, reason: Value) -> Result<Value, EvalFailure> {
2362        let promise = self.create_promise()?;
2363        self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2364            .map_err(EvalFailure::Runtime)?;
2365        Ok(promise)
2366    }
2367
2368    pub(crate) fn promise_all(&mut self, iterable: Value) -> Result<Value, EvalFailure> {
2369        let promise = self.create_promise()?;
2370        let aggregate = self
2371            .allocate(HeapEntry::PromiseAll {
2372                promise,
2373                values: Vec::new(),
2374                remaining: 1,
2375                settled: false,
2376            })
2377            .map_err(EvalFailure::Runtime)?;
2378        let iterator = match self.create_iterator(iterable, IteratorKind::Sync) {
2379            Ok(iterator) => iterator,
2380            Err(failure) => {
2381                self.mark_promise_all_settled(aggregate)?;
2382                self.reject_promise_failure(promise, failure)?;
2383                return Ok(promise);
2384            }
2385        };
2386        loop {
2387            let value = match self.iterator_next(iterator) {
2388                Ok((true, _)) => break,
2389                Ok((false, value)) => value,
2390                Err(failure) => {
2391                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2392                }
2393            };
2394            let index = match self.add_promise_all_element(aggregate) {
2395                Ok(index) => index,
2396                Err(failure) => {
2397                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2398                }
2399            };
2400            let element = match self
2401                .allocate(HeapEntry::PromiseAllElement {
2402                    aggregate,
2403                    index,
2404                    called: false,
2405                })
2406                .map_err(EvalFailure::Runtime)
2407            {
2408                Ok(element) => element,
2409                Err(failure) => {
2410                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2411                }
2412            };
2413            let (fulfill_target, reject_target) = self.intrinsics.builtins.promise_all_targets();
2414            let on_fulfilled = match self.create_promise_resolver_function(fulfill_target, element)
2415            {
2416                Ok(callback) => callback,
2417                Err(failure) => {
2418                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2419                }
2420            };
2421            let on_rejected = match self.create_promise_resolver_function(reject_target, element) {
2422                Ok(callback) => callback,
2423                Err(failure) => {
2424                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2425                }
2426            };
2427            let resolved = match self.promise_resolve(value) {
2428                Ok(resolved) => resolved,
2429                Err(failure) => {
2430                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2431                }
2432            };
2433            if let Err(failure) = self.promise_then(resolved, on_fulfilled, on_rejected) {
2434                return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2435            }
2436        }
2437        if let Some(values) = self.finish_promise_all(aggregate)? {
2438            let array = self.create_array(values)?;
2439            self.fulfill_promise(promise, array)
2440                .map_err(EvalFailure::Runtime)?;
2441        }
2442        Ok(promise)
2443    }
2444
2445    fn reject_promise_all_abrupt(
2446        &mut self,
2447        aggregate: Value,
2448        promise: Value,
2449        iterator: Value,
2450        failure: EvalFailure,
2451    ) -> Result<Value, EvalFailure> {
2452        self.mark_promise_all_settled(aggregate)?;
2453        if let Err(EvalFailure::Runtime(kind)) = self.close_iterator(iterator) {
2454            return Err(EvalFailure::Runtime(kind));
2455        }
2456        self.reject_promise_failure(promise, failure)?;
2457        Ok(promise)
2458    }
2459
2460    fn close_iterator(&mut self, iterator: Value) -> Result<(), EvalFailure> {
2461        let Some(index) = self.runtime_slot(iterator).map_err(EvalFailure::Runtime)? else {
2462            return Ok(());
2463        };
2464        let HeapEntry::Iterator {
2465            state: IteratorState::Protocol { iterator, .. },
2466        } = &self.heap[index]
2467        else {
2468            return Ok(());
2469        };
2470        let iterator = *iterator;
2471        let close = self.get_named_property(iterator, "return")?;
2472        if self.is_callable(close)? {
2473            let _ = self.call_value(close, iterator, &[])?;
2474        }
2475        Ok(())
2476    }
2477
2478    fn mark_promise_all_settled(&mut self, aggregate: Value) -> Result<bool, EvalFailure> {
2479        let index = self
2480            .runtime_slot(aggregate)
2481            .map_err(EvalFailure::Runtime)?
2482            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2483                operation: "Promise.all target",
2484            }))?;
2485        let HeapEntry::PromiseAll { settled, .. } = &mut self.heap[index] else {
2486            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2487                operation: "Promise.all target",
2488            }));
2489        };
2490        let changed = !*settled;
2491        *settled = true;
2492        Ok(changed)
2493    }
2494
2495    fn add_promise_all_element(&mut self, aggregate: Value) -> Result<usize, EvalFailure> {
2496        let index = self
2497            .runtime_slot(aggregate)
2498            .map_err(EvalFailure::Runtime)?
2499            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2500                operation: "Promise.all target",
2501            }))?;
2502        let next_remaining = match &self.heap[index] {
2503            HeapEntry::PromiseAll {
2504                remaining,
2505                settled: false,
2506                ..
2507            } => remaining.checked_add(1).ok_or(EvalFailure::Runtime(
2508                RuntimeErrorKind::HeapByteLimitExceeded {
2509                    limit: self.limits.max_heap_bytes,
2510                },
2511            ))?,
2512            HeapEntry::PromiseAll { .. } => {
2513                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2514                    operation: "Promise.all target",
2515                }));
2516            }
2517            _ => {
2518                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2519                    operation: "Promise.all target",
2520                }));
2521            }
2522        };
2523        self.charge_heap(std::mem::size_of::<Value>())
2524            .map_err(EvalFailure::Runtime)?;
2525        let HeapEntry::PromiseAll {
2526            values, remaining, ..
2527        } = &mut self.heap[index]
2528        else {
2529            unreachable!("Promise.all aggregate was checked before its heap charge");
2530        };
2531        values.try_reserve(1).map_err(|_| {
2532            EvalFailure::Runtime(RuntimeErrorKind::HeapByteLimitExceeded {
2533                limit: self.limits.max_heap_bytes,
2534            })
2535        })?;
2536        let index = values.len();
2537        values.push(Value::UNDEFINED);
2538        *remaining = next_remaining;
2539        Ok(index)
2540    }
2541
2542    fn finish_promise_all(&mut self, aggregate: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
2543        let index = self
2544            .runtime_slot(aggregate)
2545            .map_err(EvalFailure::Runtime)?
2546            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2547                operation: "Promise.all target",
2548            }))?;
2549        let HeapEntry::PromiseAll {
2550            values,
2551            remaining,
2552            settled,
2553            ..
2554        } = &mut self.heap[index]
2555        else {
2556            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2557                operation: "Promise.all target",
2558            }));
2559        };
2560        if *settled {
2561            return Ok(None);
2562        }
2563        *remaining -= 1;
2564        if *remaining != 0 {
2565            return Ok(None);
2566        }
2567        *settled = true;
2568        Ok(Some(std::mem::take(values)))
2569    }
2570
2571    pub(crate) fn resolve_promise_all_element(
2572        &mut self,
2573        element: Value,
2574        value: Value,
2575    ) -> Result<(), EvalFailure> {
2576        let index = self
2577            .runtime_slot(element)
2578            .map_err(EvalFailure::Runtime)?
2579            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2580                operation: "Promise.all target",
2581            }))?;
2582        let (aggregate, output_index) = {
2583            let HeapEntry::PromiseAllElement {
2584                aggregate,
2585                index: output_index,
2586                called,
2587            } = &mut self.heap[index]
2588            else {
2589                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2590                    operation: "Promise.all target",
2591                }));
2592            };
2593            if *called {
2594                return Ok(());
2595            }
2596            *called = true;
2597            (*aggregate, *output_index)
2598        };
2599        let aggregate_index = self
2600            .runtime_slot(aggregate)
2601            .map_err(EvalFailure::Runtime)?
2602            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2603                operation: "Promise.all target",
2604            }))?;
2605        let (promise, values) = {
2606            let HeapEntry::PromiseAll {
2607                promise,
2608                values,
2609                remaining,
2610                settled,
2611            } = &mut self.heap[aggregate_index]
2612            else {
2613                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2614                    operation: "Promise.all target",
2615                }));
2616            };
2617            if *settled {
2618                return Ok(());
2619            }
2620            values[output_index] = value;
2621            *remaining -= 1;
2622            let values = (*remaining == 0).then(|| {
2623                *settled = true;
2624                std::mem::take(values)
2625            });
2626            (*promise, values)
2627        };
2628        if let Some(values) = values {
2629            let array = self.create_array(values)?;
2630            self.fulfill_promise(promise, array)
2631                .map_err(EvalFailure::Runtime)?;
2632        }
2633        Ok(())
2634    }
2635
2636    pub(crate) fn reject_promise_all_element(
2637        &mut self,
2638        element: Value,
2639        reason: Value,
2640    ) -> Result<(), EvalFailure> {
2641        let index = self
2642            .runtime_slot(element)
2643            .map_err(EvalFailure::Runtime)?
2644            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2645                operation: "Promise.all target",
2646            }))?;
2647        let aggregate = {
2648            let HeapEntry::PromiseAllElement {
2649                aggregate, called, ..
2650            } = &mut self.heap[index]
2651            else {
2652                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2653                    operation: "Promise.all target",
2654                }));
2655            };
2656            if *called {
2657                return Ok(());
2658            }
2659            *called = true;
2660            *aggregate
2661        };
2662        let aggregate_index = self
2663            .runtime_slot(aggregate)
2664            .map_err(EvalFailure::Runtime)?
2665            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2666                operation: "Promise.all target",
2667            }))?;
2668        let HeapEntry::PromiseAll { promise, .. } = &self.heap[aggregate_index] else {
2669            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2670                operation: "Promise.all target",
2671            }));
2672        };
2673        let promise = *promise;
2674        if !self.mark_promise_all_settled(aggregate)? {
2675            return Ok(());
2676        }
2677        self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2678            .map_err(EvalFailure::Runtime)
2679    }
2680
2681    fn create_array(&mut self, elements: Vec<Value>) -> Result<Value, EvalFailure> {
2682        self.allocate(HeapEntry::Array {
2683            elements,
2684            properties: PropertyMap::default(),
2685            prototype: Some(self.intrinsics.array_prototype),
2686            extensible: true,
2687            length_writable: true,
2688        })
2689        .map_err(EvalFailure::Runtime)
2690    }
2691
2692    fn resolve_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2693        if promise == value {
2694            return self
2695                .reject_promise_failure(
2696                    promise,
2697                    EvalFailure::Throw(ThrowOrigin::TypeError {
2698                        operation: "Promise cannot resolve itself",
2699                    }),
2700                )
2701                .map_err(|failure| match failure {
2702                    EvalFailure::Runtime(kind) => kind,
2703                    _ => RuntimeErrorKind::InvalidValue { value: promise },
2704                });
2705        }
2706        if !self.is_object(value) {
2707            return self.fulfill_promise(promise, value);
2708        }
2709        let then = match self.get_named_property(value, "then") {
2710            Ok(then) => then,
2711            Err(EvalFailure::Runtime(kind)) => return Err(kind),
2712            Err(failure) => {
2713                return self.reject_promise_failure(promise, failure).map_err(
2714                    |failure| match failure {
2715                        EvalFailure::Runtime(kind) => kind,
2716                        _ => RuntimeErrorKind::InvalidValue { value: promise },
2717                    },
2718                );
2719            }
2720        };
2721        if !self.is_callable(then).map_err(|failure| match failure {
2722            EvalFailure::Runtime(kind) => kind,
2723            _ => RuntimeErrorKind::InvalidValue { value: then },
2724        })? {
2725            return self.fulfill_promise(promise, value);
2726        }
2727        self.ensure_microtask_capacity(1)?;
2728        self.microtasks.push_back(MicrotaskJob::Thenable {
2729            promise,
2730            thenable: value,
2731            then,
2732        });
2733        Ok(())
2734    }
2735
2736    fn reject_promise(
2737        &mut self,
2738        promise: Value,
2739        reason: Value,
2740        origin: ThrowOrigin,
2741    ) -> Result<(), RuntimeErrorKind> {
2742        self.settle_promise(promise, PromiseState::Rejected { reason, origin })
2743    }
2744
2745    fn fulfill_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2746        self.settle_promise(promise, PromiseState::Fulfilled { value })
2747    }
2748
2749    fn settle_promise(
2750        &mut self,
2751        promise: Value,
2752        terminal: PromiseState,
2753    ) -> Result<(), RuntimeErrorKind> {
2754        let index = self
2755            .runtime_slot(promise)?
2756            .ok_or(RuntimeErrorKind::InvalidValue { value: promise })?;
2757        let reaction_count = match &self.heap[index] {
2758            HeapEntry::Promise {
2759                state:
2760                    PromiseState::Pending {
2761                        fulfill_reactions,
2762                        reject_reactions,
2763                    },
2764                ..
2765            } => match &terminal {
2766                PromiseState::Fulfilled { .. } => fulfill_reactions.len(),
2767                PromiseState::Rejected { .. } => reject_reactions.len(),
2768                PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2769            },
2770            HeapEntry::Promise { .. } => return Ok(()),
2771            _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2772        };
2773        self.ensure_microtask_capacity(reaction_count)?;
2774        let reactions = match &mut self.heap[index] {
2775            HeapEntry::Promise { state, .. } => {
2776                let reactions = match state {
2777                    PromiseState::Pending {
2778                        fulfill_reactions,
2779                        reject_reactions,
2780                    } => match &terminal {
2781                        PromiseState::Fulfilled { .. } => std::mem::take(fulfill_reactions),
2782                        PromiseState::Rejected { .. } => std::mem::take(reject_reactions),
2783                        PromiseState::Pending { .. } => {
2784                            unreachable!("Promise settlement is terminal")
2785                        }
2786                    },
2787                    _ => return Ok(()),
2788                };
2789                *state = terminal.clone();
2790                reactions
2791            }
2792            _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2793        };
2794        let (value, origin) = match terminal {
2795            PromiseState::Fulfilled { value } => (value, ThrowOrigin::Bytecode),
2796            PromiseState::Rejected { reason, origin } => (reason, origin),
2797            PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2798        };
2799        for reaction in reactions {
2800            self.microtasks.push_back(MicrotaskJob::Reaction {
2801                reaction,
2802                value,
2803                origin,
2804            });
2805        }
2806        Ok(())
2807    }
2808
2809    fn reject_promise_failure(
2810        &mut self,
2811        promise: Value,
2812        failure: EvalFailure,
2813    ) -> Result<(), EvalFailure> {
2814        let (reason, origin) = self.promise_rejection_value(failure)?;
2815        self.reject_promise(promise, reason, origin)
2816            .map_err(EvalFailure::Runtime)
2817    }
2818
2819    fn promise_rejection_value(
2820        &mut self,
2821        failure: EvalFailure,
2822    ) -> Result<(Value, ThrowOrigin), EvalFailure> {
2823        match failure {
2824            EvalFailure::ThrowValue(value) => Ok((value, ThrowOrigin::Bytecode)),
2825            EvalFailure::ThrowValueOrigin { value, origin } => Ok((value, origin)),
2826            EvalFailure::Throw(ThrowOrigin::Bytecode) => {
2827                Ok((Value::UNDEFINED, ThrowOrigin::Bytecode))
2828            }
2829            EvalFailure::Throw(origin) => {
2830                let (name, message) = match origin {
2831                    ThrowOrigin::TypeError { operation } => ("TypeError", operation),
2832                    ThrowOrigin::RangeError { operation } => ("RangeError", operation),
2833                    ThrowOrigin::ReferenceError { operation } => ("ReferenceError", operation),
2834                    ThrowOrigin::UriError { operation } => ("URIError", operation),
2835                    ThrowOrigin::Bytecode => unreachable!("handled above"),
2836                };
2837                let id = self
2838                    .intrinsics
2839                    .builtins
2840                    .id_named(name)
2841                    .expect("error constructor is installed");
2842                match self.throw_error(id, message.to_owned()) {
2843                    EvalFailure::ThrowValue(value) => Ok((value, origin)),
2844                    EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2845                    _ => unreachable!("error materialization returns a thrown value"),
2846                }
2847            }
2848            EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2849        }
2850    }
2851
2852    fn program(&self) -> &Program<Verified> {
2853        self.program
2854            .expect("module registry operations require a whole program")
2855    }
2856
2857    fn module_code(&self, module: ModuleId) -> &Module<Verified> {
2858        let index = module.get() as usize;
2859        if index >= self.dynamic_base {
2860            return &self.dynamic[index - self.dynamic_base].program.modules()[0].code;
2861        }
2862        match self.program {
2863            Some(program) => {
2864                &program
2865                    .module(module)
2866                    .expect("verified module id remains in bounds")
2867                    .code
2868            }
2869            None => self.module,
2870        }
2871    }
2872
2873    fn program_module(&self, module: ModuleId) -> &ProgramModule<Verified> {
2874        let index = module.get() as usize;
2875        if index >= self.dynamic_base {
2876            return &self.dynamic[index - self.dynamic_base].program.modules()[0];
2877        }
2878        self.program
2879            .and_then(|program| program.module(module))
2880            .expect("verified module id remains in bounds")
2881    }
2882
2883    /// Validates host-provided code against this machine's classic-script realm.
2884    fn validate_dynamic_script(program: &Program<Verified>) -> Result<(), &'static str> {
2885        if program.modules().len() != 1 {
2886            return Err("script program must contain exactly one module");
2887        }
2888        if program.entry() != ModuleId::new(0) {
2889            return Err("script program entry must be module zero");
2890        }
2891        let module = &program.modules()[0];
2892        if !module.edges.is_empty() || !module.bindings.is_empty() || !module.exports.is_empty() {
2893            return Err("script program must not contain linkage metadata");
2894        }
2895        if module
2896            .code
2897            .functions()
2898            .iter()
2899            .flat_map(|function| function.code())
2900            .any(|instruction| {
2901                matches!(
2902                    instruction,
2903                    Instruction::Import { .. } | Instruction::Export { .. }
2904                )
2905            })
2906        {
2907            return Err("script program must not contain import or export instructions");
2908        }
2909        Ok(())
2910    }
2911
2912    fn script_heap_cost(program: &Program<Verified>) -> usize {
2913        const MODULE_BYTES: usize = 64;
2914        const FUNCTION_BYTES: usize = 32;
2915        program.modules().iter().fold(0usize, |total, module| {
2916            let constant_bytes = module
2917                .code
2918                .constants()
2919                .iter()
2920                .fold(0usize, |bytes, constant| {
2921                    let payload = match constant {
2922                        Constant::String(text) => text.len_units().saturating_mul(2),
2923                        Constant::BigInt(value) => value.as_str().len(),
2924                        Constant::Number(_)
2925                        | Constant::Int32(_)
2926                        | Constant::Boolean(_)
2927                        | Constant::Null
2928                        | Constant::Undefined => 0,
2929                    };
2930                    bytes
2931                        .saturating_add(std::mem::size_of::<Constant>())
2932                        .saturating_add(payload)
2933                });
2934            let function_bytes =
2935                module
2936                    .code
2937                    .functions()
2938                    .iter()
2939                    .fold(0usize, |bytes, function| {
2940                        bytes
2941                            .saturating_add(FUNCTION_BYTES)
2942                            .saturating_add(
2943                                function
2944                                    .code()
2945                                    .len()
2946                                    .saturating_mul(std::mem::size_of::<Instruction>()),
2947                            )
2948                            .saturating_add(function.handlers().len().saturating_mul(
2949                                std::mem::size_of::<bamts_bytecode::ExceptionHandler>(),
2950                            ))
2951                    });
2952            total
2953                .saturating_add(MODULE_BYTES)
2954                .saturating_add(constant_bytes)
2955                .saturating_add(function_bytes)
2956                .saturating_add(module.code.verification_bytes())
2957        })
2958    }
2959
2960    fn install_script_reserving(
2961        &mut self,
2962        program: Arc<Program<Verified>>,
2963        reserved_slots: usize,
2964        reserved_bytes: usize,
2965    ) -> Result<ModuleId, RuntimeErrorKind> {
2966        Self::validate_dynamic_script(&program)
2967            .map_err(|reason| RuntimeErrorKind::InvalidDynamicScript { reason })?;
2968        if self.dynamic.len() >= self.limits.max_dynamic_modules {
2969            return Err(RuntimeErrorKind::DynamicModuleLimitExceeded {
2970                limit: self.limits.max_dynamic_modules,
2971            });
2972        }
2973        let bytes = Self::script_heap_cost(&program);
2974        let retained_bytes =
2975            bytes
2976                .checked_add(reserved_bytes)
2977                .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
2978                    limit: self.limits.max_heap_bytes,
2979                })?;
2980        self.ensure_allocation_capacity(reserved_slots, retained_bytes)?;
2981        self.charge_heap(bytes)?;
2982        let index = self.dynamic_base.checked_add(self.dynamic.len()).ok_or(
2983            RuntimeErrorKind::DynamicModuleLimitExceeded {
2984                limit: self.limits.max_dynamic_modules,
2985            },
2986        )?;
2987        let module = ModuleId::new(u32::try_from(index).map_err(|_| {
2988            RuntimeErrorKind::DynamicModuleLimitExceeded {
2989                limit: self.limits.max_dynamic_modules,
2990            }
2991        })?);
2992        self.dynamic.push(DynamicModule { program, bytes });
2993        self.registry.modules.push(ModuleInstance {
2994            binding_cells: Vec::new(),
2995            constant_cells: Vec::new(),
2996            namespace: None,
2997            state: ModuleState::Unevaluated,
2998        });
2999        debug_assert_eq!(
3000            self.dynamic
3001                .last()
3002                .expect("installed script remains retained")
3003                .bytes,
3004            bytes
3005        );
3006        debug_assert_eq!(
3007            self.registry.modules.len(),
3008            self.dynamic_base + self.dynamic.len()
3009        );
3010        Ok(module)
3011    }
3012
3013    fn allocate_cell(&mut self, value: Value, module: ModuleId) -> Result<CellId, RuntimeError> {
3014        if self.registry.cells.len() >= self.limits.max_module_cells {
3015            return Err(self.program_error(
3016                module,
3017                RuntimeErrorKind::ModuleCellLimitExceeded {
3018                    limit: self.limits.max_module_cells,
3019                },
3020            ));
3021        }
3022        let id = CellId(self.registry.cells.len());
3023        self.registry.cells.push(Cell { value });
3024        Ok(id)
3025    }
3026
3027    pub(crate) fn instantiate_modules(&mut self) -> Result<(), RuntimeError> {
3028        debug_assert!(
3029            self.dynamic.is_empty(),
3030            "module instantiation precedes dynamic script installation"
3031        );
3032        let program = self
3033            .program
3034            .expect("module registry operations require a whole program");
3035        self.registry.modules = program
3036            .modules()
3037            .iter()
3038            .map(|module| ModuleInstance {
3039                binding_cells: vec![None; module.bindings.len()],
3040                constant_cells: vec![None; module.code.constants().len()],
3041                namespace: None,
3042                state: ModuleState::Unevaluated,
3043            })
3044            .collect();
3045
3046        for module_index in 0..program.modules().len() {
3047            let module_id = ModuleId::new(module_index as u32);
3048            let bindings = program.modules()[module_index].bindings.clone();
3049            for (binding_index, binding) in bindings.into_iter().enumerate() {
3050                let initial = match binding.kind {
3051                    BindingKind::Hoisted => Some(Value::UNDEFINED),
3052                    BindingKind::Lexical => Some(Value::UNINITIALIZED),
3053                    BindingKind::Imported { .. } | BindingKind::Namespace { .. } => None,
3054                };
3055                if let Some(value) = initial {
3056                    let cell = self.allocate_cell(value, module_id)?;
3057                    self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
3058                }
3059            }
3060        }
3061
3062        for module_index in 0..program.modules().len() {
3063            let module_id = ModuleId::new(module_index as u32);
3064            let bindings = program.modules()[module_index].bindings.clone();
3065            for (binding_index, binding) in bindings.into_iter().enumerate() {
3066                let cell = match binding.kind {
3067                    BindingKind::Hoisted | BindingKind::Lexical => continue,
3068                    BindingKind::Imported { edge, name } => {
3069                        let dependency = program.modules()[module_index].edges[edge.get() as usize];
3070                        match dependency.target {
3071                            EdgeTarget::External => {
3072                                let name = self.constant_text(module_id, name).clone();
3073                                self.external_export_cell(module_id, edge, &name)?
3074                            }
3075                            EdgeTarget::Local(target) => match program
3076                                .resolve_export(target, self.constant_text(module_id, name))
3077                            {
3078                                Some(ResolvedExport::Local { module, binding }) => {
3079                                    self.registry.modules[module.get() as usize].binding_cells
3080                                        [binding.get() as usize]
3081                                        .expect("own cells are allocated before aliases link")
3082                                }
3083                                Some(ResolvedExport::External { module, edge, name }) => {
3084                                    let name = self.constant_text(module, name).clone();
3085                                    self.external_export_cell(module, edge, &name)?
3086                                }
3087                                None => {
3088                                    return Err(self.program_error(
3089                                        module_id,
3090                                        RuntimeErrorKind::InvalidVerifiedProgram {
3091                                            module: module_id,
3092                                            instruction: Instruction::Import {
3093                                                dst: bamts_bytecode::Register::new(0),
3094                                                specifier: name,
3095                                            },
3096                                        },
3097                                    ));
3098                                }
3099                            },
3100                        }
3101                    }
3102                    BindingKind::Namespace { edge } => {
3103                        let dependency = program.modules()[module_index].edges[edge.get() as usize];
3104                        let namespace = match dependency.target {
3105                            EdgeTarget::Local(target) => {
3106                                self.module_namespace(target, module_id)?
3107                            }
3108                            EdgeTarget::External => self.external_namespace(module_id, edge)?,
3109                        };
3110                        self.allocate_cell(namespace, module_id)?
3111                    }
3112                };
3113                self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
3114            }
3115        }
3116
3117        for module_index in 0..program.modules().len() {
3118            let bindings = &program.modules()[module_index].bindings;
3119            let constants = program.modules()[module_index].code.constants();
3120            for (constant_index, constant) in constants.iter().enumerate() {
3121                let Constant::String(name) = constant else {
3122                    continue;
3123                };
3124                if let Some((binding_index, _)) =
3125                    bindings.iter().enumerate().find(|(_, binding)| {
3126                        self.constant_text(ModuleId::new(module_index as u32), binding.name) == name
3127                    })
3128                {
3129                    self.registry.modules[module_index].constant_cells[constant_index] =
3130                        self.registry.modules[module_index].binding_cells[binding_index];
3131                }
3132            }
3133        }
3134        Ok(())
3135    }
3136
3137    fn module_namespace(
3138        &mut self,
3139        target: ModuleId,
3140        requester: ModuleId,
3141    ) -> Result<Value, RuntimeError> {
3142        if let Some(value) = self.registry.modules[target.get() as usize].namespace {
3143            return Ok(value);
3144        }
3145        let exported_names: Vec<EcmaString> = self
3146            .program_module(target)
3147            .exports
3148            .iter()
3149            .map(|export| self.constant_text(target, export.name).clone())
3150            .collect();
3151        for exported_name in exported_names {
3152            if let Some(ResolvedExport::External { module, edge, name }) =
3153                self.program().resolve_export(target, &exported_name)
3154            {
3155                let name = self.constant_text(module, name).clone();
3156                self.external_export_cell(module, edge, &name)?;
3157            }
3158        }
3159        let value = self
3160            .allocate(HeapEntry::ModuleNamespace { module: target })
3161            .map_err(|kind| self.program_error(requester, kind))?;
3162        self.registry.modules[target.get() as usize].namespace = Some(value);
3163        Ok(value)
3164    }
3165
3166    fn external_specifier(&self, module: ModuleId, edge: EdgeId) -> Option<EcmaString> {
3167        let dependency = self.program_module(module).edges[edge.get() as usize];
3168        let specifier = self.constant_text(module, dependency.specifier);
3169        self.registry
3170            .external
3171            .contains_key(specifier)
3172            .then(|| specifier.clone())
3173    }
3174
3175    fn external_namespace(
3176        &mut self,
3177        module: ModuleId,
3178        edge: EdgeId,
3179    ) -> Result<Value, RuntimeError> {
3180        let Some(specifier) = self.external_specifier(module, edge) else {
3181            return Err(self.program_error(
3182                module,
3183                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3184            ));
3185        };
3186        let export_names: Vec<EcmaString> = self.registry.external[&specifier]
3187            .exports
3188            .keys()
3189            .cloned()
3190            .collect();
3191        for name in export_names {
3192            self.external_export_cell(module, edge, &name)?;
3193        }
3194        Ok(self.registry.external[&specifier].namespace)
3195    }
3196
3197    fn external_export_cell(
3198        &mut self,
3199        module: ModuleId,
3200        edge: EdgeId,
3201        name: &EcmaString,
3202    ) -> Result<CellId, RuntimeError> {
3203        let Some(specifier) = self.external_specifier(module, edge) else {
3204            return Err(self.program_error(
3205                module,
3206                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3207            ));
3208        };
3209        let Some(export) = self.registry.external[&specifier]
3210            .exports
3211            .get(name)
3212            .copied()
3213        else {
3214            return Err(self.program_error(
3215                module,
3216                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3217            ));
3218        };
3219        if let Some(cell) = export.cell {
3220            return Ok(cell);
3221        }
3222        let cell = self.allocate_cell(export.value, module)?;
3223        self.registry
3224            .external
3225            .get_mut(&specifier)
3226            .expect("external module remains registered")
3227            .exports
3228            .get_mut(name)
3229            .expect("external export remains registered")
3230            .cell = Some(cell);
3231        Ok(cell)
3232    }
3233
3234    pub(crate) fn resolve_import(
3235        &self,
3236        module: ModuleId,
3237        specifier: ConstantId,
3238    ) -> Result<ImportTarget, RuntimeErrorKind> {
3239        let name = self.constant_text(module, specifier);
3240        self.program_module(module)
3241            .edges
3242            .iter()
3243            .enumerate()
3244            .find(|(_, edge)| {
3245                edge.kind.has_dynamic() && self.constant_text(module, edge.specifier) == name
3246            })
3247            .map(|(index, edge)| match edge.target {
3248                EdgeTarget::Local(target) => ImportTarget::Local(target),
3249                EdgeTarget::External => ImportTarget::External(EdgeId::new(index as u32)),
3250            })
3251            .ok_or(RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier })
3252    }
3253
3254    pub(crate) fn imported_namespace(
3255        &mut self,
3256        requester: ModuleId,
3257        target: ImportTarget,
3258    ) -> Result<Value, RuntimeErrorKind> {
3259        match target {
3260            ImportTarget::Local(target) => self.module_namespace(target, requester),
3261            ImportTarget::External(edge) => self.external_namespace(requester, edge),
3262        }
3263        .map_err(|error| error.kind)
3264    }
3265
3266    fn run_import_entry(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3267        let function = self.module_code(module).entry();
3268        let stop_depth = self.frames.len();
3269        self.push_frame(
3270            RuntimeFunction { module, function },
3271            &[],
3272            Value::UNDEFINED,
3273            Value::UNDEFINED,
3274            &[],
3275            None,
3276        )?;
3277        let result = self.run_loop(stop_depth).and_then(|execution| {
3278            execution.map(|_| ()).ok_or_else(|| {
3279                self.program_error(
3280                    module,
3281                    RuntimeErrorKind::InvalidVerifiedProgram {
3282                        module,
3283                        instruction: Instruction::Halt,
3284                    },
3285                )
3286            })
3287        });
3288        if result.is_err() {
3289            self.unwind_frames_to(stop_depth);
3290        }
3291        result
3292    }
3293
3294    fn evaluate_import(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3295        let dependencies = match self.begin_module_evaluation(module)? {
3296            ModuleEvaluation::Cycle => return Ok(()),
3297            ModuleEvaluation::Evaluated(result) => return result,
3298            ModuleEvaluation::Ready(dependencies) => dependencies,
3299        };
3300        for dependency in dependencies {
3301            if let Err(error) = self.evaluate_import(dependency) {
3302                self.settle_module_evaluation(module, Err(error.clone()));
3303                return Err(error);
3304            }
3305        }
3306        let result = self.run_import_entry(module);
3307        self.settle_module_evaluation(module, result.clone());
3308        result
3309    }
3310
3311    fn import_namespace(
3312        &mut self,
3313        requester: ModuleId,
3314        specifier: ConstantId,
3315    ) -> Result<Value, EvalFailure> {
3316        let target = self
3317            .resolve_import(requester, specifier)
3318            .map_err(EvalFailure::Runtime)?;
3319        if let ImportTarget::Local(module) = target {
3320            self.evaluate_import(module)
3321                .map_err(|error| import_failure(&error))?;
3322        }
3323        self.imported_namespace(requester, target)
3324            .map_err(EvalFailure::Runtime)
3325    }
3326    fn evaluate_module(&mut self, module: ModuleId) -> Result<Option<Execution>, RuntimeError> {
3327        let dependencies = match self.begin_module_evaluation(module)? {
3328            ModuleEvaluation::Cycle => return Ok(None),
3329            ModuleEvaluation::Evaluated(result) => return result.map(|()| None),
3330            ModuleEvaluation::Ready(dependencies) => dependencies,
3331        };
3332        for dependency in dependencies {
3333            if let Err(error) = self.evaluate_module(dependency) {
3334                return self.finish_module_evaluation(module, Err(error)).map(Some);
3335            }
3336        }
3337
3338        let code = self.module_code(module);
3339        let function = code.entry().get() as usize;
3340        let metadata = &code.functions()[function];
3341        let register_count = metadata.register_count() as usize;
3342        let result = if self.limits.max_call_depth < 1 {
3343            Err(self.program_error(
3344                module,
3345                RuntimeErrorKind::CallDepthExceeded {
3346                    limit: self.limits.max_call_depth,
3347                },
3348            ))
3349        } else if register_count > self.limits.max_total_registers {
3350            Err(self.program_error(
3351                module,
3352                RuntimeErrorKind::RegisterLimitExceeded {
3353                    limit: self.limits.max_total_registers,
3354                },
3355            ))
3356        } else {
3357            self.frames.push(Frame::new(
3358                RuntimeFunction {
3359                    module,
3360                    function: FunctionId::new(function as u32),
3361                },
3362                metadata,
3363                &[],
3364                Value::UNDEFINED,
3365                Value::UNDEFINED,
3366                &[],
3367                None,
3368            ));
3369            self.live_registers = register_count;
3370            self.run_loop(0).and_then(|execution| {
3371                execution.ok_or_else(|| {
3372                    self.program_error(
3373                        module,
3374                        RuntimeErrorKind::InvalidVerifiedProgram {
3375                            module,
3376                            instruction: Instruction::Halt,
3377                        },
3378                    )
3379                })
3380            })
3381        };
3382        self.finish_module_evaluation(module, result).map(Some)
3383    }
3384
3385    pub(crate) fn begin_module_evaluation(
3386        &mut self,
3387        module: ModuleId,
3388    ) -> Result<ModuleEvaluation, RuntimeError> {
3389        match self.registry.modules[module.get() as usize].state.clone() {
3390            ModuleState::Evaluating => return Ok(ModuleEvaluation::Cycle),
3391            ModuleState::Evaluated(result) => return Ok(ModuleEvaluation::Evaluated(result)),
3392            ModuleState::Unevaluated => {}
3393        }
3394        self.registry.modules[module.get() as usize].state = ModuleState::Evaluating;
3395
3396        let mut dependencies = Vec::new();
3397        for (edge_index, edge) in self
3398            .program_module(module)
3399            .edges
3400            .iter()
3401            .copied()
3402            .enumerate()
3403        {
3404            if !edge.kind.has_static() {
3405                continue;
3406            }
3407            match edge.target {
3408                EdgeTarget::Local(dependency) => dependencies.push(dependency),
3409                EdgeTarget::External
3410                    if self
3411                        .external_specifier(module, EdgeId::new(edge_index as u32))
3412                        .is_some() => {}
3413                EdgeTarget::External => {
3414                    let error = self.program_error(
3415                        module,
3416                        RuntimeErrorKind::ExternalModuleUnavailable {
3417                            module,
3418                            edge: EdgeId::new(edge_index as u32),
3419                        },
3420                    );
3421                    self.settle_module_evaluation(module, Err(error.clone()));
3422                    return Err(error);
3423                }
3424            }
3425        }
3426        Ok(ModuleEvaluation::Ready(dependencies))
3427    }
3428
3429    pub(crate) fn finish_module_evaluation(
3430        &mut self,
3431        module: ModuleId,
3432        result: Result<Execution, RuntimeError>,
3433    ) -> Result<Execution, RuntimeError> {
3434        if result.is_err() {
3435            self.frames.clear();
3436            self.live_registers = 0;
3437        }
3438        let stored = result.as_ref().map(|_| ()).map_err(Clone::clone);
3439        self.settle_module_evaluation(module, stored);
3440        result
3441    }
3442
3443    pub(crate) fn settle_module_evaluation(
3444        &mut self,
3445        module: ModuleId,
3446        result: Result<(), RuntimeError>,
3447    ) {
3448        match result {
3449            Ok(()) => {
3450                self.registry.modules[module.get() as usize].state = ModuleState::Evaluated(Ok(()));
3451            }
3452            Err(error) if matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }) => {
3453                self.registry.modules[module.get() as usize].state =
3454                    ModuleState::Evaluated(Err(error));
3455            }
3456            Err(_) => self.abort_module_evaluation(module),
3457        }
3458    }
3459
3460    pub(crate) fn abort_module_evaluation(&mut self, module: ModuleId) {
3461        if matches!(
3462            self.registry.modules[module.get() as usize].state,
3463            ModuleState::Evaluating
3464        ) {
3465            self.registry.modules[module.get() as usize].state = ModuleState::Unevaluated;
3466        }
3467    }
3468
3469    pub(crate) fn constant_text(&self, module: ModuleId, id: ConstantId) -> &EcmaString {
3470        match &self.module_code(module).constants()[id.get() as usize] {
3471            Constant::String(text) => text,
3472            _ => unreachable!("verified module names are strings"),
3473        }
3474    }
3475
3476    fn program_error(&self, module: ModuleId, kind: RuntimeErrorKind) -> RuntimeError {
3477        let code = self.module_code(module);
3478        let function = code.entry().get() as usize;
3479        let instruction = code.functions()[function]
3480            .code()
3481            .first()
3482            .copied()
3483            .unwrap_or(Instruction::Halt);
3484        RuntimeError {
3485            kind,
3486            function: FunctionId::new(function as u32),
3487            pc: Pc::new(0),
3488            source: RuntimeSource {
3489                function_name: None,
3490                instruction,
3491            },
3492        }
3493    }
3494
3495    fn run_loop(&mut self, stop_depth: usize) -> Result<Option<Execution>, RuntimeError> {
3496        if self.frames.len().saturating_add(self.native_depth) > self.limits.max_call_depth {
3497            return Err(self.error_here(RuntimeErrorKind::CallDepthExceeded {
3498                limit: self.limits.max_call_depth,
3499            }));
3500        }
3501        if self.live_registers > self.limits.max_total_registers {
3502            return Err(self.error_here(RuntimeErrorKind::RegisterLimitExceeded {
3503                limit: self.limits.max_total_registers,
3504            }));
3505        }
3506
3507        loop {
3508            let frame_index = self.frames.len() - 1;
3509            let (module_id, function_index, pc) = {
3510                let frame = &self.frames[frame_index];
3511                (frame.module, frame.function, frame.pc)
3512            };
3513            if let Err(kind) = self.consume_fuel(1) {
3514                return Err(self.error_at(kind, function_index, pc));
3515            }
3516            let instruction = self.module_code(module_id).functions()[function_index].code()[pc];
3517
3518            match instruction {
3519                Instruction::LoadConst { dst, constant } => {
3520                    let value = self.load_constant(constant, function_index, pc)?;
3521                    self.write_register(frame_index, dst.get(), value);
3522                    self.frames[frame_index].pc = pc + 1;
3523                }
3524                Instruction::Move { dst, src } => {
3525                    let value = self.read_register(frame_index, src.get());
3526                    self.write_register(frame_index, dst.get(), value);
3527                    self.frames[frame_index].pc = pc + 1;
3528                }
3529                Instruction::Unary { dst, op, operand } => {
3530                    let value = self.read_register(frame_index, operand.get());
3531                    match self.eval_unary(op, value) {
3532                        Ok(result) => {
3533                            self.write_register(frame_index, dst.get(), result);
3534                            self.frames[frame_index].pc = pc + 1;
3535                        }
3536                        Err(failure) => self.resolve_failure(failure, pc)?,
3537                    }
3538                }
3539                Instruction::Binary {
3540                    dst,
3541                    op,
3542                    left,
3543                    right,
3544                } => {
3545                    let left = self.read_register(frame_index, left.get());
3546                    let right = self.read_register(frame_index, right.get());
3547                    match self.eval_binary(op, left, right) {
3548                        Ok(result) => {
3549                            self.write_register(frame_index, dst.get(), result);
3550                            self.frames[frame_index].pc = pc + 1;
3551                        }
3552                        Err(failure) => self.resolve_failure(failure, pc)?,
3553                    }
3554                }
3555                Instruction::CreateObject { dst } => {
3556                    let value = self
3557                        .allocate(HeapEntry::Object {
3558                            properties: PropertyMap::default(),
3559                            prototype: Some(self.intrinsics.object_prototype),
3560                            boxed_primitive: None,
3561                            extensible: true,
3562                        })
3563                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3564                    self.write_register(frame_index, dst.get(), value);
3565                    self.frames[frame_index].pc = pc + 1;
3566                }
3567                Instruction::CreateArray { dst } => {
3568                    let value = self
3569                        .allocate(HeapEntry::Array {
3570                            elements: Vec::new(),
3571                            properties: PropertyMap::default(),
3572                            prototype: Some(self.intrinsics.array_prototype),
3573                            extensible: true,
3574                            length_writable: true,
3575                        })
3576                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3577                    self.write_register(frame_index, dst.get(), value);
3578                    self.frames[frame_index].pc = pc + 1;
3579                }
3580                Instruction::CreateCell { dst } => {
3581                    let value = self
3582                        .allocate(HeapEntry::Array {
3583                            elements: vec![Value::UNINITIALIZED],
3584                            properties: PropertyMap::default(),
3585                            prototype: Some(self.intrinsics.array_prototype),
3586                            extensible: true,
3587                            length_writable: true,
3588                        })
3589                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3590                    self.write_register(frame_index, dst.get(), value);
3591                    self.frames[frame_index].pc = pc + 1;
3592                }
3593                Instruction::CreateClosure {
3594                    dst,
3595                    function,
3596                    captures,
3597                } => match self.read_captures(frame_index, captures.get(), function) {
3598                    Ok(captures) => {
3599                        let value = self
3600                            .allocate(HeapEntry::Function {
3601                                module: module_id,
3602                                function,
3603                                captures,
3604                                properties: PropertyMap::default(),
3605                                prototype: Some(self.intrinsics.function_prototype),
3606                                extensible: true,
3607                            })
3608                            .map_err(|kind| self.error_at(kind, function_index, pc))?;
3609                        self.write_register(frame_index, dst.get(), value);
3610                        self.frames[frame_index].pc = pc + 1;
3611                    }
3612                    Err(failure) => self.resolve_failure(failure, pc)?,
3613                },
3614                Instruction::GetProperty { dst, object, key } => {
3615                    let object = self.read_register(frame_index, object.get());
3616                    let key_value = self.read_register(frame_index, key.get());
3617                    let key = match self.to_property_key(key_value) {
3618                        Ok(key) => key,
3619                        Err(failure) => {
3620                            self.resolve_failure(failure, pc)?;
3621                            continue;
3622                        }
3623                    };
3624                    match self.resolve_get(object, &key) {
3625                        Ok(GetOutcome::Value(value)) => {
3626                            self.write_register(frame_index, dst.get(), value);
3627                            self.frames[frame_index].pc = pc + 1;
3628                        }
3629                        Ok(GetOutcome::Text(text)) => {
3630                            let value = self
3631                                .allocate(HeapEntry::String(text))
3632                                .map_err(|kind| self.error_at(kind, function_index, pc))?;
3633                            self.write_register(frame_index, dst.get(), value);
3634                            self.frames[frame_index].pc = pc + 1;
3635                        }
3636                        Ok(GetOutcome::Getter(getter)) => {
3637                            self.frames[frame_index].pc = pc + 1;
3638                            self.execute_call(CallRequest {
3639                                callee: getter,
3640                                this_value: object,
3641                                arguments: &[],
3642                                destination: Some(dst.get()),
3643                                call_pc: pc,
3644                                constructed: None,
3645                                new_target: Value::UNDEFINED,
3646                            })?;
3647                        }
3648                        Err(failure) => self.resolve_failure(failure, pc)?,
3649                    }
3650                }
3651                Instruction::SetProperty { object, key, value } => {
3652                    let object = self.read_register(frame_index, object.get());
3653                    let value = self.read_register(frame_index, value.get());
3654                    let key_value = self.read_register(frame_index, key.get());
3655                    let key = match self.to_property_key(key_value) {
3656                        Ok(key) => key,
3657                        Err(failure) => {
3658                            self.resolve_failure(failure, pc)?;
3659                            continue;
3660                        }
3661                    };
3662                    match self.resolve_set(object, key, value) {
3663                        Ok(SetOutcome::Done) => self.frames[frame_index].pc = pc + 1,
3664                        Ok(SetOutcome::Setter(setter)) => {
3665                            self.frames[frame_index].pc = pc + 1;
3666                            self.execute_call(CallRequest {
3667                                callee: setter,
3668                                this_value: object,
3669                                arguments: &[value],
3670                                destination: None,
3671                                call_pc: pc,
3672                                constructed: None,
3673                                new_target: Value::UNDEFINED,
3674                            })?;
3675                        }
3676                        Err(failure) => self.resolve_failure(failure, pc)?,
3677                    }
3678                }
3679                Instruction::DeleteProperty { dst, object, key } => {
3680                    let object = self.read_register(frame_index, object.get());
3681                    let key_value = self.read_register(frame_index, key.get());
3682                    let key = match self.to_property_key(key_value) {
3683                        Ok(key) => key,
3684                        Err(failure) => {
3685                            self.resolve_failure(failure, pc)?;
3686                            continue;
3687                        }
3688                    };
3689                    match self.delete_property(object, &key) {
3690                        Ok(deleted) => {
3691                            self.write_register(frame_index, dst.get(), Value::boolean(deleted));
3692                            self.frames[frame_index].pc = pc + 1;
3693                        }
3694                        Err(failure) => self.resolve_failure(failure, pc)?,
3695                    }
3696                }
3697                Instruction::DefineAccessor {
3698                    object,
3699                    key,
3700                    accessor,
3701                    kind,
3702                } => {
3703                    let object = self.read_register(frame_index, object.get());
3704                    let accessor = self.read_register(frame_index, accessor.get());
3705                    let key_value = self.read_register(frame_index, key.get());
3706                    let key = match self.to_property_key(key_value) {
3707                        Ok(key) => key,
3708                        Err(failure) => {
3709                            self.resolve_failure(failure, pc)?;
3710                            continue;
3711                        }
3712                    };
3713                    match self.define_accessor(object, key, accessor, kind) {
3714                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3715                        Err(failure) => self.resolve_failure(failure, pc)?,
3716                    }
3717                }
3718                Instruction::Call {
3719                    dst,
3720                    callee,
3721                    this_value,
3722                    arguments,
3723                } => {
3724                    let callee = self.read_register(frame_index, callee.get());
3725                    let this_value = self.read_register(frame_index, this_value.get());
3726                    match self.read_arguments(frame_index, arguments.get()) {
3727                        Ok(arguments) => {
3728                            self.frames[frame_index].pc = pc + 1;
3729                            self.execute_call(CallRequest {
3730                                callee,
3731                                this_value,
3732                                arguments: &arguments,
3733                                destination: Some(dst.get()),
3734                                call_pc: pc,
3735                                constructed: None,
3736                                new_target: Value::UNDEFINED,
3737                            })?;
3738                        }
3739                        Err(failure) => self.resolve_failure(failure, pc)?,
3740                    }
3741                }
3742                Instruction::Construct {
3743                    dst,
3744                    callee,
3745                    arguments,
3746                } => {
3747                    let callee = self.read_register(frame_index, callee.get());
3748                    match self.read_arguments(frame_index, arguments.get()) {
3749                        Ok(arguments) => {
3750                            self.frames[frame_index].pc = pc + 1;
3751                            self.execute_construct(callee, &arguments, dst.get(), pc)?;
3752                        }
3753                        Err(failure) => self.resolve_failure(failure, pc)?,
3754                    }
3755                }
3756                Instruction::LoadGlobal { dst, name } => match self.load_global(module_id, name) {
3757                    Ok(Some(value)) => {
3758                        self.write_register(frame_index, dst.get(), value);
3759                        self.frames[frame_index].pc = pc + 1;
3760                    }
3761                    Ok(None) => self.throw(
3762                        Value::UNDEFINED,
3763                        ThrowOrigin::ReferenceError {
3764                            operation: "global is not defined",
3765                        },
3766                        pc,
3767                    )?,
3768                    Err(kind) => return Err(self.error_here_at(kind, pc)),
3769                },
3770                Instruction::StoreGlobal { name, value } => {
3771                    let value = self.read_register(frame_index, value.get());
3772                    match self.store_global(module_id, name, value) {
3773                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3774                        Err(failure) => self.resolve_failure(failure, pc)?,
3775                    }
3776                }
3777                Instruction::TypeOfGlobal { dst, name } => {
3778                    let text = match self.load_global(module_id, name) {
3779                        Ok(value) => value.map_or("undefined", |value| self.type_of(value)),
3780                        Err(kind) => return Err(self.error_here_at(kind, pc)),
3781                    };
3782                    let value = self
3783                        .allocate(HeapEntry::String(EcmaString::from_utf8(text)))
3784                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3785                    self.write_register(frame_index, dst.get(), value);
3786                    self.frames[frame_index].pc = pc + 1;
3787                }
3788                Instruction::LoadThis { dst } => {
3789                    let value = self.frames[frame_index].this_value;
3790                    self.write_register(frame_index, dst.get(), value);
3791                    self.frames[frame_index].pc = pc + 1;
3792                }
3793                Instruction::LoadArguments { dst } => {
3794                    let value = self.materialize_arguments(frame_index, function_index, pc)?;
3795                    self.write_register(frame_index, dst.get(), value);
3796                    self.frames[frame_index].pc = pc + 1;
3797                }
3798                Instruction::LoadNewTarget { dst } => {
3799                    let value = self.frames[frame_index].new_target;
3800                    self.write_register(frame_index, dst.get(), value);
3801                    self.frames[frame_index].pc = pc + 1;
3802                }
3803                Instruction::ArrayPush { array, value } => {
3804                    let array = self.read_register(frame_index, array.get());
3805                    let value = self.read_register(frame_index, value.get());
3806                    match self.array_push(array, value) {
3807                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3808                        Err(failure) => self.resolve_failure(failure, pc)?,
3809                    }
3810                }
3811                Instruction::ArrayExtend { array, iterable } => {
3812                    let array = self.read_register(frame_index, array.get());
3813                    let iterable = self.read_register(frame_index, iterable.get());
3814                    match self.array_extend(array, iterable) {
3815                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3816                        Err(failure) => self.resolve_failure(failure, pc)?,
3817                    }
3818                }
3819                Instruction::ObjectSpread { target, source } => {
3820                    let target = self.read_register(frame_index, target.get());
3821                    let source = self.read_register(frame_index, source.get());
3822                    match self.object_spread(target, source) {
3823                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3824                        Err(failure) => self.resolve_failure(failure, pc)?,
3825                    }
3826                }
3827                Instruction::SetPrototype { object, prototype } => {
3828                    let object = self.read_register(frame_index, object.get());
3829                    let prototype = self.read_register(frame_index, prototype.get());
3830                    match self.set_prototype(object, prototype) {
3831                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3832                        Err(failure) => self.resolve_failure(failure, pc)?,
3833                    }
3834                }
3835                Instruction::CreatePrivateName { dst, description } => {
3836                    let description = self.constant_string(description).clone();
3837                    let value = self
3838                        .allocate(HeapEntry::PrivateName { description })
3839                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3840                    self.write_register(frame_index, dst.get(), value);
3841                    self.frames[frame_index].pc = pc + 1;
3842                }
3843                Instruction::CreateRegExp {
3844                    dst,
3845                    pattern,
3846                    flags,
3847                } => {
3848                    let pattern = self.constant_string(pattern).clone();
3849                    let flags = self.constant_string(flags).clone();
3850                    let value = self
3851                        .allocate(HeapEntry::RegExp {
3852                            pattern,
3853                            flags,
3854                            properties: PropertyMap::default(),
3855                            prototype: Some(self.intrinsics.regexp_prototype()),
3856                            extensible: true,
3857                        })
3858                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3859                    self.write_register(frame_index, dst.get(), value);
3860                    self.frames[frame_index].pc = pc + 1;
3861                }
3862                Instruction::GetIterator { dst, src, kind } => {
3863                    let src = self.read_register(frame_index, src.get());
3864                    match self.create_iterator(src, kind) {
3865                        Ok(value) => {
3866                            self.write_register(frame_index, dst.get(), value);
3867                            self.frames[frame_index].pc = pc + 1;
3868                        }
3869                        Err(failure) => self.resolve_failure(failure, pc)?,
3870                    }
3871                }
3872                Instruction::IteratorNext {
3873                    done,
3874                    value,
3875                    iterator,
3876                } => {
3877                    let iterator = self.read_register(frame_index, iterator.get());
3878                    match self.iterator_next(iterator) {
3879                        Ok((is_done, produced)) => {
3880                            self.write_register(frame_index, done.get(), Value::boolean(is_done));
3881                            self.write_register(frame_index, value.get(), produced);
3882                            self.frames[frame_index].pc = pc + 1;
3883                        }
3884                        Err(failure) => self.resolve_failure(failure, pc)?,
3885                    }
3886                }
3887                Instruction::Jump { target } => {
3888                    self.frames[frame_index].pc = target.get() as usize;
3889                }
3890                Instruction::JumpIfTrue { condition, target } => {
3891                    let condition = self.read_register(frame_index, condition.get());
3892                    self.frames[frame_index].pc = if self.truthy(condition) {
3893                        target.get() as usize
3894                    } else {
3895                        pc + 1
3896                    };
3897                }
3898                Instruction::JumpIfFalse { condition, target } => {
3899                    let condition = self.read_register(frame_index, condition.get());
3900                    self.frames[frame_index].pc = if self.truthy(condition) {
3901                        pc + 1
3902                    } else {
3903                        target.get() as usize
3904                    };
3905                }
3906                Instruction::Return { value } => {
3907                    let value = self.read_register(frame_index, value.get());
3908                    if let Some(execution) = self.complete_frame(value) {
3909                        return Ok(Some(execution));
3910                    }
3911                    if self.frames.len() == stop_depth {
3912                        return Ok(None);
3913                    }
3914                }
3915                Instruction::Throw { value } => {
3916                    let value = self.read_register(frame_index, value.get());
3917                    self.throw(value, ThrowOrigin::Bytecode, pc)?;
3918                }
3919                Instruction::Suspend { src, .. }
3920                    if self
3921                        .async_boundaries
3922                        .last()
3923                        .is_some_and(|boundary| *boundary == frame_index) =>
3924                {
3925                    let awaited = self.read_register(frame_index, src.get());
3926                    let frame = self.frames.pop().expect("async activation is executing");
3927                    self.pending_async_suspend = Some((
3928                        awaited,
3929                        SuspendedActivation {
3930                            target: RuntimeFunction {
3931                                module: frame.module,
3932                                function: FunctionId::new(frame.function as u32),
3933                            },
3934                            registers: frame.registers,
3935                            this_value: frame.this_value,
3936                            new_target: frame.new_target,
3937                            args: frame.args,
3938                            arguments_object: frame.arguments_object,
3939                            resume_token: pc as u32 + 1,
3940                        },
3941                    ));
3942                    return Ok(None);
3943                }
3944                Instruction::Suspend { src, .. }
3945                    if self
3946                        .generator_boundaries
3947                        .last()
3948                        .is_some_and(|boundary| *boundary == frame_index) =>
3949                {
3950                    let value = self.read_register(frame_index, src.get());
3951                    let frame = self
3952                        .frames
3953                        .pop()
3954                        .expect("generator activation is executing");
3955                    self.pending_generator_resume = Some(GeneratorResume::Yield {
3956                        value,
3957                        activation: SuspendedActivation {
3958                            target: RuntimeFunction {
3959                                module: frame.module,
3960                                function: FunctionId::new(frame.function as u32),
3961                            },
3962                            registers: frame.registers,
3963                            this_value: frame.this_value,
3964                            new_target: frame.new_target,
3965                            args: frame.args,
3966                            arguments_object: frame.arguments_object,
3967                            resume_token: pc as u32 + 1,
3968                        },
3969                    });
3970                    return Ok(None);
3971                }
3972                Instruction::Suspend { .. } => {
3973                    self.throw_type("suspend outside an engine-owned event loop", pc)?;
3974                }
3975                Instruction::Import { dst, specifier } => {
3976                    match self.import_namespace(module_id, specifier) {
3977                        Ok(namespace) => {
3978                            self.write_register(frame_index, dst.get(), namespace);
3979                            self.frames[frame_index].pc = pc + 1;
3980                        }
3981                        Err(failure) => self.resolve_failure(failure, pc)?,
3982                    }
3983                }
3984                Instruction::Export { .. } => {
3985                    return Err(self.error_here_at(
3986                        RuntimeErrorKind::InvalidVerifiedProgram {
3987                            module: module_id,
3988                            instruction,
3989                        },
3990                        pc,
3991                    ));
3992                }
3993                Instruction::Halt => {
3994                    if let Some(execution) = self.complete_frame(Value::UNDEFINED) {
3995                        return Ok(Some(execution));
3996                    }
3997                    if self.frames.len() == stop_depth {
3998                        return Ok(None);
3999                    }
4000                }
4001            }
4002        }
4003    }
4004
4005    fn read_register(&self, frame: usize, register: u32) -> Value {
4006        self.frames[frame].registers[register as usize]
4007    }
4008
4009    fn write_register(&mut self, frame: usize, register: u32, value: Value) {
4010        self.frames[frame].registers[register as usize] = value;
4011    }
4012
4013    fn constant_string(&self, id: ConstantId) -> &EcmaString {
4014        self.constant_text(self.active_module_id(), id)
4015    }
4016
4017    fn load_constant(
4018        &mut self,
4019        id: ConstantId,
4020        function: usize,
4021        pc: usize,
4022    ) -> Result<Value, RuntimeError> {
4023        self.load_constant_value(self.active_module_id(), id)
4024            .map_err(|kind| self.error_at(kind, function, pc))
4025    }
4026
4027    fn allocate(&mut self, entry: HeapEntry) -> Result<Value, RuntimeErrorKind> {
4028        let bytes = entry.initial_bytes();
4029        self.ensure_allocation_capacity(1, bytes)?;
4030        self.heap_bytes += bytes;
4031        let slot = self.heap.len() as u32 + 1;
4032        self.heap.push(entry);
4033        let id = SlotId::from_parts(RUNTIME_HEAP_SEGMENT, slot)
4034            .expect("runtime segment and one-based slot are nonzero");
4035        Ok(Value::heap_ref(id))
4036    }
4037
4038    fn ensure_allocation_capacity(
4039        &self,
4040        additional_slots: usize,
4041        additional_bytes: usize,
4042    ) -> Result<(), RuntimeErrorKind> {
4043        let used_slots = self.heap.len().saturating_sub(self.intrinsic_slots);
4044        let slots_fit_limit = used_slots
4045            .checked_add(additional_slots)
4046            .is_some_and(|total| total <= self.limits.max_heap_slots);
4047        let slots_fit_value = self
4048            .heap
4049            .len()
4050            .checked_add(additional_slots)
4051            .is_some_and(|total| total <= u32::MAX as usize);
4052        if !slots_fit_limit || !slots_fit_value {
4053            return Err(RuntimeErrorKind::HeapSlotLimitExceeded {
4054                limit: self.limits.max_heap_slots,
4055            });
4056        }
4057        let bytes_fit = self
4058            .heap_bytes
4059            .checked_add(additional_bytes)
4060            .is_some_and(|total| total <= self.limits.max_heap_bytes);
4061        if !bytes_fit {
4062            return Err(RuntimeErrorKind::HeapByteLimitExceeded {
4063                limit: self.limits.max_heap_bytes,
4064            });
4065        }
4066        Ok(())
4067    }
4068
4069    fn ensure_object_property_capacity(
4070        &self,
4071        property_bytes: usize,
4072    ) -> Result<(), RuntimeErrorKind> {
4073        let bytes =
4074            property_bytes
4075                .checked_add(1)
4076                .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
4077                    limit: self.limits.max_heap_bytes,
4078                })?;
4079        self.ensure_allocation_capacity(1, bytes)
4080    }
4081    fn charge_heap(&mut self, bytes: usize) -> Result<(), RuntimeErrorKind> {
4082        self.ensure_allocation_capacity(0, bytes)?;
4083        self.heap_bytes += bytes;
4084        Ok(())
4085    }
4086
4087    fn runtime_slot(&self, value: Value) -> Result<Option<usize>, RuntimeErrorKind> {
4088        let Some(decoded) = value.decode() else {
4089            return Err(RuntimeErrorKind::InvalidValue { value });
4090        };
4091        let Decoded::HeapRef(id) = decoded else {
4092            return Ok(None);
4093        };
4094        if id.segment() != RUNTIME_HEAP_SEGMENT {
4095            return Err(RuntimeErrorKind::InvalidValue { value });
4096        }
4097        let index = id.slot() as usize - 1;
4098        if index >= self.heap.len() {
4099            return Err(RuntimeErrorKind::InvalidRuntimeHeapReference { slot: id.slot() });
4100        }
4101        Ok(Some(index))
4102    }
4103
4104    fn active_module_id(&self) -> ModuleId {
4105        self.frames
4106            .last()
4107            .map_or(ModuleId::new(0), |frame| frame.module)
4108    }
4109
4110    pub(crate) fn load_global(
4111        &self,
4112        module: ModuleId,
4113        name: ConstantId,
4114    ) -> Result<Option<Value>, RuntimeErrorKind> {
4115        if let Some(cell) = self
4116            .registry
4117            .modules
4118            .get(module.get() as usize)
4119            .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4120            .copied()
4121            .flatten()
4122        {
4123            let value = self.registry.cells[cell.0].value;
4124            if value.is_uninitialized() {
4125                let binding = self.registry.modules[module.get() as usize]
4126                    .binding_cells
4127                    .iter()
4128                    .position(|candidate| *candidate == Some(cell))
4129                    .map(|index| BindingId::new(index as u32))
4130                    .expect("linked cell belongs to a binding");
4131                return Err(RuntimeErrorKind::TemporalDeadZone { module, binding });
4132            }
4133            return Ok(Some(value));
4134        }
4135        Ok(self.resolve_global_binding(self.constant_text(module, name)))
4136    }
4137
4138    pub(crate) fn store_global(
4139        &mut self,
4140        module: ModuleId,
4141        name: ConstantId,
4142        value: Value,
4143    ) -> Result<(), EvalFailure> {
4144        let cell = self
4145            .registry
4146            .modules
4147            .get(module.get() as usize)
4148            .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4149            .copied()
4150            .flatten();
4151        if let Some(cell) = cell {
4152            let binding = self.registry.modules[module.get() as usize]
4153                .binding_cells
4154                .iter()
4155                .position(|candidate| *candidate == Some(cell))
4156                .expect("mapped module cell belongs to a binding");
4157            if matches!(
4158                self.program_module(module).bindings[binding].kind,
4159                BindingKind::Imported { .. } | BindingKind::Namespace { .. }
4160            ) {
4161                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4162                    operation: "assign to immutable module binding",
4163                }));
4164            }
4165            self.registry.cells[cell.0].value = value;
4166        } else {
4167            let name = self.constant_text(module, name).to_owned();
4168            if let Some(global_this) = self.intrinsics.global("globalThis") {
4169                let key = PropertyKey::Named(name.clone());
4170                if matches!(
4171                    self.own_descriptor(global_this, &key)?,
4172                    Some(
4173                        Property::Data {
4174                            writable: false,
4175                            ..
4176                        } | Property::Accessor { setter: None, .. }
4177                    )
4178                ) {
4179                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4180                        operation: "assign to non-writable global property",
4181                    }));
4182                }
4183            }
4184            self.globals.insert(name, value);
4185        }
4186        Ok(())
4187    }
4188
4189    /// Resolves a true realm global after module bindings have been considered.
4190    fn resolve_global_binding(&self, name: &EcmaString) -> Option<Value> {
4191        self.globals.get(name).copied().or_else(|| {
4192            self.intrinsics
4193                .globals
4194                .iter()
4195                .find_map(|(candidate, value)| (candidate == name).then_some(*value))
4196        })
4197    }
4198
4199    /// Classifies a callee into the shared dispatch categories.
4200    fn callee_kind(&self, callee: Value) -> Result<CalleeKind, RuntimeErrorKind> {
4201        match self.runtime_slot(callee)? {
4202            Some(index) => match &self.heap[index] {
4203                HeapEntry::Function {
4204                    module,
4205                    function,
4206                    captures,
4207                    ..
4208                } => Ok(CalleeKind::Runtime {
4209                    target: RuntimeFunction {
4210                        module: *module,
4211                        function: *function,
4212                    },
4213                    captures: captures.clone(),
4214                }),
4215                HeapEntry::NativeFunction { callable, .. } => match callable {
4216                    NativeCallable::Builtin(id) => Ok(CalleeKind::Builtin { id: *id }),
4217                    NativeCallable::Bound(_) => Ok(CalleeKind::Bound),
4218                },
4219                _ => Ok(CalleeKind::NotCallable),
4220            },
4221            None => Ok(CalleeKind::NotCallable),
4222        }
4223    }
4224
4225    pub(crate) fn flatten_bound(
4226        &self,
4227        callee: Value,
4228        this_value: Value,
4229        arguments: &[Value],
4230    ) -> Result<BoundCall, RuntimeErrorKind> {
4231        let mut target = callee;
4232        let mut receiver = this_value;
4233        let mut segments = Vec::new();
4234        let mut total = arguments.len();
4235        while let Some(index) = self.runtime_slot(target)? {
4236            let HeapEntry::NativeFunction {
4237                callable: NativeCallable::Bound(bound),
4238                ..
4239            } = &self.heap[index]
4240            else {
4241                break;
4242            };
4243            total = total.checked_add(bound.arguments.len()).ok_or(
4244                RuntimeErrorKind::ArgumentLimitExceeded {
4245                    limit: self.limits.max_argument_count,
4246                    requested: u32::MAX,
4247                },
4248            )?;
4249            if total > self.limits.max_argument_count as usize {
4250                return Err(RuntimeErrorKind::ArgumentLimitExceeded {
4251                    limit: self.limits.max_argument_count,
4252                    requested: u32::try_from(total).unwrap_or(u32::MAX),
4253                });
4254            }
4255            segments.push(bound.arguments.as_slice());
4256            receiver = bound.this_value;
4257            target = bound.target;
4258        }
4259        let mut flattened = Vec::with_capacity(total);
4260        for segment in segments.iter().rev() {
4261            flattened.extend_from_slice(segment);
4262        }
4263        flattened.extend_from_slice(arguments);
4264        Ok(BoundCall {
4265            target,
4266            this_value: receiver,
4267            arguments: flattened,
4268        })
4269    }
4270
4271    fn bound_target(&self, mut value: Value) -> Result<Value, RuntimeErrorKind> {
4272        loop {
4273            let Some(index) = self.runtime_slot(value)? else {
4274                return Ok(value);
4275            };
4276            let HeapEntry::NativeFunction {
4277                callable: NativeCallable::Bound(bound),
4278                ..
4279            } = &self.heap[index]
4280            else {
4281                return Ok(value);
4282            };
4283            value = bound.target;
4284        }
4285    }
4286
4287    /// Materializes a constant into an ABI value, interning strings and bigints
4288    /// into the slot heap. Shared with the native engine.
4289    pub(crate) fn load_constant_value(
4290        &mut self,
4291        module: ModuleId,
4292        id: ConstantId,
4293    ) -> Result<Value, RuntimeErrorKind> {
4294        match &self.module_code(module).constants()[id.get() as usize] {
4295            Constant::String(text) => self.allocate(HeapEntry::String(text.clone())),
4296            Constant::BigInt(value) => self.allocate(HeapEntry::BigInt(value.as_str().to_owned())),
4297            constant => Ok(constant_value(constant).expect("non-heap constant")),
4298        }
4299    }
4300
4301    /// Reads a call/construct arguments array from a register: it must hold a
4302    /// runtime array, whose length is capped by `max_argument_count`.
4303    fn read_arguments(&self, frame: usize, register: u32) -> Result<Vec<Value>, EvalFailure> {
4304        let value = self.read_register(frame, register);
4305        self.arguments_from_array(value)
4306    }
4307
4308    /// Validates a call/construct arguments array value: it must be a runtime
4309    /// array whose length is capped by `max_argument_count`, with holes read as
4310    /// `undefined`. Shared with the native engine.
4311    fn arguments_from_array(&self, arguments: Value) -> Result<Vec<Value>, EvalFailure> {
4312        match self.runtime_slot(arguments).map_err(EvalFailure::Runtime)? {
4313            Some(index) => match &self.heap[index] {
4314                HeapEntry::Array { elements, .. } => {
4315                    if elements.len() as u64 > u64::from(self.limits.max_argument_count) {
4316                        return Err(EvalFailure::Runtime(
4317                            RuntimeErrorKind::ArgumentLimitExceeded {
4318                                limit: self.limits.max_argument_count,
4319                                requested: u32::try_from(elements.len()).unwrap_or(u32::MAX),
4320                            },
4321                        ));
4322                    }
4323                    Ok(elements
4324                        .iter()
4325                        .map(|value| {
4326                            if *value == Value::HOLE {
4327                                Value::UNDEFINED
4328                            } else {
4329                                *value
4330                            }
4331                        })
4332                        .collect())
4333                }
4334                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4335                    operation: "call arguments are not an array",
4336                })),
4337            },
4338            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4339                operation: "call arguments are not an array",
4340            })),
4341        }
4342    }
4343
4344    /// Reads a `CreateClosure` captures array: it must hold a runtime array whose
4345    /// length matches the target function's capture count.
4346    fn read_captures(
4347        &self,
4348        frame: usize,
4349        register: u32,
4350        function: FunctionId,
4351    ) -> Result<Vec<Value>, EvalFailure> {
4352        let value = self.read_register(frame, register);
4353        self.captures_from_array(self.active_module_id(), value, function)
4354    }
4355
4356    /// Validates a `CreateClosure` captures array value: it must be a runtime
4357    /// array whose length matches the target function's capture count, with
4358    /// holes read as `undefined`. Shared with the native engine.
4359    pub(crate) fn captures_from_array(
4360        &self,
4361        module: ModuleId,
4362        captures: Value,
4363        function: FunctionId,
4364    ) -> Result<Vec<Value>, EvalFailure> {
4365        let expected =
4366            self.module_code(module).functions()[function.get() as usize].capture_count() as usize;
4367        match self.runtime_slot(captures).map_err(EvalFailure::Runtime)? {
4368            Some(index) => match &self.heap[index] {
4369                HeapEntry::Array { elements, .. } => {
4370                    if elements.len() != expected {
4371                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4372                            operation: "closure capture array arity",
4373                        }));
4374                    }
4375                    Ok(elements
4376                        .iter()
4377                        .map(|value| {
4378                            if *value == Value::HOLE {
4379                                Value::UNDEFINED
4380                            } else {
4381                                *value
4382                            }
4383                        })
4384                        .collect())
4385                }
4386                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4387                    operation: "closure captures are not an array",
4388                })),
4389            },
4390            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4391                operation: "closure captures are not an array",
4392            })),
4393        }
4394    }
4395
4396    pub(crate) fn materialize_arguments(
4397        &mut self,
4398        frame: usize,
4399        function: usize,
4400        pc: usize,
4401    ) -> Result<Value, RuntimeError> {
4402        if let Some(existing) = self.frames[frame].arguments_object {
4403            return Ok(existing);
4404        }
4405        let args = self.frames[frame].args.clone();
4406        let value = self
4407            .allocate(HeapEntry::Array {
4408                elements: args,
4409                properties: PropertyMap::default(),
4410                prototype: Some(self.intrinsics.array_prototype),
4411                extensible: true,
4412                length_writable: true,
4413            })
4414            .map_err(|kind| self.error_at(kind, function, pc))?;
4415        self.frames[frame].arguments_object = Some(value);
4416        Ok(value)
4417    }
4418
4419    fn push_frame(
4420        &mut self,
4421        target: RuntimeFunction,
4422        captures: &[Value],
4423        this_value: Value,
4424        new_target: Value,
4425        arguments: &[Value],
4426        return_to: Option<ReturnTo>,
4427    ) -> Result<(), RuntimeError> {
4428        let function_index = target.function.get() as usize;
4429        let metadata = &self.module_code(target.module).functions()[function_index];
4430        let limit_error = |kind| match (self.frames.last(), return_to) {
4431            (Some(caller), Some(return_to)) => {
4432                self.error_at_in_module(kind, caller.module, caller.function, return_to.call_pc)
4433            }
4434            (_, None) => self.error_at_in_module(kind, target.module, function_index, 0),
4435            (None, Some(_)) => unreachable!("a returning frame has a caller"),
4436        };
4437        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4438            return Err(limit_error(RuntimeErrorKind::CallDepthExceeded {
4439                limit: self.limits.max_call_depth,
4440            }));
4441        }
4442        let next_registers = metadata.register_count() as usize;
4443        if self.live_registers.saturating_add(next_registers) > self.limits.max_total_registers {
4444            return Err(limit_error(RuntimeErrorKind::RegisterLimitExceeded {
4445                limit: self.limits.max_total_registers,
4446            }));
4447        }
4448        let frame = Frame::new(
4449            target, metadata, captures, this_value, new_target, arguments, return_to,
4450        );
4451        self.live_registers += next_registers;
4452        self.frames.push(frame);
4453        Ok(())
4454    }
4455
4456    pub(crate) fn consume_fuel(&mut self, amount: u64) -> Result<(), RuntimeErrorKind> {
4457        if self.fuel < amount {
4458            self.fuel = 0;
4459            return Err(RuntimeErrorKind::FuelExhausted {
4460                limit: self.limits.fuel,
4461            });
4462        }
4463        self.fuel -= amount;
4464        Ok(())
4465    }
4466
4467    pub(crate) fn reserve_native_activation(
4468        &mut self,
4469        register_count: usize,
4470    ) -> Result<(), RuntimeErrorKind> {
4471        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4472            return Err(RuntimeErrorKind::CallDepthExceeded {
4473                limit: self.limits.max_call_depth,
4474            });
4475        }
4476        if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4477            return Err(RuntimeErrorKind::RegisterLimitExceeded {
4478                limit: self.limits.max_total_registers,
4479            });
4480        }
4481        self.native_depth += 1;
4482        self.live_registers += register_count;
4483        Ok(())
4484    }
4485
4486    pub(crate) fn release_native_activation(&mut self, register_count: usize) {
4487        self.native_depth -= 1;
4488        self.live_registers -= register_count;
4489    }
4490
4491    pub(crate) fn reserve_suspended_activation_registers(
4492        &mut self,
4493        register_count: usize,
4494    ) -> Result<(), RuntimeErrorKind> {
4495        if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4496            return Err(RuntimeErrorKind::RegisterLimitExceeded {
4497                limit: self.limits.max_total_registers,
4498            });
4499        }
4500        self.live_registers += register_count;
4501        Ok(())
4502    }
4503
4504    pub(crate) fn release_suspended_activation_registers(&mut self, register_count: usize) {
4505        self.live_registers -= register_count;
4506    }
4507
4508    pub(crate) fn enter_native_generator(&mut self) -> Result<(), RuntimeErrorKind> {
4509        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4510            return Err(RuntimeErrorKind::CallDepthExceeded {
4511                limit: self.limits.max_call_depth,
4512            });
4513        }
4514        self.native_depth += 1;
4515        Ok(())
4516    }
4517
4518    pub(crate) fn leave_native_generator(&mut self) {
4519        self.native_depth -= 1;
4520    }
4521
4522    fn execute_call(&mut self, request: CallRequest<'_>) -> Result<(), RuntimeError> {
4523        let CallRequest {
4524            callee,
4525            this_value,
4526            arguments,
4527            destination,
4528            call_pc,
4529            constructed,
4530            new_target,
4531        } = request;
4532        let mut callee = callee;
4533        let mut this_value = this_value;
4534        let mut arguments = Cow::Borrowed(arguments);
4535        loop {
4536            match self.callee_kind(callee) {
4537                Ok(CalleeKind::Runtime { target, captures }) => {
4538                    let flags = self.module_code(target.module).functions()
4539                        [target.function.get() as usize]
4540                        .flags();
4541                    if flags.is_generator && !flags.is_async {
4542                        let generator = self
4543                            .create_generator(GeneratorStart {
4544                                target,
4545                                captures,
4546                                this_value,
4547                                new_target,
4548                                args: arguments.as_ref().to_vec(),
4549                            })
4550                            .map_err(|kind| self.error_here_at(kind, call_pc))?;
4551                        if let Some(register) = destination {
4552                            self.write_register(self.frames.len() - 1, register, generator);
4553                        }
4554                        return Ok(());
4555                    }
4556                    if flags.is_async && !flags.is_generator {
4557                        return match self.start_async_call(
4558                            target,
4559                            &captures,
4560                            this_value,
4561                            new_target,
4562                            arguments.as_ref(),
4563                        ) {
4564                            Ok(promise) => {
4565                                if let Some(register) = destination {
4566                                    self.write_register(self.frames.len() - 1, register, promise);
4567                                }
4568                                Ok(())
4569                            }
4570                            Err(failure) => self.resolve_failure(failure, call_pc),
4571                        };
4572                    }
4573                    return self.push_frame(
4574                        target,
4575                        &captures,
4576                        this_value,
4577                        new_target,
4578                        arguments.as_ref(),
4579                        Some(ReturnTo {
4580                            destination: destination.map(|register| register as usize),
4581                            call_pc,
4582                            constructed,
4583                        }),
4584                    );
4585                }
4586                Ok(CalleeKind::Builtin { id }) => {
4587                    match self.call_builtin(id, this_value, arguments.as_ref(), false) {
4588                        Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4589                            if let Some(register) = destination {
4590                                self.write_register(self.frames.len() - 1, register, value);
4591                            }
4592                            return Ok(());
4593                        }
4594                        Ok(intrinsics::BuiltinOutcome::Call {
4595                            callee: next,
4596                            this_value: next_this,
4597                            arguments: next_arguments,
4598                        }) => {
4599                            callee = next;
4600                            this_value = next_this;
4601                            arguments = Cow::Owned(next_arguments);
4602                        }
4603                        Ok(intrinsics::BuiltinOutcome::GeneratorNext {
4604                            generator,
4605                            resume_value,
4606                        }) => match self.resume_generator(generator, resume_value) {
4607                            Ok(value) => {
4608                                if let Some(register) = destination {
4609                                    self.write_register(self.frames.len() - 1, register, value);
4610                                }
4611                                return Ok(());
4612                            }
4613                            Err(failure) => return self.resolve_failure(failure, call_pc),
4614                        },
4615                        Ok(intrinsics::BuiltinOutcome::ConstructCall { .. }) => {
4616                            return self.throw_type("call", call_pc);
4617                        }
4618                        Err(failure) => return self.resolve_failure(failure, call_pc),
4619                    }
4620                }
4621                Ok(CalleeKind::Bound) => {
4622                    let bound = self
4623                        .flatten_bound(callee, this_value, arguments.as_ref())
4624                        .map_err(|kind| self.error_here_at(kind, call_pc))?;
4625                    callee = bound.target;
4626                    if constructed.is_none() {
4627                        this_value = bound.this_value;
4628                    }
4629                    arguments = Cow::Owned(bound.arguments);
4630                }
4631                Ok(CalleeKind::NotCallable) => return self.throw_type("call", call_pc),
4632                Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4633            }
4634        }
4635    }
4636
4637    fn execute_construct(
4638        &mut self,
4639        callee: Value,
4640        arguments: &[Value],
4641        destination: u32,
4642        call_pc: usize,
4643    ) -> Result<(), RuntimeError> {
4644        let mut callee = callee;
4645        let mut arguments = Cow::Borrowed(arguments);
4646        if matches!(self.callee_kind(callee), Ok(CalleeKind::Bound)) {
4647            let bound = self
4648                .flatten_bound(callee, Value::UNDEFINED, arguments.as_ref())
4649                .map_err(|kind| self.error_here_at(kind, call_pc))?;
4650            callee = bound.target;
4651            arguments = Cow::Owned(bound.arguments);
4652        }
4653        let index = match self.runtime_slot(callee) {
4654            Ok(Some(index)) => index,
4655            Ok(None) => return self.throw_type("construct", call_pc),
4656            Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4657        };
4658        let builtin = match &self.heap[index] {
4659            HeapEntry::NativeFunction {
4660                callable: NativeCallable::Builtin(id),
4661                ..
4662            } => Some(*id),
4663            _ => None,
4664        };
4665        if let Some(id) = builtin {
4666            return match self.call_builtin(id, Value::UNDEFINED, arguments.as_ref(), true) {
4667                Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4668                    self.write_register(self.frames.len() - 1, destination, value);
4669                    Ok(())
4670                }
4671                Ok(
4672                    intrinsics::BuiltinOutcome::Call { .. }
4673                    | intrinsics::BuiltinOutcome::GeneratorNext { .. },
4674                ) => self.throw_type("construct", call_pc),
4675                Ok(intrinsics::BuiltinOutcome::ConstructCall {
4676                    callee: continuation,
4677                    this_value,
4678                    arguments: continuation_arguments,
4679                    prototype,
4680                }) => {
4681                    let object = self
4682                        .allocate_constructed_receiver_with(prototype)
4683                        .map_err(|kind| self.error_here_at(kind, call_pc))?;
4684                    self.execute_call(CallRequest {
4685                        callee: continuation,
4686                        this_value,
4687                        arguments: &continuation_arguments,
4688                        destination: Some(destination),
4689                        call_pc,
4690                        constructed: Some(object),
4691                        new_target: callee,
4692                    })
4693                }
4694                Err(failure) => self.resolve_failure(failure, call_pc),
4695            };
4696        }
4697        if !matches!(
4698            self.heap[index],
4699            HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
4700        ) {
4701            return self.throw_type("construct", call_pc);
4702        }
4703        if let HeapEntry::Function {
4704            module, function, ..
4705        } = self.heap[index]
4706        {
4707            if self.module_code(module).functions()[function.get() as usize]
4708                .flags()
4709                .is_async
4710            {
4711                return self.throw_type("construct", call_pc);
4712            }
4713        }
4714        let object = self
4715            .allocate_constructed_receiver(callee)
4716            .map_err(|kind| self.error_here_at(kind, call_pc))?;
4717        self.execute_call(CallRequest {
4718            callee,
4719            this_value: object,
4720            arguments: arguments.as_ref(),
4721            destination: Some(destination),
4722            call_pc,
4723            constructed: Some(object),
4724            new_target: callee,
4725        })
4726    }
4727
4728    fn constructed_prototype(&self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4729        let index = self
4730            .runtime_slot(callee)?
4731            .ok_or(RuntimeErrorKind::InvalidValue { value: callee })?;
4732        Ok(match self.own_data_property(index, "prototype") {
4733            Some(value) if self.is_object(value) => value,
4734            _ => self.intrinsics.object_prototype,
4735        })
4736    }
4737
4738    fn allocate_constructed_receiver(&mut self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4739        let prototype = self.constructed_prototype(callee)?;
4740        self.allocate_constructed_receiver_with(prototype)
4741    }
4742
4743    fn allocate_constructed_receiver_with(
4744        &mut self,
4745        prototype: Value,
4746    ) -> Result<Value, RuntimeErrorKind> {
4747        self.allocate(HeapEntry::Object {
4748            properties: PropertyMap::default(),
4749            prototype: Some(prototype),
4750            boxed_primitive: None,
4751            extensible: true,
4752        })
4753    }
4754
4755    pub(crate) fn array_elements(&self, value: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
4756        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4757            return Ok(None);
4758        };
4759        match &self.heap[index] {
4760            HeapEntry::Array { elements, .. } => Ok(Some(elements.clone())),
4761            _ => Ok(None),
4762        }
4763    }
4764
4765    pub(crate) fn array_length(&self, value: Value) -> Result<usize, EvalFailure> {
4766        self.array_elements(value)?
4767            .map(|elements| elements.len())
4768            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
4769                operation: "array method called on incompatible receiver",
4770            }))
4771    }
4772
4773    pub(crate) fn replace_array_elements(
4774        &mut self,
4775        value: Value,
4776        elements: Vec<Value>,
4777    ) -> Result<(), EvalFailure> {
4778        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4779            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4780                operation: "array method called on incompatible receiver",
4781            }));
4782        };
4783        let HeapEntry::Array {
4784            elements: current, ..
4785        } = &mut self.heap[index]
4786        else {
4787            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4788                operation: "array method called on incompatible receiver",
4789            }));
4790        };
4791        *current = elements;
4792        Ok(())
4793    }
4794
4795    pub(crate) fn string_value(&self, value: Value) -> Option<EcmaString> {
4796        let index = self.runtime_slot(value).ok().flatten()?;
4797        match &self.heap[index] {
4798            HeapEntry::String(text) => Some(text.clone()),
4799            _ => None,
4800        }
4801    }
4802
4803    pub(crate) fn get_named_property(
4804        &mut self,
4805        object: Value,
4806        name: &str,
4807    ) -> Result<Value, EvalFailure> {
4808        self.get_property_ascii(object, name)
4809    }
4810
4811    fn get_property_ascii(&mut self, object: Value, name: &str) -> Result<Value, EvalFailure> {
4812        debug_assert!(name.is_ascii());
4813        match self.resolve_get_ascii(object, name)? {
4814            GetOutcome::Value(value) => Ok(value),
4815            GetOutcome::Text(text) => self
4816                .allocate(HeapEntry::String(text))
4817                .map_err(EvalFailure::Runtime),
4818            GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4819        }
4820    }
4821
4822    pub(crate) fn get_property_key(
4823        &mut self,
4824        object: Value,
4825        key: &PropertyKey,
4826    ) -> Result<Value, EvalFailure> {
4827        match self.resolve_get(object, key)? {
4828            GetOutcome::Value(value) => Ok(value),
4829            GetOutcome::Text(text) => self
4830                .allocate(HeapEntry::String(text))
4831                .map_err(EvalFailure::Runtime),
4832            GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4833        }
4834    }
4835
4836    pub(crate) fn set_data_property(
4837        &mut self,
4838        object: Value,
4839        name: &str,
4840        value: Value,
4841    ) -> Result<(), EvalFailure> {
4842        self.set_data_property_key(
4843            object,
4844            PropertyKey::Named(EcmaString::from_utf8(name)),
4845            value,
4846        )
4847    }
4848
4849    pub(crate) fn set_data_property_key(
4850        &mut self,
4851        object: Value,
4852        key: PropertyKey,
4853        value: Value,
4854    ) -> Result<(), EvalFailure> {
4855        match self.resolve_set(object, key, value)? {
4856            SetOutcome::Done => Ok(()),
4857            SetOutcome::Setter(setter) => {
4858                self.call_value(setter, object, &[value])?;
4859                Ok(())
4860            }
4861        }
4862    }
4863
4864    pub(crate) fn is_callable(&self, value: Value) -> Result<bool, EvalFailure> {
4865        Ok(!matches!(
4866            self.callee_kind(value).map_err(EvalFailure::Runtime)?,
4867            CalleeKind::NotCallable
4868        ))
4869    }
4870
4871    pub(crate) fn box_primitive(&mut self, value: Value) -> Result<Value, EvalFailure> {
4872        let prototype = match value.decode() {
4873            Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
4874            Some(Decoded::Number(_) | Decoded::Int32(_)) => self.intrinsics.number_prototype,
4875            Some(Decoded::HeapRef(_)) if self.string_value(value).is_some() => {
4876                self.intrinsics.string_prototype
4877            }
4878            _ => self.intrinsics.object_prototype,
4879        };
4880        self.allocate(HeapEntry::Object {
4881            properties: PropertyMap::default(),
4882            prototype: Some(prototype),
4883            boxed_primitive: Some(value),
4884            extensible: true,
4885        })
4886        .map_err(EvalFailure::Runtime)
4887    }
4888
4889    pub(crate) fn unbox_primitive_or_self(&self, value: Value) -> Result<Value, EvalFailure> {
4890        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4891            return Ok(value);
4892        };
4893        match self.heap[index] {
4894            HeapEntry::Object {
4895                boxed_primitive: Some(primitive),
4896                ..
4897            } => Ok(primitive),
4898            _ => Ok(value),
4899        }
4900    }
4901
4902    pub(crate) fn unbox_primitive(
4903        &self,
4904        value: Value,
4905        operation: &'static str,
4906    ) -> Result<Value, EvalFailure> {
4907        let unboxed = self.unbox_primitive_or_self(value)?;
4908        if unboxed == value && self.is_object(value) {
4909            Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }))
4910        } else {
4911            Ok(unboxed)
4912        }
4913    }
4914
4915    pub(crate) fn current_builtin_id(&self) -> Option<intrinsics::BuiltinId> {
4916        self.current_builtin_id
4917    }
4918
4919    pub(crate) fn throw_error(
4920        &mut self,
4921        id: intrinsics::BuiltinId,
4922        message: String,
4923    ) -> EvalFailure {
4924        let message = match self.allocate(HeapEntry::String(EcmaString::from_utf8(&message))) {
4925            Ok(value) => value,
4926            Err(kind) => return EvalFailure::Runtime(kind),
4927        };
4928        let mut properties = PropertyMap::default();
4929        properties.insert(
4930            PropertyKey::Named(EcmaString::from_utf8("message")),
4931            Property::Data {
4932                value: message,
4933                writable: true,
4934                enumerable: true,
4935                configurable: true,
4936            },
4937        );
4938        match self.allocate(HeapEntry::Object {
4939            properties,
4940            prototype: Some(self.intrinsics.error_prototype(id)),
4941            boxed_primitive: None,
4942            extensible: true,
4943        }) {
4944            Ok(value) => EvalFailure::ThrowValue(value),
4945            Err(kind) => EvalFailure::Runtime(kind),
4946        }
4947    }
4948
4949    pub(crate) fn has_own_property_key(
4950        &self,
4951        object: Value,
4952        key: &PropertyKey,
4953    ) -> Result<bool, EvalFailure> {
4954        let Some(index) = self.runtime_slot(object).map_err(EvalFailure::Runtime)? else {
4955            return Ok(false);
4956        };
4957        Ok(self.own_get(index, key).is_some())
4958    }
4959
4960    pub(crate) fn call_value(
4961        &mut self,
4962        callee: Value,
4963        this_value: Value,
4964        arguments: &[Value],
4965    ) -> Result<Value, EvalFailure> {
4966        let mut callee = callee;
4967        let mut this_value = this_value;
4968        let mut arguments = Cow::Borrowed(arguments);
4969        loop {
4970            match self.callee_kind(callee).map_err(EvalFailure::Runtime)? {
4971                CalleeKind::Builtin { id } => {
4972                    match self.call_builtin(id, this_value, arguments.as_ref(), false)? {
4973                        intrinsics::BuiltinOutcome::Value(value) => return Ok(value),
4974                        intrinsics::BuiltinOutcome::Call {
4975                            callee: next,
4976                            this_value: next_this,
4977                            arguments: next_arguments,
4978                        } => {
4979                            callee = next;
4980                            this_value = next_this;
4981                            arguments = Cow::Owned(next_arguments);
4982                        }
4983                        intrinsics::BuiltinOutcome::GeneratorNext {
4984                            generator,
4985                            resume_value,
4986                        } => return self.resume_generator(generator, resume_value),
4987                        intrinsics::BuiltinOutcome::ConstructCall { .. } => {
4988                            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4989                                operation: "call",
4990                            }));
4991                        }
4992                    }
4993                }
4994                CalleeKind::Runtime { target, captures } => {
4995                    let flags = self.module_code(target.module).functions()
4996                        [target.function.get() as usize]
4997                        .flags();
4998                    if flags.is_generator && !flags.is_async {
4999                        return self
5000                            .create_generator(GeneratorStart {
5001                                target,
5002                                captures,
5003                                this_value,
5004                                new_target: Value::UNDEFINED,
5005                                args: arguments.as_ref().to_vec(),
5006                            })
5007                            .map_err(EvalFailure::Runtime);
5008                    }
5009                    if flags.is_async && !flags.is_generator {
5010                        return self.start_async_call(
5011                            target,
5012                            &captures,
5013                            this_value,
5014                            Value::UNDEFINED,
5015                            arguments.as_ref(),
5016                        );
5017                    }
5018                    let stop_depth = self.frames.len();
5019                    let return_to = self.frames.last().map(|frame| ReturnTo {
5020                        destination: None,
5021                        call_pc: frame.pc,
5022                        constructed: None,
5023                    });
5024                    self.push_frame(
5025                        target,
5026                        &captures,
5027                        this_value,
5028                        Value::UNDEFINED,
5029                        arguments.as_ref(),
5030                        return_to,
5031                    )
5032                    .map_err(|error| EvalFailure::Runtime(error.kind))?;
5033                    self.callback_boundaries.push(stop_depth);
5034                    let result = self.run_loop(stop_depth);
5035                    self.callback_boundaries
5036                        .pop()
5037                        .expect("nested runtime callback owns its unwind boundary");
5038                    return match result {
5039                        Ok(None) => self.last_completion.take().ok_or(EvalFailure::Runtime(
5040                            RuntimeErrorKind::InvalidValue {
5041                                value: Value::UNDEFINED,
5042                            },
5043                        )),
5044                        Ok(Some(execution)) => Ok(execution.value),
5045                        Err(error) => {
5046                            self.unwind_frames_to(stop_depth);
5047                            match error.kind {
5048                                RuntimeErrorKind::UncaughtThrow { value, .. } => {
5049                                    Err(EvalFailure::ThrowValue(value))
5050                                }
5051                                kind => Err(EvalFailure::Runtime(kind)),
5052                            }
5053                        }
5054                    };
5055                }
5056                CalleeKind::Bound => {
5057                    let bound = self
5058                        .flatten_bound(callee, this_value, arguments.as_ref())
5059                        .map_err(EvalFailure::Runtime)?;
5060                    callee = bound.target;
5061                    this_value = bound.this_value;
5062                    arguments = Cow::Owned(bound.arguments);
5063                }
5064                CalleeKind::NotCallable => {
5065                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5066                        operation: "call",
5067                    }));
5068                }
5069            }
5070        }
5071    }
5072
5073    fn unwind_frames_to(&mut self, depth: usize) {
5074        while self.frames.len() > depth {
5075            let frame = self.frames.pop().expect("frame depth was checked");
5076            self.live_registers -= frame.registers.len();
5077        }
5078    }
5079
5080    fn complete_frame(&mut self, returned: Value) -> Option<Execution> {
5081        let frame = self.frames.pop().expect("an activation is executing");
5082        self.live_registers -= frame.registers.len();
5083        match frame.return_to {
5084            None => {
5085                let outcome = ExecutionOutcome {
5086                    stdout: Vec::new(),
5087                    exit_code: 0,
5088                };
5089                Some(Execution {
5090                    outcome,
5091                    value: returned,
5092                    link: returned,
5093                    entry_registers: frame.registers,
5094                })
5095            }
5096            Some(return_to) => {
5097                let value = match return_to.constructed {
5098                    Some(object) if !self.is_object(returned) => object,
5099                    _ => returned,
5100                };
5101                if let Some(destination) = return_to.destination {
5102                    self.frames.last_mut().expect("callee has caller").registers[destination] =
5103                        value;
5104                } else {
5105                    self.last_completion = Some(value);
5106                }
5107                None
5108            }
5109        }
5110    }
5111
5112    fn resolve_failure(&mut self, failure: EvalFailure, pc: usize) -> Result<(), RuntimeError> {
5113        match failure {
5114            EvalFailure::Throw(origin) => self.throw(Value::UNDEFINED, origin, pc),
5115            EvalFailure::ThrowValue(value) => self.throw(value, ThrowOrigin::Bytecode, pc),
5116            EvalFailure::ThrowValueOrigin { value, origin } => self.throw(value, origin, pc),
5117            EvalFailure::Runtime(kind) => Err(self.error_here_at(kind, pc)),
5118        }
5119    }
5120
5121    fn throw_type(&mut self, operation: &'static str, pc: usize) -> Result<(), RuntimeError> {
5122        self.throw(Value::UNDEFINED, ThrowOrigin::TypeError { operation }, pc)
5123    }
5124
5125    fn throw(
5126        &mut self,
5127        value: Value,
5128        origin: ThrowOrigin,
5129        faulting_pc: usize,
5130    ) -> Result<(), RuntimeError> {
5131        let site_module = self
5132            .frames
5133            .last()
5134            .expect("an activation is executing")
5135            .module;
5136        let site_function = self
5137            .frames
5138            .last()
5139            .expect("an activation is executing")
5140            .function;
5141        let mut search_pc = faulting_pc;
5142        loop {
5143            if self
5144                .callback_boundaries
5145                .last()
5146                .is_some_and(|boundary| self.frames.len() == *boundary)
5147            {
5148                return Err(self.error_at_in_module(
5149                    RuntimeErrorKind::UncaughtThrow { value, origin },
5150                    site_module,
5151                    site_function,
5152                    faulting_pc,
5153                ));
5154            }
5155            let frame_index = self.frames.len() - 1;
5156            let function_index = self.frames[frame_index].function;
5157            let module = self.frames[frame_index].module;
5158            let function = &self.module_code(module).functions()[function_index];
5159            if let Some(handler) = innermost_handler(function, search_pc) {
5160                let frame = &mut self.frames[frame_index];
5161                frame.registers[handler.catch_register.get() as usize] = value;
5162                frame.pc = handler.handler.get() as usize;
5163                return Ok(());
5164            }
5165            let frame = self.frames.pop().expect("throw walks live frames");
5166            self.live_registers -= frame.registers.len();
5167            match frame.return_to {
5168                Some(return_to) => search_pc = return_to.call_pc,
5169                None => {
5170                    return Err(self.error_at_in_module(
5171                        RuntimeErrorKind::UncaughtThrow { value, origin },
5172                        site_module,
5173                        site_function,
5174                        faulting_pc,
5175                    ));
5176                }
5177            }
5178        }
5179    }
5180
5181    fn error_here(&self, kind: RuntimeErrorKind) -> RuntimeError {
5182        let frame = self.frames.last().expect("an activation is executing");
5183        self.error_at(kind, frame.function, frame.pc)
5184    }
5185
5186    fn error_here_at(&self, kind: RuntimeErrorKind, pc: usize) -> RuntimeError {
5187        let function = self
5188            .frames
5189            .last()
5190            .expect("an activation is executing")
5191            .function;
5192        self.error_at(kind, function, pc)
5193    }
5194
5195    fn error_at(&self, kind: RuntimeErrorKind, function: usize, pc: usize) -> RuntimeError {
5196        self.error_at_in_module(kind, self.active_module_id(), function, pc)
5197    }
5198
5199    pub(crate) fn error_at_in_module(
5200        &self,
5201        kind: RuntimeErrorKind,
5202        module: ModuleId,
5203        function: usize,
5204        pc: usize,
5205    ) -> RuntimeError {
5206        let code = self.module_code(module);
5207        let metadata = &code.functions()[function];
5208        let function_name =
5209            metadata
5210                .name()
5211                .and_then(|id| match &code.constants()[id.get() as usize] {
5212                    Constant::String(name) => Some(name.clone()),
5213                    _ => None,
5214                });
5215        RuntimeError {
5216            kind,
5217            function: FunctionId::new(function as u32),
5218            pc: Pc::new(pc as u32),
5219            source: RuntimeSource {
5220                function_name,
5221                instruction: metadata.code()[pc],
5222            },
5223        }
5224    }
5225
5226    // ---- property keys -----------------------------------------------------
5227
5228    /// Normalizes a register value into a property key. A runtime string borrows
5229    /// its text; a private name yields its slot identity; everything else is
5230    /// coerced with `ToString`.
5231    fn to_property_key(&self, value: Value) -> Result<PropertyKey, EvalFailure> {
5232        match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
5233            Some(index) => match &self.heap[index] {
5234                HeapEntry::String(text) => Ok(PropertyKey::Named(text.clone())),
5235                HeapEntry::Symbol { .. } => Ok(PropertyKey::Symbol(index as u32)),
5236                HeapEntry::PrivateName { .. } => Ok(PropertyKey::Private(index as u32)),
5237                _ => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5238            },
5239            None => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5240        }
5241    }
5242
5243    // ---- property get ------------------------------------------------------
5244
5245    fn resolve_get(&mut self, object: Value, key: &PropertyKey) -> Result<GetOutcome, EvalFailure> {
5246        let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5247        let start = match slot {
5248            Some(index) => {
5249                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5250                    let PropertyKey::Named(name) = key else {
5251                        return Ok(GetOutcome::Value(Value::UNDEFINED));
5252                    };
5253                    let text = name
5254                        .to_utf8_strict()
5255                        .ok()
5256                        .and_then(|name| self.host.env(&name))
5257                        .map(EcmaString::from_utf8);
5258                    return match text {
5259                        Some(text) => self
5260                            .allocate(HeapEntry::String(text))
5261                            .map(GetOutcome::Value)
5262                            .map_err(EvalFailure::Runtime),
5263                        None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5264                    };
5265                }
5266                if let Some(found) = self.primitive_get(index, key) {
5267                    return self.found_outcome(found);
5268                }
5269                match self.heap[index] {
5270                    HeapEntry::String(_) => self
5271                        .runtime_slot(self.intrinsics.string_prototype)
5272                        .map_err(EvalFailure::Runtime)?,
5273                    HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5274                        .runtime_slot(self.intrinsics.object_prototype)
5275                        .map_err(EvalFailure::Runtime)?,
5276                    HeapEntry::Symbol { .. } => self
5277                        .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5278                        .map_err(EvalFailure::Runtime)?,
5279                    _ => Some(index),
5280                }
5281            }
5282            None => {
5283                let prototype = match object.decode() {
5284                    Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5285                    Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5286                        self.intrinsics.number_prototype
5287                    }
5288                    _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5289                };
5290                self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5291            }
5292        };
5293        let Some(mut node) = start else {
5294            return Ok(GetOutcome::Value(Value::UNDEFINED));
5295        };
5296        for _ in 0..=self.heap.len() {
5297            if let Some(found) = self.own_get(node, key) {
5298                return self.found_outcome(found);
5299            }
5300            match self.prototype_index(node)? {
5301                Some(next) => node = next,
5302                None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5303            }
5304        }
5305        Ok(GetOutcome::Value(Value::UNDEFINED))
5306    }
5307
5308    fn resolve_get_ascii(&mut self, object: Value, name: &str) -> Result<GetOutcome, EvalFailure> {
5309        debug_assert!(name.is_ascii());
5310        let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5311        let start = match slot {
5312            Some(index) => {
5313                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5314                    return match self.host.env(name).map(EcmaString::from_utf8) {
5315                        Some(text) => self
5316                            .allocate(HeapEntry::String(text))
5317                            .map(GetOutcome::Value)
5318                            .map_err(EvalFailure::Runtime),
5319                        None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5320                    };
5321                }
5322                if let HeapEntry::String(text) = &self.heap[index] {
5323                    if name == "length" {
5324                        return Ok(GetOutcome::Value(number_value(text.len_units() as f64)));
5325                    }
5326                    if let Some(offset) = array_index_ascii(name)
5327                        && let Some(unit) = text.unit_at(offset as usize)
5328                    {
5329                        return Ok(GetOutcome::Text(EcmaString::from_units(&[unit])));
5330                    }
5331                }
5332                match self.heap[index] {
5333                    HeapEntry::String(_) => self
5334                        .runtime_slot(self.intrinsics.string_prototype)
5335                        .map_err(EvalFailure::Runtime)?,
5336                    HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5337                        .runtime_slot(self.intrinsics.object_prototype)
5338                        .map_err(EvalFailure::Runtime)?,
5339                    HeapEntry::Symbol { .. } => self
5340                        .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5341                        .map_err(EvalFailure::Runtime)?,
5342                    _ => Some(index),
5343                }
5344            }
5345            None => {
5346                let prototype = match object.decode() {
5347                    Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5348                    Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5349                        self.intrinsics.number_prototype
5350                    }
5351                    _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5352                };
5353                self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5354            }
5355        };
5356        let Some(mut node) = start else {
5357            return Ok(GetOutcome::Value(Value::UNDEFINED));
5358        };
5359        for _ in 0..=self.heap.len() {
5360            if let Some(found) = self.own_get_ascii(node, name) {
5361                return self.found_outcome(found);
5362            }
5363            match self.prototype_index(node)? {
5364                Some(next) => node = next,
5365                None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5366            }
5367        }
5368        Ok(GetOutcome::Value(Value::UNDEFINED))
5369    }
5370
5371    fn found_outcome(&mut self, found: Found) -> Result<GetOutcome, EvalFailure> {
5372        match found {
5373            Found::Value(Value::UNINITIALIZED) => {
5374                let id = self
5375                    .intrinsics
5376                    .builtins
5377                    .id_named("ReferenceError")
5378                    .expect("ReferenceError intrinsic is installed");
5379                match self.throw_error(
5380                    id,
5381                    "Cannot access lexical binding before initialization".into(),
5382                ) {
5383                    EvalFailure::ThrowValue(value) => Err(EvalFailure::ThrowValueOrigin {
5384                        value,
5385                        origin: ThrowOrigin::ReferenceError {
5386                            operation: "lexical binding is uninitialized",
5387                        },
5388                    }),
5389                    failure => Err(failure),
5390                }
5391            }
5392            Found::Value(value) => Ok(GetOutcome::Value(value)),
5393            Found::Text(text) => Ok(GetOutcome::Text(text)),
5394            Found::Getter(getter) => Ok(GetOutcome::Getter(getter)),
5395            Found::Failure(kind) => Err(EvalFailure::Runtime(kind)),
5396            Found::NoGetter => Ok(GetOutcome::Value(Value::UNDEFINED)),
5397        }
5398    }
5399
5400    fn primitive_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5401        if let HeapEntry::String(text) = &self.heap[index]
5402            && let PropertyKey::Named(name) = key
5403        {
5404            if name.eq_ascii("length") {
5405                return Some(Found::Value(number_value(text.len_units() as f64)));
5406            }
5407            if let Some(offset) = array_index(name)
5408                && let Some(unit) = text.unit_at(offset as usize)
5409            {
5410                return Some(Found::Text(EcmaString::from_units(&[unit])));
5411            }
5412        }
5413        None
5414    }
5415    fn own_get_ascii(&self, index: usize, name: &str) -> Option<Found> {
5416        debug_assert!(name.is_ascii());
5417        let slot = |value| self.runtime_slot(value).ok().flatten();
5418        if slot(self.intrinsics.object_prototype) == Some(index) && name == "toString" {
5419            return Some(Found::Value(self.intrinsics.object_to_string()));
5420        }
5421        match &self.heap[index] {
5422            HeapEntry::Object { properties, .. }
5423            | HeapEntry::Generator { properties, .. }
5424            | HeapEntry::Script { properties, .. }
5425            | HeapEntry::NativeFunction { properties, .. }
5426            | HeapEntry::Date { properties, .. }
5427            | HeapEntry::BuiltinIterator { properties, .. }
5428            | HeapEntry::Collection { properties, .. }
5429            | HeapEntry::Promise { properties, .. }
5430            | HeapEntry::Timeout { properties, .. } => property_lookup_ascii(properties, name),
5431            HeapEntry::Array {
5432                elements,
5433                properties,
5434                ..
5435            } => {
5436                if name == "length" {
5437                    return Some(Found::Value(number_value(elements.len() as f64)));
5438                }
5439                if let Some(offset) = array_index_ascii(name)
5440                    && let Some(element) = elements.get(offset as usize)
5441                    && *element != Value::HOLE
5442                {
5443                    return Some(Found::Value(*element));
5444                }
5445                property_lookup_ascii(properties, name)
5446            }
5447            HeapEntry::Function {
5448                module,
5449                function,
5450                properties,
5451                ..
5452            } => {
5453                if let Some(found) = property_lookup_ascii(properties, name) {
5454                    return Some(found);
5455                }
5456                let metadata = &self.module_code(*module).functions()[function.get() as usize];
5457                if name == "length" {
5458                    return Some(Found::Value(
5459                        number_value(metadata.parameter_count() as f64),
5460                    ));
5461                }
5462                if name == "name" {
5463                    return Some(Found::Text(
5464                        metadata
5465                            .name()
5466                            .map(|id| self.constant_text(*module, id).clone())
5467                            .unwrap_or_default(),
5468                    ));
5469                }
5470                None
5471            }
5472            HeapEntry::ModuleNamespace { module } => {
5473                let key = self
5474                    .program_module(*module)
5475                    .exports
5476                    .iter()
5477                    .map(|export| self.constant_text(*module, export.name))
5478                    .find(|candidate| candidate.eq_ascii(name))?
5479                    .clone();
5480                match self.namespace_export(*module, &key) {
5481                    Ok(Some(value)) => Some(Found::Value(value)),
5482                    Ok(None) => None,
5483                    Err(kind) => Some(Found::Failure(kind)),
5484                }
5485            }
5486            HeapEntry::ExternalModuleNamespace { specifier } => {
5487                let export = self.registry.external[specifier]
5488                    .exports
5489                    .iter()
5490                    .find_map(|(candidate, export)| candidate.eq_ascii(name).then_some(export))?;
5491                let cell = export
5492                    .cell
5493                    .expect("external namespace exports link before evaluation");
5494                Some(Found::Value(self.registry.cells[cell.0].value))
5495            }
5496            HeapEntry::RegExp {
5497                pattern,
5498                flags,
5499                properties,
5500                ..
5501            } => {
5502                if let Some(found) = property_lookup_ascii(properties, name) {
5503                    return Some(found);
5504                }
5505                let flag = |unit| {
5506                    Found::Value(Value::boolean(flags.as_units().contains(&u16::from(unit))))
5507                };
5508                match name {
5509                    "source" => Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5510                        pattern,
5511                    ))),
5512                    "flags" => Some(Found::Text(flags.clone())),
5513                    "global" => Some(flag(b'g')),
5514                    "ignoreCase" => Some(flag(b'i')),
5515                    "multiline" => Some(flag(b'm')),
5516                    "sticky" => Some(flag(b'y')),
5517                    "unicode" => Some(flag(b'u')),
5518                    "dotAll" => Some(flag(b's')),
5519                    "lastIndex" => Some(Found::Value(Value::int32(0))),
5520                    _ => None,
5521                }
5522            }
5523            HeapEntry::HashState { update, digest, .. } => match name {
5524                "update" => Some(Found::Value(*update)),
5525                "digest" => Some(Found::Value(*digest)),
5526                _ => None,
5527            },
5528            HeapEntry::ProcessEnv { .. }
5529            | HeapEntry::String(_)
5530            | HeapEntry::BigInt(_)
5531            | HeapEntry::Symbol { .. }
5532            | HeapEntry::PrivateName { .. }
5533            | HeapEntry::Iterator { .. }
5534            | HeapEntry::PromiseResolver { .. }
5535            | HeapEntry::PromiseFinally { .. }
5536            | HeapEntry::PromiseAll { .. }
5537            | HeapEntry::AsyncActivation { .. }
5538            | HeapEntry::PromiseAllElement { .. } => None,
5539        }
5540    }
5541
5542    /// Looks up an own property of the heap entry at `index`, returning `None`
5543    /// when the key is absent so the caller may continue up the prototype chain.
5544    fn own_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5545        if let PropertyKey::Named(name) = key {
5546            let slot = |value| self.runtime_slot(value).ok().flatten();
5547            if slot(self.intrinsics.object_prototype) == Some(index) && name.eq_ascii("toString") {
5548                return Some(Found::Value(self.intrinsics.object_to_string()));
5549            }
5550        }
5551        match &self.heap[index] {
5552            HeapEntry::Object { properties, .. }
5553            | HeapEntry::Generator { properties, .. }
5554            | HeapEntry::Script { properties, .. }
5555            | HeapEntry::Date { properties, .. }
5556            | HeapEntry::BuiltinIterator { properties, .. }
5557            | HeapEntry::Collection { properties, .. }
5558            | HeapEntry::Promise { properties, .. }
5559            | HeapEntry::Timeout { properties, .. } => property_lookup(properties, key),
5560            HeapEntry::Array {
5561                elements,
5562                properties,
5563                ..
5564            } => {
5565                if let PropertyKey::Named(name) = key {
5566                    if name.eq_ascii("length") {
5567                        return Some(Found::Value(number_value(elements.len() as f64)));
5568                    }
5569                    if let Some(offset) = array_index(name)
5570                        && let Some(element) = elements.get(offset as usize)
5571                        && *element != Value::HOLE
5572                    {
5573                        return Some(Found::Value(*element));
5574                    }
5575                }
5576                property_lookup(properties, key)
5577            }
5578            HeapEntry::Function {
5579                module,
5580                function,
5581                properties,
5582                ..
5583            } => {
5584                if let Some(found) = property_lookup(properties, key) {
5585                    return Some(found);
5586                }
5587                if let PropertyKey::Named(name) = key {
5588                    let metadata = &self.module_code(*module).functions()[function.get() as usize];
5589                    if name.eq_ascii("length") {
5590                        return Some(Found::Value(
5591                            number_value(metadata.parameter_count() as f64),
5592                        ));
5593                    }
5594                    if name.eq_ascii("name") {
5595                        return Some(Found::Text(
5596                            metadata
5597                                .name()
5598                                .map(|id| self.constant_text(*module, id).clone())
5599                                .unwrap_or_default(),
5600                        ));
5601                    }
5602                }
5603                None
5604            }
5605            HeapEntry::ModuleNamespace { module } => {
5606                let PropertyKey::Named(name) = key else {
5607                    return None;
5608                };
5609                match self.namespace_export(*module, name) {
5610                    Ok(Some(value)) => Some(Found::Value(value)),
5611                    Ok(None) => None,
5612                    Err(kind) => Some(Found::Failure(kind)),
5613                }
5614            }
5615            HeapEntry::ExternalModuleNamespace { specifier } => {
5616                let PropertyKey::Named(name) = key else {
5617                    return None;
5618                };
5619                let export = self.registry.external[specifier].exports.get(name)?;
5620                Some(Found::Value(export.cell.map_or(export.value, |cell| {
5621                    self.registry.cells[cell.0].value
5622                })))
5623            }
5624            HeapEntry::NativeFunction { properties, .. } => property_lookup(properties, key),
5625            HeapEntry::RegExp {
5626                pattern,
5627                flags,
5628                properties,
5629                ..
5630            } => {
5631                if let Some(found) = property_lookup(properties, key) {
5632                    return Some(found);
5633                }
5634                if let PropertyKey::Named(name) = key {
5635                    let flag = |ascii: &str| {
5636                        Found::Value(Value::boolean(
5637                            flags.as_units().contains(&u16::from(ascii.as_bytes()[0])),
5638                        ))
5639                    };
5640                    if name.eq_ascii("source") {
5641                        return Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5642                            pattern,
5643                        )));
5644                    }
5645                    if name.eq_ascii("flags") {
5646                        return Some(Found::Text(flags.clone()));
5647                    }
5648                    if name.eq_ascii("global") {
5649                        return Some(flag("g"));
5650                    }
5651                    if name.eq_ascii("ignoreCase") {
5652                        return Some(flag("i"));
5653                    }
5654                    if name.eq_ascii("multiline") {
5655                        return Some(flag("m"));
5656                    }
5657                    if name.eq_ascii("sticky") {
5658                        return Some(flag("y"));
5659                    }
5660                    if name.eq_ascii("unicode") {
5661                        return Some(flag("u"));
5662                    }
5663                    if name.eq_ascii("dotAll") {
5664                        return Some(flag("s"));
5665                    }
5666                    if name.eq_ascii("lastIndex") {
5667                        return Some(Found::Value(Value::int32(0)));
5668                    }
5669                }
5670                None
5671            }
5672            HeapEntry::HashState { update, digest, .. } => {
5673                let PropertyKey::Named(name) = key else {
5674                    return None;
5675                };
5676                if name.eq_ascii("update") {
5677                    Some(Found::Value(*update))
5678                } else if name.eq_ascii("digest") {
5679                    Some(Found::Value(*digest))
5680                } else {
5681                    None
5682                }
5683            }
5684            HeapEntry::ProcessEnv { .. }
5685            | HeapEntry::String(_)
5686            | HeapEntry::BigInt(_)
5687            | HeapEntry::Symbol { .. }
5688            | HeapEntry::PrivateName { .. }
5689            | HeapEntry::Iterator { .. }
5690            | HeapEntry::PromiseResolver { .. }
5691            | HeapEntry::PromiseFinally { .. }
5692            | HeapEntry::PromiseAll { .. }
5693            | HeapEntry::AsyncActivation { .. }
5694            | HeapEntry::PromiseAllElement { .. } => None,
5695        }
5696    }
5697
5698    fn namespace_export(
5699        &self,
5700        module: ModuleId,
5701        name: &EcmaString,
5702    ) -> Result<Option<Value>, RuntimeErrorKind> {
5703        if module.get() as usize >= self.dynamic_base {
5704            return Ok(None);
5705        }
5706        match self.program().resolve_export(module, name) {
5707            Some(ResolvedExport::Local { module, binding }) => {
5708                let cell = self.registry.modules[module.get() as usize].binding_cells
5709                    [binding.get() as usize]
5710                    .expect("verified export resolves to a linked cell");
5711                let value = self.registry.cells[cell.0].value;
5712                if value.is_uninitialized() {
5713                    Err(RuntimeErrorKind::TemporalDeadZone { module, binding })
5714                } else {
5715                    Ok(Some(value))
5716                }
5717            }
5718            Some(ResolvedExport::External { module, edge, name }) => {
5719                let Some(specifier) = self.external_specifier(module, edge) else {
5720                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5721                };
5722                let name = self.constant_text(module, name);
5723                let Some(export) = self.registry.external[&specifier].exports.get(name) else {
5724                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5725                };
5726                let Some(cell) = export.cell else {
5727                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5728                };
5729                Ok(Some(self.registry.cells[cell.0].value))
5730            }
5731            None => Ok(None),
5732        }
5733    }
5734
5735    fn own_data_property(&self, index: usize, name: &str) -> Option<Value> {
5736        let properties = match &self.heap[index] {
5737            HeapEntry::Object { properties, .. }
5738            | HeapEntry::Generator { properties, .. }
5739            | HeapEntry::Script { properties, .. }
5740            | HeapEntry::Array { properties, .. }
5741            | HeapEntry::Function { properties, .. }
5742            | HeapEntry::NativeFunction { properties, .. }
5743            | HeapEntry::RegExp { properties, .. }
5744            | HeapEntry::Date { properties, .. }
5745            | HeapEntry::BuiltinIterator { properties, .. }
5746            | HeapEntry::Collection { properties, .. }
5747            | HeapEntry::Promise { properties, .. }
5748            | HeapEntry::Timeout { properties, .. } => properties,
5749            _ => return None,
5750        };
5751        match properties.get_ascii(name) {
5752            Some(Property::Data { value, .. }) => Some(*value),
5753            _ => None,
5754        }
5755    }
5756
5757    fn prototype_index(&self, index: usize) -> Result<Option<usize>, EvalFailure> {
5758        let prototype = match &self.heap[index] {
5759            HeapEntry::Object { prototype, .. }
5760            | HeapEntry::Generator { prototype, .. }
5761            | HeapEntry::Script { prototype, .. }
5762            | HeapEntry::Array { prototype, .. }
5763            | HeapEntry::Function { prototype, .. }
5764            | HeapEntry::RegExp { prototype, .. }
5765            | HeapEntry::Date { prototype, .. }
5766            | HeapEntry::BuiltinIterator { prototype, .. }
5767            | HeapEntry::Collection { prototype, .. }
5768            | HeapEntry::Promise { prototype, .. }
5769            | HeapEntry::Timeout { prototype, .. }
5770            | HeapEntry::ProcessEnv { prototype, .. } => *prototype,
5771            HeapEntry::NativeFunction { .. } => Some(self.intrinsics.function_prototype),
5772            _ => None,
5773        };
5774        match prototype {
5775            Some(value) => self.runtime_slot(value).map_err(EvalFailure::Runtime),
5776            None => Ok(None),
5777        }
5778    }
5779
5780    pub(crate) fn inherits_from_prototype(
5781        &self,
5782        value: Value,
5783        prototype: Value,
5784    ) -> Result<bool, EvalFailure> {
5785        let Some(mut current) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
5786            return Ok(false);
5787        };
5788        let Some(target) = self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? else {
5789            return Ok(false);
5790        };
5791        let mut traversed = 0;
5792        while let Some(next) = self.prototype_index(current)? {
5793            if next == target {
5794                return Ok(true);
5795            }
5796            current = next;
5797            traversed += 1;
5798            if traversed > self.heap.len() {
5799                return Ok(false);
5800            }
5801        }
5802        Ok(false)
5803    }
5804
5805    // ---- property set ------------------------------------------------------
5806
5807    fn resolve_set(
5808        &mut self,
5809        object: Value,
5810        key: PropertyKey,
5811        value: Value,
5812    ) -> Result<SetOutcome, EvalFailure> {
5813        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
5814            Some(index) => {
5815                if matches!(self.heap[index], HeapEntry::ModuleNamespace { .. }) {
5816                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5817                        operation: "assign to module namespace",
5818                    }));
5819                }
5820                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5821                    let PropertyKey::Named(name) = &key else {
5822                        return Ok(SetOutcome::Done);
5823                    };
5824                    let Ok(name) = name.to_utf8_strict() else {
5825                        return Ok(SetOutcome::Done);
5826                    };
5827                    let text = self.to_string(value)?;
5828                    let text = crate::host_objects::env_value_text_lossy(&text);
5829                    self.host.set_env(&name, &text);
5830                    return Ok(SetOutcome::Done);
5831                }
5832                if let Some(setter) = self.find_setter(index, &key)? {
5833                    return Ok(match setter {
5834                        Some(setter) => SetOutcome::Setter(setter),
5835                        None => SetOutcome::Done,
5836                    });
5837                }
5838                self.set_own_data(index, key, value)?;
5839                Ok(SetOutcome::Done)
5840            }
5841            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5842                operation: "set property on primitive",
5843            })),
5844        }
5845    }
5846
5847    fn find_setter(
5848        &self,
5849        index: usize,
5850        key: &PropertyKey,
5851    ) -> Result<Option<Option<Value>>, EvalFailure> {
5852        if self.own_has_non_accessor(index, key) {
5853            return Ok(None);
5854        }
5855        let mut node = index;
5856        let mut guard = 0;
5857        loop {
5858            let accessor = match &self.heap[node] {
5859                HeapEntry::Object { properties, .. }
5860                | HeapEntry::Generator { properties, .. }
5861                | HeapEntry::Script { properties, .. }
5862                | HeapEntry::Array { properties, .. }
5863                | HeapEntry::Function { properties, .. }
5864                | HeapEntry::NativeFunction { properties, .. }
5865                | HeapEntry::RegExp { properties, .. }
5866                | HeapEntry::Date { properties, .. }
5867                | HeapEntry::BuiltinIterator { properties, .. }
5868                | HeapEntry::Collection { properties, .. }
5869                | HeapEntry::Promise { properties, .. }
5870                | HeapEntry::Timeout { properties, .. } => match properties.get(key) {
5871                    Some(Property::Accessor { setter, .. }) => Some(Some(*setter)),
5872                    Some(Property::Data { .. }) => Some(None),
5873                    None => None,
5874                },
5875                _ => None,
5876            };
5877            match accessor {
5878                Some(Some(setter)) => return Ok(Some(setter)),
5879                Some(None) => return Ok(None),
5880                None => {}
5881            }
5882            match self.prototype_index(node)? {
5883                Some(next) => {
5884                    node = next;
5885                    guard += 1;
5886                    if guard > self.heap.len() + 1 {
5887                        return Ok(None);
5888                    }
5889                }
5890                None => return Ok(None),
5891            }
5892        }
5893    }
5894
5895    fn own_has_non_accessor(&self, index: usize, key: &PropertyKey) -> bool {
5896        match &self.heap[index] {
5897            HeapEntry::Array { elements, .. } => {
5898                if let PropertyKey::Named(name) = key {
5899                    if name.eq_ascii("length") {
5900                        return true;
5901                    }
5902                    if let Some(offset) = array_index(name) {
5903                        return elements
5904                            .get(offset as usize)
5905                            .is_some_and(|element| *element != Value::HOLE);
5906                    }
5907                }
5908                false
5909            }
5910            HeapEntry::Function { .. } => {
5911                (key.eq_ascii("length") || key.eq_ascii("name"))
5912                    && match key {
5913                        PropertyKey::Named(name) if name.eq_ascii("length") => {
5914                            self.own_data_property(index, "length").is_none()
5915                        }
5916                        PropertyKey::Named(_) => self.own_data_property(index, "name").is_none(),
5917                        _ => false,
5918                    }
5919            }
5920            _ => false,
5921        }
5922    }
5923
5924    fn set_own_data(
5925        &mut self,
5926        index: usize,
5927        key: PropertyKey,
5928        value: Value,
5929    ) -> Result<(), EvalFailure> {
5930        if matches!(key, PropertyKey::Named(ref name) if name.eq_ascii("length"))
5931            && matches!(self.heap[index], HeapEntry::Array { .. })
5932        {
5933            let HeapEntry::Array {
5934                elements,
5935                properties,
5936                length_writable,
5937                ..
5938            } = &mut self.heap[index]
5939            else {
5940                unreachable!("array checked above");
5941            };
5942            return array_set_length(
5943                elements,
5944                properties,
5945                *length_writable,
5946                value,
5947                "set array length",
5948            );
5949        }
5950        if let HeapEntry::Array {
5951            elements,
5952            length_writable,
5953            ..
5954        } = &self.heap[index]
5955            && let Some(offset) = key.as_string().and_then(array_index)
5956            && offset as usize >= elements.len()
5957            && !*length_writable
5958        {
5959            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5960                operation: "add index beyond non-writable array length",
5961            }));
5962        }
5963        let (properties, extensible, virtual_exists) = match &self.heap[index] {
5964            HeapEntry::Object {
5965                properties,
5966                extensible,
5967                ..
5968            }
5969            | HeapEntry::Generator {
5970                properties,
5971                extensible,
5972                ..
5973            }
5974            | HeapEntry::Script {
5975                properties,
5976                extensible,
5977                ..
5978            }
5979            | HeapEntry::Function {
5980                properties,
5981                extensible,
5982                ..
5983            }
5984            | HeapEntry::NativeFunction {
5985                properties,
5986                extensible,
5987                ..
5988            }
5989            | HeapEntry::RegExp {
5990                properties,
5991                extensible,
5992                ..
5993            }
5994            | HeapEntry::Date {
5995                properties,
5996                extensible,
5997                ..
5998            }
5999            | HeapEntry::BuiltinIterator {
6000                properties,
6001                extensible,
6002                ..
6003            }
6004            | HeapEntry::Collection {
6005                properties,
6006                extensible,
6007                ..
6008            }
6009            | HeapEntry::Promise {
6010                properties,
6011                extensible,
6012                ..
6013            } => (Some(properties), *extensible, false),
6014            HeapEntry::Array {
6015                elements,
6016                properties,
6017                extensible,
6018                ..
6019            } => {
6020                let virtual_exists = key.as_string().is_some_and(|name| {
6021                    name.eq_ascii("length")
6022                        || array_index(name).is_some_and(|offset| {
6023                            elements
6024                                .get(offset as usize)
6025                                .is_some_and(|element| *element != Value::HOLE)
6026                        })
6027                });
6028                (Some(properties), *extensible, virtual_exists)
6029            }
6030            _ => (None, true, false),
6031        };
6032        if let Some(property) = properties.and_then(|properties| properties.get(&key)) {
6033            match property {
6034                Property::Data {
6035                    writable: false, ..
6036                }
6037                | Property::Accessor { .. } => {
6038                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6039                        operation: "assign to read only property",
6040                    }));
6041                }
6042                Property::Data { writable: true, .. } => {}
6043            }
6044        } else if !extensible && !virtual_exists {
6045            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6046                operation: "add property to non-extensible object",
6047            }));
6048        }
6049
6050        let growth = match &self.heap[index] {
6051            HeapEntry::Object { properties, .. }
6052            | HeapEntry::Generator { properties, .. }
6053            | HeapEntry::Script { properties, .. }
6054            | HeapEntry::Function { properties, .. }
6055            | HeapEntry::NativeFunction { properties, .. }
6056            | HeapEntry::RegExp { properties, .. }
6057            | HeapEntry::Date { properties, .. }
6058            | HeapEntry::BuiltinIterator { properties, .. }
6059            | HeapEntry::Collection { properties, .. }
6060            | HeapEntry::Promise { properties, .. }
6061            | HeapEntry::Timeout { properties, .. } => {
6062                usize::from(!properties.contains_key(&key)) * key.charge_bytes()
6063            }
6064            HeapEntry::Array {
6065                elements,
6066                properties,
6067                ..
6068            } => match &key {
6069                PropertyKey::Named(name) if name.eq_ascii("length") => 0,
6070                PropertyKey::Named(name) => {
6071                    if let Some(offset) = array_index(name) {
6072                        (offset as usize + 1).saturating_sub(elements.len()) * 8
6073                    } else {
6074                        usize::from(!properties.contains_key(&key)) * key.charge_bytes()
6075                    }
6076                }
6077                PropertyKey::Symbol(_) | PropertyKey::Private(_) => {
6078                    usize::from(!properties.contains_key(&key)) * key.charge_bytes()
6079                }
6080            },
6081            HeapEntry::String(_) | HeapEntry::BigInt(_) => {
6082                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6083                    operation: "set property on primitive",
6084                }));
6085            }
6086            HeapEntry::Symbol { .. }
6087            | HeapEntry::PrivateName { .. }
6088            | HeapEntry::Iterator { .. }
6089            | HeapEntry::PromiseResolver { .. }
6090            | HeapEntry::PromiseFinally { .. }
6091            | HeapEntry::PromiseAll { .. }
6092            | HeapEntry::AsyncActivation { .. }
6093            | HeapEntry::PromiseAllElement { .. } => {
6094                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6095                    operation: "set property on non-object",
6096                }));
6097            }
6098            HeapEntry::ProcessEnv { .. } => {
6099                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6100                    operation: "set internal process environment",
6101                }));
6102            }
6103            HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6104                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6105                    operation: "assign to module namespace",
6106                }));
6107            }
6108            HeapEntry::HashState { .. } => {
6109                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6110                    operation: "assign to hash state",
6111                }));
6112            }
6113        };
6114        self.charge_heap(growth).map_err(EvalFailure::Runtime)?;
6115        match &mut self.heap[index] {
6116            HeapEntry::Object { properties, .. }
6117            | HeapEntry::Generator { properties, .. }
6118            | HeapEntry::Script { properties, .. }
6119            | HeapEntry::Function { properties, .. }
6120            | HeapEntry::NativeFunction { properties, .. }
6121            | HeapEntry::RegExp { properties, .. }
6122            | HeapEntry::Date { properties, .. }
6123            | HeapEntry::BuiltinIterator { properties, .. }
6124            | HeapEntry::Collection { properties, .. }
6125            | HeapEntry::Promise { properties, .. }
6126            | HeapEntry::Timeout { properties, .. } => {
6127                properties.insert(
6128                    key,
6129                    Property::Data {
6130                        value,
6131                        writable: true,
6132                        enumerable: true,
6133                        configurable: true,
6134                    },
6135                );
6136                Ok(())
6137            }
6138            HeapEntry::Array {
6139                elements,
6140                properties,
6141                length_writable,
6142                ..
6143            } => {
6144                match key {
6145                    PropertyKey::Named(name) => {
6146                        if let Some(offset) = array_index(&name) {
6147                            let offset = offset as usize;
6148                            if elements.len() <= offset {
6149                                array_set_length(
6150                                    elements,
6151                                    properties,
6152                                    *length_writable,
6153                                    number_value((offset + 1) as f64),
6154                                    "set array index",
6155                                )?;
6156                            }
6157                            elements[offset] = value;
6158                        } else {
6159                            properties.insert(
6160                                PropertyKey::Named(name),
6161                                Property::Data {
6162                                    value,
6163                                    writable: true,
6164                                    enumerable: true,
6165                                    configurable: true,
6166                                },
6167                            );
6168                        }
6169                    }
6170                    identity @ (PropertyKey::Symbol(_) | PropertyKey::Private(_)) => {
6171                        properties.insert(
6172                            identity,
6173                            Property::Data {
6174                                value,
6175                                writable: true,
6176                                enumerable: true,
6177                                configurable: true,
6178                            },
6179                        );
6180                    }
6181                }
6182                Ok(())
6183            }
6184            HeapEntry::ProcessEnv { .. } => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6185                operation: "set internal process environment",
6186            })),
6187            _ => unreachable!("primitive and identity entries rejected above"),
6188        }
6189    }
6190
6191    fn define_accessor(
6192        &mut self,
6193        object: Value,
6194        key: PropertyKey,
6195        accessor: Value,
6196        kind: AccessorKind,
6197    ) -> Result<(), EvalFailure> {
6198        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6199            Some(index) => {
6200                self.charge_heap(key.charge_bytes() + 8)
6201                    .map_err(EvalFailure::Runtime)?;
6202                let (properties, extensible) = match &mut self.heap[index] {
6203                    HeapEntry::Object {
6204                        properties,
6205                        extensible,
6206                        ..
6207                    }
6208                    | HeapEntry::Generator {
6209                        properties,
6210                        extensible,
6211                        ..
6212                    }
6213                    | HeapEntry::Script {
6214                        properties,
6215                        extensible,
6216                        ..
6217                    }
6218                    | HeapEntry::Array {
6219                        properties,
6220                        extensible,
6221                        ..
6222                    }
6223                    | HeapEntry::Function {
6224                        properties,
6225                        extensible,
6226                        ..
6227                    }
6228                    | HeapEntry::NativeFunction {
6229                        properties,
6230                        extensible,
6231                        ..
6232                    }
6233                    | HeapEntry::RegExp {
6234                        properties,
6235                        extensible,
6236                        ..
6237                    }
6238                    | HeapEntry::Date {
6239                        properties,
6240                        extensible,
6241                        ..
6242                    }
6243                    | HeapEntry::BuiltinIterator {
6244                        properties,
6245                        extensible,
6246                        ..
6247                    }
6248                    | HeapEntry::Collection {
6249                        properties,
6250                        extensible,
6251                        ..
6252                    }
6253                    | HeapEntry::Promise {
6254                        properties,
6255                        extensible,
6256                        ..
6257                    } => (properties, *extensible),
6258                    _ => {
6259                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6260                            operation: "define accessor on primitive",
6261                        }));
6262                    }
6263                };
6264                if properties
6265                    .get(&key)
6266                    .is_some_and(|property| !property.configurable())
6267                    || (!properties.contains_key(&key) && !extensible)
6268                {
6269                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6270                        operation: "define accessor on non-configurable object",
6271                    }));
6272                }
6273                let property = properties.get_mut(&key);
6274                match property {
6275                    Some(Property::Accessor { getter, setter, .. }) => match kind {
6276                        AccessorKind::Getter => *getter = Some(accessor),
6277                        AccessorKind::Setter => *setter = Some(accessor),
6278                    },
6279                    Some(Property::Data { .. }) | None => {
6280                        let (getter, setter) = match kind {
6281                            AccessorKind::Getter => (Some(accessor), None),
6282                            AccessorKind::Setter => (None, Some(accessor)),
6283                        };
6284                        properties.insert(
6285                            key,
6286                            Property::Accessor {
6287                                getter,
6288                                setter,
6289                                enumerable: true,
6290                                configurable: true,
6291                            },
6292                        );
6293                    }
6294                }
6295                Ok(())
6296            }
6297            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6298                operation: "define accessor on host object",
6299            })),
6300        }
6301    }
6302
6303    fn delete_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6304        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6305            Some(index) => match &mut self.heap[index] {
6306                HeapEntry::Object { properties, .. }
6307                | HeapEntry::Generator { properties, .. }
6308                | HeapEntry::Script { properties, .. }
6309                | HeapEntry::Function { properties, .. }
6310                | HeapEntry::NativeFunction { properties, .. }
6311                | HeapEntry::RegExp { properties, .. }
6312                | HeapEntry::Date { properties, .. }
6313                | HeapEntry::BuiltinIterator { properties, .. }
6314                | HeapEntry::Collection { properties, .. }
6315                | HeapEntry::Promise { properties, .. }
6316                | HeapEntry::Timeout { properties, .. } => {
6317                    if properties
6318                        .get(key)
6319                        .is_some_and(|property| !property.configurable())
6320                    {
6321                        return Ok(false);
6322                    }
6323                    properties.remove(key);
6324                    Ok(true)
6325                }
6326                HeapEntry::Array {
6327                    elements,
6328                    properties,
6329                    ..
6330                } => {
6331                    if properties
6332                        .get(key)
6333                        .is_some_and(|property| !property.configurable())
6334                    {
6335                        return Ok(false);
6336                    }
6337                    if properties.remove(key).is_some() {
6338                        return Ok(true);
6339                    }
6340                    if let PropertyKey::Named(name) = key {
6341                        if name.eq_ascii("length") {
6342                            return Ok(false);
6343                        }
6344                        if let Some(offset) = array_index(name) {
6345                            if let Some(element) = elements.get_mut(offset as usize) {
6346                                *element = Value::HOLE;
6347                            }
6348                            return Ok(true);
6349                        }
6350                    }
6351                    Ok(true)
6352                }
6353                HeapEntry::ProcessEnv { .. } => {
6354                    let PropertyKey::Named(name) = key else {
6355                        return Ok(true);
6356                    };
6357                    Ok(name
6358                        .to_utf8_strict()
6359                        .is_ok_and(|name| self.host.delete_env(&name)))
6360                }
6361                HeapEntry::String(_)
6362                | HeapEntry::BigInt(_)
6363                | HeapEntry::Symbol { .. }
6364                | HeapEntry::PrivateName { .. }
6365                | HeapEntry::Iterator { .. }
6366                | HeapEntry::PromiseResolver { .. }
6367                | HeapEntry::PromiseFinally { .. }
6368                | HeapEntry::PromiseAll { .. }
6369                | HeapEntry::AsyncActivation { .. }
6370                | HeapEntry::PromiseAllElement { .. }
6371                | HeapEntry::HashState { .. } => Ok(true),
6372                HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6373                    Ok(false)
6374                }
6375            },
6376            None => Ok(true),
6377        }
6378    }
6379
6380    fn has_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6381        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6382            Some(index) => {
6383                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
6384                    let PropertyKey::Named(name) = key else {
6385                        return Ok(false);
6386                    };
6387                    return Ok(name
6388                        .to_utf8_strict()
6389                        .is_ok_and(|name| self.host.env(&name).is_some()));
6390                }
6391                if matches!(key, PropertyKey::Private(_)) {
6392                    return Ok(self.own_get(index, key).is_some());
6393                }
6394                let mut node = index;
6395                let mut guard = 0;
6396                loop {
6397                    if self.own_get(node, key).is_some() {
6398                        return Ok(true);
6399                    }
6400                    match self.prototype_index(node)? {
6401                        Some(next) => {
6402                            node = next;
6403                            guard += 1;
6404                            if guard > self.heap.len() + 1 {
6405                                return Ok(false);
6406                            }
6407                        }
6408                        None => return Ok(false),
6409                    }
6410                }
6411            }
6412            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6413                operation: "in",
6414            })),
6415        }
6416    }
6417
6418    // ---- aggregates & prototypes ------------------------------------------
6419
6420    pub(crate) fn array_push(&mut self, array: Value, value: Value) -> Result<(), EvalFailure> {
6421        match self.runtime_slot(array).map_err(EvalFailure::Runtime)? {
6422            Some(index) => {
6423                if !matches!(self.heap[index], HeapEntry::Array { .. }) {
6424                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6425                        operation: "push on non-array",
6426                    }));
6427                }
6428                self.charge_heap(8).map_err(EvalFailure::Runtime)?;
6429                if let HeapEntry::Array {
6430                    elements,
6431                    properties,
6432                    length_writable,
6433                    ..
6434                } = &mut self.heap[index]
6435                {
6436                    let offset = elements.len();
6437                    array_set_length(
6438                        elements,
6439                        properties,
6440                        *length_writable,
6441                        number_value((offset + 1) as f64),
6442                        "push beyond non-writable array length",
6443                    )?;
6444                    elements[offset] = value;
6445                }
6446                Ok(())
6447            }
6448            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6449                operation: "push on non-array",
6450            })),
6451        }
6452    }
6453
6454    fn array_extend(&mut self, array: Value, iterable: Value) -> Result<(), EvalFailure> {
6455        let iterator = self.create_iterator(iterable, IteratorKind::Sync)?;
6456        loop {
6457            let (done, value) = self.iterator_next(iterator)?;
6458            if done {
6459                return Ok(());
6460            }
6461            self.array_push(array, value)?;
6462        }
6463    }
6464
6465    fn object_spread(&mut self, target: Value, source: Value) -> Result<(), EvalFailure> {
6466        let target_index = match self.runtime_slot(target).map_err(EvalFailure::Runtime)? {
6467            Some(index)
6468                if matches!(
6469                    self.heap[index],
6470                    HeapEntry::Object { .. }
6471                        | HeapEntry::Generator { .. }
6472                        | HeapEntry::Script { .. }
6473                        | HeapEntry::Array { .. }
6474                        | HeapEntry::Promise { .. }
6475                ) =>
6476            {
6477                index
6478            }
6479            _ => {
6480                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6481                    operation: "object spread target is not an object",
6482                }));
6483            }
6484        };
6485        let keys = self.own_property_keys(source)?;
6486        for key in keys {
6487            if !self.own_property_is_enumerable(source, &key)? {
6488                continue;
6489            }
6490            let value = self.get_property_key(source, &key)?;
6491            self.set_own_data(target_index, key, value)?;
6492        }
6493        Ok(())
6494    }
6495
6496    fn set_prototype(&mut self, object: Value, prototype: Value) -> Result<(), EvalFailure> {
6497        let prototype = match self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? {
6498            Some(_) => Some(prototype),
6499            None => match prototype.decode() {
6500                Some(Decoded::Null) => None,
6501                Some(Decoded::HeapRef(_)) => Some(prototype),
6502                _ => {
6503                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6504                        operation: "set prototype to non-object",
6505                    }));
6506                }
6507            },
6508        };
6509        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6510            Some(index) => match &mut self.heap[index] {
6511                HeapEntry::Object {
6512                    prototype: slot, ..
6513                }
6514                | HeapEntry::Generator {
6515                    prototype: slot, ..
6516                }
6517                | HeapEntry::Script {
6518                    prototype: slot, ..
6519                }
6520                | HeapEntry::Array {
6521                    prototype: slot, ..
6522                }
6523                | HeapEntry::Function {
6524                    prototype: slot, ..
6525                }
6526                | HeapEntry::RegExp {
6527                    prototype: slot, ..
6528                }
6529                | HeapEntry::Date {
6530                    prototype: slot, ..
6531                }
6532                | HeapEntry::BuiltinIterator {
6533                    prototype: slot, ..
6534                }
6535                | HeapEntry::Collection {
6536                    prototype: slot, ..
6537                }
6538                | HeapEntry::Promise {
6539                    prototype: slot, ..
6540                } => {
6541                    *slot = prototype;
6542                    Ok(())
6543                }
6544                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6545                    operation: "set prototype on primitive",
6546                })),
6547            },
6548            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6549                operation: "set prototype on host object",
6550            })),
6551        }
6552    }
6553
6554    pub(crate) fn create_generator(
6555        &mut self,
6556        start: GeneratorStart,
6557    ) -> Result<Value, RuntimeErrorKind> {
6558        self.allocate(HeapEntry::Generator {
6559            state: GeneratorState::SuspendedStart(start),
6560            properties: PropertyMap::default(),
6561            prototype: Some(self.intrinsics.builtins.generator_prototype()),
6562            extensible: true,
6563        })
6564    }
6565
6566    fn resume_generator(
6567        &mut self,
6568        generator: Value,
6569        resume_value: Value,
6570    ) -> Result<Value, EvalFailure> {
6571        let state = self.take_generator_state(generator)?;
6572        if matches!(&state, GeneratorState::Completed) {
6573            return self.iterator_result(Value::UNDEFINED, true);
6574        }
6575
6576        let stop_depth = self.frames.len();
6577        let return_to = self.frames.last().map(|frame| ReturnTo {
6578            destination: None,
6579            call_pc: frame.pc,
6580            constructed: None,
6581        });
6582        let prepared = match state {
6583            GeneratorState::SuspendedStart(start) => self
6584                .push_frame(
6585                    start.target,
6586                    &start.captures,
6587                    start.this_value,
6588                    start.new_target,
6589                    &start.args,
6590                    return_to,
6591                )
6592                .map_err(|error| EvalFailure::Runtime(error.kind)),
6593            GeneratorState::Suspended(activation) => {
6594                self.push_resumed_generator_frame(activation, resume_value, return_to)
6595            }
6596            GeneratorState::Executing | GeneratorState::Completed => unreachable!(),
6597        };
6598        if let Err(failure) = prepared {
6599            self.settle_generator_completed(generator)?;
6600            return Err(failure);
6601        }
6602
6603        let resumed = self.run_generator_activation(stop_depth);
6604        match resumed {
6605            Ok(GeneratorResume::Yield { value, activation }) => {
6606                self.settle_generator_yield(generator, value, activation)
6607            }
6608            Ok(GeneratorResume::Return(value)) => {
6609                self.settle_generator_completed(generator)?;
6610                self.iterator_result(value, true)
6611            }
6612            Ok(GeneratorResume::Throw { value, origin }) => {
6613                self.settle_generator_completed(generator)?;
6614                Err(EvalFailure::ThrowValueOrigin { value, origin })
6615            }
6616            Err(failure) => {
6617                self.settle_generator_completed(generator)?;
6618                Err(failure)
6619            }
6620        }
6621    }
6622
6623    fn push_resumed_generator_frame(
6624        &mut self,
6625        activation: SuspendedActivation,
6626        resume_value: Value,
6627        return_to: Option<ReturnTo>,
6628    ) -> Result<(), EvalFailure> {
6629        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6630            self.release_suspended_activation_registers(activation.registers.len());
6631            return Err(EvalFailure::Runtime(RuntimeErrorKind::CallDepthExceeded {
6632                limit: self.limits.max_call_depth,
6633            }));
6634        }
6635        let suspend_pc = activation
6636            .resume_token
6637            .checked_sub(1)
6638            .expect("suspended generator token is nonzero") as usize;
6639        let instruction = self.module_code(activation.target.module).functions()
6640            [activation.target.function.get() as usize]
6641            .code()[suspend_pc];
6642        let Instruction::Suspend { dst, resume, .. } = instruction else {
6643            unreachable!("generator resume token names a suspend instruction");
6644        };
6645        let mut frame = Frame {
6646            module: activation.target.module,
6647            function: activation.target.function.get() as usize,
6648            pc: resume.get() as usize,
6649            registers: activation.registers,
6650            return_to,
6651            this_value: activation.this_value,
6652            new_target: activation.new_target,
6653            args: activation.args,
6654            arguments_object: activation.arguments_object,
6655        };
6656        frame.registers[dst.get() as usize] = resume_value;
6657        self.frames.push(frame);
6658        Ok(())
6659    }
6660
6661    fn run_generator_activation(
6662        &mut self,
6663        stop_depth: usize,
6664    ) -> Result<GeneratorResume, EvalFailure> {
6665        self.last_completion = None;
6666        self.pending_generator_resume = None;
6667        self.callback_boundaries.push(stop_depth);
6668        self.generator_boundaries.push(stop_depth);
6669        let result = self.run_loop(stop_depth);
6670        self.generator_boundaries
6671            .pop()
6672            .expect("generator execution owns its suspend boundary");
6673        self.callback_boundaries
6674            .pop()
6675            .expect("generator execution owns its unwind boundary");
6676
6677        match result {
6678            Ok(Some(execution)) => Ok(GeneratorResume::Return(execution.value)),
6679            Ok(None) => {
6680                if let Some(resume) = self.pending_generator_resume.take() {
6681                    return Ok(resume);
6682                }
6683                let value = self.last_completion.take().unwrap_or(Value::UNDEFINED);
6684                Ok(GeneratorResume::Return(value))
6685            }
6686            Err(error) => {
6687                self.unwind_frames_to(stop_depth);
6688                match error.kind {
6689                    RuntimeErrorKind::UncaughtThrow { value, origin } => {
6690                        Ok(GeneratorResume::Throw { value, origin })
6691                    }
6692                    kind => Err(EvalFailure::Runtime(kind)),
6693                }
6694            }
6695        }
6696    }
6697
6698    pub(crate) fn take_generator_state(
6699        &mut self,
6700        generator: Value,
6701    ) -> Result<GeneratorState, EvalFailure> {
6702        let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6703            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6704                operation: "Generator.prototype.next called on incompatible receiver",
6705            }));
6706        };
6707        let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6708            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6709                operation: "Generator.prototype.next called on incompatible receiver",
6710            }));
6711        };
6712        match std::mem::replace(state, GeneratorState::Executing) {
6713            GeneratorState::Executing => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6714                operation: "generator is already running",
6715            })),
6716            GeneratorState::Completed => {
6717                *state = GeneratorState::Completed;
6718                Ok(GeneratorState::Completed)
6719            }
6720            state => Ok(state),
6721        }
6722    }
6723
6724    pub(crate) fn settle_generator_yield(
6725        &mut self,
6726        generator: Value,
6727        value: Value,
6728        activation: SuspendedActivation,
6729    ) -> Result<Value, EvalFailure> {
6730        let register_count = activation.registers.len();
6731        let result = match self.iterator_result(value, false) {
6732            Ok(result) => result,
6733            Err(failure) => {
6734                self.release_suspended_activation_registers(register_count);
6735                self.replace_executing_generator(generator, GeneratorState::Completed)?;
6736                return Err(failure);
6737            }
6738        };
6739        if let Err(failure) =
6740            self.replace_executing_generator(generator, GeneratorState::Suspended(activation))
6741        {
6742            self.release_suspended_activation_registers(register_count);
6743            return Err(failure);
6744        }
6745        Ok(result)
6746    }
6747
6748    pub(crate) fn settle_generator_completed(
6749        &mut self,
6750        generator: Value,
6751    ) -> Result<(), EvalFailure> {
6752        self.replace_executing_generator(generator, GeneratorState::Completed)
6753    }
6754
6755    fn replace_executing_generator(
6756        &mut self,
6757        generator: Value,
6758        next: GeneratorState,
6759    ) -> Result<(), EvalFailure> {
6760        let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6761            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6762                value: generator,
6763            }));
6764        };
6765        let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6766            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6767                value: generator,
6768            }));
6769        };
6770        if !matches!(state, GeneratorState::Executing) {
6771            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6772                value: generator,
6773            }));
6774        }
6775        *state = next;
6776        Ok(())
6777    }
6778
6779    /// Starts an ordinary async function: creates the implicit result Promise,
6780    /// drives the body synchronously to its first `await` or completion under a
6781    /// detached suspend boundary, and returns the Promise. A `return` resolves
6782    /// it and an escaping throw rejects it; a runtime limit failure stays fatal.
6783    pub(crate) fn start_async_call(
6784        &mut self,
6785        target: RuntimeFunction,
6786        captures: &[Value],
6787        this_value: Value,
6788        new_target: Value,
6789        arguments: &[Value],
6790    ) -> Result<Value, EvalFailure> {
6791        let promise = self.create_promise()?;
6792        let record = self.create_async_activation(promise)?;
6793        let stop_depth = self.frames.len();
6794        let return_to = self.frames.last().map(|frame| ReturnTo {
6795            destination: None,
6796            call_pc: frame.pc,
6797            constructed: None,
6798        });
6799        self.push_frame(
6800            target, captures, this_value, new_target, arguments, return_to,
6801        )
6802        .map_err(|error| EvalFailure::Runtime(error.kind))?;
6803        let step = self.drive_async_activation(stop_depth, None);
6804        self.settle_async_step(record, promise, step)?;
6805        Ok(promise)
6806    }
6807
6808    /// Resumes a suspended async activation inside its Promise reaction job. On
6809    /// fulfillment the awaited value is written to `Suspend.dst`; on rejection
6810    /// the reason is thrown at the `Suspend` pc so a covering `try`/`catch`
6811    /// runs. The activation is one-shot; a second resume is a hard error.
6812    fn resume_async(
6813        &mut self,
6814        record: Value,
6815        value: Value,
6816        rejection: Option<ThrowOrigin>,
6817    ) -> Result<(), RuntimeErrorKind> {
6818        let promise = self.async_activation_promise(record)?;
6819        let activation = self.take_async_activation(record)?;
6820        let register_count = activation.registers.len();
6821        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6822            self.release_suspended_activation_registers(register_count);
6823            return Err(RuntimeErrorKind::CallDepthExceeded {
6824                limit: self.limits.max_call_depth,
6825            });
6826        }
6827        let suspend_pc = activation
6828            .resume_token
6829            .checked_sub(1)
6830            .expect("suspended async token is nonzero") as usize;
6831        let instruction = self.module_code(activation.target.module).functions()
6832            [activation.target.function.get() as usize]
6833            .code()[suspend_pc];
6834        let Instruction::Suspend { dst, resume, .. } = instruction else {
6835            unreachable!("async resume token names a suspend instruction");
6836        };
6837        let stop_depth = self.frames.len();
6838        let return_to = self.frames.last().map(|frame| ReturnTo {
6839            destination: None,
6840            call_pc: frame.pc,
6841            constructed: None,
6842        });
6843        let mut frame = Frame {
6844            module: activation.target.module,
6845            function: activation.target.function.get() as usize,
6846            pc: resume.get() as usize,
6847            registers: activation.registers,
6848            return_to,
6849            this_value: activation.this_value,
6850            new_target: activation.new_target,
6851            args: activation.args,
6852            arguments_object: activation.arguments_object,
6853        };
6854        let inject = match rejection {
6855            None => {
6856                frame.registers[dst.get() as usize] = value;
6857                None
6858            }
6859            Some(origin) => Some((value, origin, suspend_pc)),
6860        };
6861        self.frames.push(frame);
6862        let step = self.drive_async_activation(stop_depth, inject);
6863        match self.settle_async_step(record, promise, step) {
6864            Ok(()) => Ok(()),
6865            Err(EvalFailure::Runtime(kind)) => Err(kind),
6866            Err(_) => Err(RuntimeErrorKind::InvalidValue { value: record }),
6867        }
6868    }
6869
6870    /// Runs the interpreter loop for a detached async activation under one
6871    /// suspend and one unwind boundary, optionally injecting a rejection at the
6872    /// resumed `Suspend` pc first. It reports the awaited value on suspension,
6873    /// the returned value on completion, or an uncaught throw; runtime limit
6874    /// failures propagate as fatal `EvalFailure::Runtime`.
6875    fn drive_async_activation(
6876        &mut self,
6877        stop_depth: usize,
6878        inject: Option<(Value, ThrowOrigin, usize)>,
6879    ) -> Result<AsyncStep, EvalFailure> {
6880        self.last_completion = None;
6881        self.pending_async_suspend = None;
6882        self.callback_boundaries.push(stop_depth);
6883        self.async_boundaries.push(stop_depth);
6884        let result = match inject {
6885            None => self.run_loop(stop_depth),
6886            Some((value, origin, faulting_pc)) => match self.throw(value, origin, faulting_pc) {
6887                Ok(()) => self.run_loop(stop_depth),
6888                Err(error) => Err(error),
6889            },
6890        };
6891        self.async_boundaries
6892            .pop()
6893            .expect("async execution owns its suspend boundary");
6894        self.callback_boundaries
6895            .pop()
6896            .expect("async execution owns its unwind boundary");
6897        match result {
6898            Ok(Some(execution)) => Ok(AsyncStep::Return(execution.value)),
6899            Ok(None) => {
6900                if let Some((awaited, activation)) = self.pending_async_suspend.take() {
6901                    Ok(AsyncStep::Suspend {
6902                        awaited,
6903                        activation,
6904                    })
6905                } else {
6906                    Ok(AsyncStep::Return(
6907                        self.last_completion.take().unwrap_or(Value::UNDEFINED),
6908                    ))
6909                }
6910            }
6911            Err(error) => {
6912                self.unwind_frames_to(stop_depth);
6913                match error.kind {
6914                    RuntimeErrorKind::UncaughtThrow { value, origin } => {
6915                        Ok(AsyncStep::Throw { value, origin })
6916                    }
6917                    kind => Err(EvalFailure::Runtime(kind)),
6918                }
6919            }
6920        }
6921    }
6922
6923    /// Settles the result Promise (or arms the next await) for one async step.
6924    fn settle_async_step(
6925        &mut self,
6926        record: Value,
6927        promise: Value,
6928        step: Result<AsyncStep, EvalFailure>,
6929    ) -> Result<(), EvalFailure> {
6930        match step {
6931            Ok(AsyncStep::Suspend {
6932                awaited,
6933                activation,
6934            }) => {
6935                let register_count = activation.registers.len();
6936                let result = self
6937                    .store_async_activation(record, activation)
6938                    .and_then(|()| self.await_promise(awaited, record));
6939                if result.is_err() {
6940                    let released = self
6941                        .take_async_activation(record)
6942                        .map_or(register_count, |stored| stored.registers.len());
6943                    self.release_suspended_activation_registers(released);
6944                }
6945                result
6946            }
6947            Ok(AsyncStep::Return(value)) => self
6948                .resolve_promise(promise, value)
6949                .map_err(EvalFailure::Runtime),
6950            Ok(AsyncStep::Throw { value, origin }) => self
6951                .reject_promise(promise, value, origin)
6952                .map_err(EvalFailure::Runtime),
6953            Err(failure) => Err(failure),
6954        }
6955    }
6956
6957    /// Resolves the awaited value through Promise resolution and attaches the
6958    /// two direct resume reactions that point only at the activation record. An
6959    /// already-settled Promise costs exactly one microtask tick.
6960    fn await_promise(&mut self, awaited: Value, record: Value) -> Result<(), EvalFailure> {
6961        let promise = self.promise_resolve(awaited)?;
6962        let index = self
6963            .runtime_slot(promise)
6964            .map_err(EvalFailure::Runtime)?
6965            .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6966                value: promise,
6967            }))?;
6968        let settled = match &self.heap[index] {
6969            HeapEntry::Promise {
6970                state: PromiseState::Pending { .. },
6971                ..
6972            } => None,
6973            HeapEntry::Promise {
6974                state: PromiseState::Fulfilled { value },
6975                ..
6976            } => Some((true, *value, ThrowOrigin::Bytecode)),
6977            HeapEntry::Promise {
6978                state: PromiseState::Rejected { reason, origin },
6979                ..
6980            } => Some((false, *reason, *origin)),
6981            _ => {
6982                return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6983                    value: promise,
6984                }));
6985            }
6986        };
6987        if let Some((fulfilled, value, origin)) = settled {
6988            self.ensure_microtask_capacity(1)
6989                .map_err(EvalFailure::Runtime)?;
6990            let reaction = if fulfilled {
6991                PromiseReaction::AsyncFulfill { activation: record }
6992            } else {
6993                PromiseReaction::AsyncReject { activation: record }
6994            };
6995            self.microtasks.push_back(MicrotaskJob::Reaction {
6996                reaction,
6997                value,
6998                origin,
6999            });
7000            return Ok(());
7001        }
7002        self.charge_promise_reactions(2)?;
7003        let HeapEntry::Promise {
7004            state:
7005                PromiseState::Pending {
7006                    fulfill_reactions,
7007                    reject_reactions,
7008                },
7009            ..
7010        } = &mut self.heap[index]
7011        else {
7012            unreachable!("pending Promise state was checked before reaction registration");
7013        };
7014        fulfill_reactions.push(PromiseReaction::AsyncFulfill { activation: record });
7015        reject_reactions.push(PromiseReaction::AsyncReject { activation: record });
7016        Ok(())
7017    }
7018
7019    fn create_async_activation(&mut self, promise: Value) -> Result<Value, EvalFailure> {
7020        self.allocate(HeapEntry::AsyncActivation {
7021            activation: None,
7022            promise,
7023        })
7024        .map_err(EvalFailure::Runtime)
7025    }
7026
7027    fn store_async_activation(
7028        &mut self,
7029        record: Value,
7030        activation: SuspendedActivation,
7031    ) -> Result<(), EvalFailure> {
7032        let index = self
7033            .runtime_slot(record)
7034            .map_err(EvalFailure::Runtime)?
7035            .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7036                value: record,
7037            }))?;
7038        let HeapEntry::AsyncActivation {
7039            activation: slot, ..
7040        } = &mut self.heap[index]
7041        else {
7042            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7043                value: record,
7044            }));
7045        };
7046        *slot = Some(activation);
7047        Ok(())
7048    }
7049
7050    /// Takes the one suspended activation out of the record, making resume
7051    /// one-shot. A second take (a second resume) is a hard invalid-state error.
7052    fn take_async_activation(
7053        &mut self,
7054        record: Value,
7055    ) -> Result<SuspendedActivation, RuntimeErrorKind> {
7056        let index = self
7057            .runtime_slot(record)?
7058            .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
7059        let HeapEntry::AsyncActivation {
7060            activation: slot, ..
7061        } = &mut self.heap[index]
7062        else {
7063            return Err(RuntimeErrorKind::InvalidValue { value: record });
7064        };
7065        slot.take()
7066            .ok_or(RuntimeErrorKind::InvalidValue { value: record })
7067    }
7068
7069    fn async_activation_promise(&self, record: Value) -> Result<Value, RuntimeErrorKind> {
7070        let index = self
7071            .runtime_slot(record)?
7072            .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
7073        let HeapEntry::AsyncActivation { promise, .. } = &self.heap[index] else {
7074            return Err(RuntimeErrorKind::InvalidValue { value: record });
7075        };
7076        Ok(*promise)
7077    }
7078
7079    pub(crate) fn iterator_result(
7080        &mut self,
7081        value: Value,
7082        done: bool,
7083    ) -> Result<Value, EvalFailure> {
7084        let result = self
7085            .allocate(HeapEntry::Object {
7086                properties: PropertyMap::default(),
7087                prototype: Some(self.intrinsics.object_prototype),
7088                boxed_primitive: None,
7089                extensible: true,
7090            })
7091            .map_err(EvalFailure::Runtime)?;
7092        self.set_data_property(result, "value", value)?;
7093        self.set_data_property(result, "done", Value::boolean(done))?;
7094        Ok(result)
7095    }
7096
7097    // ---- iterators ---------------------------------------------------------
7098
7099    fn create_iterator(&mut self, src: Value, kind: IteratorKind) -> Result<Value, EvalFailure> {
7100        if kind == IteratorKind::Keys {
7101            let keys = self.enumerable_keys(src)?;
7102            return self
7103                .allocate(HeapEntry::Iterator {
7104                    state: IteratorState::Keys { index: 0, keys },
7105                })
7106                .map_err(EvalFailure::Runtime);
7107        }
7108
7109        let iterator_symbol = self.intrinsics.builtins.symbol_iterator();
7110        let iterator_key = self.to_property_key(iterator_symbol)?;
7111        let method = self.get_property_key(src, &iterator_key)?;
7112        if !self.is_callable(method)? {
7113            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7114                operation: "value is not iterable",
7115            }));
7116        }
7117        let iterator = self.call_value(method, src, &[])?;
7118        if !self.is_object(iterator) {
7119            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7120                operation: "iterator method returned a non-object",
7121            }));
7122        }
7123        let next = self.get_named_property(iterator, "next")?;
7124        self.create_protocol_iterator(iterator, next)
7125    }
7126
7127    pub(crate) fn create_protocol_iterator(
7128        &mut self,
7129        iterator: Value,
7130        next: Value,
7131    ) -> Result<Value, EvalFailure> {
7132        self.allocate(HeapEntry::Iterator {
7133            state: IteratorState::Protocol { iterator, next },
7134        })
7135        .map_err(EvalFailure::Runtime)
7136    }
7137
7138    fn own_property_keys(&self, src: Value) -> Result<Vec<PropertyKey>, EvalFailure> {
7139        match self.runtime_slot(src).map_err(EvalFailure::Runtime)? {
7140            Some(index) => match &self.heap[index] {
7141                HeapEntry::Object { properties, .. }
7142                | HeapEntry::Generator { properties, .. }
7143                | HeapEntry::Script { properties, .. }
7144                | HeapEntry::Function { properties, .. }
7145                | HeapEntry::NativeFunction { properties, .. }
7146                | HeapEntry::RegExp { properties, .. }
7147                | HeapEntry::Date { properties, .. }
7148                | HeapEntry::BuiltinIterator { properties, .. }
7149                | HeapEntry::Collection { properties, .. }
7150                | HeapEntry::Promise { properties, .. }
7151                | HeapEntry::Timeout { properties, .. } => Ok(ordered_property_keys(properties)),
7152                HeapEntry::Array {
7153                    elements,
7154                    properties,
7155                    ..
7156                } => {
7157                    let mut indices: Vec<(usize, PropertyKey)> = elements
7158                        .iter()
7159                        .enumerate()
7160                        .filter(|(_, element)| **element != Value::HOLE)
7161                        .map(|(offset, _)| {
7162                            (
7163                                offset,
7164                                PropertyKey::Named(EcmaString::from_utf8(&offset.to_string())),
7165                            )
7166                        })
7167                        .collect();
7168                    let mut suffix = Vec::new();
7169                    for key in ordered_property_keys(properties) {
7170                        let Some(offset) = key.as_string().and_then(array_index) else {
7171                            suffix.push(key);
7172                            continue;
7173                        };
7174                        let offset = offset as usize;
7175                        if elements
7176                            .get(offset)
7177                            .is_some_and(|element| *element != Value::HOLE)
7178                        {
7179                            continue;
7180                        }
7181                        indices.push((offset, key));
7182                    }
7183                    indices.sort_unstable_by_key(|(offset, _)| *offset);
7184                    Ok(indices
7185                        .into_iter()
7186                        .map(|(_, key)| key)
7187                        .chain(suffix)
7188                        .collect())
7189                }
7190                HeapEntry::String(text) => Ok((0..text.len_units())
7191                    .map(|index| PropertyKey::Named(EcmaString::from_utf8(&index.to_string())))
7192                    .collect()),
7193                HeapEntry::ModuleNamespace { module } => {
7194                    let mut names: Vec<EcmaString> = self
7195                        .program_module(*module)
7196                        .exports
7197                        .iter()
7198                        .map(|export| self.constant_text(*module, export.name).clone())
7199                        .collect();
7200                    names.sort();
7201                    Ok(names.into_iter().map(PropertyKey::Named).collect())
7202                }
7203                HeapEntry::ExternalModuleNamespace { specifier } => Ok(self.registry.external
7204                    [specifier]
7205                    .exports
7206                    .keys()
7207                    .cloned()
7208                    .map(PropertyKey::Named)
7209                    .collect()),
7210                HeapEntry::ProcessEnv { .. }
7211                | HeapEntry::BigInt(_)
7212                | HeapEntry::Symbol { .. }
7213                | HeapEntry::PrivateName { .. }
7214                | HeapEntry::HashState { .. }
7215                | HeapEntry::Iterator { .. }
7216                | HeapEntry::PromiseResolver { .. }
7217                | HeapEntry::PromiseFinally { .. }
7218                | HeapEntry::PromiseAll { .. }
7219                | HeapEntry::AsyncActivation { .. }
7220                | HeapEntry::PromiseAllElement { .. } => Ok(Vec::new()),
7221            },
7222            None => Ok(Vec::new()),
7223        }
7224    }
7225
7226    fn own_property_is_enumerable(
7227        &self,
7228        src: Value,
7229        key: &PropertyKey,
7230    ) -> Result<bool, EvalFailure> {
7231        let Some(index) = self.runtime_slot(src).map_err(EvalFailure::Runtime)? else {
7232            return Ok(false);
7233        };
7234        Ok(match &self.heap[index] {
7235            HeapEntry::Array {
7236                elements,
7237                properties,
7238                ..
7239            } => properties.get(key).map_or_else(
7240                || {
7241                    key.as_string().is_some_and(|name| {
7242                        array_index(name).is_some_and(|offset| {
7243                            elements
7244                                .get(offset as usize)
7245                                .is_some_and(|element| *element != Value::HOLE)
7246                        })
7247                    })
7248                },
7249                Property::enumerable,
7250            ),
7251            HeapEntry::String(text) => key.as_string().is_some_and(|name| {
7252                array_index(name).is_some_and(|offset| (offset as usize) < text.len_units())
7253            }),
7254            HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
7255                matches!(key, PropertyKey::Named(_))
7256            }
7257            HeapEntry::Object { properties, .. }
7258            | HeapEntry::Generator { properties, .. }
7259            | HeapEntry::Script { properties, .. }
7260            | HeapEntry::Function { properties, .. }
7261            | HeapEntry::NativeFunction { properties, .. }
7262            | HeapEntry::RegExp { properties, .. }
7263            | HeapEntry::Date { properties, .. }
7264            | HeapEntry::BuiltinIterator { properties, .. }
7265            | HeapEntry::Collection { properties, .. }
7266            | HeapEntry::Promise { properties, .. }
7267            | HeapEntry::Timeout { properties, .. } => {
7268                properties.get(key).is_some_and(Property::enumerable)
7269            }
7270            _ => false,
7271        })
7272    }
7273
7274    fn enumerable_keys(&self, src: Value) -> Result<Vec<EcmaString>, EvalFailure> {
7275        let mut names = Vec::new();
7276        for key in self.own_property_keys(src)? {
7277            if !self.own_property_is_enumerable(src, &key)? {
7278                continue;
7279            }
7280            if let PropertyKey::Named(name) = key {
7281                names.push(name);
7282            }
7283        }
7284        Ok(names)
7285    }
7286
7287    fn iterator_next(&mut self, iterator: Value) -> Result<(bool, Value), EvalFailure> {
7288        let (callee, this_value) = match self.prepare_iterator_next(iterator)? {
7289            IteratorNextPrepared::Ready { done, value } => return Ok((done, value)),
7290            IteratorNextPrepared::Call { callee, this_value } => (callee, this_value),
7291        };
7292
7293        let result = self.call_value(callee, this_value, &[])?;
7294        if !self.is_object(result) {
7295            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7296                operation: "iterator next returned a non-object",
7297            }));
7298        }
7299        let done = self.get_named_property(result, "done")?;
7300        if self.truthy(done) {
7301            return Ok((true, Value::UNDEFINED));
7302        }
7303        let value = self.get_named_property(result, "value")?;
7304        Ok((false, value))
7305    }
7306
7307    pub(crate) fn prepare_iterator_next(
7308        &mut self,
7309        iterator: Value,
7310    ) -> Result<IteratorNextPrepared, EvalFailure> {
7311        let iterator_index = self
7312            .runtime_slot(iterator)
7313            .map_err(EvalFailure::Runtime)?
7314            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
7315                operation: "iterator next on non-iterator",
7316            }))?;
7317        match &self.heap[iterator_index] {
7318            HeapEntry::Iterator {
7319                state: IteratorState::Keys { index, keys },
7320            } => {
7321                let Some(text) = keys.get(*index).cloned() else {
7322                    return Ok(IteratorNextPrepared::Ready {
7323                        done: true,
7324                        value: Value::UNDEFINED,
7325                    });
7326                };
7327                let value = self
7328                    .allocate(HeapEntry::String(text))
7329                    .map_err(EvalFailure::Runtime)?;
7330                self.advance_iterator(iterator_index);
7331                Ok(IteratorNextPrepared::Ready { done: false, value })
7332            }
7333            HeapEntry::Iterator {
7334                state: IteratorState::Protocol { iterator, next },
7335            } => Ok(IteratorNextPrepared::Call {
7336                callee: *next,
7337                this_value: *iterator,
7338            }),
7339            _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7340                operation: "iterator next on non-iterator",
7341            })),
7342        }
7343    }
7344
7345    pub(crate) fn iterable_values(&mut self, source: Value) -> Result<Vec<Value>, EvalFailure> {
7346        let iterator = self.create_iterator(source, IteratorKind::Sync)?;
7347        let mut values = Vec::new();
7348        loop {
7349            let (done, value) = self.iterator_next(iterator)?;
7350            if done {
7351                return Ok(values);
7352            }
7353            let bytes = values
7354                .len()
7355                .checked_add(1)
7356                .and_then(|length| length.checked_mul(std::mem::size_of::<Value>()))
7357                .ok_or(EvalFailure::Runtime(
7358                    RuntimeErrorKind::HeapByteLimitExceeded {
7359                        limit: self.limits.max_heap_bytes,
7360                    },
7361                ))?;
7362            self.ensure_allocation_capacity(1, bytes)
7363                .map_err(EvalFailure::Runtime)?;
7364            values.push(value);
7365        }
7366    }
7367
7368    fn advance_iterator(&mut self, iterator_index: usize) {
7369        if let HeapEntry::Iterator {
7370            state: IteratorState::Keys { index, .. },
7371        } = &mut self.heap[iterator_index]
7372        {
7373            *index += 1;
7374        }
7375    }
7376
7377    // ---- operators & coercions --------------------------------------------
7378
7379    fn eval_unary(&mut self, op: UnaryOp, operand: Value) -> Result<Value, EvalFailure> {
7380        match op {
7381            UnaryOp::Void => Ok(Value::UNDEFINED),
7382            UnaryOp::TypeOf => {
7383                let text = EcmaString::from_utf8(self.type_of(operand));
7384                self.allocate(HeapEntry::String(text))
7385                    .map_err(EvalFailure::Runtime)
7386            }
7387            UnaryOp::Plus => self.to_number(operand),
7388            UnaryOp::Negate => {
7389                if let Some(text) = self.bigint_text(operand) {
7390                    let negated = if text == "0" {
7391                        "0".to_owned()
7392                    } else if let Some(positive) = text.strip_prefix('-') {
7393                        positive.to_owned()
7394                    } else {
7395                        format!("-{text}")
7396                    };
7397                    return self
7398                        .allocate(HeapEntry::BigInt(negated))
7399                        .map_err(EvalFailure::Runtime);
7400                }
7401                let number =
7402                    numeric_f64(self.to_number(operand)?).expect("ToNumber returns numeric");
7403                Ok(number_value(-number))
7404            }
7405            UnaryOp::BitwiseNot => {
7406                if let Some(text) = self.bigint_text(operand) {
7407                    let value = text.parse::<i128>().map_err(|_| {
7408                        EvalFailure::Throw(ThrowOrigin::RangeError {
7409                            operation: "bigint bitwise not",
7410                        })
7411                    })?;
7412                    return self
7413                        .allocate(HeapEntry::BigInt((!value).to_string()))
7414                        .map_err(EvalFailure::Runtime);
7415                }
7416                Ok(Value::int32(
7417                    (!to_int32(numeric_f64(self.to_number(operand)?).unwrap())) as u32,
7418                ))
7419            }
7420            UnaryOp::LogicalNot => Ok(Value::boolean(!self.truthy(operand))),
7421        }
7422    }
7423
7424    fn eval_binary(
7425        &mut self,
7426        op: BinaryOp,
7427        left: Value,
7428        right: Value,
7429    ) -> Result<Value, EvalFailure> {
7430        match op {
7431            BinaryOp::StrictEqual => Ok(Value::boolean(self.strict_equal(left, right))),
7432            BinaryOp::StrictNotEqual => Ok(Value::boolean(!self.strict_equal(left, right))),
7433            BinaryOp::Equal | BinaryOp::NotEqual => {
7434                let equal = self.abstract_equal(left, right)?;
7435                Ok(Value::boolean(if op == BinaryOp::Equal {
7436                    equal
7437                } else {
7438                    !equal
7439                }))
7440            }
7441            BinaryOp::LessThan
7442            | BinaryOp::LessThanOrEqual
7443            | BinaryOp::GreaterThan
7444            | BinaryOp::GreaterThanOrEqual => {
7445                let ordering = self.relational_compare(left, right)?;
7446                let result = match (op, ordering) {
7447                    (_, None) => false,
7448                    (BinaryOp::LessThan, Some(order)) => order == Ordering::Less,
7449                    (BinaryOp::LessThanOrEqual, Some(order)) => order != Ordering::Greater,
7450                    (BinaryOp::GreaterThan, Some(order)) => order == Ordering::Greater,
7451                    (BinaryOp::GreaterThanOrEqual, Some(order)) => order != Ordering::Less,
7452                    _ => unreachable!(),
7453                };
7454                Ok(Value::boolean(result))
7455            }
7456            BinaryOp::InstanceOf => self.instance_of(left, right).map(Value::boolean),
7457            BinaryOp::In => {
7458                let key = self.to_property_key(left)?;
7459                self.has_property(right, &key).map(Value::boolean)
7460            }
7461            BinaryOp::Add => self.add(left, right),
7462            BinaryOp::Subtract
7463            | BinaryOp::Multiply
7464            | BinaryOp::Divide
7465            | BinaryOp::Remainder
7466            | BinaryOp::Exponent
7467            | BinaryOp::BitAnd
7468            | BinaryOp::BitOr
7469            | BinaryOp::BitXor
7470            | BinaryOp::ShiftLeft
7471            | BinaryOp::ShiftRight
7472            | BinaryOp::UnsignedShiftRight => self.numeric_binary(op, left, right),
7473        }
7474    }
7475
7476    fn add(&mut self, left: Value, right: Value) -> Result<Value, EvalFailure> {
7477        let left = self.to_primitive_default(left)?;
7478        let right = self.to_primitive_default(right)?;
7479        let left_string = self.string_text(left).cloned();
7480        let right_string = self.string_text(right).cloned();
7481        if left_string.is_some() || right_string.is_some() {
7482            let left = match left_string {
7483                Some(text) => text,
7484                None => self.to_string(left)?,
7485            };
7486            let right = match right_string {
7487                Some(text) => text,
7488                None => self.to_string(right)?,
7489            };
7490            let mut builder = EcmaStringBuilder::with_capacity(
7491                left.len_units().saturating_add(right.len_units()),
7492            );
7493            for &unit in left.as_units() {
7494                builder.push_unit(unit);
7495            }
7496            for &unit in right.as_units() {
7497                builder.push_unit(unit);
7498            }
7499            return self
7500                .allocate(HeapEntry::String(builder.finish()))
7501                .map_err(EvalFailure::Runtime);
7502        }
7503        let left_bigint = self.bigint_text(left).map(str::to_owned);
7504        let right_bigint = self.bigint_text(right).map(str::to_owned);
7505        match (left_bigint, right_bigint) {
7506            (Some(left), Some(right)) => {
7507                let sum = bigint_i128(&left)?
7508                    .checked_add(bigint_i128(&right)?)
7509                    .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError {
7510                        operation: "bigint add overflow",
7511                    }))?;
7512                return self
7513                    .allocate(HeapEntry::BigInt(sum.to_string()))
7514                    .map_err(EvalFailure::Runtime);
7515            }
7516            (Some(_), None) | (None, Some(_)) => {
7517                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7518                    operation: "add bigint and number",
7519                }));
7520            }
7521            (None, None) => {}
7522        }
7523        let left = numeric_f64(self.to_number(left)?).unwrap();
7524        let right = numeric_f64(self.to_number(right)?).unwrap();
7525        Ok(number_value(left + right))
7526    }
7527
7528    fn numeric_binary(
7529        &mut self,
7530        op: BinaryOp,
7531        left: Value,
7532        right: Value,
7533    ) -> Result<Value, EvalFailure> {
7534        let left_bigint = self.bigint_text(left).map(str::to_owned);
7535        let right_bigint = self.bigint_text(right).map(str::to_owned);
7536        if left_bigint.is_some() || right_bigint.is_some() {
7537            let (Some(left), Some(right)) = (left_bigint, right_bigint) else {
7538                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7539                    operation: "mix bigint and number",
7540                }));
7541            };
7542            let result = bigint_binary(op, &left, &right)?;
7543            return self
7544                .allocate(HeapEntry::BigInt(result))
7545                .map_err(EvalFailure::Runtime);
7546        }
7547        let left = numeric_f64(self.to_number(left)?).unwrap();
7548        let right = numeric_f64(self.to_number(right)?).unwrap();
7549        let value = match op {
7550            BinaryOp::Subtract => number_value(left - right),
7551            BinaryOp::Multiply => number_value(left * right),
7552            BinaryOp::Divide => Value::number(left / right),
7553            BinaryOp::Remainder => Value::number(left % right),
7554            BinaryOp::Exponent => Value::number(left.powf(right)),
7555            BinaryOp::BitAnd => Value::int32((to_int32(left) & to_int32(right)) as u32),
7556            BinaryOp::BitOr => Value::int32((to_int32(left) | to_int32(right)) as u32),
7557            BinaryOp::BitXor => Value::int32((to_int32(left) ^ to_int32(right)) as u32),
7558            BinaryOp::ShiftLeft => {
7559                Value::int32(to_int32(left).wrapping_shl(to_uint32(right) & 31) as u32)
7560            }
7561            BinaryOp::ShiftRight => {
7562                Value::int32((to_int32(left) >> (to_uint32(right) & 31)) as u32)
7563            }
7564            BinaryOp::UnsignedShiftRight => {
7565                number_value((to_uint32(left) >> (to_uint32(right) & 31)) as f64)
7566            }
7567            _ => unreachable!("numeric binary operator partition"),
7568        };
7569        Ok(value)
7570    }
7571
7572    fn coercion_is_primitive(&self, value: Value) -> Result<bool, EvalFailure> {
7573        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
7574            return Ok(true);
7575        };
7576        Ok(matches!(
7577            self.heap[index],
7578            HeapEntry::String(_)
7579                | HeapEntry::BigInt(_)
7580                | HeapEntry::Symbol { .. }
7581                | HeapEntry::PrivateName { .. }
7582        ))
7583    }
7584
7585    fn to_primitive_default(&mut self, value: Value) -> Result<Value, EvalFailure> {
7586        let prefer_string = self
7587            .runtime_slot(value)
7588            .map_err(EvalFailure::Runtime)?
7589            .is_some_and(|index| matches!(self.heap[index], HeapEntry::Date { .. }));
7590        self.to_primitive_observable(value, prefer_string)
7591    }
7592
7593    pub(crate) fn to_primitive_observable(
7594        &mut self,
7595        value: Value,
7596        prefer_string: bool,
7597    ) -> Result<Value, EvalFailure> {
7598        if self.coercion_is_primitive(value)? {
7599            return Ok(value);
7600        }
7601        let methods = if prefer_string {
7602            ["toString", "valueOf"]
7603        } else {
7604            ["valueOf", "toString"]
7605        };
7606        for name in methods {
7607            let method = self.get_named_property(value, name)?;
7608            if !self.is_callable(method)? {
7609                continue;
7610            }
7611            let primitive = self.call_value(method, value, &[])?;
7612            if self.coercion_is_primitive(primitive)? {
7613                return Ok(primitive);
7614            }
7615        }
7616        Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7617            operation: "cannot convert object to primitive",
7618        }))
7619    }
7620
7621    pub(crate) fn to_string_observable(&mut self, value: Value) -> Result<EcmaString, EvalFailure> {
7622        let primitive = self.to_primitive_observable(value, true)?;
7623        self.to_string(primitive)
7624    }
7625
7626    pub(crate) fn to_number_observable(&mut self, value: Value) -> Result<Value, EvalFailure> {
7627        let primitive = self.to_primitive_observable(value, false)?;
7628        self.to_number(primitive)
7629    }
7630
7631    fn to_number(&self, value: Value) -> Result<Value, EvalFailure> {
7632        match value.decode() {
7633            Some(Decoded::Number(_)) | Some(Decoded::Int32(_)) => self.to_primitive(value),
7634            Some(Decoded::Undefined) => Ok(Value::number(f64::NAN)),
7635            Some(Decoded::Null) => Ok(Value::int32(0)),
7636            Some(Decoded::Boolean(value)) => Ok(Value::int32(u32::from(value))),
7637            Some(Decoded::Hole) | Some(Decoded::Uninitialized) => Ok(Value::number(f64::NAN)),
7638            Some(Decoded::HeapRef(_)) => {
7639                match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7640                    Some(index) => match &self.heap[index] {
7641                        HeapEntry::String(text) => Ok(number_value(parse_number(text))),
7642                        HeapEntry::BigInt(_) => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7643                            operation: "convert bigint to number",
7644                        })),
7645                        HeapEntry::Array { elements, .. } if elements.is_empty() => {
7646                            Ok(Value::int32(0))
7647                        }
7648                        HeapEntry::Array { elements, .. } if elements.len() == 1 => {
7649                            self.to_number(elements[0])
7650                        }
7651                        HeapEntry::Symbol { .. } | HeapEntry::PrivateName { .. } => {
7652                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7653                                operation: "convert symbol to number",
7654                            }))
7655                        }
7656                        HeapEntry::Object { .. }
7657                        | HeapEntry::Generator { .. }
7658                        | HeapEntry::Script { .. }
7659                        | HeapEntry::Array { .. }
7660                        | HeapEntry::Function { .. }
7661                        | HeapEntry::ModuleNamespace { .. }
7662                        | HeapEntry::ExternalModuleNamespace { .. }
7663                        | HeapEntry::HashState { .. }
7664                        | HeapEntry::NativeFunction { .. }
7665                        | HeapEntry::RegExp { .. }
7666                        | HeapEntry::Date { .. }
7667                        | HeapEntry::BuiltinIterator { .. }
7668                        | HeapEntry::Collection { .. }
7669                        | HeapEntry::Promise { .. }
7670                        | HeapEntry::PromiseResolver { .. }
7671                        | HeapEntry::PromiseFinally { .. }
7672                        | HeapEntry::PromiseAll { .. }
7673                        | HeapEntry::AsyncActivation { .. }
7674                        | HeapEntry::PromiseAllElement { .. }
7675                        | HeapEntry::ProcessEnv { .. }
7676                        | HeapEntry::Iterator { .. }
7677                        | HeapEntry::Timeout { .. } => Ok(Value::number(f64::NAN)),
7678                    },
7679                    None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7680                        operation: "coerce host object to number",
7681                    })),
7682                }
7683            }
7684            None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7685                value,
7686            })),
7687        }
7688    }
7689
7690    fn truthy(&self, value: Value) -> bool {
7691        match value.decode() {
7692            Some(Decoded::Number(number)) => number != 0.0 && !number.is_nan(),
7693            Some(Decoded::Int32(value)) => value != 0,
7694            Some(Decoded::Undefined | Decoded::Null | Decoded::Hole | Decoded::Uninitialized)
7695            | None => false,
7696            Some(Decoded::Boolean(value)) => value,
7697            Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7698                Ok(Some(index)) => match &self.heap[index] {
7699                    HeapEntry::String(text) => !text.is_empty(),
7700                    HeapEntry::BigInt(text) => text != "0",
7701                    HeapEntry::Object { .. }
7702                    | HeapEntry::Generator { .. }
7703                    | HeapEntry::Script { .. }
7704                    | HeapEntry::Array { .. }
7705                    | HeapEntry::Function { .. }
7706                    | HeapEntry::ModuleNamespace { .. }
7707                    | HeapEntry::ExternalModuleNamespace { .. }
7708                    | HeapEntry::HashState { .. }
7709                    | HeapEntry::NativeFunction { .. }
7710                    | HeapEntry::Symbol { .. }
7711                    | HeapEntry::PrivateName { .. }
7712                    | HeapEntry::RegExp { .. }
7713                    | HeapEntry::Date { .. }
7714                    | HeapEntry::BuiltinIterator { .. }
7715                    | HeapEntry::Collection { .. }
7716                    | HeapEntry::Promise { .. }
7717                    | HeapEntry::PromiseResolver { .. }
7718                    | HeapEntry::PromiseFinally { .. }
7719                    | HeapEntry::PromiseAll { .. }
7720                    | HeapEntry::AsyncActivation { .. }
7721                    | HeapEntry::PromiseAllElement { .. }
7722                    | HeapEntry::ProcessEnv { .. }
7723                    | HeapEntry::Iterator { .. }
7724                    | HeapEntry::Timeout { .. } => true,
7725                },
7726                Ok(None) => true,
7727                Err(_) => false,
7728            },
7729        }
7730    }
7731
7732    fn type_of(&self, value: Value) -> &'static str {
7733        match value.decode() {
7734            Some(Decoded::Undefined | Decoded::Hole | Decoded::Uninitialized) | None => "undefined",
7735            Some(Decoded::Number(_) | Decoded::Int32(_)) => "number",
7736            Some(Decoded::Null) => "object",
7737            Some(Decoded::Boolean(_)) => "boolean",
7738            Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7739                Ok(Some(index)) => match &self.heap[index] {
7740                    HeapEntry::String(_) => "string",
7741                    HeapEntry::BigInt(_) => "bigint",
7742                    HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. } => "function",
7743                    HeapEntry::Symbol { .. } => "symbol",
7744                    HeapEntry::PrivateName { .. } => "object",
7745                    HeapEntry::Object { .. }
7746                    | HeapEntry::Generator { .. }
7747                    | HeapEntry::Script { .. }
7748                    | HeapEntry::Array { .. }
7749                    | HeapEntry::ModuleNamespace { .. }
7750                    | HeapEntry::ExternalModuleNamespace { .. }
7751                    | HeapEntry::HashState { .. }
7752                    | HeapEntry::RegExp { .. }
7753                    | HeapEntry::Date { .. }
7754                    | HeapEntry::BuiltinIterator { .. }
7755                    | HeapEntry::Collection { .. }
7756                    | HeapEntry::Promise { .. }
7757                    | HeapEntry::PromiseResolver { .. }
7758                    | HeapEntry::PromiseFinally { .. }
7759                    | HeapEntry::PromiseAll { .. }
7760                    | HeapEntry::AsyncActivation { .. }
7761                    | HeapEntry::PromiseAllElement { .. }
7762                    | HeapEntry::ProcessEnv { .. }
7763                    | HeapEntry::Iterator { .. }
7764                    | HeapEntry::Timeout { .. } => "object",
7765                },
7766                _ => "object",
7767            },
7768        }
7769    }
7770
7771    fn strict_equal(&self, left: Value, right: Value) -> bool {
7772        match (left.decode(), right.decode()) {
7773            (Some(Decoded::Number(a)), Some(Decoded::Number(b))) => a == b,
7774            (Some(Decoded::Number(a)), Some(Decoded::Int32(b)))
7775            | (Some(Decoded::Int32(b)), Some(Decoded::Number(a))) => a == f64::from(b as i32),
7776            (Some(Decoded::Int32(a)), Some(Decoded::Int32(b))) => a == b,
7777            (Some(Decoded::HeapRef(_)), Some(Decoded::HeapRef(_))) => {
7778                match (self.runtime_slot(left), self.runtime_slot(right)) {
7779                    (Ok(Some(a)), Ok(Some(b))) => match (&self.heap[a], &self.heap[b]) {
7780                        (HeapEntry::String(a), HeapEntry::String(b)) => a == b,
7781                        (HeapEntry::BigInt(a), HeapEntry::BigInt(b)) => a == b,
7782                        _ => left == right,
7783                    },
7784                    _ => left == right,
7785                }
7786            }
7787            _ => left == right,
7788        }
7789    }
7790
7791    fn abstract_equal(&self, left: Value, right: Value) -> Result<bool, EvalFailure> {
7792        if self.strict_equal(left, right) {
7793            return Ok(true);
7794        }
7795        if matches!(
7796            (left.decode(), right.decode()),
7797            (Some(Decoded::Null), Some(Decoded::Undefined))
7798                | (Some(Decoded::Undefined), Some(Decoded::Null))
7799        ) {
7800            return Ok(true);
7801        }
7802        let left_number = self.to_number(left);
7803        let right_number = self.to_number(right);
7804        match (left_number, right_number) {
7805            (Ok(left), Ok(right)) => Ok(numeric_f64(left).unwrap() == numeric_f64(right).unwrap()),
7806            _ => Ok(false),
7807        }
7808    }
7809
7810    fn relational_compare(
7811        &self,
7812        left: Value,
7813        right: Value,
7814    ) -> Result<Option<Ordering>, EvalFailure> {
7815        if let (Some(left), Some(right)) = (self.string_text(left), self.string_text(right)) {
7816            return Ok(Some(left.cmp(right)));
7817        }
7818        if let (Some(left), Some(right)) = (self.bigint_text(left), self.bigint_text(right)) {
7819            return Ok(Some(bigint_i128(left)?.cmp(&bigint_i128(right)?)));
7820        }
7821        let left = numeric_f64(self.to_number(left)?).unwrap();
7822        let right = numeric_f64(self.to_number(right)?).unwrap();
7823        Ok(left.partial_cmp(&right))
7824    }
7825
7826    /// `value instanceof constructor`: walks `value`'s prototype chain for the
7827    /// constructor's own `prototype` object, matching by heap identity.
7828    fn instance_of(&mut self, value: Value, constructor: Value) -> Result<bool, EvalFailure> {
7829        let constructor = self
7830            .bound_target(constructor)
7831            .map_err(EvalFailure::Runtime)?;
7832        match self
7833            .runtime_slot(constructor)
7834            .map_err(EvalFailure::Runtime)?
7835        {
7836            Some(index) => {
7837                if !matches!(
7838                    self.heap[index],
7839                    HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
7840                ) {
7841                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7842                        operation: "instanceof",
7843                    }));
7844                }
7845                let target = match self.own_get_ascii(index, "prototype") {
7846                    Some(Found::Value(value)) if self.is_object(value) => value,
7847                    _ => {
7848                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7849                            operation: "instanceof prototype is not an object",
7850                        }));
7851                    }
7852                };
7853                let target_slot = self.runtime_slot(target).map_err(EvalFailure::Runtime)?;
7854                let mut node = match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7855                    Some(node) => node,
7856                    None => return Ok(false),
7857                };
7858                let mut guard = 0;
7859                loop {
7860                    if Some(node) == target_slot {
7861                        return Ok(true);
7862                    }
7863                    match self.prototype_index(node)? {
7864                        Some(next) => {
7865                            node = next;
7866                            guard += 1;
7867                            if guard > self.heap.len() + 1 {
7868                                return Ok(false);
7869                            }
7870                        }
7871                        None => return Ok(false),
7872                    }
7873                }
7874            }
7875            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7876                operation: "instanceof",
7877            })),
7878        }
7879    }
7880
7881    fn value_to_string(&self, value: Value, depth: usize) -> Result<EcmaString, EvalFailure> {
7882        if depth >= 32 {
7883            return Ok(EcmaString::default());
7884        }
7885        let ascii = |text: String| EcmaString::from_utf8(&text);
7886        match value.decode() {
7887            Some(Decoded::Number(number)) => Ok(ascii(Self::ordinary_number_to_string(number))),
7888            Some(Decoded::Int32(raw)) => Ok(ascii((raw as i32).to_string())),
7889            Some(Decoded::Undefined | Decoded::Uninitialized) => {
7890                Ok(EcmaString::from_utf8("undefined"))
7891            }
7892            Some(Decoded::Null) => Ok(EcmaString::from_utf8("null")),
7893            Some(Decoded::Boolean(value)) => {
7894                Ok(EcmaString::from_utf8(if value { "true" } else { "false" }))
7895            }
7896            Some(Decoded::Hole) => Ok(EcmaString::default()),
7897            Some(Decoded::HeapRef(_)) => {
7898                match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7899                    Some(index) => match &self.heap[index] {
7900                        HeapEntry::String(text) => Ok(text.clone()),
7901                        HeapEntry::BigInt(text) => Ok(EcmaString::from_utf8(text)),
7902                        HeapEntry::Object { .. }
7903                        | HeapEntry::Generator { .. }
7904                        | HeapEntry::Script { .. }
7905                        | HeapEntry::Date { .. }
7906                        | HeapEntry::BuiltinIterator { .. }
7907                        | HeapEntry::Collection { .. }
7908                        | HeapEntry::Promise { .. }
7909                        | HeapEntry::PromiseResolver { .. }
7910                        | HeapEntry::PromiseFinally { .. }
7911                        | HeapEntry::PromiseAll { .. }
7912                        | HeapEntry::AsyncActivation { .. }
7913                        | HeapEntry::PromiseAllElement { .. }
7914                        | HeapEntry::ModuleNamespace { .. }
7915                        | HeapEntry::ExternalModuleNamespace { .. }
7916                        | HeapEntry::ProcessEnv { .. }
7917                        | HeapEntry::Iterator { .. }
7918                        | HeapEntry::Timeout { .. }
7919                        | HeapEntry::HashState { .. } => {
7920                            Ok(EcmaString::from_utf8("[object Object]"))
7921                        }
7922                        HeapEntry::RegExp { pattern, flags, .. } => {
7923                            let mut builder = EcmaStringBuilder::with_capacity(
7924                                pattern
7925                                    .len_units()
7926                                    .saturating_add(flags.len_units())
7927                                    .saturating_add(2),
7928                            );
7929                            builder.push_unit(u16::from(b'/'));
7930                            for &unit in pattern.as_units() {
7931                                builder.push_unit(unit);
7932                            }
7933                            builder.push_unit(u16::from(b'/'));
7934                            for &unit in flags.as_units() {
7935                                builder.push_unit(unit);
7936                            }
7937                            Ok(builder.finish())
7938                        }
7939                        HeapEntry::Symbol { .. } => {
7940                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7941                                operation: "convert symbol to string",
7942                            }))
7943                        }
7944                        HeapEntry::PrivateName { .. } => {
7945                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7946                                operation: "convert private name to string",
7947                            }))
7948                        }
7949                        HeapEntry::Function {
7950                            module, function, ..
7951                        } => {
7952                            let flags = self.module_code(*module).functions()
7953                                [function.get() as usize]
7954                                .flags();
7955                            Ok(EcmaString::from_utf8(
7956                                match (flags.is_async, flags.is_generator) {
7957                                    (true, true) => "async function* () { [bytecode] }",
7958                                    (true, false) => "async function () { [bytecode] }",
7959                                    (false, true) => "function* () { [bytecode] }",
7960                                    (false, false) => "function () { [bytecode] }",
7961                                },
7962                            ))
7963                        }
7964                        HeapEntry::NativeFunction { .. } => {
7965                            Ok(EcmaString::from_utf8("function () { [native code] }"))
7966                        }
7967                        HeapEntry::Array { elements, .. } => {
7968                            let mut text = EcmaStringBuilder::new();
7969                            for (index, element) in elements.iter().copied().enumerate() {
7970                                if index != 0 {
7971                                    text.push_unit(u16::from(b','));
7972                                }
7973                                if element != Value::HOLE
7974                                    && element != Value::NULL
7975                                    && element != Value::UNDEFINED
7976                                {
7977                                    for &unit in
7978                                        self.value_to_string(element, depth + 1)?.as_units()
7979                                    {
7980                                        text.push_unit(unit);
7981                                    }
7982                                }
7983                            }
7984                            Ok(text.finish())
7985                        }
7986                    },
7987                    None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7988                        operation: "coerce host object to string",
7989                    })),
7990                }
7991            }
7992            None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7993                value,
7994            })),
7995        }
7996    }
7997
7998    fn string_text(&self, value: Value) -> Option<&EcmaString> {
7999        let index = self.runtime_slot(value).ok()??;
8000        match &self.heap[index] {
8001            HeapEntry::String(text) => Some(text),
8002            _ => None,
8003        }
8004    }
8005
8006    fn bigint_text(&self, value: Value) -> Option<&str> {
8007        let index = self.runtime_slot(value).ok()??;
8008        match &self.heap[index] {
8009            HeapEntry::BigInt(text) => Some(text),
8010            _ => None,
8011        }
8012    }
8013
8014    fn is_object(&self, value: Value) -> bool {
8015        match self.runtime_slot(value) {
8016            Ok(Some(index)) => !matches!(
8017                self.heap[index],
8018                HeapEntry::String(_)
8019                    | HeapEntry::BigInt(_)
8020                    | HeapEntry::PromiseResolver { .. }
8021                    | HeapEntry::PromiseFinally { .. }
8022                    | HeapEntry::PromiseAll { .. }
8023                    | HeapEntry::AsyncActivation { .. }
8024                    | HeapEntry::PromiseAllElement { .. }
8025            ),
8026            Ok(None) => matches!(value.decode(), Some(Decoded::HeapRef(_))),
8027            Err(_) => false,
8028        }
8029    }
8030}
8031
8032fn ordered_property_keys(properties: &PropertyMap) -> Vec<PropertyKey> {
8033    let mut indices = Vec::new();
8034    let mut strings = Vec::new();
8035    let mut symbols = Vec::new();
8036    for (key, _) in properties.iter() {
8037        match key {
8038            PropertyKey::Named(name) => match array_index(name) {
8039                Some(index) => indices.push((index, key.clone())),
8040                None => strings.push(key.clone()),
8041            },
8042            PropertyKey::Symbol(_) => symbols.push(key.clone()),
8043            PropertyKey::Private(_) => {}
8044        }
8045    }
8046    indices.sort_unstable_by_key(|(index, _)| *index);
8047    indices
8048        .into_iter()
8049        .map(|(_, key)| key)
8050        .chain(strings)
8051        .chain(symbols)
8052        .collect()
8053}
8054
8055fn property_lookup(properties: &PropertyMap, key: &PropertyKey) -> Option<Found> {
8056    match properties.get(key) {
8057        Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
8058        Some(Property::Accessor { getter, .. }) => Some(match getter {
8059            Some(getter) => Found::Getter(*getter),
8060            None => Found::NoGetter,
8061        }),
8062        None => None,
8063    }
8064}
8065
8066fn property_lookup_ascii(properties: &PropertyMap, name: &str) -> Option<Found> {
8067    match properties.get_ascii(name) {
8068        Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
8069        Some(Property::Accessor { getter, .. }) => Some(match getter {
8070            Some(getter) => Found::Getter(*getter),
8071            None => Found::NoGetter,
8072        }),
8073        None => None,
8074    }
8075}
8076
8077fn innermost_handler(function: &Function, pc: usize) -> Option<bamts_bytecode::ExceptionHandler> {
8078    function
8079        .handlers()
8080        .iter()
8081        .copied()
8082        .filter(|handler| handler.start.get() as usize <= pc && pc < handler.end.get() as usize)
8083        .max_by(|left, right| {
8084            left.start
8085                .get()
8086                .cmp(&right.start.get())
8087                .then_with(|| right.end.get().cmp(&left.end.get()))
8088        })
8089}
8090
8091fn numeric_f64(value: Value) -> Option<f64> {
8092    match value.decode()? {
8093        Decoded::Number(number) => Some(number),
8094        Decoded::Int32(raw) => Some(f64::from(raw as i32)),
8095        _ => None,
8096    }
8097}
8098
8099fn number_value(number: f64) -> Value {
8100    if number.is_finite()
8101        && number.fract() == 0.0
8102        && number >= f64::from(i32::MIN)
8103        && number <= f64::from(i32::MAX)
8104    {
8105        Value::int32(number as i32 as u32)
8106    } else {
8107        Value::number(number)
8108    }
8109}
8110
8111fn parse_number(text: &EcmaString) -> f64 {
8112    let Ok(text) = text.to_utf8_strict() else {
8113        return f64::NAN;
8114    };
8115    parse_number_utf8(&text)
8116}
8117
8118fn parse_number_utf8(text: &str) -> f64 {
8119    let trimmed = text.trim();
8120    if trimmed.is_empty() {
8121        0.0
8122    } else {
8123        trimmed.parse::<f64>().unwrap_or(f64::NAN)
8124    }
8125}
8126
8127fn format_number(number: f64) -> String {
8128    if number.is_nan() {
8129        return "NaN".to_owned();
8130    }
8131    if number == f64::INFINITY {
8132        return "Infinity".to_owned();
8133    }
8134    if number == f64::NEG_INFINITY {
8135        return "-Infinity".to_owned();
8136    }
8137    if number == 0.0 {
8138        return "0".to_owned();
8139    }
8140
8141    let negative = number.is_sign_negative();
8142    let raw = number.abs().to_string();
8143    let (mantissa, explicit_exponent) = match raw.split_once(['e', 'E']) {
8144        Some((mantissa, exponent)) => (
8145            mantissa,
8146            exponent
8147                .parse::<i32>()
8148                .expect("Rust formats finite f64 exponents as i32"),
8149        ),
8150        None => (raw.as_str(), 0),
8151    };
8152    let decimal = mantissa.find('.').unwrap_or(mantissa.len());
8153    let untrimmed: String = mantissa.chars().filter(|ch| *ch != '.').collect();
8154    let first = untrimmed
8155        .find(|ch| ch != '0')
8156        .expect("a nonzero number has a nonzero decimal digit");
8157    let digits = untrimmed[first..].trim_end_matches('0');
8158    let exponent = explicit_exponent + decimal as i32 - first as i32 - 1;
8159
8160    let mut result = String::new();
8161    if negative {
8162        result.push('-');
8163    }
8164    if !(-6..21).contains(&exponent) {
8165        result.push(digits.as_bytes()[0] as char);
8166        if digits.len() > 1 {
8167            result.push('.');
8168            result.push_str(&digits[1..]);
8169        }
8170        result.push('e');
8171        if exponent >= 0 {
8172            result.push('+');
8173        }
8174        result.push_str(&exponent.to_string());
8175    } else if exponent >= 0 {
8176        let integer_digits = exponent as usize + 1;
8177        if digits.len() <= integer_digits {
8178            result.push_str(digits);
8179            result.extend(std::iter::repeat_n('0', integer_digits - digits.len()));
8180        } else {
8181            result.push_str(&digits[..integer_digits]);
8182            result.push('.');
8183            result.push_str(&digits[integer_digits..]);
8184        }
8185    } else {
8186        result.push_str("0.");
8187        result.extend(std::iter::repeat_n('0', (-exponent - 1) as usize));
8188        result.push_str(digits);
8189    }
8190    result
8191}
8192
8193fn to_uint32(number: f64) -> u32 {
8194    if !number.is_finite() || number == 0.0 {
8195        0
8196    } else {
8197        number.trunc().rem_euclid(4_294_967_296.0) as u32
8198    }
8199}
8200
8201fn to_int32(number: f64) -> i32 {
8202    to_uint32(number) as i32
8203}
8204
8205fn array_index_ascii(key: &str) -> Option<u32> {
8206    if !key.is_ascii() || key.is_empty() || (key.len() > 1 && key.as_bytes()[0] == b'0') {
8207        return None;
8208    }
8209    let mut index = 0_u32;
8210    for byte in key.bytes() {
8211        if !byte.is_ascii_digit() {
8212            return None;
8213        }
8214        index = index.checked_mul(10)?.checked_add(u32::from(byte - b'0'))?;
8215    }
8216    (index != u32::MAX).then_some(index)
8217}
8218
8219fn array_index(key: &EcmaString) -> Option<u32> {
8220    let units = key.as_units();
8221    if units.is_empty() || (units.len() > 1 && units[0] == u16::from(b'0')) {
8222        return None;
8223    }
8224    let mut index = 0_u32;
8225    for &unit in units {
8226        if !(u16::from(b'0')..=u16::from(b'9')).contains(&unit) {
8227            return None;
8228        }
8229        index = index
8230            .checked_mul(10)?
8231            .checked_add(u32::from(unit - u16::from(b'0')))?;
8232    }
8233    (index != u32::MAX).then_some(index)
8234}
8235
8236fn exact_array_length(value: Value) -> Option<usize> {
8237    let number = numeric_f64(value)?;
8238    if number.is_finite() && number >= 0.0 && number.fract() == 0.0 && number <= u32::MAX as f64 {
8239        Some(number as usize)
8240    } else {
8241        None
8242    }
8243}
8244
8245pub(crate) fn apply_array_length(
8246    elements: &mut Vec<Value>,
8247    properties: &mut PropertyMap,
8248    length: usize,
8249    operation: &'static str,
8250) -> Result<(), EvalFailure> {
8251    if length >= elements.len() {
8252        elements.resize(length, Value::HOLE);
8253        return Ok(());
8254    }
8255    let blocked = properties
8256        .iter()
8257        .filter_map(|(key, property)| {
8258            (!property.configurable())
8259                .then(|| key.as_string().and_then(array_index))
8260                .flatten()
8261        })
8262        .map(|offset| offset as usize)
8263        .filter(|offset| *offset >= length)
8264        .max();
8265    let effective_length = blocked.map_or(length, |offset| offset + 1);
8266    properties.0.retain(|(key, _)| {
8267        key.as_string()
8268            .and_then(array_index)
8269            .is_none_or(|offset| (offset as usize) < effective_length)
8270    });
8271    elements.resize(effective_length, Value::HOLE);
8272    if blocked.is_some() {
8273        return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8274    }
8275    Ok(())
8276}
8277
8278pub(crate) fn array_set_length(
8279    elements: &mut Vec<Value>,
8280    properties: &mut PropertyMap,
8281    length_writable: bool,
8282    value: Value,
8283    operation: &'static str,
8284) -> Result<(), EvalFailure> {
8285    let length = exact_array_length(value)
8286        .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError { operation }))?;
8287    if !length_writable {
8288        return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8289    }
8290    apply_array_length(elements, properties, length, operation)
8291}
8292
8293fn bigint_i128(text: &str) -> Result<i128, EvalFailure> {
8294    text.parse::<i128>().map_err(|_| {
8295        EvalFailure::Throw(ThrowOrigin::RangeError {
8296            operation: "bigint magnitude exceeds runtime width",
8297        })
8298    })
8299}
8300
8301fn bigint_binary(op: BinaryOp, left: &str, right: &str) -> Result<String, EvalFailure> {
8302    let left = bigint_i128(left)?;
8303    let right = bigint_i128(right)?;
8304    let overflow =
8305        |operation: &'static str| EvalFailure::Throw(ThrowOrigin::RangeError { operation });
8306    let result = match op {
8307        BinaryOp::Subtract => left
8308            .checked_sub(right)
8309            .ok_or_else(|| overflow("bigint subtract overflow"))?,
8310        BinaryOp::Multiply => left
8311            .checked_mul(right)
8312            .ok_or_else(|| overflow("bigint multiply overflow"))?,
8313        BinaryOp::Divide => {
8314            if right == 0 {
8315                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8316                    operation: "bigint division by zero",
8317                }));
8318            }
8319            left.checked_div(right)
8320                .ok_or_else(|| overflow("bigint divide overflow"))?
8321        }
8322        BinaryOp::Remainder => {
8323            if right == 0 {
8324                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8325                    operation: "bigint remainder by zero",
8326                }));
8327            }
8328            left.checked_rem(right)
8329                .ok_or_else(|| overflow("bigint remainder overflow"))?
8330        }
8331        BinaryOp::Exponent => {
8332            if right < 0 {
8333                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8334                    operation: "bigint negative exponent",
8335                }));
8336            }
8337            let exponent =
8338                u32::try_from(right).map_err(|_| overflow("bigint exponent overflow"))?;
8339            left.checked_pow(exponent)
8340                .ok_or_else(|| overflow("bigint exponent overflow"))?
8341        }
8342        BinaryOp::BitAnd => left & right,
8343        BinaryOp::BitOr => left | right,
8344        BinaryOp::BitXor => left ^ right,
8345        BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
8346            let left_shift = (op == BinaryOp::ShiftLeft) == (right >= 0);
8347            let amount =
8348                u32::try_from(right.unsigned_abs()).map_err(|_| overflow("bigint shift width"))?;
8349            let shifted = if left_shift {
8350                left.checked_shl(amount)
8351            } else {
8352                left.checked_shr(amount)
8353            };
8354            shifted.ok_or_else(|| overflow("bigint shift overflow"))?
8355        }
8356        BinaryOp::UnsignedShiftRight => {
8357            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
8358                operation: "unsigned shift on bigint",
8359            }));
8360        }
8361        _ => unreachable!("bigint arithmetic partition"),
8362    };
8363    Ok(result.to_string())
8364}
8365
8366pub(crate) fn unary_from_selector(op: u32) -> Option<UnaryOp> {
8367    match op {
8368        0 => Some(UnaryOp::Void),
8369        1 => Some(UnaryOp::TypeOf),
8370        2 => Some(UnaryOp::Plus),
8371        3 => Some(UnaryOp::Negate),
8372        4 => Some(UnaryOp::BitwiseNot),
8373        5 => Some(UnaryOp::LogicalNot),
8374        _ => None,
8375    }
8376}
8377
8378pub(crate) fn binary_from_selector(op: u32) -> Option<BinaryOp> {
8379    match op {
8380        0 => Some(BinaryOp::Add),
8381        1 => Some(BinaryOp::Subtract),
8382        2 => Some(BinaryOp::Multiply),
8383        3 => Some(BinaryOp::Divide),
8384        4 => Some(BinaryOp::Remainder),
8385        5 => Some(BinaryOp::Exponent),
8386        6 => Some(BinaryOp::BitAnd),
8387        7 => Some(BinaryOp::BitOr),
8388        8 => Some(BinaryOp::BitXor),
8389        9 => Some(BinaryOp::ShiftLeft),
8390        10 => Some(BinaryOp::ShiftRight),
8391        11 => Some(BinaryOp::UnsignedShiftRight),
8392        12 => Some(BinaryOp::Equal),
8393        13 => Some(BinaryOp::NotEqual),
8394        14 => Some(BinaryOp::StrictEqual),
8395        15 => Some(BinaryOp::StrictNotEqual),
8396        16 => Some(BinaryOp::LessThan),
8397        17 => Some(BinaryOp::LessThanOrEqual),
8398        18 => Some(BinaryOp::GreaterThan),
8399        19 => Some(BinaryOp::GreaterThanOrEqual),
8400        20 => Some(BinaryOp::InstanceOf),
8401        21 => Some(BinaryOp::In),
8402        _ => None,
8403    }
8404}
8405
8406pub(crate) fn iterator_kind_from_selector(kind: u32) -> Option<IteratorKind> {
8407    match kind {
8408        0 => Some(IteratorKind::Sync),
8409        1 => Some(IteratorKind::Async),
8410        2 => Some(IteratorKind::Keys),
8411        _ => None,
8412    }
8413}
8414
8415pub(crate) fn accessor_from_selector(kind: u32) -> Option<AccessorKind> {
8416    match kind {
8417        0 => Some(AccessorKind::Getter),
8418        1 => Some(AccessorKind::Setter),
8419        _ => None,
8420    }
8421}
8422
8423#[cfg(test)]
8424mod tests {
8425    use std::sync::Arc;
8426
8427    use super::*;
8428    use crate::intrinsics::BuiltinOutcome;
8429    use bamts_bytecode::{
8430        Binding, Edge, EdgeKind, ExceptionHandler, Export, ExportSource, FunctionFlags, NumberBits,
8431        ProgramModule, Register,
8432    };
8433
8434    fn reg(raw: u32) -> Register {
8435        Register::new(raw)
8436    }
8437    fn pc(raw: u32) -> Pc {
8438        Pc::new(raw)
8439    }
8440    fn cid(raw: u32) -> ConstantId {
8441        ConstantId::new(raw)
8442    }
8443
8444    /// A function with no captures.
8445    fn function(
8446        parameters: u32,
8447        registers: u32,
8448        code: Vec<Instruction>,
8449        handlers: Vec<ExceptionHandler>,
8450    ) -> Function {
8451        Function::new(
8452            None,
8453            0,
8454            parameters,
8455            registers,
8456            FunctionFlags::default(),
8457            code,
8458            handlers,
8459        )
8460    }
8461
8462    fn generator_function(
8463        parameters: u32,
8464        registers: u32,
8465        code: Vec<Instruction>,
8466        handlers: Vec<ExceptionHandler>,
8467    ) -> Function {
8468        Function::new(
8469            None,
8470            0,
8471            parameters,
8472            registers,
8473            FunctionFlags {
8474                is_async: false,
8475                is_generator: true,
8476            },
8477            code,
8478            handlers,
8479        )
8480    }
8481
8482    fn async_function(
8483        parameters: u32,
8484        registers: u32,
8485        code: Vec<Instruction>,
8486        handlers: Vec<ExceptionHandler>,
8487    ) -> Function {
8488        Function::new(
8489            None,
8490            0,
8491            parameters,
8492            registers,
8493            FunctionFlags {
8494                is_async: true,
8495                is_generator: false,
8496            },
8497            code,
8498            handlers,
8499        )
8500    }
8501
8502    /// A function with `captures` leading capture registers.
8503    fn closure_function(
8504        captures: u32,
8505        parameters: u32,
8506        registers: u32,
8507        code: Vec<Instruction>,
8508    ) -> Function {
8509        Function::new(
8510            None,
8511            captures,
8512            parameters,
8513            registers,
8514            FunctionFlags::default(),
8515            code,
8516            Vec::new(),
8517        )
8518    }
8519
8520    fn verified(mut constants: Vec<Constant>, functions: Vec<Function>) -> Program<Verified> {
8521        let name = ConstantId::new(constants.len() as u32);
8522        constants.push(Constant::String(EcmaString::from_utf8("<test>")));
8523        let code = Module::new(constants, functions, FunctionId::new(0))
8524            .verify()
8525            .expect("valid test bytecode");
8526        Program::link(
8527            vec![ProgramModule {
8528                name,
8529                code,
8530                edges: Vec::new(),
8531                bindings: Vec::new(),
8532                exports: Vec::new(),
8533            }],
8534            ModuleId::new(0),
8535        )
8536        .expect("valid one-module test program")
8537    }
8538    fn program_module(
8539        name: &str,
8540        mut constants: Vec<Constant>,
8541        functions: Vec<Function>,
8542        edges: Vec<Edge>,
8543        bindings: Vec<Binding>,
8544        exports: Vec<Export>,
8545    ) -> ProgramModule<Verified> {
8546        constants.insert(0, Constant::String(EcmaString::from_utf8(name)));
8547        let code = Module::new(constants, functions, FunctionId::new(0))
8548            .verify()
8549            .expect("valid test bytecode");
8550        ProgramModule {
8551            name: ConstantId::new(0),
8552            code,
8553            edges,
8554            bindings,
8555            exports,
8556        }
8557    }
8558
8559    fn linked(modules: Vec<ProgramModule<Verified>>, entry: u32) -> Program<Verified> {
8560        Program::link(modules, ModuleId::new(entry)).expect("valid linked test program")
8561    }
8562
8563    fn namespace_descriptor_entry() -> Function {
8564        function(
8565            0,
8566            7,
8567            vec![
8568                Instruction::LoadGlobal {
8569                    dst: reg(0),
8570                    name: cid(1),
8571                },
8572                Instruction::LoadGlobal {
8573                    dst: reg(1),
8574                    name: cid(3),
8575                },
8576                Instruction::LoadConst {
8577                    dst: reg(2),
8578                    constant: cid(4),
8579                },
8580                Instruction::GetProperty {
8581                    dst: reg(3),
8582                    object: reg(1),
8583                    key: reg(2),
8584                },
8585                Instruction::CreateArray { dst: reg(4) },
8586                Instruction::ArrayPush {
8587                    array: reg(4),
8588                    value: reg(0),
8589                },
8590                Instruction::LoadConst {
8591                    dst: reg(5),
8592                    constant: cid(5),
8593                },
8594                Instruction::ArrayPush {
8595                    array: reg(4),
8596                    value: reg(5),
8597                },
8598                Instruction::Call {
8599                    dst: reg(6),
8600                    callee: reg(3),
8601                    this_value: reg(4),
8602                    arguments: reg(4),
8603                },
8604                Instruction::Return { value: reg(6) },
8605            ],
8606            Vec::new(),
8607        )
8608    }
8609
8610    #[derive(Default)]
8611    struct TestHost;
8612    impl Host for TestHost {}
8613
8614    #[test]
8615    fn async_await_setup_failure_releases_suspended_registers() {
8616        let program = verified(
8617            vec![Constant::Undefined],
8618            vec![
8619                function(0, 1, vec![Instruction::Halt], Vec::new()),
8620                async_function(
8621                    0,
8622                    2,
8623                    vec![
8624                        Instruction::LoadConst {
8625                            dst: reg(0),
8626                            constant: cid(0),
8627                        },
8628                        Instruction::Suspend {
8629                            dst: reg(1),
8630                            src: reg(0),
8631                            resume: pc(2),
8632                        },
8633                        Instruction::Return { value: reg(1) },
8634                    ],
8635                    Vec::new(),
8636                ),
8637            ],
8638        );
8639        let mut host = TestHost;
8640        let limits = Limits {
8641            max_microtasks: 0,
8642            ..Limits::default()
8643        };
8644        let mut machine = Machine::new(&program, &mut host, limits);
8645        machine.frames.clear();
8646        machine.live_registers = 0;
8647        let callable = generator_callable(&mut machine, 1);
8648
8649        assert!(matches!(
8650            machine.call_value(callable, Value::UNDEFINED, &[]),
8651            Err(EvalFailure::Runtime(
8652                RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 0 }
8653            ))
8654        ));
8655        assert_eq!(machine.live_registers, 0);
8656    }
8657
8658    fn run_ok(program: &Program<Verified>) -> Execution {
8659        let mut host = TestHost;
8660        Machine::new(program, &mut host, Limits::default())
8661            .run()
8662            .unwrap()
8663    }
8664
8665    fn generator_callable<H: Host>(machine: &mut Machine<'_, H>, function: u32) -> Value {
8666        machine
8667            .allocate(HeapEntry::Function {
8668                module: ModuleId::new(0),
8669                function: FunctionId::new(function),
8670                captures: Vec::new(),
8671                properties: PropertyMap::default(),
8672                prototype: Some(machine.intrinsics.function_prototype),
8673                extensible: true,
8674            })
8675            .unwrap()
8676    }
8677
8678    fn generator_next<H: Host>(
8679        machine: &mut Machine<'_, H>,
8680        generator: Value,
8681        resume_value: Value,
8682    ) -> Result<(Value, bool), EvalFailure> {
8683        let next = machine.get_named_property(generator, "next")?;
8684        let result = machine.call_value(next, generator, &[resume_value])?;
8685        let done = machine.get_named_property(result, "done")?;
8686        let value = machine.get_named_property(result, "value")?;
8687        Ok((value, machine.truthy(done)))
8688    }
8689
8690    #[test]
8691    fn sync_generator_is_lazy_resumes_registers_and_stays_completed() {
8692        let program = verified(
8693            vec![Constant::Int32(10)],
8694            vec![
8695                function(0, 1, vec![Instruction::Halt], Vec::new()),
8696                generator_function(
8697                    0,
8698                    3,
8699                    vec![
8700                        Instruction::LoadConst {
8701                            dst: reg(0),
8702                            constant: cid(0),
8703                        },
8704                        Instruction::Suspend {
8705                            dst: reg(1),
8706                            src: reg(0),
8707                            resume: pc(2),
8708                        },
8709                        Instruction::Binary {
8710                            dst: reg(2),
8711                            op: BinaryOp::Add,
8712                            left: reg(0),
8713                            right: reg(1),
8714                        },
8715                        Instruction::Return { value: reg(2) },
8716                    ],
8717                    Vec::new(),
8718                ),
8719            ],
8720        );
8721        let mut host = TestHost;
8722        let mut machine = Machine::new(&program, &mut host, Limits::default());
8723        machine.frames.clear();
8724        machine.live_registers = 0;
8725        let callable = generator_callable(&mut machine, 1);
8726        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8727        assert_eq!(machine.live_registers, 0, "calling must not start the body");
8728        machine
8729            .set_data_property(generator, "visible", Value::int32(1))
8730            .unwrap();
8731        assert_eq!(
8732            machine.get_named_property(generator, "visible").unwrap(),
8733            Value::int32(1),
8734        );
8735        assert_eq!(
8736            machine.own_property_keys(generator).unwrap(),
8737            vec![PropertyKey::Named(EcmaString::from_utf8("visible"))],
8738        );
8739        assert!(
8740            machine
8741                .inherits_from_prototype(
8742                    generator,
8743                    machine.intrinsics.builtins.generator_prototype(),
8744                )
8745                .unwrap()
8746        );
8747
8748        assert_eq!(
8749            generator_next(&mut machine, generator, Value::int32(99)).unwrap(),
8750            (Value::int32(10), false),
8751        );
8752        assert_eq!(machine.live_registers, 3);
8753        assert_eq!(
8754            generator_next(&mut machine, generator, Value::int32(5)).unwrap(),
8755            (Value::int32(15), true),
8756        );
8757        assert_eq!(machine.live_registers, 0);
8758        assert_eq!(
8759            generator_next(&mut machine, generator, Value::int32(8)).unwrap(),
8760            (Value::UNDEFINED, true),
8761        );
8762    }
8763
8764    #[test]
8765    fn sync_generator_reentrant_next_is_a_type_error() {
8766        let program = verified(
8767            Vec::new(),
8768            vec![
8769                function(0, 1, vec![Instruction::Halt], Vec::new()),
8770                generator_function(0, 1, vec![Instruction::Halt], Vec::new()),
8771            ],
8772        );
8773        let mut host = TestHost;
8774        let mut machine = Machine::new(&program, &mut host, Limits::default());
8775        machine.frames.clear();
8776        machine.live_registers = 0;
8777        let callable = generator_callable(&mut machine, 1);
8778        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8779        let _ = machine.take_generator_state(generator).unwrap();
8780
8781        assert!(matches!(
8782            generator_next(&mut machine, generator, Value::UNDEFINED),
8783            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
8784        ));
8785    }
8786
8787    #[test]
8788    fn sync_generator_uncaught_throw_preserves_origin_and_completes() {
8789        let program = verified(
8790            vec![Constant::Int32(7)],
8791            vec![
8792                function(0, 1, vec![Instruction::Halt], Vec::new()),
8793                generator_function(
8794                    0,
8795                    1,
8796                    vec![
8797                        Instruction::LoadConst {
8798                            dst: reg(0),
8799                            constant: cid(0),
8800                        },
8801                        Instruction::Throw { value: reg(0) },
8802                    ],
8803                    Vec::new(),
8804                ),
8805            ],
8806        );
8807        let mut host = TestHost;
8808        let mut machine = Machine::new(&program, &mut host, Limits::default());
8809        machine.frames.clear();
8810        machine.live_registers = 0;
8811        let callable = generator_callable(&mut machine, 1);
8812        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8813
8814        assert!(matches!(
8815            generator_next(&mut machine, generator, Value::UNDEFINED),
8816            Err(EvalFailure::ThrowValueOrigin {
8817                value,
8818                origin: ThrowOrigin::Bytecode,
8819            }) if value == Value::int32(7)
8820        ));
8821        assert_eq!(
8822            generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8823            (Value::UNDEFINED, true),
8824        );
8825        assert_eq!(machine.live_registers, 0);
8826    }
8827
8828    #[test]
8829    fn outer_compiled_handler_catches_generator_throw_value() {
8830        let program = verified(
8831            vec![
8832                Constant::Int32(7),
8833                Constant::Undefined,
8834                Constant::String(EcmaString::from_utf8("next")),
8835            ],
8836            vec![
8837                function(
8838                    0,
8839                    8,
8840                    vec![
8841                        Instruction::CreateArray { dst: reg(0) },
8842                        Instruction::CreateClosure {
8843                            dst: reg(1),
8844                            function: FunctionId::new(1),
8845                            captures: reg(0),
8846                        },
8847                        Instruction::CreateArray { dst: reg(2) },
8848                        Instruction::LoadConst {
8849                            dst: reg(3),
8850                            constant: cid(1),
8851                        },
8852                        Instruction::Call {
8853                            dst: reg(4),
8854                            callee: reg(1),
8855                            this_value: reg(3),
8856                            arguments: reg(2),
8857                        },
8858                        Instruction::LoadConst {
8859                            dst: reg(5),
8860                            constant: cid(2),
8861                        },
8862                        Instruction::GetProperty {
8863                            dst: reg(6),
8864                            object: reg(4),
8865                            key: reg(5),
8866                        },
8867                        Instruction::Call {
8868                            dst: reg(7),
8869                            callee: reg(6),
8870                            this_value: reg(4),
8871                            arguments: reg(2),
8872                        },
8873                        Instruction::Return { value: reg(3) },
8874                        Instruction::Return { value: reg(7) },
8875                    ],
8876                    vec![ExceptionHandler {
8877                        start: pc(7),
8878                        end: pc(8),
8879                        handler: pc(9),
8880                        catch_register: reg(7),
8881                    }],
8882                ),
8883                generator_function(
8884                    0,
8885                    1,
8886                    vec![
8887                        Instruction::LoadConst {
8888                            dst: reg(0),
8889                            constant: cid(0),
8890                        },
8891                        Instruction::Throw { value: reg(0) },
8892                    ],
8893                    Vec::new(),
8894                ),
8895            ],
8896        );
8897
8898        assert_eq!(run_ok(&program).value, Value::int32(7));
8899    }
8900
8901    #[test]
8902    fn sync_generator_catches_body_throw_before_suspending() {
8903        let program = verified(
8904            vec![Constant::Int32(7)],
8905            vec![
8906                function(0, 1, vec![Instruction::Halt], Vec::new()),
8907                generator_function(
8908                    0,
8909                    3,
8910                    vec![
8911                        Instruction::LoadConst {
8912                            dst: reg(0),
8913                            constant: cid(0),
8914                        },
8915                        Instruction::Throw { value: reg(0) },
8916                        Instruction::Suspend {
8917                            dst: reg(2),
8918                            src: reg(1),
8919                            resume: pc(3),
8920                        },
8921                        Instruction::Return { value: reg(2) },
8922                    ],
8923                    vec![ExceptionHandler {
8924                        start: pc(1),
8925                        end: pc(2),
8926                        handler: pc(2),
8927                        catch_register: reg(1),
8928                    }],
8929                ),
8930            ],
8931        );
8932        let mut host = TestHost;
8933        let mut machine = Machine::new(&program, &mut host, Limits::default());
8934        machine.frames.clear();
8935        machine.live_registers = 0;
8936        let callable = generator_callable(&mut machine, 1);
8937        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8938
8939        assert_eq!(
8940            generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8941            (Value::int32(7), false),
8942        );
8943        assert_eq!(
8944            generator_next(&mut machine, generator, Value::int32(9)).unwrap(),
8945            (Value::int32(9), true),
8946        );
8947    }
8948
8949    #[test]
8950    fn suspended_generator_registers_remain_charged() {
8951        let program = verified(
8952            vec![Constant::Int32(1)],
8953            vec![
8954                function(0, 1, vec![Instruction::Halt], Vec::new()),
8955                generator_function(
8956                    0,
8957                    3,
8958                    vec![
8959                        Instruction::LoadConst {
8960                            dst: reg(0),
8961                            constant: cid(0),
8962                        },
8963                        Instruction::Suspend {
8964                            dst: reg(1),
8965                            src: reg(0),
8966                            resume: pc(2),
8967                        },
8968                        Instruction::Return { value: reg(1) },
8969                    ],
8970                    Vec::new(),
8971                ),
8972            ],
8973        );
8974        let mut host = TestHost;
8975        let mut machine = Machine::new(
8976            &program,
8977            &mut host,
8978            Limits {
8979                max_total_registers: 3,
8980                ..Limits::default()
8981            },
8982        );
8983        machine.frames.clear();
8984        machine.live_registers = 0;
8985        let callable = generator_callable(&mut machine, 1);
8986        let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8987        let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8988        assert_eq!(
8989            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8990            (Value::int32(1), false),
8991        );
8992        assert!(matches!(
8993            generator_next(&mut machine, second, Value::UNDEFINED),
8994            Err(EvalFailure::Runtime(
8995                RuntimeErrorKind::RegisterLimitExceeded { .. }
8996            ))
8997        ));
8998        assert_eq!(machine.live_registers, 3);
8999        assert_eq!(
9000            generator_next(&mut machine, first, Value::int32(4)).unwrap(),
9001            (Value::int32(4), true),
9002        );
9003        assert_eq!(machine.live_registers, 0);
9004    }
9005
9006    #[test]
9007    fn resumed_generator_call_depth_failure_releases_registers() {
9008        let program = verified(
9009            vec![Constant::Int32(1)],
9010            vec![
9011                function(0, 1, vec![Instruction::Halt], Vec::new()),
9012                generator_function(
9013                    0,
9014                    2,
9015                    vec![
9016                        Instruction::LoadConst {
9017                            dst: reg(0),
9018                            constant: cid(0),
9019                        },
9020                        Instruction::Suspend {
9021                            dst: reg(1),
9022                            src: reg(0),
9023                            resume: pc(2),
9024                        },
9025                        Instruction::Return { value: reg(1) },
9026                    ],
9027                    Vec::new(),
9028                ),
9029            ],
9030        );
9031        let mut host = TestHost;
9032        let mut machine = Machine::new(
9033            &program,
9034            &mut host,
9035            Limits {
9036                max_total_registers: 2,
9037                ..Limits::default()
9038            },
9039        );
9040        machine.frames.clear();
9041        machine.live_registers = 0;
9042
9043        let callable = generator_callable(&mut machine, 1);
9044        let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9045
9046        // Start and suspend the first generator, charging its two registers.
9047        assert_eq!(
9048            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
9049            (Value::int32(1), false),
9050        );
9051        assert_eq!(machine.live_registers, 2);
9052
9053        // Fill the compiled call depth to the exact limit so the next resume
9054        // fails in push_resumed_generator_frame before it can take ownership.
9055        machine.frames.push(Frame {
9056            module: ModuleId::new(0),
9057            function: 0,
9058            pc: 0,
9059            registers: Vec::new(),
9060            return_to: None,
9061            this_value: Value::UNDEFINED,
9062            new_target: Value::UNDEFINED,
9063            args: Vec::new(),
9064            arguments_object: None,
9065        });
9066        machine.limits.max_call_depth = machine.frames.len();
9067
9068        assert!(matches!(
9069            generator_next(&mut machine, first, Value::int32(7)),
9070            Err(EvalFailure::Runtime(
9071                RuntimeErrorKind::CallDepthExceeded { .. }
9072            ))
9073        ));
9074        assert_eq!(machine.live_registers, 0);
9075
9076        // The generator is now sticky Completed.
9077        assert_eq!(
9078            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
9079            (Value::UNDEFINED, true),
9080        );
9081
9082        // Remove the artificial depth and make room for another activation.
9083        machine.frames.pop();
9084        machine.limits.max_call_depth = Limits::default().max_call_depth;
9085
9086        // A second generator can suspend again only if the first's charge was released.
9087        let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9088        assert_eq!(
9089            generator_next(&mut machine, second, Value::UNDEFINED).unwrap(),
9090            (Value::int32(1), false),
9091        );
9092        assert_eq!(machine.live_registers, 2);
9093        assert_eq!(
9094            generator_next(&mut machine, second, Value::int32(9)).unwrap(),
9095            (Value::int32(9), true),
9096        );
9097        assert_eq!(machine.live_registers, 0);
9098    }
9099    #[test]
9100    fn array_extend_consumes_generator_through_sync_iterator_protocol() {
9101        let program = verified(
9102            vec![Constant::Int32(1), Constant::Int32(2)],
9103            vec![
9104                function(0, 1, vec![Instruction::Halt], Vec::new()),
9105                generator_function(
9106                    0,
9107                    3,
9108                    vec![
9109                        Instruction::LoadConst {
9110                            dst: reg(0),
9111                            constant: cid(0),
9112                        },
9113                        Instruction::Suspend {
9114                            dst: reg(2),
9115                            src: reg(0),
9116                            resume: pc(2),
9117                        },
9118                        Instruction::LoadConst {
9119                            dst: reg(1),
9120                            constant: cid(1),
9121                        },
9122                        Instruction::Suspend {
9123                            dst: reg(2),
9124                            src: reg(1),
9125                            resume: pc(4),
9126                        },
9127                        Instruction::Return { value: reg(2) },
9128                    ],
9129                    Vec::new(),
9130                ),
9131            ],
9132        );
9133        let mut host = TestHost;
9134        let mut machine = Machine::new(&program, &mut host, Limits::default());
9135        machine.frames.clear();
9136        machine.live_registers = 0;
9137        let callable = generator_callable(&mut machine, 1);
9138        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9139        let array = machine
9140            .allocate(HeapEntry::Array {
9141                elements: Vec::new(),
9142                properties: PropertyMap::default(),
9143                prototype: Some(machine.intrinsics.array_prototype),
9144                extensible: true,
9145                length_writable: true,
9146            })
9147            .unwrap();
9148
9149        machine.array_extend(array, generator).unwrap();
9150        assert_eq!(
9151            machine.array_elements(array).unwrap(),
9152            Some(vec![Value::int32(1), Value::int32(2)]),
9153        );
9154        assert_eq!(machine.live_registers, 0);
9155    }
9156
9157    #[test]
9158    fn runtime_callback_without_interpreter_caller_propagates_throw() {
9159        let program = verified(
9160            Vec::new(),
9161            vec![
9162                function(0, 1, vec![Instruction::Halt], Vec::new()),
9163                function(1, 1, vec![Instruction::Throw { value: reg(0) }], Vec::new()),
9164            ],
9165        );
9166        let mut host = TestHost;
9167        let mut machine = Machine::new(&program, &mut host, Limits::default());
9168        machine.frames.clear();
9169        machine.live_registers = 0;
9170        let callee = machine
9171            .allocate(HeapEntry::Function {
9172                module: ModuleId::new(0),
9173                function: FunctionId::new(1),
9174                captures: Vec::new(),
9175                properties: PropertyMap::default(),
9176                prototype: Some(machine.intrinsics.function_prototype),
9177                extensible: true,
9178            })
9179            .unwrap();
9180        let thrown = Value::int32(7);
9181
9182        assert!(matches!(
9183            machine.call_value(callee, Value::UNDEFINED, &[thrown]),
9184            Err(EvalFailure::ThrowValue(value)) if value == thrown
9185        ));
9186    }
9187
9188    #[test]
9189    fn runtime_callback_failure_releases_root_frame() {
9190        let program = verified(
9191            Vec::new(),
9192            vec![
9193                function(0, 1, vec![Instruction::Halt], Vec::new()),
9194                function(
9195                    1,
9196                    1,
9197                    vec![Instruction::Return { value: reg(0) }],
9198                    Vec::new(),
9199                ),
9200            ],
9201        );
9202        let mut host = TestHost;
9203        let mut machine = Machine::new(&program, &mut host, Limits::default());
9204        machine.frames.clear();
9205        machine.live_registers = 0;
9206        let callee = machine
9207            .allocate(HeapEntry::Function {
9208                module: ModuleId::new(0),
9209                function: FunctionId::new(1),
9210                captures: Vec::new(),
9211                properties: PropertyMap::default(),
9212                prototype: Some(machine.intrinsics.function_prototype),
9213                extensible: true,
9214            })
9215            .unwrap();
9216        machine.fuel = 0;
9217
9218        assert!(matches!(
9219            machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9220            Err(EvalFailure::Runtime(RuntimeErrorKind::FuelExhausted { .. }))
9221        ));
9222        assert!(machine.frames.is_empty());
9223        assert_eq!(machine.live_registers, 0);
9224
9225        machine.fuel = 1;
9226        assert!(matches!(
9227            machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9228            Ok(value) if value == Value::int32(7)
9229        ));
9230    }
9231
9232    #[test]
9233    fn object_values_have_stable_distinct_heap_identity() {
9234        let module = verified(
9235            vec![],
9236            vec![function(
9237                0,
9238                5,
9239                vec![
9240                    Instruction::CreateObject { dst: reg(0) },
9241                    Instruction::CreateObject { dst: reg(1) },
9242                    Instruction::Binary {
9243                        dst: reg(2),
9244                        op: BinaryOp::StrictEqual,
9245                        left: reg(0),
9246                        right: reg(1),
9247                    },
9248                    Instruction::Move {
9249                        dst: reg(3),
9250                        src: reg(0),
9251                    },
9252                    Instruction::Binary {
9253                        dst: reg(4),
9254                        op: BinaryOp::StrictEqual,
9255                        left: reg(0),
9256                        right: reg(3),
9257                    },
9258                    Instruction::Return { value: reg(4) },
9259                ],
9260                vec![],
9261            )],
9262        );
9263        let execution = run_ok(&module);
9264        assert_eq!(execution.entry_registers[2], Value::FALSE);
9265        assert_eq!(execution.value, Value::TRUE);
9266    }
9267
9268    #[test]
9269    fn addition_coerces_objects_left_to_right_and_interpolates_errors() {
9270        let module = verified(
9271            vec![
9272                Constant::String(EcmaString::from_utf8("L")),
9273                Constant::String(EcmaString::from_utf8("additionOrder")),
9274                Constant::String(EcmaString::from_utf8("message")),
9275            ],
9276            vec![
9277                function(0, 1, vec![Instruction::Halt], Vec::new()),
9278                function(
9279                    0,
9280                    1,
9281                    vec![
9282                        Instruction::LoadConst {
9283                            dst: reg(0),
9284                            constant: cid(0),
9285                        },
9286                        Instruction::StoreGlobal {
9287                            name: cid(1),
9288                            value: reg(0),
9289                        },
9290                        Instruction::Return { value: reg(0) },
9291                    ],
9292                    Vec::new(),
9293                ),
9294                function(
9295                    0,
9296                    1,
9297                    vec![
9298                        Instruction::LoadGlobal {
9299                            dst: reg(0),
9300                            name: cid(1),
9301                        },
9302                        Instruction::Return { value: reg(0) },
9303                    ],
9304                    Vec::new(),
9305                ),
9306            ],
9307        );
9308        let mut host = TestHost;
9309        let mut machine = Machine::new(&module, &mut host, Limits::default());
9310        machine.frames.clear();
9311        machine.live_registers = 0;
9312        let left = machine
9313            .allocate(HeapEntry::Object {
9314                properties: PropertyMap::default(),
9315                prototype: Some(machine.intrinsics.object_prototype),
9316                extensible: true,
9317                boxed_primitive: None,
9318            })
9319            .unwrap();
9320        let right = machine
9321            .allocate(HeapEntry::Object {
9322                properties: PropertyMap::default(),
9323                prototype: Some(machine.intrinsics.object_prototype),
9324                extensible: true,
9325                boxed_primitive: None,
9326            })
9327            .unwrap();
9328        let left_value_of = machine
9329            .allocate(HeapEntry::Function {
9330                module: ModuleId::new(0),
9331                function: FunctionId::new(1),
9332                captures: Vec::new(),
9333                properties: PropertyMap::default(),
9334                prototype: Some(machine.intrinsics.function_prototype),
9335                extensible: true,
9336            })
9337            .unwrap();
9338        let right_value_of = machine
9339            .allocate(HeapEntry::Function {
9340                module: ModuleId::new(0),
9341                function: FunctionId::new(2),
9342                captures: Vec::new(),
9343                properties: PropertyMap::default(),
9344                prototype: Some(machine.intrinsics.function_prototype),
9345                extensible: true,
9346            })
9347            .unwrap();
9348        machine
9349            .set_data_property(left, "valueOf", left_value_of)
9350            .unwrap();
9351        machine
9352            .set_data_property(right, "valueOf", right_value_of)
9353            .unwrap();
9354        let coerced = machine.add(left, right).unwrap();
9355        assert!(
9356            machine
9357                .string_value(coerced)
9358                .is_some_and(|text| text.eq_ascii("LL"))
9359        );
9360
9361        let error_constructor = machine.intrinsics.global("Error").unwrap();
9362        let message = machine
9363            .allocate(HeapEntry::String(EcmaString::from_utf8("message")))
9364            .unwrap();
9365        let error = machine
9366            .call_value(error_constructor, Value::UNDEFINED, &[message])
9367            .unwrap();
9368        let empty = machine
9369            .allocate(HeapEntry::String(EcmaString::default()))
9370            .unwrap();
9371        let interpolated = machine.add(empty, error).unwrap();
9372        assert!(
9373            machine
9374                .string_value(interpolated)
9375                .is_some_and(|text| text.eq_ascii("Error: message"))
9376        );
9377
9378        let date_constructor = machine.intrinsics.global("Date").unwrap();
9379        let date_prototype = machine
9380            .get_named_property(date_constructor, "prototype")
9381            .unwrap();
9382        let date = machine
9383            .allocate(HeapEntry::Date {
9384                time: 0.0,
9385                properties: PropertyMap::default(),
9386                prototype: Some(date_prototype),
9387                extensible: true,
9388            })
9389            .unwrap();
9390        machine
9391            .set_data_property(date, "toString", left_value_of)
9392            .unwrap();
9393        let date_text = machine.add(date, empty).unwrap();
9394        assert!(
9395            machine
9396                .string_value(date_text)
9397                .is_some_and(|text| text.eq_ascii("L"))
9398        );
9399    }
9400
9401    #[test]
9402    fn computed_member_access_uses_dynamic_register_key() {
9403        // key = "a" + "b"; obj[key] = 7; return obj[key].
9404        let module = verified(
9405            vec![
9406                Constant::String(EcmaString::from_utf8("a")),
9407                Constant::String(EcmaString::from_utf8("b")),
9408                Constant::Int32(7),
9409            ],
9410            vec![function(
9411                0,
9412                6,
9413                vec![
9414                    Instruction::LoadConst {
9415                        dst: reg(1),
9416                        constant: cid(0),
9417                    },
9418                    Instruction::LoadConst {
9419                        dst: reg(2),
9420                        constant: cid(1),
9421                    },
9422                    Instruction::Binary {
9423                        dst: reg(3),
9424                        op: BinaryOp::Add,
9425                        left: reg(1),
9426                        right: reg(2),
9427                    },
9428                    Instruction::CreateObject { dst: reg(0) },
9429                    Instruction::LoadConst {
9430                        dst: reg(4),
9431                        constant: cid(2),
9432                    },
9433                    Instruction::SetProperty {
9434                        object: reg(0),
9435                        key: reg(3),
9436                        value: reg(4),
9437                    },
9438                    Instruction::GetProperty {
9439                        dst: reg(5),
9440                        object: reg(0),
9441                        key: reg(3),
9442                    },
9443                    Instruction::Return { value: reg(5) },
9444                ],
9445                vec![],
9446            )],
9447        );
9448        assert_eq!(run_ok(&module).value, Value::int32(7));
9449    }
9450
9451    #[test]
9452    fn property_delete_and_array_holes_are_real_mutations() {
9453        let module = verified(
9454            vec![
9455                Constant::String(EcmaString::from_utf8("0")),
9456                Constant::Int32(5),
9457            ],
9458            vec![function(
9459                0,
9460                5,
9461                vec![
9462                    Instruction::CreateArray { dst: reg(0) },
9463                    Instruction::LoadConst {
9464                        dst: reg(1),
9465                        constant: cid(0),
9466                    },
9467                    Instruction::LoadConst {
9468                        dst: reg(4),
9469                        constant: cid(1),
9470                    },
9471                    Instruction::SetProperty {
9472                        object: reg(0),
9473                        key: reg(1),
9474                        value: reg(4),
9475                    },
9476                    Instruction::GetProperty {
9477                        dst: reg(2),
9478                        object: reg(0),
9479                        key: reg(1),
9480                    },
9481                    Instruction::DeleteProperty {
9482                        dst: reg(3),
9483                        object: reg(0),
9484                        key: reg(1),
9485                    },
9486                    Instruction::GetProperty {
9487                        dst: reg(4),
9488                        object: reg(0),
9489                        key: reg(1),
9490                    },
9491                    Instruction::Return { value: reg(3) },
9492                ],
9493                vec![],
9494            )],
9495        );
9496        let execution = run_ok(&module);
9497        assert_eq!(execution.entry_registers[2], Value::int32(5));
9498        assert_eq!(execution.entry_registers[4], Value::UNDEFINED);
9499        assert_eq!(execution.value, Value::TRUE);
9500    }
9501
9502    #[test]
9503    fn closure_captures_seed_leading_registers_before_parameters() {
9504        // captures = [42]; fn1(7) => capture(r0) + param(r1) = 49.
9505        let entry = function(
9506            0,
9507            3,
9508            vec![
9509                Instruction::CreateArray { dst: reg(0) },
9510                Instruction::LoadConst {
9511                    dst: reg(1),
9512                    constant: cid(0),
9513                },
9514                Instruction::ArrayPush {
9515                    array: reg(0),
9516                    value: reg(1),
9517                },
9518                Instruction::CreateClosure {
9519                    dst: reg(2),
9520                    function: FunctionId::new(1),
9521                    captures: reg(0),
9522                },
9523                // arguments array [7]
9524                Instruction::CreateArray { dst: reg(0) },
9525                Instruction::LoadConst {
9526                    dst: reg(1),
9527                    constant: cid(1),
9528                },
9529                Instruction::ArrayPush {
9530                    array: reg(0),
9531                    value: reg(1),
9532                },
9533                Instruction::LoadConst {
9534                    dst: reg(1),
9535                    constant: cid(2),
9536                },
9537                Instruction::Call {
9538                    dst: reg(1),
9539                    callee: reg(2),
9540                    this_value: reg(1),
9541                    arguments: reg(0),
9542                },
9543                Instruction::Return { value: reg(1) },
9544            ],
9545            vec![],
9546        );
9547        // capture_count = 1, parameter_count = 1: r0 = capture, r1 = param.
9548        let callee = closure_function(
9549            1,
9550            1,
9551            3,
9552            vec![
9553                Instruction::Binary {
9554                    dst: reg(2),
9555                    op: BinaryOp::Add,
9556                    left: reg(0),
9557                    right: reg(1),
9558                },
9559                Instruction::Return { value: reg(2) },
9560            ],
9561        );
9562        let module = verified(
9563            vec![Constant::Int32(42), Constant::Int32(7), Constant::Undefined],
9564            vec![entry, callee],
9565        );
9566        assert_eq!(run_ok(&module).value, Value::int32(49));
9567    }
9568
9569    #[test]
9570    fn calls_scale_past_fixed_window_via_arguments_array() {
9571        // Build a 500-element arguments array and call a callee returning
9572        // arguments.length — impossible under a 127 fixed window.
9573        let mut code = vec![Instruction::CreateArray { dst: reg(0) }];
9574        code.push(Instruction::LoadConst {
9575            dst: reg(1),
9576            constant: cid(0),
9577        });
9578        for _ in 0..500 {
9579            code.push(Instruction::ArrayPush {
9580                array: reg(0),
9581                value: reg(1),
9582            });
9583        }
9584        code.push(Instruction::CreateClosure {
9585            dst: reg(2),
9586            function: FunctionId::new(1),
9587            captures: reg(3),
9588        });
9589        // captures array for a zero-capture function
9590        // (reg(3) must be an empty array)
9591        // Insert its creation before CreateClosure:
9592        let mut prelude = vec![Instruction::CreateArray { dst: reg(3) }];
9593        prelude.append(&mut code);
9594        let mut code = prelude;
9595        code.push(Instruction::LoadConst {
9596            dst: reg(1),
9597            constant: cid(1),
9598        });
9599        code.push(Instruction::Call {
9600            dst: reg(1),
9601            callee: reg(2),
9602            this_value: reg(1),
9603            arguments: reg(0),
9604        });
9605        code.push(Instruction::Return { value: reg(1) });
9606
9607        let entry = function(0, 4, code, vec![]);
9608        let callee = function(
9609            0,
9610            2,
9611            vec![
9612                Instruction::LoadArguments { dst: reg(0) },
9613                Instruction::LoadConst {
9614                    dst: reg(1),
9615                    constant: cid(2),
9616                },
9617                Instruction::GetProperty {
9618                    dst: reg(0),
9619                    object: reg(0),
9620                    key: reg(1),
9621                },
9622                Instruction::Return { value: reg(0) },
9623            ],
9624            vec![],
9625        );
9626        let module = verified(
9627            vec![
9628                Constant::Int32(1),
9629                Constant::Undefined,
9630                Constant::String(EcmaString::from_utf8("length")),
9631            ],
9632            vec![entry, callee],
9633        );
9634        assert_eq!(run_ok(&module).value, Value::int32(500));
9635    }
9636
9637    #[test]
9638    fn array_extend_spreads_iterable_elements() {
9639        // dst = []; dst.push(1); dst.extend([2,3]); return dst.length == 3.
9640        let entry = function(
9641            0,
9642            4,
9643            vec![
9644                Instruction::CreateArray { dst: reg(0) },
9645                Instruction::LoadConst {
9646                    dst: reg(1),
9647                    constant: cid(0),
9648                },
9649                Instruction::ArrayPush {
9650                    array: reg(0),
9651                    value: reg(1),
9652                },
9653                // source [2,3]
9654                Instruction::CreateArray { dst: reg(2) },
9655                Instruction::LoadConst {
9656                    dst: reg(1),
9657                    constant: cid(1),
9658                },
9659                Instruction::ArrayPush {
9660                    array: reg(2),
9661                    value: reg(1),
9662                },
9663                Instruction::LoadConst {
9664                    dst: reg(1),
9665                    constant: cid(2),
9666                },
9667                Instruction::ArrayPush {
9668                    array: reg(2),
9669                    value: reg(1),
9670                },
9671                Instruction::ArrayExtend {
9672                    array: reg(0),
9673                    iterable: reg(2),
9674                },
9675                Instruction::LoadConst {
9676                    dst: reg(3),
9677                    constant: cid(3),
9678                },
9679                Instruction::GetProperty {
9680                    dst: reg(0),
9681                    object: reg(0),
9682                    key: reg(3),
9683                },
9684                Instruction::Return { value: reg(0) },
9685            ],
9686            vec![],
9687        );
9688        let module = verified(
9689            vec![
9690                Constant::Int32(1),
9691                Constant::Int32(2),
9692                Constant::Int32(3),
9693                Constant::String(EcmaString::from_utf8("length")),
9694            ],
9695            vec![entry],
9696        );
9697        assert_eq!(run_ok(&module).value, Value::int32(3));
9698    }
9699
9700    #[test]
9701    fn array_extend_uses_sync_protocol_for_set_and_rejects_plain_object() {
9702        let module = verified(
9703            Vec::new(),
9704            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9705        );
9706        let mut host = TestHost;
9707        let mut machine = Machine::new(&module, &mut host, Limits::default());
9708        let set_constructor = machine.intrinsics.global("Set").unwrap();
9709        let set_prototype = machine
9710            .get_named_property(set_constructor, "prototype")
9711            .unwrap();
9712        let set = machine
9713            .allocate(HeapEntry::Collection {
9714                entries: vec![CollectionEntry {
9715                    order: 0,
9716                    key: Value::int32(7),
9717                    value: Value::int32(7),
9718                }],
9719                next_order: 1,
9720                properties: PropertyMap::default(),
9721                prototype: Some(set_prototype),
9722                extensible: true,
9723            })
9724            .unwrap();
9725        let target = machine
9726            .allocate(HeapEntry::Array {
9727                elements: Vec::new(),
9728                properties: PropertyMap::default(),
9729                prototype: Some(machine.intrinsics.array_prototype),
9730                extensible: true,
9731                length_writable: true,
9732            })
9733            .unwrap();
9734
9735        machine.array_extend(target, set).unwrap();
9736        assert_eq!(
9737            machine.array_elements(target).unwrap(),
9738            Some(vec![Value::int32(7)])
9739        );
9740
9741        let plain_object = machine
9742            .allocate(HeapEntry::Object {
9743                properties: PropertyMap::default(),
9744                prototype: Some(machine.intrinsics.object_prototype),
9745                boxed_primitive: None,
9746                extensible: true,
9747            })
9748            .unwrap();
9749        assert!(matches!(
9750            machine.array_extend(target, plain_object),
9751            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9752                operation: "value is not iterable"
9753            }))
9754        ));
9755    }
9756
9757    #[test]
9758    fn sync_iterator_uses_symbol_method_and_caches_next() {
9759        fn iterator_identity<H: Host>(
9760            _machine: &mut Machine<'_, H>,
9761            this: Value,
9762            _args: &[Value],
9763            _constructing: bool,
9764        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9765            Ok(intrinsics::BuiltinOutcome::Value(this))
9766        }
9767
9768        fn next_getter<H: Host>(
9769            machine: &mut Machine<'_, H>,
9770            this: Value,
9771            _args: &[Value],
9772            _constructing: bool,
9773        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9774            let reads = machine.get_named_property(this, "nextReads")?;
9775            let reads = if reads == Value::int32(0) { 1 } else { 2 };
9776            machine.set_data_property(this, "nextReads", Value::int32(reads))?;
9777            Ok(intrinsics::BuiltinOutcome::Value(
9778                machine.get_named_property(this, "nextFunction")?,
9779            ))
9780        }
9781
9782        fn next_result<H: Host>(
9783            machine: &mut Machine<'_, H>,
9784            this: Value,
9785            _args: &[Value],
9786            _constructing: bool,
9787        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9788            Ok(intrinsics::BuiltinOutcome::Value(
9789                machine.get_named_property(this, "result")?,
9790            ))
9791        }
9792
9793        fn done_getter<H: Host>(
9794            machine: &mut Machine<'_, H>,
9795            this: Value,
9796            _args: &[Value],
9797            _constructing: bool,
9798        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9799            machine.set_data_property(this, "order", Value::int32(1))?;
9800            Ok(intrinsics::BuiltinOutcome::Value(Value::FALSE))
9801        }
9802
9803        fn value_getter<H: Host>(
9804            machine: &mut Machine<'_, H>,
9805            this: Value,
9806            _args: &[Value],
9807            _constructing: bool,
9808        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9809            if machine.get_named_property(this, "order")? != Value::int32(1) {
9810                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9811                    operation: "iterator value read before done",
9812                }));
9813            }
9814            Ok(intrinsics::BuiltinOutcome::Value(Value::int32(42)))
9815        }
9816
9817        let module = verified(
9818            Vec::new(),
9819            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9820        );
9821        let mut host = TestHost;
9822        let mut machine = Machine::new(&module, &mut host, Limits::default());
9823        let mut install = |name, handler| {
9824            let id = machine
9825                .intrinsics
9826                .builtins
9827                .register(intrinsics::BuiltinDef {
9828                    name,
9829                    length: 0,
9830                    handler,
9831                });
9832            intrinsics::native_function(&mut machine.heap, id, name, 0)
9833        };
9834        let iterator_identity = install(
9835            "[Symbol.iterator]",
9836            iterator_identity::<TestHost> as intrinsics::BuiltinHandler<TestHost>,
9837        );
9838        let next_getter = install("get next", next_getter::<TestHost>);
9839        let next_result = install("next", next_result::<TestHost>);
9840        let done_getter = install("get done", done_getter::<TestHost>);
9841        let value_getter = install("get value", value_getter::<TestHost>);
9842        let object_prototype = machine.intrinsics.object_prototype;
9843        let result = machine
9844            .allocate(HeapEntry::Object {
9845                properties: {
9846                    let mut properties = PropertyMap::default();
9847                    for (key, property) in [
9848                        (
9849                            PropertyKey::Named(EcmaString::from_utf8("order")),
9850                            Property::Data {
9851                                value: Value::int32(0),
9852                                writable: true,
9853                                enumerable: true,
9854                                configurable: true,
9855                            },
9856                        ),
9857                        (
9858                            PropertyKey::Named(EcmaString::from_utf8("done")),
9859                            Property::Accessor {
9860                                getter: Some(done_getter),
9861                                setter: None,
9862                                enumerable: true,
9863                                configurable: true,
9864                            },
9865                        ),
9866                        (
9867                            PropertyKey::Named(EcmaString::from_utf8("value")),
9868                            Property::Accessor {
9869                                getter: Some(value_getter),
9870                                setter: None,
9871                                enumerable: true,
9872                                configurable: true,
9873                            },
9874                        ),
9875                    ] {
9876                        properties.insert(key, property);
9877                    }
9878                    properties
9879                },
9880                prototype: Some(object_prototype),
9881                boxed_primitive: None,
9882                extensible: true,
9883            })
9884            .unwrap();
9885        let iterator_symbol = machine.intrinsics.builtins.symbol_iterator();
9886        let iterator_key = machine.to_property_key(iterator_symbol).unwrap();
9887        let source = machine
9888            .allocate(HeapEntry::Object {
9889                properties: {
9890                    let mut properties = PropertyMap::default();
9891                    for (key, property) in [
9892                        (
9893                            iterator_key,
9894                            Property::Data {
9895                                value: iterator_identity,
9896                                writable: true,
9897                                enumerable: false,
9898                                configurable: true,
9899                            },
9900                        ),
9901                        (
9902                            PropertyKey::Named(EcmaString::from_utf8("next")),
9903                            Property::Accessor {
9904                                getter: Some(next_getter),
9905                                setter: None,
9906                                enumerable: false,
9907                                configurable: true,
9908                            },
9909                        ),
9910                        (
9911                            PropertyKey::Named(EcmaString::from_utf8("nextReads")),
9912                            Property::Data {
9913                                value: Value::int32(0),
9914                                writable: true,
9915                                enumerable: true,
9916                                configurable: true,
9917                            },
9918                        ),
9919                        (
9920                            PropertyKey::Named(EcmaString::from_utf8("nextFunction")),
9921                            Property::Data {
9922                                value: next_result,
9923                                writable: true,
9924                                enumerable: true,
9925                                configurable: true,
9926                            },
9927                        ),
9928                        (
9929                            PropertyKey::Named(EcmaString::from_utf8("result")),
9930                            Property::Data {
9931                                value: result,
9932                                writable: true,
9933                                enumerable: true,
9934                                configurable: true,
9935                            },
9936                        ),
9937                    ] {
9938                        properties.insert(key, property);
9939                    }
9940                    properties
9941                },
9942                prototype: Some(object_prototype),
9943                boxed_primitive: None,
9944                extensible: true,
9945            })
9946            .unwrap();
9947
9948        let iterator = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9949        assert_eq!(
9950            machine.iterator_next(iterator).unwrap(),
9951            (false, Value::int32(42))
9952        );
9953        assert_eq!(
9954            machine.iterator_next(iterator).unwrap(),
9955            (false, Value::int32(42))
9956        );
9957        assert_eq!(
9958            machine.get_named_property(source, "nextReads").unwrap(),
9959            Value::int32(1)
9960        );
9961
9962        let mut completed_properties = PropertyMap::default();
9963        completed_properties.insert(
9964            PropertyKey::Named(EcmaString::from_utf8("done")),
9965            Property::Data {
9966                value: Value::TRUE,
9967                writable: true,
9968                enumerable: true,
9969                configurable: true,
9970            },
9971        );
9972        completed_properties.insert(
9973            PropertyKey::Named(EcmaString::from_utf8("value")),
9974            Property::Accessor {
9975                getter: Some(value_getter),
9976                setter: None,
9977                enumerable: true,
9978                configurable: true,
9979            },
9980        );
9981        let completed = machine
9982            .allocate(HeapEntry::Object {
9983                properties: completed_properties,
9984                prototype: Some(object_prototype),
9985                boxed_primitive: None,
9986                extensible: true,
9987            })
9988            .unwrap();
9989        machine
9990            .set_data_property(source, "result", completed)
9991            .unwrap();
9992        assert_eq!(
9993            machine.iterator_next(iterator).unwrap(),
9994            (true, Value::UNDEFINED)
9995        );
9996
9997        machine
9998            .delete_property(source, &PropertyKey::Named(EcmaString::from_utf8("next")))
9999            .unwrap();
10000        machine
10001            .set_data_property(source, "next", Value::int32(1))
10002            .unwrap();
10003        let invalid_next = machine.create_iterator(source, IteratorKind::Sync).unwrap();
10004        assert!(matches!(
10005            machine.iterator_next(invalid_next),
10006            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
10007        ));
10008    }
10009
10010    #[test]
10011    fn object_spread_copies_own_properties() {
10012        // src = {}; src.x = 9; target = {}; { ...src }; return target.x.
10013        let key = |c: u32| Instruction::LoadConst {
10014            dst: reg(3),
10015            constant: cid(c),
10016        };
10017        let module = verified(
10018            vec![
10019                Constant::String(EcmaString::from_utf8("x")),
10020                Constant::Int32(9),
10021            ],
10022            vec![function(
10023                0,
10024                4,
10025                vec![
10026                    Instruction::CreateObject { dst: reg(0) },
10027                    key(0),
10028                    Instruction::LoadConst {
10029                        dst: reg(2),
10030                        constant: cid(1),
10031                    },
10032                    Instruction::SetProperty {
10033                        object: reg(0),
10034                        key: reg(3),
10035                        value: reg(2),
10036                    },
10037                    Instruction::CreateObject { dst: reg(1) },
10038                    Instruction::ObjectSpread {
10039                        target: reg(1),
10040                        source: reg(0),
10041                    },
10042                    key(0),
10043                    Instruction::GetProperty {
10044                        dst: reg(2),
10045                        object: reg(1),
10046                        key: reg(3),
10047                    },
10048                    Instruction::Return { value: reg(2) },
10049                ],
10050                vec![],
10051            )],
10052        );
10053        assert_eq!(run_ok(&module).value, Value::int32(9));
10054    }
10055
10056    #[test]
10057    fn object_spread_copies_enumerable_symbol_properties() {
10058        let module = verified(
10059            Vec::new(),
10060            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
10061        );
10062        let mut host = TestHost;
10063        let mut machine = Machine::new(&module, &mut host, Limits::default());
10064        let prototype = machine.intrinsics.object_prototype;
10065        let object = |machine: &mut Machine<'_, TestHost>| {
10066            machine
10067                .allocate(HeapEntry::Object {
10068                    properties: PropertyMap::default(),
10069                    prototype: Some(prototype),
10070                    boxed_primitive: None,
10071                    extensible: true,
10072                })
10073                .unwrap()
10074        };
10075        let source = object(&mut machine);
10076        let target = object(&mut machine);
10077        let symbol = machine
10078            .allocate(HeapEntry::Symbol {
10079                description: EcmaString::from_utf8("key"),
10080            })
10081            .unwrap();
10082        let key = machine.to_property_key(symbol).unwrap();
10083        machine
10084            .set_data_property_key(source, key.clone(), Value::int32(42))
10085            .unwrap();
10086
10087        machine.object_spread(target, source).unwrap();
10088
10089        assert_eq!(
10090            machine.get_property_key(target, &key).unwrap(),
10091            Value::int32(42)
10092        );
10093    }
10094
10095    #[test]
10096    fn object_spread_rechecks_descriptors_after_getters() {
10097        fn delete_next<H: Host>(
10098            machine: &mut Machine<'_, H>,
10099            this: Value,
10100            _args: &[Value],
10101            _constructing: bool,
10102        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
10103            machine.delete_property(this, &PropertyKey::Named(EcmaString::from_utf8("next")))?;
10104            Ok(intrinsics::BuiltinOutcome::Value(Value::int32(1)))
10105        }
10106
10107        let module = verified(
10108            Vec::new(),
10109            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
10110        );
10111        let mut host = TestHost;
10112        let mut machine = Machine::new(&module, &mut host, Limits::default());
10113        let getter_id = machine
10114            .intrinsics
10115            .builtins
10116            .register(intrinsics::BuiltinDef {
10117                name: "delete next",
10118                length: 0,
10119                handler: delete_next::<TestHost>,
10120            });
10121        let getter = intrinsics::native_function(&mut machine.heap, getter_id, "delete next", 0);
10122        let first = PropertyKey::Named(EcmaString::from_utf8("first"));
10123        let next = PropertyKey::Named(EcmaString::from_utf8("next"));
10124        let mut source_properties = PropertyMap::default();
10125        source_properties.insert(
10126            first.clone(),
10127            Property::Accessor {
10128                getter: Some(getter),
10129                setter: None,
10130                enumerable: true,
10131                configurable: true,
10132            },
10133        );
10134        source_properties.insert(
10135            next.clone(),
10136            Property::Data {
10137                value: Value::int32(2),
10138                writable: true,
10139                enumerable: true,
10140                configurable: true,
10141            },
10142        );
10143        let prototype = machine.intrinsics.object_prototype;
10144        let source = machine
10145            .allocate(HeapEntry::Object {
10146                properties: source_properties,
10147                prototype: Some(prototype),
10148                boxed_primitive: None,
10149                extensible: true,
10150            })
10151            .unwrap();
10152        let target = machine
10153            .allocate(HeapEntry::Object {
10154                properties: PropertyMap::default(),
10155                prototype: Some(prototype),
10156                boxed_primitive: None,
10157                extensible: true,
10158            })
10159            .unwrap();
10160
10161        machine.object_spread(target, source).unwrap();
10162
10163        assert_eq!(
10164            machine.get_property_key(target, &first).unwrap(),
10165            Value::int32(1)
10166        );
10167        assert!(!machine.has_own_property_key(target, &next).unwrap());
10168    }
10169
10170    #[test]
10171    fn private_names_have_distinct_identity_and_are_gettable() {
10172        // Two private names with the same description are distinct keys.
10173        let module = verified(
10174            vec![
10175                Constant::String(EcmaString::from_utf8("x")),
10176                Constant::Int32(1),
10177                Constant::Int32(2),
10178            ],
10179            vec![function(
10180                0,
10181                6,
10182                vec![
10183                    Instruction::CreateObject { dst: reg(0) },
10184                    Instruction::CreatePrivateName {
10185                        dst: reg(1),
10186                        description: cid(0),
10187                    },
10188                    Instruction::CreatePrivateName {
10189                        dst: reg(2),
10190                        description: cid(0),
10191                    },
10192                    Instruction::LoadConst {
10193                        dst: reg(3),
10194                        constant: cid(1),
10195                    },
10196                    Instruction::SetProperty {
10197                        object: reg(0),
10198                        key: reg(1),
10199                        value: reg(3),
10200                    },
10201                    Instruction::LoadConst {
10202                        dst: reg(3),
10203                        constant: cid(2),
10204                    },
10205                    Instruction::SetProperty {
10206                        object: reg(0),
10207                        key: reg(2),
10208                        value: reg(3),
10209                    },
10210                    // r4 = obj[#1] (1), r5 = obj[#2] (2)
10211                    Instruction::GetProperty {
10212                        dst: reg(4),
10213                        object: reg(0),
10214                        key: reg(1),
10215                    },
10216                    Instruction::GetProperty {
10217                        dst: reg(5),
10218                        object: reg(0),
10219                        key: reg(2),
10220                    },
10221                    // distinctness: #1 !== #2
10222                    Instruction::Binary {
10223                        dst: reg(3),
10224                        op: BinaryOp::StrictEqual,
10225                        left: reg(1),
10226                        right: reg(2),
10227                    },
10228                    Instruction::Return { value: reg(4) },
10229                ],
10230                vec![],
10231            )],
10232        );
10233        let execution = run_ok(&module);
10234        assert_eq!(execution.value, Value::int32(1));
10235        assert_eq!(execution.entry_registers[5], Value::int32(2));
10236        assert_eq!(execution.entry_registers[3], Value::FALSE);
10237    }
10238
10239    #[test]
10240    fn accessor_getter_is_invoked_on_property_read() {
10241        // Define a getter returning 99, then read the property.
10242        let entry = function(
10243            0,
10244            4,
10245            vec![
10246                Instruction::CreateObject { dst: reg(0) },
10247                Instruction::CreateArray { dst: reg(3) },
10248                Instruction::CreateClosure {
10249                    dst: reg(1),
10250                    function: FunctionId::new(1),
10251                    captures: reg(3),
10252                },
10253                Instruction::LoadConst {
10254                    dst: reg(2),
10255                    constant: cid(0),
10256                },
10257                Instruction::DefineAccessor {
10258                    object: reg(0),
10259                    key: reg(2),
10260                    accessor: reg(1),
10261                    kind: AccessorKind::Getter,
10262                },
10263                Instruction::GetProperty {
10264                    dst: reg(1),
10265                    object: reg(0),
10266                    key: reg(2),
10267                },
10268                Instruction::Return { value: reg(1) },
10269            ],
10270            vec![],
10271        );
10272        let getter = function(
10273            0,
10274            1,
10275            vec![
10276                Instruction::LoadConst {
10277                    dst: reg(0),
10278                    constant: cid(1),
10279                },
10280                Instruction::Return { value: reg(0) },
10281            ],
10282            vec![],
10283        );
10284        let module = verified(
10285            vec![
10286                Constant::String(EcmaString::from_utf8("g")),
10287                Constant::Int32(99),
10288            ],
10289            vec![entry, getter],
10290        );
10291        assert_eq!(run_ok(&module).value, Value::int32(99));
10292    }
10293
10294    #[test]
10295    fn prototype_chain_lookup_and_instanceof() {
10296        // proto = {}; proto.m = 5; ctor.prototype = proto; obj = new ctor();
10297        // return (obj.m == 5) && (obj instanceof ctor).
10298        let entry = function(
10299            0,
10300            6,
10301            vec![
10302                // proto object with m = 5
10303                Instruction::CreateObject { dst: reg(0) },
10304                Instruction::LoadConst {
10305                    dst: reg(1),
10306                    constant: cid(0),
10307                },
10308                Instruction::LoadConst {
10309                    dst: reg(2),
10310                    constant: cid(1),
10311                },
10312                Instruction::SetProperty {
10313                    object: reg(0),
10314                    key: reg(1),
10315                    value: reg(2),
10316                },
10317                // ctor closure
10318                Instruction::CreateArray { dst: reg(4) },
10319                Instruction::CreateClosure {
10320                    dst: reg(3),
10321                    function: FunctionId::new(1),
10322                    captures: reg(4),
10323                },
10324                // ctor.prototype = proto
10325                Instruction::LoadConst {
10326                    dst: reg(1),
10327                    constant: cid(2),
10328                },
10329                Instruction::SetProperty {
10330                    object: reg(3),
10331                    key: reg(1),
10332                    value: reg(0),
10333                },
10334                // obj = new ctor()  (empty args)
10335                Instruction::CreateArray { dst: reg(4) },
10336                Instruction::Construct {
10337                    dst: reg(0),
10338                    callee: reg(3),
10339                    arguments: reg(4),
10340                },
10341                // obj.m via prototype chain
10342                Instruction::LoadConst {
10343                    dst: reg(1),
10344                    constant: cid(0),
10345                },
10346                Instruction::GetProperty {
10347                    dst: reg(2),
10348                    object: reg(0),
10349                    key: reg(1),
10350                },
10351                // obj instanceof ctor
10352                Instruction::Binary {
10353                    dst: reg(5),
10354                    op: BinaryOp::InstanceOf,
10355                    left: reg(0),
10356                    right: reg(3),
10357                },
10358                Instruction::Return { value: reg(2) },
10359            ],
10360            vec![],
10361        );
10362        let ctor = function(0, 1, vec![Instruction::Halt], vec![]);
10363        let module = verified(
10364            vec![
10365                Constant::String(EcmaString::from_utf8("m")),
10366                Constant::Int32(5),
10367                Constant::String(EcmaString::from_utf8("prototype")),
10368            ],
10369            vec![entry, ctor],
10370        );
10371        let execution = run_ok(&module);
10372        assert_eq!(execution.value, Value::int32(5));
10373        assert_eq!(execution.entry_registers[5], Value::TRUE);
10374    }
10375
10376    #[test]
10377    fn sync_iterator_walks_array_elements() {
10378        // Sum [10,20] via GetIterator/IteratorNext loop.
10379        let entry = function(
10380            0,
10381            6,
10382            vec![
10383                Instruction::CreateArray { dst: reg(0) },
10384                Instruction::LoadConst {
10385                    dst: reg(1),
10386                    constant: cid(0),
10387                },
10388                Instruction::ArrayPush {
10389                    array: reg(0),
10390                    value: reg(1),
10391                },
10392                Instruction::LoadConst {
10393                    dst: reg(1),
10394                    constant: cid(1),
10395                },
10396                Instruction::ArrayPush {
10397                    array: reg(0),
10398                    value: reg(1),
10399                },
10400                // acc = 0
10401                Instruction::LoadConst {
10402                    dst: reg(2),
10403                    constant: cid(2),
10404                },
10405                Instruction::GetIterator {
10406                    dst: reg(3),
10407                    src: reg(0),
10408                    kind: IteratorKind::Sync,
10409                },
10410                // loop head @7: next
10411                Instruction::IteratorNext {
10412                    done: reg(4),
10413                    value: reg(5),
10414                    iterator: reg(3),
10415                },
10416                Instruction::JumpIfTrue {
10417                    condition: reg(4),
10418                    target: pc(11),
10419                },
10420                Instruction::Binary {
10421                    dst: reg(2),
10422                    op: BinaryOp::Add,
10423                    left: reg(2),
10424                    right: reg(5),
10425                },
10426                Instruction::Jump { target: pc(7) },
10427                // @11 done
10428                Instruction::Return { value: reg(2) },
10429            ],
10430            vec![],
10431        );
10432        let module = verified(
10433            vec![Constant::Int32(10), Constant::Int32(20), Constant::Int32(0)],
10434            vec![entry],
10435        );
10436        assert_eq!(run_ok(&module).value, Value::int32(30));
10437    }
10438
10439    #[test]
10440    fn keys_iterator_enumerates_own_object_keys() {
10441        // obj = {a:1}; for-in yields "a".
10442        let entry = function(
10443            0,
10444            6,
10445            vec![
10446                Instruction::CreateObject { dst: reg(0) },
10447                Instruction::LoadConst {
10448                    dst: reg(1),
10449                    constant: cid(0),
10450                },
10451                Instruction::LoadConst {
10452                    dst: reg(2),
10453                    constant: cid(1),
10454                },
10455                Instruction::SetProperty {
10456                    object: reg(0),
10457                    key: reg(1),
10458                    value: reg(2),
10459                },
10460                Instruction::GetIterator {
10461                    dst: reg(3),
10462                    src: reg(0),
10463                    kind: IteratorKind::Keys,
10464                },
10465                Instruction::IteratorNext {
10466                    done: reg(4),
10467                    value: reg(5),
10468                    iterator: reg(3),
10469                },
10470                Instruction::Return { value: reg(5) },
10471            ],
10472            vec![],
10473        );
10474        let module = verified(
10475            vec![
10476                Constant::String(EcmaString::from_utf8("a")),
10477                Constant::Int32(1),
10478            ],
10479            vec![entry],
10480        );
10481        let execution = run_ok(&module);
10482        // The produced key must equal a fresh "a" string.
10483        let key = execution.value;
10484        // Compare via a second machine's constant is awkward; instead assert it
10485        // is a heap string by checking done flag was false.
10486        assert_eq!(execution.entry_registers[4], Value::FALSE);
10487        assert_ne!(key, Value::UNDEFINED);
10488    }
10489
10490    #[test]
10491    fn async_iterator_steps_like_sync() {
10492        let entry = function(
10493            0,
10494            5,
10495            vec![
10496                Instruction::CreateArray { dst: reg(0) },
10497                Instruction::LoadConst {
10498                    dst: reg(1),
10499                    constant: cid(0),
10500                },
10501                Instruction::ArrayPush {
10502                    array: reg(0),
10503                    value: reg(1),
10504                },
10505                Instruction::GetIterator {
10506                    dst: reg(2),
10507                    src: reg(0),
10508                    kind: IteratorKind::Async,
10509                },
10510                Instruction::IteratorNext {
10511                    done: reg(3),
10512                    value: reg(4),
10513                    iterator: reg(2),
10514                },
10515                Instruction::Return { value: reg(4) },
10516            ],
10517            vec![],
10518        );
10519        let module = verified(vec![Constant::Int32(8)], vec![entry]);
10520        let execution = run_ok(&module);
10521        assert_eq!(execution.value, Value::int32(8));
10522        assert_eq!(execution.entry_registers[3], Value::FALSE);
10523    }
10524
10525    #[test]
10526    fn globals_store_load_and_typeof_undeclared() {
10527        // StoreGlobal x=5; TypeOfGlobal y (undeclared) -> "undefined";
10528        // TypeOfGlobal x -> "number"; return LoadGlobal x.
10529        let entry = function(
10530            0,
10531            3,
10532            vec![
10533                Instruction::LoadConst {
10534                    dst: reg(0),
10535                    constant: cid(2),
10536                },
10537                Instruction::StoreGlobal {
10538                    name: cid(0),
10539                    value: reg(0),
10540                },
10541                Instruction::TypeOfGlobal {
10542                    dst: reg(1),
10543                    name: cid(1),
10544                },
10545                Instruction::TypeOfGlobal {
10546                    dst: reg(2),
10547                    name: cid(0),
10548                },
10549                Instruction::LoadGlobal {
10550                    dst: reg(0),
10551                    name: cid(0),
10552                },
10553                Instruction::Return { value: reg(0) },
10554            ],
10555            vec![],
10556        );
10557        let module = verified(
10558            vec![
10559                Constant::String(EcmaString::from_utf8("x")),
10560                Constant::String(EcmaString::from_utf8("y")),
10561                Constant::Int32(5),
10562            ],
10563            vec![entry],
10564        );
10565        assert_eq!(run_ok(&module).value, Value::int32(5));
10566    }
10567
10568    #[test]
10569    fn create_cell_throws_reference_error_before_initialization() {
10570        let module = verified(
10571            vec![Constant::Int32(0)],
10572            vec![function(
10573                0,
10574                3,
10575                vec![
10576                    Instruction::CreateCell { dst: reg(0) },
10577                    Instruction::LoadConst {
10578                        dst: reg(1),
10579                        constant: cid(0),
10580                    },
10581                    Instruction::GetProperty {
10582                        dst: reg(2),
10583                        object: reg(0),
10584                        key: reg(1),
10585                    },
10586                    Instruction::Return { value: reg(2) },
10587                ],
10588                vec![],
10589            )],
10590        );
10591        let mut host = TestHost;
10592        let error = Machine::new(&module, &mut host, Limits::default())
10593            .run()
10594            .expect_err("uninitialized cell read throws");
10595        assert!(matches!(
10596            error.kind,
10597            RuntimeErrorKind::UncaughtThrow {
10598                origin: ThrowOrigin::ReferenceError { .. },
10599                ..
10600            }
10601        ));
10602    }
10603
10604    #[test]
10605    fn create_cell_can_be_initialized_to_undefined() {
10606        let module = verified(
10607            vec![Constant::Int32(0), Constant::Undefined],
10608            vec![function(
10609                0,
10610                4,
10611                vec![
10612                    Instruction::CreateCell { dst: reg(0) },
10613                    Instruction::LoadConst {
10614                        dst: reg(1),
10615                        constant: cid(0),
10616                    },
10617                    Instruction::LoadConst {
10618                        dst: reg(2),
10619                        constant: cid(1),
10620                    },
10621                    Instruction::SetProperty {
10622                        object: reg(0),
10623                        key: reg(1),
10624                        value: reg(2),
10625                    },
10626                    Instruction::GetProperty {
10627                        dst: reg(3),
10628                        object: reg(0),
10629                        key: reg(1),
10630                    },
10631                    Instruction::Return { value: reg(3) },
10632                ],
10633                vec![],
10634            )],
10635        );
10636        let mut host = TestHost;
10637        let execution = Machine::new(&module, &mut host, Limits::default())
10638            .run()
10639            .expect("explicit undefined initializes the cell");
10640        assert_eq!(execution.value, Value::UNDEFINED);
10641    }
10642
10643    #[test]
10644    fn load_undeclared_global_throws_reference_error() {
10645        let module = verified(
10646            vec![Constant::String(EcmaString::from_utf8("missing"))],
10647            vec![function(
10648                0,
10649                2,
10650                vec![
10651                    Instruction::LoadGlobal {
10652                        dst: reg(0),
10653                        name: cid(0),
10654                    },
10655                    Instruction::Halt,
10656                    Instruction::Return { value: reg(1) },
10657                ],
10658                vec![ExceptionHandler {
10659                    start: pc(0),
10660                    end: pc(1),
10661                    handler: pc(2),
10662                    catch_register: reg(1),
10663                }],
10664            )],
10665        );
10666        let mut host = TestHost;
10667        // No handler at top level path would raise; here it is caught, and the
10668        // caught value is undefined (the ReferenceError marker value).
10669        let execution = Machine::new(&module, &mut host, Limits::default())
10670            .run()
10671            .unwrap();
10672        assert_eq!(execution.value, Value::UNDEFINED);
10673    }
10674
10675    #[test]
10676    fn uncaught_reference_error_reports_origin() {
10677        let module = verified(
10678            vec![Constant::String(EcmaString::from_utf8("missing"))],
10679            vec![function(
10680                0,
10681                1,
10682                vec![
10683                    Instruction::LoadGlobal {
10684                        dst: reg(0),
10685                        name: cid(0),
10686                    },
10687                    Instruction::Return { value: reg(0) },
10688                ],
10689                vec![],
10690            )],
10691        );
10692        let mut host = TestHost;
10693        let error = Machine::new(&module, &mut host, Limits::default())
10694            .run()
10695            .unwrap_err();
10696        assert_eq!(error.pc, pc(0));
10697        assert!(matches!(
10698            error.kind,
10699            RuntimeErrorKind::UncaughtThrow {
10700                origin: ThrowOrigin::ReferenceError { .. },
10701                ..
10702            }
10703        ));
10704    }
10705
10706    fn assert_uri_error(global: &str, argument: EcmaString) {
10707        let module = verified(
10708            vec![
10709                Constant::String(EcmaString::from_utf8(global)),
10710                Constant::String(argument),
10711                Constant::Undefined,
10712            ],
10713            vec![function(
10714                0,
10715                5,
10716                vec![
10717                    Instruction::LoadGlobal {
10718                        dst: reg(0),
10719                        name: cid(0),
10720                    },
10721                    Instruction::LoadConst {
10722                        dst: reg(1),
10723                        constant: cid(1),
10724                    },
10725                    Instruction::LoadConst {
10726                        dst: reg(2),
10727                        constant: cid(2),
10728                    },
10729                    Instruction::CreateArray { dst: reg(3) },
10730                    Instruction::ArrayPush {
10731                        array: reg(3),
10732                        value: reg(1),
10733                    },
10734                    Instruction::Call {
10735                        dst: reg(4),
10736                        callee: reg(0),
10737                        this_value: reg(2),
10738                        arguments: reg(3),
10739                    },
10740                    Instruction::Return { value: reg(4) },
10741                ],
10742                Vec::new(),
10743            )],
10744        );
10745        let mut host = TestHost;
10746        let error = Machine::new(&module, &mut host, Limits::default())
10747            .run()
10748            .unwrap_err();
10749        assert_eq!(error.pc, pc(5));
10750        assert!(matches!(
10751            error.kind,
10752            RuntimeErrorKind::UncaughtThrow {
10753                origin: ThrowOrigin::UriError {
10754                    operation: "URI malformed"
10755                },
10756                ..
10757            }
10758        ));
10759    }
10760
10761    #[test]
10762    fn uri_builtins_report_uri_error() {
10763        for (global, argument) in [
10764            ("encodeURIComponent", EcmaString::from_units(&[0xd800])),
10765            ("decodeURIComponent", EcmaString::from_utf8("%")),
10766            ("decodeURIComponent", EcmaString::from_utf8("%GG")),
10767            ("decodeURIComponent", EcmaString::from_utf8("%FF")),
10768            ("decodeURIComponent", EcmaString::from_utf8("%80")),
10769            ("decodeURIComponent", EcmaString::from_utf8("%C0%80")),
10770            ("decodeURIComponent", EcmaString::from_utf8("%E2%82")),
10771            ("decodeURIComponent", EcmaString::from_utf8("%ED%A0%80")),
10772            ("decodeURIComponent", EcmaString::from_utf8("%F4%90%80%80")),
10773            (
10774                "decodeURIComponent",
10775                EcmaString::from_utf8("%F8%80%80%80%80"),
10776            ),
10777        ] {
10778            assert_uri_error(global, argument);
10779        }
10780    }
10781
10782    fn assert_uri_decode(argument: EcmaString, expected: EcmaString) {
10783        let module = verified(
10784            vec![
10785                Constant::String(EcmaString::from_utf8("decodeURIComponent")),
10786                Constant::String(argument),
10787                Constant::Undefined,
10788                Constant::String(expected),
10789            ],
10790            vec![function(
10791                0,
10792                7,
10793                vec![
10794                    Instruction::LoadGlobal {
10795                        dst: reg(0),
10796                        name: cid(0),
10797                    },
10798                    Instruction::LoadConst {
10799                        dst: reg(1),
10800                        constant: cid(1),
10801                    },
10802                    Instruction::LoadConst {
10803                        dst: reg(2),
10804                        constant: cid(2),
10805                    },
10806                    Instruction::CreateArray { dst: reg(3) },
10807                    Instruction::ArrayPush {
10808                        array: reg(3),
10809                        value: reg(1),
10810                    },
10811                    Instruction::Call {
10812                        dst: reg(4),
10813                        callee: reg(0),
10814                        this_value: reg(2),
10815                        arguments: reg(3),
10816                    },
10817                    Instruction::LoadConst {
10818                        dst: reg(5),
10819                        constant: cid(3),
10820                    },
10821                    Instruction::Binary {
10822                        dst: reg(6),
10823                        op: BinaryOp::StrictEqual,
10824                        left: reg(4),
10825                        right: reg(5),
10826                    },
10827                    Instruction::Return { value: reg(6) },
10828                ],
10829                Vec::new(),
10830            )],
10831        );
10832        let mut host = TestHost;
10833        let execution = Machine::new(&module, &mut host, Limits::default())
10834            .run()
10835            .unwrap();
10836        assert_eq!(execution.value, Value::TRUE);
10837    }
10838
10839    #[test]
10840    fn decode_uri_component_preserves_units_and_decodes_utf8() {
10841        let exact = EcmaString::from_units(&[0xd800, 0x61, 0xdfff]);
10842        for (argument, expected) in [
10843            (exact.clone(), exact),
10844            (EcmaString::from_utf8("%2F"), EcmaString::from_utf8("/")),
10845            (
10846                EcmaString::from_utf8("%F0%9F%98%80"),
10847                EcmaString::from_utf8("😀"),
10848            ),
10849            (
10850                EcmaString::from_utf8("%E4%B8%ADA"),
10851                EcmaString::from_utf8("中A"),
10852            ),
10853            (EcmaString::from_utf8("%00"), EcmaString::from_units(&[0])),
10854        ] {
10855            assert_uri_decode(argument, expected);
10856        }
10857    }
10858
10859    #[test]
10860    fn regexp_is_object_with_source_and_flags() {
10861        // typeof re === "object" is not directly returnable; return re.source.
10862        let module = verified(
10863            vec![
10864                Constant::String(EcmaString::from_utf8("ab")),
10865                Constant::String(EcmaString::from_utf8("gi")),
10866                Constant::String(EcmaString::from_utf8("source")),
10867                Constant::String(EcmaString::from_utf8("global")),
10868            ],
10869            vec![function(
10870                0,
10871                4,
10872                vec![
10873                    Instruction::CreateRegExp {
10874                        dst: reg(0),
10875                        pattern: cid(0),
10876                        flags: cid(1),
10877                    },
10878                    Instruction::LoadConst {
10879                        dst: reg(1),
10880                        constant: cid(3),
10881                    },
10882                    Instruction::GetProperty {
10883                        dst: reg(2),
10884                        object: reg(0),
10885                        key: reg(1),
10886                    },
10887                    Instruction::Unary {
10888                        dst: reg(3),
10889                        op: UnaryOp::TypeOf,
10890                        operand: reg(0),
10891                    },
10892                    Instruction::Return { value: reg(2) },
10893                ],
10894                vec![],
10895            )],
10896        );
10897        let execution = run_ok(&module);
10898        // re.global -> true
10899        assert_eq!(execution.value, Value::TRUE);
10900    }
10901
10902    #[test]
10903    fn this_and_new_target_are_frame_owned() {
10904        // Call passes this; new.target is undefined in a plain call.
10905        let entry = function(
10906            0,
10907            4,
10908            vec![
10909                Instruction::CreateObject { dst: reg(0) },
10910                Instruction::CreateArray { dst: reg(3) },
10911                Instruction::CreateClosure {
10912                    dst: reg(1),
10913                    function: FunctionId::new(1),
10914                    captures: reg(3),
10915                },
10916                Instruction::CreateArray { dst: reg(2) },
10917                Instruction::Call {
10918                    dst: reg(0),
10919                    callee: reg(1),
10920                    this_value: reg(0),
10921                    arguments: reg(2),
10922                },
10923                Instruction::Return { value: reg(0) },
10924            ],
10925            vec![],
10926        );
10927        // returns (this === passed) is hard cross-frame; instead return typeof
10928        // new.target which is "undefined" for a plain call.
10929        let callee = function(
10930            0,
10931            2,
10932            vec![
10933                Instruction::LoadNewTarget { dst: reg(0) },
10934                Instruction::Unary {
10935                    dst: reg(1),
10936                    op: UnaryOp::TypeOf,
10937                    operand: reg(0),
10938                },
10939                Instruction::Return { value: reg(1) },
10940            ],
10941            vec![],
10942        );
10943        let module = verified(vec![], vec![entry, callee]);
10944        let execution = run_ok(&module);
10945        // typeof undefined is a heap "undefined" string; strict-compare against
10946        // typeof of a known-undefined value is awkward, so assert non-undefined
10947        // heap string was produced and the call completed.
10948        assert_ne!(execution.value, Value::UNDEFINED);
10949    }
10950
10951    #[test]
10952    fn new_target_is_constructor_during_construct() {
10953        // In a constructor, new.target === callee; verify via instanceof-style
10954        // check: store new.target on this, then read back after construct.
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                // ctor.prototype = {}
10966                Instruction::CreateObject { dst: reg(1) },
10967                Instruction::LoadConst {
10968                    dst: reg(2),
10969                    constant: cid(0),
10970                },
10971                Instruction::SetProperty {
10972                    object: reg(0),
10973                    key: reg(2),
10974                    value: reg(1),
10975                },
10976                Instruction::CreateArray { dst: reg(3) },
10977                Instruction::Construct {
10978                    dst: reg(1),
10979                    callee: reg(0),
10980                    arguments: reg(3),
10981                },
10982                // read back this.nt === ctor
10983                Instruction::LoadConst {
10984                    dst: reg(2),
10985                    constant: cid(1),
10986                },
10987                Instruction::GetProperty {
10988                    dst: reg(3),
10989                    object: reg(1),
10990                    key: reg(2),
10991                },
10992                Instruction::Binary {
10993                    dst: reg(3),
10994                    op: BinaryOp::StrictEqual,
10995                    left: reg(3),
10996                    right: reg(0),
10997                },
10998                Instruction::Return { value: reg(3) },
10999            ],
11000            vec![],
11001        );
11002        let ctor = function(
11003            0,
11004            3,
11005            vec![
11006                Instruction::LoadNewTarget { dst: reg(0) },
11007                Instruction::LoadThis { dst: reg(1) },
11008                Instruction::LoadConst {
11009                    dst: reg(2),
11010                    constant: cid(1),
11011                },
11012                Instruction::SetProperty {
11013                    object: reg(1),
11014                    key: reg(2),
11015                    value: reg(0),
11016                },
11017                Instruction::Halt,
11018            ],
11019            vec![],
11020        );
11021        let module = verified(
11022            vec![
11023                Constant::String(EcmaString::from_utf8("prototype")),
11024                Constant::String(EcmaString::from_utf8("nt")),
11025            ],
11026            vec![entry, ctor],
11027        );
11028        assert_eq!(run_ok(&module).value, Value::TRUE);
11029    }
11030
11031    #[test]
11032    fn arguments_object_reflects_passed_values() {
11033        // callee returns arguments[0].
11034        let entry = function(
11035            0,
11036            4,
11037            vec![
11038                Instruction::CreateArray { dst: reg(3) },
11039                Instruction::CreateClosure {
11040                    dst: reg(0),
11041                    function: FunctionId::new(1),
11042                    captures: reg(3),
11043                },
11044                // args = [42]
11045                Instruction::CreateArray { dst: reg(2) },
11046                Instruction::LoadConst {
11047                    dst: reg(1),
11048                    constant: cid(0),
11049                },
11050                Instruction::ArrayPush {
11051                    array: reg(2),
11052                    value: reg(1),
11053                },
11054                Instruction::Call {
11055                    dst: reg(0),
11056                    callee: reg(0),
11057                    this_value: reg(1),
11058                    arguments: reg(2),
11059                },
11060                Instruction::Return { value: reg(0) },
11061            ],
11062            vec![],
11063        );
11064        let callee = function(
11065            0,
11066            2,
11067            vec![
11068                Instruction::LoadArguments { dst: reg(0) },
11069                Instruction::LoadConst {
11070                    dst: reg(1),
11071                    constant: cid(1),
11072                },
11073                Instruction::GetProperty {
11074                    dst: reg(0),
11075                    object: reg(0),
11076                    key: reg(1),
11077                },
11078                Instruction::Return { value: reg(0) },
11079            ],
11080            vec![],
11081        );
11082        let module = verified(
11083            vec![
11084                Constant::Int32(42),
11085                Constant::String(EcmaString::from_utf8("0")),
11086            ],
11087            vec![entry, callee],
11088        );
11089        assert_eq!(run_ok(&module).value, Value::int32(42));
11090    }
11091
11092    #[test]
11093    fn catch_register_receives_exact_thrown_value() {
11094        let module = verified(
11095            vec![Constant::Int32(9)],
11096            vec![function(
11097                0,
11098                2,
11099                vec![
11100                    Instruction::LoadConst {
11101                        dst: reg(0),
11102                        constant: cid(0),
11103                    },
11104                    Instruction::Throw { value: reg(0) },
11105                    Instruction::Return { value: reg(1) },
11106                ],
11107                vec![ExceptionHandler {
11108                    start: pc(1),
11109                    end: pc(2),
11110                    handler: pc(2),
11111                    catch_register: reg(1),
11112                }],
11113            )],
11114        );
11115        assert_eq!(run_ok(&module).value, Value::int32(9));
11116    }
11117
11118    #[test]
11119    fn native_callback_throw_is_caught_at_outer_call_site() {
11120        let entry = function(
11121            0,
11122            9,
11123            vec![
11124                Instruction::CreateArray { dst: reg(0) },
11125                Instruction::LoadConst {
11126                    dst: reg(1),
11127                    constant: cid(0),
11128                },
11129                Instruction::ArrayPush {
11130                    array: reg(0),
11131                    value: reg(1),
11132                },
11133                Instruction::CreateArray { dst: reg(2) },
11134                Instruction::CreateClosure {
11135                    dst: reg(3),
11136                    function: FunctionId::new(1),
11137                    captures: reg(2),
11138                },
11139                Instruction::LoadConst {
11140                    dst: reg(4),
11141                    constant: cid(1),
11142                },
11143                Instruction::GetProperty {
11144                    dst: reg(5),
11145                    object: reg(0),
11146                    key: reg(4),
11147                },
11148                Instruction::CreateArray { dst: reg(6) },
11149                Instruction::ArrayPush {
11150                    array: reg(6),
11151                    value: reg(3),
11152                },
11153                Instruction::Call {
11154                    dst: reg(7),
11155                    callee: reg(5),
11156                    this_value: reg(0),
11157                    arguments: reg(6),
11158                },
11159                Instruction::Halt,
11160                Instruction::Return { value: reg(8) },
11161            ],
11162            vec![ExceptionHandler {
11163                start: pc(9),
11164                end: pc(10),
11165                handler: pc(11),
11166                catch_register: reg(8),
11167            }],
11168        );
11169        let callback = closure_function(
11170            0,
11171            0,
11172            1,
11173            vec![
11174                Instruction::LoadConst {
11175                    dst: reg(0),
11176                    constant: cid(0),
11177                },
11178                Instruction::Throw { value: reg(0) },
11179            ],
11180        );
11181        let module = verified(
11182            vec![
11183                Constant::Int32(7),
11184                Constant::String(EcmaString::from_utf8("map")),
11185            ],
11186            vec![entry, callback],
11187        );
11188
11189        assert_eq!(run_ok(&module).value, Value::int32(7));
11190    }
11191
11192    #[test]
11193    fn native_callback_throw_uncaught_at_outer_call_site() {
11194        let entry = function(
11195            0,
11196            9,
11197            vec![
11198                Instruction::CreateArray { dst: reg(0) },
11199                Instruction::LoadConst {
11200                    dst: reg(1),
11201                    constant: cid(0),
11202                },
11203                Instruction::ArrayPush {
11204                    array: reg(0),
11205                    value: reg(1),
11206                },
11207                Instruction::CreateArray { dst: reg(2) },
11208                Instruction::CreateClosure {
11209                    dst: reg(3),
11210                    function: FunctionId::new(1),
11211                    captures: reg(2),
11212                },
11213                Instruction::LoadConst {
11214                    dst: reg(4),
11215                    constant: cid(1),
11216                },
11217                Instruction::GetProperty {
11218                    dst: reg(5),
11219                    object: reg(0),
11220                    key: reg(4),
11221                },
11222                Instruction::CreateArray { dst: reg(6) },
11223                Instruction::ArrayPush {
11224                    array: reg(6),
11225                    value: reg(3),
11226                },
11227                Instruction::Call {
11228                    dst: reg(7),
11229                    callee: reg(5),
11230                    this_value: reg(0),
11231                    arguments: reg(6),
11232                },
11233                Instruction::Halt,
11234            ],
11235            Vec::new(),
11236        );
11237        let callback = closure_function(
11238            0,
11239            0,
11240            1,
11241            vec![
11242                Instruction::LoadConst {
11243                    dst: reg(0),
11244                    constant: cid(0),
11245                },
11246                Instruction::Throw { value: reg(0) },
11247            ],
11248        );
11249        let simple = closure_function(
11250            0,
11251            0,
11252            1,
11253            vec![
11254                Instruction::LoadConst {
11255                    dst: reg(0),
11256                    constant: cid(2),
11257                },
11258                Instruction::Return { value: reg(0) },
11259            ],
11260        );
11261        let module = verified(
11262            vec![
11263                Constant::Int32(7),
11264                Constant::String(EcmaString::from_utf8("map")),
11265                Constant::Int32(42),
11266            ],
11267            vec![entry, callback, simple],
11268        );
11269
11270        let mut host = TestHost;
11271        let mut machine = Machine::new(&module, &mut host, Limits::default());
11272        let error = machine.run_loop(0).unwrap_err();
11273        assert_eq!(
11274            error.kind,
11275            RuntimeErrorKind::UncaughtThrow {
11276                value: Value::int32(7),
11277                origin: ThrowOrigin::Bytecode,
11278            }
11279        );
11280        assert!(machine.callback_boundaries.is_empty());
11281        assert!(machine.frames.is_empty());
11282        assert_eq!(machine.live_registers, 0);
11283
11284        let callee = machine
11285            .allocate(HeapEntry::Function {
11286                module: ModuleId::new(0),
11287                function: FunctionId::new(2),
11288                captures: Vec::new(),
11289                properties: PropertyMap::default(),
11290                prototype: Some(machine.intrinsics.function_prototype),
11291                extensible: true,
11292            })
11293            .unwrap();
11294        assert_eq!(
11295            machine.call_value(callee, Value::UNDEFINED, &[]).unwrap(),
11296            Value::int32(42)
11297        );
11298    }
11299
11300    #[test]
11301    fn callee_throw_unwinds_to_call_site_handler() {
11302        let entry = function(
11303            0,
11304            4,
11305            vec![
11306                Instruction::CreateArray { dst: reg(3) },
11307                Instruction::CreateClosure {
11308                    dst: reg(0),
11309                    function: FunctionId::new(1),
11310                    captures: reg(3),
11311                },
11312                Instruction::CreateArray { dst: reg(1) },
11313                Instruction::Call {
11314                    dst: reg(2),
11315                    callee: reg(0),
11316                    this_value: reg(1),
11317                    arguments: reg(1),
11318                },
11319                Instruction::Halt,
11320                Instruction::Return { value: reg(3) },
11321            ],
11322            vec![ExceptionHandler {
11323                start: pc(3),
11324                end: pc(4),
11325                handler: pc(5),
11326                catch_register: reg(3),
11327            }],
11328        );
11329        let callee = function(
11330            0,
11331            1,
11332            vec![
11333                Instruction::LoadConst {
11334                    dst: reg(0),
11335                    constant: cid(0),
11336                },
11337                Instruction::Throw { value: reg(0) },
11338            ],
11339            vec![],
11340        );
11341        let module = verified(vec![Constant::Int32(7)], vec![entry, callee]);
11342        assert_eq!(run_ok(&module).value, Value::int32(7));
11343    }
11344
11345    #[test]
11346    fn heap_and_register_limits_fail_before_unbounded_growth() {
11347        let module = verified(
11348            vec![],
11349            vec![function(
11350                0,
11351                2,
11352                vec![
11353                    Instruction::CreateObject { dst: reg(0) },
11354                    Instruction::CreateObject { dst: reg(1) },
11355                    Instruction::Halt,
11356                ],
11357                vec![],
11358            )],
11359        );
11360        let mut host = TestHost;
11361        let error = Machine::new(
11362            &module,
11363            &mut host,
11364            Limits {
11365                max_heap_slots: 1,
11366                ..Limits::default()
11367            },
11368        )
11369        .run()
11370        .unwrap_err();
11371        assert_eq!(error.pc, pc(1));
11372        assert_eq!(
11373            error.kind,
11374            RuntimeErrorKind::HeapSlotLimitExceeded { limit: 1 }
11375        );
11376
11377        let mut host = TestHost;
11378        let error = Machine::new(
11379            &module,
11380            &mut host,
11381            Limits {
11382                max_total_registers: 1,
11383                ..Limits::default()
11384            },
11385        )
11386        .run()
11387        .unwrap_err();
11388        assert_eq!(
11389            error.kind,
11390            RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
11391        );
11392    }
11393
11394    #[test]
11395    fn argument_array_length_limit_is_enforced() {
11396        let entry = function(
11397            0,
11398            4,
11399            vec![
11400                Instruction::CreateArray { dst: reg(3) },
11401                Instruction::CreateClosure {
11402                    dst: reg(0),
11403                    function: FunctionId::new(1),
11404                    captures: reg(3),
11405                },
11406                Instruction::CreateArray { dst: reg(2) },
11407                Instruction::LoadConst {
11408                    dst: reg(1),
11409                    constant: cid(0),
11410                },
11411                Instruction::ArrayPush {
11412                    array: reg(2),
11413                    value: reg(1),
11414                },
11415                Instruction::Call {
11416                    dst: reg(0),
11417                    callee: reg(0),
11418                    this_value: reg(1),
11419                    arguments: reg(2),
11420                },
11421                Instruction::Halt,
11422            ],
11423            vec![],
11424        );
11425        let callee = function(1, 1, vec![Instruction::Return { value: reg(0) }], vec![]);
11426        let module = verified(vec![Constant::Int32(1)], vec![entry, callee]);
11427        let mut host = TestHost;
11428        let error = Machine::new(
11429            &module,
11430            &mut host,
11431            Limits {
11432                max_argument_count: 0,
11433                ..Limits::default()
11434            },
11435        )
11436        .run()
11437        .unwrap_err();
11438        assert_eq!(
11439            error.kind,
11440            RuntimeErrorKind::ArgumentLimitExceeded {
11441                limit: 0,
11442                requested: 1
11443            }
11444        );
11445    }
11446
11447    #[test]
11448    fn u32_registers_and_instruction_pcs_do_not_truncate_at_127() {
11449        let mut code = vec![Instruction::LoadConst {
11450            dst: reg(0),
11451            constant: cid(0),
11452        }];
11453        for register in 1..=199 {
11454            code.push(Instruction::Move {
11455                dst: reg(register),
11456                src: reg(register - 1),
11457            });
11458        }
11459        code.push(Instruction::Return { value: reg(199) });
11460        let module = verified(
11461            vec![Constant::Number(NumberBits::from_f64(3.5))],
11462            vec![function(0, 200, code, vec![])],
11463        );
11464        let execution = run_ok(&module);
11465        assert_eq!(execution.value, Value::number(3.5));
11466        assert_eq!(execution.entry_registers[199], Value::number(3.5));
11467    }
11468
11469    #[test]
11470    fn construct_returned_object_overrides_default_instance() {
11471        // A constructor returning its own object overrides the default instance.
11472        let entry = function(
11473            0,
11474            3,
11475            vec![
11476                Instruction::CreateArray { dst: reg(2) },
11477                Instruction::CreateClosure {
11478                    dst: reg(0),
11479                    function: FunctionId::new(1),
11480                    captures: reg(2),
11481                },
11482                Instruction::CreateArray { dst: reg(2) },
11483                Instruction::Construct {
11484                    dst: reg(1),
11485                    callee: reg(0),
11486                    arguments: reg(2),
11487                },
11488                // returned object has marker property set to 5
11489                Instruction::LoadConst {
11490                    dst: reg(0),
11491                    constant: cid(0),
11492                },
11493                Instruction::GetProperty {
11494                    dst: reg(2),
11495                    object: reg(1),
11496                    key: reg(0),
11497                },
11498                Instruction::Return { value: reg(2) },
11499            ],
11500            vec![],
11501        );
11502        let returns_object = function(
11503            0,
11504            3,
11505            vec![
11506                Instruction::CreateObject { dst: reg(0) },
11507                Instruction::LoadConst {
11508                    dst: reg(1),
11509                    constant: cid(0),
11510                },
11511                Instruction::LoadConst {
11512                    dst: reg(2),
11513                    constant: cid(1),
11514                },
11515                Instruction::SetProperty {
11516                    object: reg(0),
11517                    key: reg(1),
11518                    value: reg(2),
11519                },
11520                Instruction::Return { value: reg(0) },
11521            ],
11522            vec![],
11523        );
11524        let module = verified(
11525            vec![
11526                Constant::String(EcmaString::from_utf8("marker")),
11527                Constant::Int32(5),
11528            ],
11529            vec![entry, returns_object],
11530        );
11531        assert_eq!(run_ok(&module).value, Value::int32(5));
11532    }
11533
11534    #[test]
11535    fn ecmascript_number_formatting_is_shortest_round_trip() {
11536        let cases = [
11537            (0.1 + 0.2, "0.30000000000000004"),
11538            (1e21, "1e+21"),
11539            (-0.0, "0"),
11540            (1.0 / 3.0, "0.3333333333333333"),
11541            (1e-6, "0.000001"),
11542            (1e-7, "1e-7"),
11543        ];
11544        for (number, expected) in cases {
11545            assert_eq!(
11546                Machine::<TestHost>::ordinary_number_to_string(number),
11547                expected
11548            );
11549        }
11550    }
11551
11552    #[test]
11553    fn own_keys_put_indices_before_insertion_ordered_strings() {
11554        let module = verified(
11555            Vec::new(),
11556            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11557        );
11558        let mut host = TestHost;
11559        let mut machine = Machine::new(&module, &mut host, Limits::default());
11560        let object = machine
11561            .allocate(HeapEntry::Object {
11562                properties: PropertyMap::default(),
11563                prototype: Some(machine.intrinsics.object_prototype),
11564                boxed_primitive: None,
11565                extensible: true,
11566            })
11567            .unwrap();
11568        let index = machine.runtime_slot(object).unwrap().unwrap();
11569        for (key, value) in [("b", 1), ("2", 2), ("a", 3), ("1", 4)] {
11570            machine
11571                .set_own_data(
11572                    index,
11573                    PropertyKey::Named(EcmaString::from_utf8(key)),
11574                    Value::int32(value),
11575                )
11576                .unwrap();
11577        }
11578        assert_eq!(
11579            machine.enumerable_keys(object).unwrap(),
11580            ["1", "2", "b", "a"].map(EcmaString::from_utf8)
11581        );
11582    }
11583
11584    #[test]
11585    fn object_prototype_to_string_uses_realm_tags() {
11586        let module = verified(
11587            Vec::new(),
11588            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11589        );
11590        let mut host = TestHost;
11591        let mut machine = Machine::new(&module, &mut host, Limits::default());
11592        let array = machine
11593            .allocate(HeapEntry::Array {
11594                elements: Vec::new(),
11595                properties: PropertyMap::default(),
11596                prototype: Some(machine.intrinsics.array_prototype),
11597                extensible: true,
11598                length_writable: true,
11599            })
11600            .unwrap();
11601        let object = machine
11602            .allocate(HeapEntry::Object {
11603                properties: PropertyMap::default(),
11604                prototype: Some(machine.intrinsics.object_prototype),
11605                boxed_primitive: None,
11606                extensible: true,
11607            })
11608            .unwrap();
11609        let function = machine.intrinsics.global("Object").unwrap();
11610        let to_string = machine.intrinsics.object_to_string();
11611        for (value, expected) in [
11612            (Value::UNDEFINED, "[object Undefined]"),
11613            (Value::NULL, "[object Null]"),
11614            (Value::TRUE, "[object Boolean]"),
11615            (array, "[object Array]"),
11616            (object, "[object Object]"),
11617            (function, "[object Function]"),
11618        ] {
11619            let tag = machine.call_value(to_string, value, &[]).unwrap();
11620            assert!(
11621                machine
11622                    .string_text(tag)
11623                    .is_some_and(|text| text.eq_ascii(expected))
11624            );
11625        }
11626    }
11627
11628    #[derive(Default)]
11629    struct CapabilityHost {
11630        stdout: Vec<u8>,
11631        stderr: Vec<u8>,
11632        env: BTreeMap<String, String>,
11633    }
11634
11635    impl Host for CapabilityHost {
11636        fn write_stdout(&mut self, bytes: &[u8]) {
11637            self.stdout.extend_from_slice(bytes);
11638        }
11639
11640        fn write_stderr(&mut self, bytes: &[u8]) {
11641            self.stderr.extend_from_slice(bytes);
11642        }
11643
11644        fn env(&self, name: &str) -> Option<&str> {
11645            self.env.get(name).map(String::as_str)
11646        }
11647
11648        fn set_env(&mut self, name: &str, value: &str) {
11649            self.env.insert(name.to_owned(), value.to_owned());
11650        }
11651
11652        fn delete_env(&mut self, name: &str) -> bool {
11653            self.env.remove(name).is_some()
11654        }
11655    }
11656
11657    #[test]
11658    fn console_formats_node_value_shapes_byte_exactly() {
11659        let module = verified(
11660            Vec::new(),
11661            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11662        );
11663        let mut host = CapabilityHost::default();
11664        {
11665            let mut machine = Machine::new(&module, &mut host, Limits::default());
11666            let console = machine.intrinsics.global("console").unwrap();
11667            let log = machine.get_named_property(console, "log").unwrap();
11668            let string = machine
11669                .allocate(HeapEntry::String(EcmaString::from_utf8("hello")))
11670                .unwrap();
11671            let array_string = machine
11672                .allocate(HeapEntry::String(EcmaString::from_utf8("x")))
11673                .unwrap();
11674            let array = machine
11675                .allocate(HeapEntry::Array {
11676                    elements: vec![Value::int32(1), array_string],
11677                    properties: PropertyMap::default(),
11678                    prototype: Some(machine.intrinsics.array_prototype),
11679                    extensible: true,
11680                    length_writable: true,
11681                })
11682                .unwrap();
11683            let mut inner_properties = PropertyMap::default();
11684            inner_properties.insert(
11685                PropertyKey::Named(EcmaString::from_utf8("answer")),
11686                Property::Data {
11687                    value: Value::int32(42),
11688                    writable: true,
11689                    enumerable: true,
11690                    configurable: true,
11691                },
11692            );
11693            let inner = machine
11694                .allocate(HeapEntry::Object {
11695                    properties: inner_properties,
11696                    prototype: Some(machine.intrinsics.object_prototype),
11697                    boxed_primitive: None,
11698                    extensible: true,
11699                })
11700                .unwrap();
11701            let mut outer_properties = PropertyMap::default();
11702            outer_properties.insert(
11703                PropertyKey::Named(EcmaString::from_utf8("nested")),
11704                Property::Data {
11705                    value: inner,
11706                    writable: true,
11707                    enumerable: true,
11708                    configurable: true,
11709                },
11710            );
11711            let outer = machine
11712                .allocate(HeapEntry::Object {
11713                    properties: outer_properties,
11714                    prototype: Some(machine.intrinsics.object_prototype),
11715                    boxed_primitive: None,
11716                    extensible: true,
11717                })
11718                .unwrap();
11719            let symbol = machine
11720                .allocate(HeapEntry::Symbol {
11721                    description: EcmaString::from_utf8("token"),
11722                })
11723                .unwrap();
11724            for value in [
11725                string,
11726                Value::int32(42),
11727                array,
11728                outer,
11729                Value::UNDEFINED,
11730                Value::NULL,
11731                symbol,
11732            ] {
11733                machine.call_value(log, console, &[value]).unwrap();
11734            }
11735        }
11736        assert_eq!(
11737            host.stdout,
11738            b"hello\n42\n[ 1, 'x' ]\n{ nested: { answer: 42 } }\nundefined\nnull\nSymbol(token)\n"
11739        );
11740        assert!(host.stderr.is_empty());
11741    }
11742
11743    #[test]
11744    fn console_and_process_properties_are_reassignable_and_env_is_live() {
11745        let module = verified(
11746            Vec::new(),
11747            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11748        );
11749        let mut host = CapabilityHost::default();
11750        {
11751            let mut machine = Machine::new(&module, &mut host, Limits::default());
11752            let console = machine.intrinsics.global("console").unwrap();
11753            let warn = machine.get_named_property(console, "warn").unwrap();
11754            machine
11755                .set_data_property(console, "warn", Value::int32(91))
11756                .unwrap();
11757            assert_eq!(
11758                machine.get_named_property(console, "warn").unwrap(),
11759                Value::int32(91)
11760            );
11761            machine.set_data_property(console, "warn", warn).unwrap();
11762
11763            let process = machine.intrinsics.global("process").unwrap();
11764            let env = machine.get_named_property(process, "env").unwrap();
11765            machine
11766                .set_data_property(env, "BAMTS_MODE", Value::int32(7))
11767                .unwrap();
11768            let value = machine.get_named_property(env, "BAMTS_MODE").unwrap();
11769            assert!(
11770                machine
11771                    .string_text(value)
11772                    .is_some_and(|text| text.eq_ascii("7"))
11773            );
11774            assert!(
11775                machine
11776                    .delete_property(
11777                        env,
11778                        &PropertyKey::Named(EcmaString::from_utf8("BAMTS_MODE"))
11779                    )
11780                    .unwrap()
11781            );
11782            assert_eq!(
11783                machine.get_named_property(env, "BAMTS_MODE").unwrap(),
11784                Value::UNDEFINED
11785            );
11786        }
11787        assert_eq!(host.env("BAMTS_MODE"), None);
11788    }
11789
11790    #[test]
11791    fn independent_modules_keep_same_name_globals_isolated() {
11792        let dependency = |name: &str, value: i32| {
11793            program_module(
11794                name,
11795                vec![
11796                    Constant::String(EcmaString::from_utf8("x")),
11797                    Constant::Int32(value),
11798                ],
11799                vec![function(
11800                    0,
11801                    1,
11802                    vec![
11803                        Instruction::LoadConst {
11804                            dst: reg(0),
11805                            constant: cid(2),
11806                        },
11807                        Instruction::StoreGlobal {
11808                            name: cid(1),
11809                            value: reg(0),
11810                        },
11811                        Instruction::Return { value: reg(0) },
11812                    ],
11813                    Vec::new(),
11814                )],
11815                Vec::new(),
11816                vec![Binding {
11817                    name: cid(1),
11818                    kind: BindingKind::Hoisted,
11819                }],
11820                vec![Export {
11821                    name: cid(1),
11822                    source: ExportSource::Local(BindingId::new(0)),
11823                }],
11824            )
11825        };
11826        let root = program_module(
11827            "root",
11828            vec![
11829                Constant::String(EcmaString::from_utf8("left")),
11830                Constant::String(EcmaString::from_utf8("right")),
11831                Constant::String(EcmaString::from_utf8("x")),
11832            ],
11833            vec![function(
11834                0,
11835                5,
11836                vec![
11837                    Instruction::LoadGlobal {
11838                        dst: reg(0),
11839                        name: cid(1),
11840                    },
11841                    Instruction::LoadGlobal {
11842                        dst: reg(1),
11843                        name: cid(2),
11844                    },
11845                    Instruction::LoadConst {
11846                        dst: reg(2),
11847                        constant: cid(3),
11848                    },
11849                    Instruction::GetProperty {
11850                        dst: reg(3),
11851                        object: reg(0),
11852                        key: reg(2),
11853                    },
11854                    Instruction::GetProperty {
11855                        dst: reg(4),
11856                        object: reg(1),
11857                        key: reg(2),
11858                    },
11859                    Instruction::Binary {
11860                        dst: reg(0),
11861                        op: BinaryOp::Add,
11862                        left: reg(3),
11863                        right: reg(4),
11864                    },
11865                    Instruction::Return { value: reg(0) },
11866                ],
11867                Vec::new(),
11868            )],
11869            vec![
11870                Edge {
11871                    specifier: cid(1),
11872                    target: EdgeTarget::Local(ModuleId::new(0)),
11873                    kind: EdgeKind::Static,
11874                },
11875                Edge {
11876                    specifier: cid(2),
11877                    target: EdgeTarget::Local(ModuleId::new(1)),
11878                    kind: EdgeKind::Static,
11879                },
11880            ],
11881            vec![
11882                Binding {
11883                    name: cid(1),
11884                    kind: BindingKind::Namespace {
11885                        edge: EdgeId::new(0),
11886                    },
11887                },
11888                Binding {
11889                    name: cid(2),
11890                    kind: BindingKind::Namespace {
11891                        edge: EdgeId::new(1),
11892                    },
11893                },
11894            ],
11895            Vec::new(),
11896        );
11897        let program = linked(vec![dependency("left", 1), dependency("right", 2), root], 2);
11898        assert_eq!(run_ok(&program).value, Value::int32(3));
11899    }
11900
11901    #[test]
11902    fn imported_binding_observes_post_link_mutation_live() {
11903        let dependency = program_module(
11904            "dependency",
11905            vec![
11906                Constant::String(EcmaString::from_utf8("x")),
11907                Constant::Int32(1),
11908                Constant::Int32(2),
11909                Constant::String(EcmaString::from_utf8("set")),
11910            ],
11911            vec![
11912                function(
11913                    0,
11914                    3,
11915                    vec![
11916                        Instruction::LoadConst {
11917                            dst: reg(0),
11918                            constant: cid(2),
11919                        },
11920                        Instruction::StoreGlobal {
11921                            name: cid(1),
11922                            value: reg(0),
11923                        },
11924                        Instruction::CreateArray { dst: reg(1) },
11925                        Instruction::CreateClosure {
11926                            dst: reg(2),
11927                            function: FunctionId::new(1),
11928                            captures: reg(1),
11929                        },
11930                        Instruction::StoreGlobal {
11931                            name: cid(4),
11932                            value: reg(2),
11933                        },
11934                        Instruction::Return { value: reg(0) },
11935                    ],
11936                    Vec::new(),
11937                ),
11938                function(
11939                    0,
11940                    1,
11941                    vec![
11942                        Instruction::LoadConst {
11943                            dst: reg(0),
11944                            constant: cid(3),
11945                        },
11946                        Instruction::StoreGlobal {
11947                            name: cid(1),
11948                            value: reg(0),
11949                        },
11950                        Instruction::Return { value: reg(0) },
11951                    ],
11952                    Vec::new(),
11953                ),
11954            ],
11955            Vec::new(),
11956            vec![
11957                Binding {
11958                    name: cid(1),
11959                    kind: BindingKind::Hoisted,
11960                },
11961                Binding {
11962                    name: cid(4),
11963                    kind: BindingKind::Hoisted,
11964                },
11965            ],
11966            vec![
11967                Export {
11968                    name: cid(1),
11969                    source: ExportSource::Local(BindingId::new(0)),
11970                },
11971                Export {
11972                    name: cid(4),
11973                    source: ExportSource::Local(BindingId::new(1)),
11974                },
11975            ],
11976        );
11977        let root = program_module(
11978            "root",
11979            vec![
11980                Constant::String(EcmaString::from_utf8("x")),
11981                Constant::String(EcmaString::from_utf8("set")),
11982                Constant::String(EcmaString::from_utf8("dep")),
11983            ],
11984            vec![function(
11985                0,
11986                3,
11987                vec![
11988                    Instruction::LoadGlobal {
11989                        dst: reg(0),
11990                        name: cid(2),
11991                    },
11992                    Instruction::CreateArray { dst: reg(1) },
11993                    Instruction::Call {
11994                        dst: reg(2),
11995                        callee: reg(0),
11996                        this_value: reg(1),
11997                        arguments: reg(1),
11998                    },
11999                    Instruction::LoadGlobal {
12000                        dst: reg(0),
12001                        name: cid(1),
12002                    },
12003                    Instruction::Return { value: reg(0) },
12004                ],
12005                Vec::new(),
12006            )],
12007            vec![Edge {
12008                specifier: cid(3),
12009                target: EdgeTarget::Local(ModuleId::new(0)),
12010                kind: EdgeKind::Static,
12011            }],
12012            vec![
12013                Binding {
12014                    name: cid(1),
12015                    kind: BindingKind::Imported {
12016                        edge: EdgeId::new(0),
12017                        name: cid(1),
12018                    },
12019                },
12020                Binding {
12021                    name: cid(2),
12022                    kind: BindingKind::Imported {
12023                        edge: EdgeId::new(0),
12024                        name: cid(2),
12025                    },
12026                },
12027            ],
12028            Vec::new(),
12029        );
12030        assert_eq!(
12031            run_ok(&linked(vec![dependency, root], 1)).value,
12032            Value::int32(2)
12033        );
12034    }
12035
12036    #[test]
12037    fn closure_globals_resolve_in_the_defining_module() {
12038        let dependency = program_module(
12039            "dependency",
12040            vec![
12041                Constant::String(EcmaString::from_utf8("x")),
12042                Constant::Int32(10),
12043                Constant::String(EcmaString::from_utf8("read")),
12044            ],
12045            vec![
12046                function(
12047                    0,
12048                    3,
12049                    vec![
12050                        Instruction::LoadConst {
12051                            dst: reg(0),
12052                            constant: cid(2),
12053                        },
12054                        Instruction::StoreGlobal {
12055                            name: cid(1),
12056                            value: reg(0),
12057                        },
12058                        Instruction::CreateArray { dst: reg(1) },
12059                        Instruction::CreateClosure {
12060                            dst: reg(2),
12061                            function: FunctionId::new(1),
12062                            captures: reg(1),
12063                        },
12064                        Instruction::StoreGlobal {
12065                            name: cid(3),
12066                            value: reg(2),
12067                        },
12068                        Instruction::Return { value: reg(0) },
12069                    ],
12070                    Vec::new(),
12071                ),
12072                function(
12073                    0,
12074                    1,
12075                    vec![
12076                        Instruction::LoadGlobal {
12077                            dst: reg(0),
12078                            name: cid(1),
12079                        },
12080                        Instruction::Return { value: reg(0) },
12081                    ],
12082                    Vec::new(),
12083                ),
12084            ],
12085            Vec::new(),
12086            vec![
12087                Binding {
12088                    name: cid(1),
12089                    kind: BindingKind::Hoisted,
12090                },
12091                Binding {
12092                    name: cid(3),
12093                    kind: BindingKind::Hoisted,
12094                },
12095            ],
12096            vec![Export {
12097                name: cid(3),
12098                source: ExportSource::Local(BindingId::new(1)),
12099            }],
12100        );
12101        let root = program_module(
12102            "root",
12103            vec![
12104                Constant::String(EcmaString::from_utf8("x")),
12105                Constant::Int32(20),
12106                Constant::String(EcmaString::from_utf8("read")),
12107                Constant::String(EcmaString::from_utf8("dep")),
12108            ],
12109            vec![function(
12110                0,
12111                4,
12112                vec![
12113                    Instruction::LoadConst {
12114                        dst: reg(0),
12115                        constant: cid(2),
12116                    },
12117                    Instruction::StoreGlobal {
12118                        name: cid(1),
12119                        value: reg(0),
12120                    },
12121                    Instruction::LoadGlobal {
12122                        dst: reg(1),
12123                        name: cid(3),
12124                    },
12125                    Instruction::CreateArray { dst: reg(2) },
12126                    Instruction::Call {
12127                        dst: reg(3),
12128                        callee: reg(1),
12129                        this_value: reg(2),
12130                        arguments: reg(2),
12131                    },
12132                    Instruction::Return { value: reg(3) },
12133                ],
12134                Vec::new(),
12135            )],
12136            vec![Edge {
12137                specifier: cid(4),
12138                target: EdgeTarget::Local(ModuleId::new(0)),
12139                kind: EdgeKind::Static,
12140            }],
12141            vec![
12142                Binding {
12143                    name: cid(1),
12144                    kind: BindingKind::Hoisted,
12145                },
12146                Binding {
12147                    name: cid(3),
12148                    kind: BindingKind::Imported {
12149                        edge: EdgeId::new(0),
12150                        name: cid(3),
12151                    },
12152                },
12153            ],
12154            Vec::new(),
12155        );
12156        assert_eq!(
12157            run_ok(&linked(vec![dependency, root], 1)).value,
12158            Value::int32(10)
12159        );
12160    }
12161
12162    #[test]
12163    fn cycle_traps_a_lexical_read_before_initialization() {
12164        let first = program_module(
12165            "first",
12166            vec![
12167                Constant::String(EcmaString::from_utf8("a")),
12168                Constant::Int32(1),
12169                Constant::String(EcmaString::from_utf8("second")),
12170            ],
12171            vec![function(
12172                0,
12173                1,
12174                vec![
12175                    Instruction::LoadConst {
12176                        dst: reg(0),
12177                        constant: cid(2),
12178                    },
12179                    Instruction::StoreGlobal {
12180                        name: cid(1),
12181                        value: reg(0),
12182                    },
12183                    Instruction::Return { value: reg(0) },
12184                ],
12185                Vec::new(),
12186            )],
12187            vec![Edge {
12188                specifier: cid(3),
12189                target: EdgeTarget::Local(ModuleId::new(1)),
12190                kind: EdgeKind::Static,
12191            }],
12192            vec![Binding {
12193                name: cid(1),
12194                kind: BindingKind::Lexical,
12195            }],
12196            vec![Export {
12197                name: cid(1),
12198                source: ExportSource::Local(BindingId::new(0)),
12199            }],
12200        );
12201        let second = program_module(
12202            "second",
12203            vec![
12204                Constant::String(EcmaString::from_utf8("a")),
12205                Constant::String(EcmaString::from_utf8("first")),
12206            ],
12207            vec![function(
12208                0,
12209                1,
12210                vec![
12211                    Instruction::LoadGlobal {
12212                        dst: reg(0),
12213                        name: cid(1),
12214                    },
12215                    Instruction::Return { value: reg(0) },
12216                ],
12217                Vec::new(),
12218            )],
12219            vec![Edge {
12220                specifier: cid(2),
12221                target: EdgeTarget::Local(ModuleId::new(0)),
12222                kind: EdgeKind::Static,
12223            }],
12224            vec![Binding {
12225                name: cid(1),
12226                kind: BindingKind::Imported {
12227                    edge: EdgeId::new(0),
12228                    name: cid(1),
12229                },
12230            }],
12231            Vec::new(),
12232        );
12233        let program = linked(vec![first, second], 0);
12234        let mut host = TestHost;
12235        let error = Machine::new(&program, &mut host, Limits::default())
12236            .run()
12237            .unwrap_err();
12238        assert!(matches!(
12239            error.kind,
12240            RuntimeErrorKind::TemporalDeadZone { module, binding }
12241                if module == ModuleId::new(1) && binding == BindingId::new(0)
12242        ));
12243    }
12244
12245    #[test]
12246    fn cycle_reentry_with_a_hoisted_binding_completes() {
12247        let first = program_module(
12248            "first",
12249            vec![
12250                Constant::String(EcmaString::from_utf8("a")),
12251                Constant::Int32(1),
12252                Constant::String(EcmaString::from_utf8("second")),
12253            ],
12254            vec![function(
12255                0,
12256                1,
12257                vec![
12258                    Instruction::LoadConst {
12259                        dst: reg(0),
12260                        constant: cid(2),
12261                    },
12262                    Instruction::StoreGlobal {
12263                        name: cid(1),
12264                        value: reg(0),
12265                    },
12266                    Instruction::Return { value: reg(0) },
12267                ],
12268                Vec::new(),
12269            )],
12270            vec![Edge {
12271                specifier: cid(3),
12272                target: EdgeTarget::Local(ModuleId::new(1)),
12273                kind: EdgeKind::Static,
12274            }],
12275            vec![Binding {
12276                name: cid(1),
12277                kind: BindingKind::Hoisted,
12278            }],
12279            vec![Export {
12280                name: cid(1),
12281                source: ExportSource::Local(BindingId::new(0)),
12282            }],
12283        );
12284        let second = program_module(
12285            "second",
12286            vec![
12287                Constant::String(EcmaString::from_utf8("a")),
12288                Constant::String(EcmaString::from_utf8("first")),
12289            ],
12290            vec![function(
12291                0,
12292                1,
12293                vec![
12294                    Instruction::LoadGlobal {
12295                        dst: reg(0),
12296                        name: cid(1),
12297                    },
12298                    Instruction::Return { value: reg(0) },
12299                ],
12300                Vec::new(),
12301            )],
12302            vec![Edge {
12303                specifier: cid(2),
12304                target: EdgeTarget::Local(ModuleId::new(0)),
12305                kind: EdgeKind::Static,
12306            }],
12307            vec![Binding {
12308                name: cid(1),
12309                kind: BindingKind::Imported {
12310                    edge: EdgeId::new(0),
12311                    name: cid(1),
12312                },
12313            }],
12314            Vec::new(),
12315        );
12316        assert_eq!(
12317            run_ok(&linked(vec![first, second], 0)).value,
12318            Value::int32(1)
12319        );
12320    }
12321
12322    #[test]
12323    fn namespace_identity_reads_live_cells_and_enumerates_sorted_keys() {
12324        let dependency = program_module(
12325            "dependency",
12326            vec![
12327                Constant::String(EcmaString::from_utf8("z")),
12328                Constant::String(EcmaString::from_utf8("a")),
12329                Constant::String(EcmaString::from_utf8("mutate")),
12330                Constant::Int32(1),
12331                Constant::Int32(2),
12332                Constant::Int32(3),
12333            ],
12334            vec![
12335                function(
12336                    0,
12337                    4,
12338                    vec![
12339                        Instruction::LoadConst {
12340                            dst: reg(0),
12341                            constant: cid(4),
12342                        },
12343                        Instruction::StoreGlobal {
12344                            name: cid(1),
12345                            value: reg(0),
12346                        },
12347                        Instruction::LoadConst {
12348                            dst: reg(0),
12349                            constant: cid(5),
12350                        },
12351                        Instruction::StoreGlobal {
12352                            name: cid(2),
12353                            value: reg(0),
12354                        },
12355                        Instruction::CreateArray { dst: reg(1) },
12356                        Instruction::CreateClosure {
12357                            dst: reg(2),
12358                            function: FunctionId::new(1),
12359                            captures: reg(1),
12360                        },
12361                        Instruction::StoreGlobal {
12362                            name: cid(3),
12363                            value: reg(2),
12364                        },
12365                        Instruction::Return { value: reg(0) },
12366                    ],
12367                    Vec::new(),
12368                ),
12369                function(
12370                    0,
12371                    1,
12372                    vec![
12373                        Instruction::LoadConst {
12374                            dst: reg(0),
12375                            constant: cid(6),
12376                        },
12377                        Instruction::StoreGlobal {
12378                            name: cid(1),
12379                            value: reg(0),
12380                        },
12381                        Instruction::Return { value: reg(0) },
12382                    ],
12383                    Vec::new(),
12384                ),
12385            ],
12386            Vec::new(),
12387            vec![
12388                Binding {
12389                    name: cid(1),
12390                    kind: BindingKind::Hoisted,
12391                },
12392                Binding {
12393                    name: cid(2),
12394                    kind: BindingKind::Hoisted,
12395                },
12396                Binding {
12397                    name: cid(3),
12398                    kind: BindingKind::Hoisted,
12399                },
12400            ],
12401            vec![
12402                Export {
12403                    name: cid(1),
12404                    source: ExportSource::Local(BindingId::new(0)),
12405                },
12406                Export {
12407                    name: cid(2),
12408                    source: ExportSource::Local(BindingId::new(1)),
12409                },
12410                Export {
12411                    name: cid(3),
12412                    source: ExportSource::Local(BindingId::new(2)),
12413                },
12414            ],
12415        );
12416        let root = program_module(
12417            "root",
12418            vec![
12419                Constant::String(EcmaString::from_utf8("ns1")),
12420                Constant::String(EcmaString::from_utf8("ns2")),
12421                Constant::String(EcmaString::from_utf8("mutate")),
12422                Constant::String(EcmaString::from_utf8("z")),
12423                Constant::String(EcmaString::from_utf8("a")),
12424                Constant::String(EcmaString::from_utf8("dep")),
12425                Constant::String(EcmaString::from_utf8("Object")),
12426                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
12427                Constant::String(EcmaString::from_utf8("value")),
12428                Constant::String(EcmaString::from_utf8("writable")),
12429                Constant::String(EcmaString::from_utf8("enumerable")),
12430                Constant::String(EcmaString::from_utf8("configurable")),
12431                Constant::String(EcmaString::from_utf8("missing")),
12432            ],
12433            vec![function(
12434                0,
12435                31,
12436                vec![
12437                    Instruction::LoadGlobal {
12438                        dst: reg(0),
12439                        name: cid(1),
12440                    },
12441                    Instruction::LoadGlobal {
12442                        dst: reg(1),
12443                        name: cid(2),
12444                    },
12445                    Instruction::Binary {
12446                        dst: reg(2),
12447                        op: BinaryOp::StrictEqual,
12448                        left: reg(0),
12449                        right: reg(1),
12450                    },
12451                    Instruction::LoadGlobal {
12452                        dst: reg(3),
12453                        name: cid(3),
12454                    },
12455                    Instruction::CreateArray { dst: reg(4) },
12456                    Instruction::Call {
12457                        dst: reg(5),
12458                        callee: reg(3),
12459                        this_value: reg(4),
12460                        arguments: reg(4),
12461                    },
12462                    Instruction::LoadConst {
12463                        dst: reg(6),
12464                        constant: cid(4),
12465                    },
12466                    Instruction::GetProperty {
12467                        dst: reg(7),
12468                        object: reg(0),
12469                        key: reg(6),
12470                    },
12471                    Instruction::GetIterator {
12472                        dst: reg(8),
12473                        src: reg(0),
12474                        kind: IteratorKind::Keys,
12475                    },
12476                    Instruction::IteratorNext {
12477                        done: reg(9),
12478                        value: reg(10),
12479                        iterator: reg(8),
12480                    },
12481                    Instruction::LoadConst {
12482                        dst: reg(11),
12483                        constant: cid(5),
12484                    },
12485                    Instruction::Binary {
12486                        dst: reg(12),
12487                        op: BinaryOp::StrictEqual,
12488                        left: reg(10),
12489                        right: reg(11),
12490                    },
12491                    Instruction::IteratorNext {
12492                        done: reg(9),
12493                        value: reg(10),
12494                        iterator: reg(8),
12495                    },
12496                    Instruction::LoadConst {
12497                        dst: reg(13),
12498                        constant: cid(3),
12499                    },
12500                    Instruction::Binary {
12501                        dst: reg(5),
12502                        op: BinaryOp::StrictEqual,
12503                        left: reg(10),
12504                        right: reg(13),
12505                    },
12506                    Instruction::IteratorNext {
12507                        done: reg(9),
12508                        value: reg(10),
12509                        iterator: reg(8),
12510                    },
12511                    Instruction::Binary {
12512                        dst: reg(14),
12513                        op: BinaryOp::StrictEqual,
12514                        left: reg(10),
12515                        right: reg(6),
12516                    },
12517                    Instruction::LoadGlobal {
12518                        dst: reg(15),
12519                        name: cid(7),
12520                    },
12521                    Instruction::LoadConst {
12522                        dst: reg(16),
12523                        constant: cid(8),
12524                    },
12525                    Instruction::GetProperty {
12526                        dst: reg(17),
12527                        object: reg(15),
12528                        key: reg(16),
12529                    },
12530                    Instruction::CreateArray { dst: reg(18) },
12531                    Instruction::ArrayPush {
12532                        array: reg(18),
12533                        value: reg(0),
12534                    },
12535                    Instruction::ArrayPush {
12536                        array: reg(18),
12537                        value: reg(6),
12538                    },
12539                    Instruction::Call {
12540                        dst: reg(19),
12541                        callee: reg(17),
12542                        this_value: reg(18),
12543                        arguments: reg(18),
12544                    },
12545                    Instruction::LoadConst {
12546                        dst: reg(20),
12547                        constant: cid(9),
12548                    },
12549                    Instruction::GetProperty {
12550                        dst: reg(21),
12551                        object: reg(19),
12552                        key: reg(20),
12553                    },
12554                    Instruction::LoadConst {
12555                        dst: reg(22),
12556                        constant: cid(10),
12557                    },
12558                    Instruction::GetProperty {
12559                        dst: reg(23),
12560                        object: reg(19),
12561                        key: reg(22),
12562                    },
12563                    Instruction::LoadConst {
12564                        dst: reg(24),
12565                        constant: cid(11),
12566                    },
12567                    Instruction::GetProperty {
12568                        dst: reg(25),
12569                        object: reg(19),
12570                        key: reg(24),
12571                    },
12572                    Instruction::LoadConst {
12573                        dst: reg(26),
12574                        constant: cid(12),
12575                    },
12576                    Instruction::GetProperty {
12577                        dst: reg(27),
12578                        object: reg(19),
12579                        key: reg(26),
12580                    },
12581                    Instruction::CreateArray { dst: reg(28) },
12582                    Instruction::LoadConst {
12583                        dst: reg(29),
12584                        constant: cid(13),
12585                    },
12586                    Instruction::ArrayPush {
12587                        array: reg(28),
12588                        value: reg(0),
12589                    },
12590                    Instruction::ArrayPush {
12591                        array: reg(28),
12592                        value: reg(29),
12593                    },
12594                    Instruction::Call {
12595                        dst: reg(30),
12596                        callee: reg(17),
12597                        this_value: reg(28),
12598                        arguments: reg(28),
12599                    },
12600                    Instruction::Return { value: reg(21) },
12601                ],
12602                Vec::new(),
12603            )],
12604            vec![Edge {
12605                specifier: cid(6),
12606                target: EdgeTarget::Local(ModuleId::new(0)),
12607                kind: EdgeKind::Static,
12608            }],
12609            vec![
12610                Binding {
12611                    name: cid(1),
12612                    kind: BindingKind::Namespace {
12613                        edge: EdgeId::new(0),
12614                    },
12615                },
12616                Binding {
12617                    name: cid(2),
12618                    kind: BindingKind::Namespace {
12619                        edge: EdgeId::new(0),
12620                    },
12621                },
12622                Binding {
12623                    name: cid(3),
12624                    kind: BindingKind::Imported {
12625                        edge: EdgeId::new(0),
12626                        name: cid(3),
12627                    },
12628                },
12629            ],
12630            Vec::new(),
12631        );
12632        let execution = run_ok(&linked(vec![dependency, root], 1));
12633        assert_eq!(execution.value, Value::int32(3));
12634        assert_eq!(execution.entry_registers[2], Value::TRUE);
12635        assert_eq!(execution.entry_registers[5], Value::TRUE);
12636        assert_eq!(execution.entry_registers[12], Value::TRUE);
12637        assert_eq!(execution.entry_registers[14], Value::TRUE);
12638        assert_eq!(execution.entry_registers[23], Value::TRUE);
12639        assert_eq!(execution.entry_registers[25], Value::TRUE);
12640        assert_eq!(execution.entry_registers[27], Value::FALSE);
12641        assert_eq!(execution.entry_registers[30], Value::UNDEFINED);
12642    }
12643
12644    #[test]
12645    fn side_effect_module_runs_once_with_single_or_duplicate_static_edges() {
12646        for duplicate in [false, true] {
12647            let dependency = program_module(
12648                "dependency",
12649                vec![
12650                    Constant::String(EcmaString::from_utf8("count")),
12651                    Constant::Int32(0),
12652                    Constant::Int32(1),
12653                ],
12654                vec![function(
12655                    0,
12656                    2,
12657                    vec![
12658                        Instruction::LoadGlobal {
12659                            dst: reg(0),
12660                            name: cid(1),
12661                        },
12662                        Instruction::JumpIfFalse {
12663                            condition: reg(0),
12664                            target: pc(3),
12665                        },
12666                        Instruction::Jump { target: pc(5) },
12667                        Instruction::LoadConst {
12668                            dst: reg(0),
12669                            constant: cid(2),
12670                        },
12671                        Instruction::StoreGlobal {
12672                            name: cid(1),
12673                            value: reg(0),
12674                        },
12675                        Instruction::LoadConst {
12676                            dst: reg(1),
12677                            constant: cid(3),
12678                        },
12679                        Instruction::Binary {
12680                            dst: reg(0),
12681                            op: BinaryOp::Add,
12682                            left: reg(0),
12683                            right: reg(1),
12684                        },
12685                        Instruction::StoreGlobal {
12686                            name: cid(1),
12687                            value: reg(0),
12688                        },
12689                        Instruction::Return { value: reg(0) },
12690                    ],
12691                    Vec::new(),
12692                )],
12693                Vec::new(),
12694                vec![Binding {
12695                    name: cid(1),
12696                    kind: BindingKind::Hoisted,
12697                }],
12698                vec![Export {
12699                    name: cid(1),
12700                    source: ExportSource::Local(BindingId::new(0)),
12701                }],
12702            );
12703            let mut edges = vec![Edge {
12704                specifier: cid(2),
12705                target: EdgeTarget::Local(ModuleId::new(0)),
12706                kind: EdgeKind::Static,
12707            }];
12708            if duplicate {
12709                edges.push(Edge {
12710                    specifier: cid(3),
12711                    target: EdgeTarget::Local(ModuleId::new(0)),
12712                    kind: EdgeKind::Static,
12713                });
12714            }
12715            let root = program_module(
12716                "root",
12717                vec![
12718                    Constant::String(EcmaString::from_utf8("count")),
12719                    Constant::String(EcmaString::from_utf8("dep-one")),
12720                    Constant::String(EcmaString::from_utf8("dep-two")),
12721                ],
12722                vec![function(
12723                    0,
12724                    1,
12725                    vec![
12726                        Instruction::LoadGlobal {
12727                            dst: reg(0),
12728                            name: cid(1),
12729                        },
12730                        Instruction::Return { value: reg(0) },
12731                    ],
12732                    Vec::new(),
12733                )],
12734                edges,
12735                vec![Binding {
12736                    name: cid(1),
12737                    kind: BindingKind::Imported {
12738                        edge: EdgeId::new(0),
12739                        name: cid(1),
12740                    },
12741                }],
12742                Vec::new(),
12743            );
12744            assert_eq!(
12745                run_ok(&linked(vec![dependency, root], 1)).value,
12746                Value::int32(1)
12747            );
12748        }
12749    }
12750
12751    #[test]
12752    fn failed_module_rethrows_the_identical_stored_value() {
12753        let module = program_module(
12754            "throws",
12755            Vec::new(),
12756            vec![function(
12757                0,
12758                1,
12759                vec![
12760                    Instruction::CreateObject { dst: reg(0) },
12761                    Instruction::Throw { value: reg(0) },
12762                ],
12763                Vec::new(),
12764            )],
12765            Vec::new(),
12766            Vec::new(),
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.frames.clear();
12773        machine.live_registers = 0;
12774        machine.instantiate_modules().unwrap();
12775        let first = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12776        let second = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12777        let RuntimeErrorKind::UncaughtThrow { value: first, .. } = first.kind else {
12778            panic!("module must fail by throwing");
12779        };
12780        let RuntimeErrorKind::UncaughtThrow { value: second, .. } = second.kind else {
12781            panic!("stored failure must remain a throw");
12782        };
12783        assert_eq!(first, second);
12784        assert!(first.as_heap_ref().is_some());
12785    }
12786
12787    #[test]
12788    fn external_static_edge_is_a_typed_runtime_error() {
12789        let module = program_module(
12790            "root",
12791            vec![Constant::String(EcmaString::from_utf8("external"))],
12792            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12793            vec![Edge {
12794                specifier: cid(1),
12795                target: EdgeTarget::External,
12796                kind: EdgeKind::Static,
12797            }],
12798            Vec::new(),
12799            Vec::new(),
12800        );
12801        let program = linked(vec![module], 0);
12802        let mut host = TestHost;
12803        let error = Machine::new(&program, &mut host, Limits::default())
12804            .run()
12805            .unwrap_err();
12806        assert!(matches!(
12807            error.kind,
12808            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
12809                if module == ModuleId::new(0) && edge == EdgeId::new(0)
12810        ));
12811    }
12812
12813    #[test]
12814    fn external_module_and_export_names_preserve_unicode() {
12815        for (specifier, export) in [("módulo", "value"), ("external", "café")] {
12816            let module = program_module(
12817                "root",
12818                vec![
12819                    Constant::String(EcmaString::from_utf8(export)),
12820                    Constant::String(EcmaString::from_utf8(specifier)),
12821                ],
12822                vec![function(
12823                    0,
12824                    1,
12825                    vec![
12826                        Instruction::LoadGlobal {
12827                            dst: reg(0),
12828                            name: cid(1),
12829                        },
12830                        Instruction::Return { value: reg(0) },
12831                    ],
12832                    Vec::new(),
12833                )],
12834                vec![Edge {
12835                    specifier: cid(2),
12836                    target: EdgeTarget::External,
12837                    kind: EdgeKind::Static,
12838                }],
12839                vec![Binding {
12840                    name: cid(1),
12841                    kind: BindingKind::Imported {
12842                        edge: EdgeId::new(0),
12843                        name: cid(1),
12844                    },
12845                }],
12846                Vec::new(),
12847            );
12848            let program = linked(vec![module], 0);
12849            let mut host = TestHost;
12850            let mut machine = Machine::new(&program, &mut host, Limits::default());
12851            machine.registry.external.insert(
12852                EcmaString::from_utf8(specifier),
12853                ExternalModuleInstance {
12854                    namespace: Value::UNDEFINED,
12855                    exports: BTreeMap::from([(
12856                        EcmaString::from_utf8(export),
12857                        ExternalExport {
12858                            value: Value::int32(7),
12859                            cell: None,
12860                        },
12861                    )]),
12862                    internals: BTreeMap::new(),
12863                },
12864            );
12865
12866            assert_eq!(machine.run().unwrap().value, Value::int32(7));
12867        }
12868    }
12869
12870    #[test]
12871    fn dynamic_import_preserves_cycles_identity_and_single_evaluation() {
12872        let root = program_module(
12873            "root",
12874            vec![
12875                Constant::String(EcmaString::from_utf8("./dependency")),
12876                Constant::String(EcmaString::from_utf8("count")),
12877                Constant::Int32(0),
12878                Constant::String(EcmaString::from_utf8("value")),
12879            ],
12880            vec![function(
12881                0,
12882                7,
12883                vec![
12884                    Instruction::LoadConst {
12885                        dst: reg(0),
12886                        constant: cid(3),
12887                    },
12888                    Instruction::StoreGlobal {
12889                        name: cid(2),
12890                        value: reg(0),
12891                    },
12892                    Instruction::Import {
12893                        dst: reg(1),
12894                        specifier: cid(1),
12895                    },
12896                    Instruction::Import {
12897                        dst: reg(2),
12898                        specifier: cid(1),
12899                    },
12900                    Instruction::Binary {
12901                        dst: reg(3),
12902                        op: BinaryOp::StrictEqual,
12903                        left: reg(1),
12904                        right: reg(2),
12905                    },
12906                    Instruction::LoadConst {
12907                        dst: reg(4),
12908                        constant: cid(4),
12909                    },
12910                    Instruction::GetProperty {
12911                        dst: reg(5),
12912                        object: reg(2),
12913                        key: reg(4),
12914                    },
12915                    Instruction::LoadGlobal {
12916                        dst: reg(6),
12917                        name: cid(2),
12918                    },
12919                    Instruction::Return { value: reg(5) },
12920                ],
12921                Vec::new(),
12922            )],
12923            vec![Edge {
12924                specifier: cid(1),
12925                target: EdgeTarget::Local(ModuleId::new(1)),
12926                kind: EdgeKind::Dynamic,
12927            }],
12928            Vec::new(),
12929            Vec::new(),
12930        );
12931        let dependency = program_module(
12932            "dependency",
12933            vec![
12934                Constant::String(EcmaString::from_utf8("./root")),
12935                Constant::String(EcmaString::from_utf8("count")),
12936                Constant::Int32(1),
12937                Constant::Int32(7),
12938                Constant::String(EcmaString::from_utf8("value")),
12939            ],
12940            vec![function(
12941                0,
12942                3,
12943                vec![
12944                    Instruction::LoadGlobal {
12945                        dst: reg(0),
12946                        name: cid(2),
12947                    },
12948                    Instruction::LoadConst {
12949                        dst: reg(1),
12950                        constant: cid(3),
12951                    },
12952                    Instruction::Binary {
12953                        dst: reg(2),
12954                        op: BinaryOp::Add,
12955                        left: reg(0),
12956                        right: reg(1),
12957                    },
12958                    Instruction::StoreGlobal {
12959                        name: cid(2),
12960                        value: reg(2),
12961                    },
12962                    Instruction::LoadConst {
12963                        dst: reg(0),
12964                        constant: cid(4),
12965                    },
12966                    Instruction::StoreGlobal {
12967                        name: cid(5),
12968                        value: reg(0),
12969                    },
12970                    Instruction::Return { value: reg(0) },
12971                ],
12972                Vec::new(),
12973            )],
12974            vec![Edge {
12975                specifier: cid(1),
12976                target: EdgeTarget::Local(ModuleId::new(0)),
12977                kind: EdgeKind::Static,
12978            }],
12979            vec![Binding {
12980                name: cid(5),
12981                kind: BindingKind::Hoisted,
12982            }],
12983            vec![Export {
12984                name: cid(5),
12985                source: ExportSource::Local(BindingId::new(0)),
12986            }],
12987        );
12988
12989        let execution = run_ok(&linked(vec![root, dependency], 0));
12990        assert_eq!(execution.value, Value::int32(7));
12991        assert_eq!(execution.entry_registers[1], execution.entry_registers[2]);
12992        assert_eq!(execution.entry_registers[3], Value::TRUE);
12993        assert_eq!(execution.entry_registers[6], Value::int32(1));
12994    }
12995
12996    #[test]
12997    fn dynamic_import_counts_live_registers_and_retries_engine_failures() {
12998        let target = program_module(
12999            "target",
13000            Vec::new(),
13001            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13002            Vec::new(),
13003            Vec::new(),
13004            Vec::new(),
13005        );
13006        let root = program_module(
13007            "root",
13008            vec![Constant::String(EcmaString::from_utf8("./target"))],
13009            vec![function(
13010                0,
13011                1,
13012                vec![
13013                    Instruction::Import {
13014                        dst: reg(0),
13015                        specifier: cid(1),
13016                    },
13017                    Instruction::Return { value: reg(0) },
13018                ],
13019                Vec::new(),
13020            )],
13021            vec![Edge {
13022                specifier: cid(1),
13023                target: EdgeTarget::Local(ModuleId::new(1)),
13024                kind: EdgeKind::Dynamic,
13025            }],
13026            Vec::new(),
13027            Vec::new(),
13028        );
13029        let program = linked(vec![root, target], 0);
13030        let mut host = TestHost;
13031        let mut machine = Machine::new(
13032            &program,
13033            &mut host,
13034            Limits {
13035                max_total_registers: 1,
13036                ..Limits::default()
13037            },
13038        );
13039        machine.frames.clear();
13040        machine.live_registers = 0;
13041        machine.instantiate_modules().unwrap();
13042
13043        let error = machine.evaluate_import(ModuleId::new(0)).unwrap_err();
13044        assert!(matches!(
13045            error.kind,
13046            RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
13047        ));
13048        assert_eq!(machine.frames.len(), 0);
13049        assert_eq!(machine.live_registers, 0);
13050
13051        machine.limits.max_total_registers = 2;
13052        machine.evaluate_import(ModuleId::new(0)).unwrap();
13053    }
13054
13055    #[test]
13056    fn dynamic_import_rethrows_one_stored_failure_at_each_import_site() {
13057        let root = program_module(
13058            "root",
13059            vec![
13060                Constant::String(EcmaString::from_utf8("./target")),
13061                Constant::String(EcmaString::from_utf8("count")),
13062                Constant::Int32(0),
13063            ],
13064            vec![function(
13065                0,
13066                4,
13067                vec![
13068                    Instruction::LoadConst {
13069                        dst: reg(0),
13070                        constant: cid(3),
13071                    },
13072                    Instruction::StoreGlobal {
13073                        name: cid(2),
13074                        value: reg(0),
13075                    },
13076                    Instruction::Import {
13077                        dst: reg(0),
13078                        specifier: cid(1),
13079                    },
13080                    Instruction::Halt,
13081                    Instruction::Import {
13082                        dst: reg(0),
13083                        specifier: cid(1),
13084                    },
13085                    Instruction::Halt,
13086                    Instruction::LoadGlobal {
13087                        dst: reg(3),
13088                        name: cid(2),
13089                    },
13090                    Instruction::Return { value: reg(2) },
13091                ],
13092                vec![
13093                    ExceptionHandler {
13094                        start: pc(2),
13095                        end: pc(3),
13096                        handler: pc(4),
13097                        catch_register: reg(1),
13098                    },
13099                    ExceptionHandler {
13100                        start: pc(4),
13101                        end: pc(5),
13102                        handler: pc(6),
13103                        catch_register: reg(2),
13104                    },
13105                ],
13106            )],
13107            vec![Edge {
13108                specifier: cid(1),
13109                target: EdgeTarget::Local(ModuleId::new(1)),
13110                kind: EdgeKind::Dynamic,
13111            }],
13112            Vec::new(),
13113            Vec::new(),
13114        );
13115        let target = program_module(
13116            "target",
13117            vec![
13118                Constant::String(EcmaString::from_utf8("count")),
13119                Constant::Int32(1),
13120                Constant::Int32(9),
13121            ],
13122            vec![function(
13123                0,
13124                3,
13125                vec![
13126                    Instruction::LoadGlobal {
13127                        dst: reg(0),
13128                        name: cid(1),
13129                    },
13130                    Instruction::LoadConst {
13131                        dst: reg(1),
13132                        constant: cid(2),
13133                    },
13134                    Instruction::Binary {
13135                        dst: reg(2),
13136                        op: BinaryOp::Add,
13137                        left: reg(0),
13138                        right: reg(1),
13139                    },
13140                    Instruction::StoreGlobal {
13141                        name: cid(1),
13142                        value: reg(2),
13143                    },
13144                    Instruction::LoadConst {
13145                        dst: reg(0),
13146                        constant: cid(3),
13147                    },
13148                    Instruction::Throw { value: reg(0) },
13149                ],
13150                Vec::new(),
13151            )],
13152            Vec::new(),
13153            Vec::new(),
13154            Vec::new(),
13155        );
13156
13157        let execution = run_ok(&linked(vec![root, target], 0));
13158        assert_eq!(execution.value, Value::int32(9));
13159        assert_eq!(execution.entry_registers[1], Value::int32(9));
13160        assert_eq!(execution.entry_registers[2], Value::int32(9));
13161        assert_eq!(execution.entry_registers[3], Value::int32(1));
13162    }
13163
13164    #[test]
13165    fn dynamic_import_returns_the_registered_external_namespace() {
13166        let module = program_module(
13167            "root",
13168            vec![Constant::String(EcmaString::from_utf8("external"))],
13169            vec![function(
13170                0,
13171                3,
13172                vec![
13173                    Instruction::Import {
13174                        dst: reg(0),
13175                        specifier: cid(1),
13176                    },
13177                    Instruction::Import {
13178                        dst: reg(1),
13179                        specifier: cid(1),
13180                    },
13181                    Instruction::Binary {
13182                        dst: reg(2),
13183                        op: BinaryOp::StrictEqual,
13184                        left: reg(0),
13185                        right: reg(1),
13186                    },
13187                    Instruction::Return { value: reg(2) },
13188                ],
13189                Vec::new(),
13190            )],
13191            vec![Edge {
13192                specifier: cid(1),
13193                target: EdgeTarget::External,
13194                kind: EdgeKind::Dynamic,
13195            }],
13196            Vec::new(),
13197            Vec::new(),
13198        );
13199        let program = linked(vec![module], 0);
13200        let mut host = TestHost;
13201        let mut machine = Machine::new(&program, &mut host, Limits::default());
13202        let namespace = machine
13203            .allocate(HeapEntry::Object {
13204                properties: PropertyMap::default(),
13205                prototype: Some(machine.intrinsics.object_prototype),
13206                boxed_primitive: None,
13207                extensible: true,
13208            })
13209            .unwrap();
13210        machine.registry.external.insert(
13211            EcmaString::from_utf8("external"),
13212            ExternalModuleInstance {
13213                namespace,
13214                exports: BTreeMap::new(),
13215                internals: BTreeMap::new(),
13216            },
13217        );
13218
13219        let execution = machine.run().unwrap();
13220        assert_eq!(execution.value, Value::TRUE);
13221        assert_eq!(execution.entry_registers[0], namespace);
13222        assert_eq!(execution.entry_registers[1], namespace);
13223    }
13224
13225    #[test]
13226    fn dynamic_import_resolution_is_requester_scoped() {
13227        let requester = |name, target| {
13228            program_module(
13229                name,
13230                vec![Constant::String(EcmaString::from_utf8("./target"))],
13231                vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13232                vec![Edge {
13233                    specifier: cid(1),
13234                    target: EdgeTarget::Local(ModuleId::new(target)),
13235                    kind: EdgeKind::Dynamic,
13236                }],
13237                Vec::new(),
13238                Vec::new(),
13239            )
13240        };
13241        let target = |name| {
13242            program_module(
13243                name,
13244                Vec::new(),
13245                vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13246                Vec::new(),
13247                Vec::new(),
13248                Vec::new(),
13249            )
13250        };
13251        let program = linked(
13252            vec![
13253                requester("first", 2),
13254                requester("second", 3),
13255                target("first-target"),
13256                target("second-target"),
13257            ],
13258            0,
13259        );
13260        let mut host = TestHost;
13261        let machine = Machine::new(&program, &mut host, Limits::default());
13262
13263        assert_eq!(
13264            machine.resolve_import(ModuleId::new(0), cid(1)),
13265            Ok(ImportTarget::Local(ModuleId::new(2)))
13266        );
13267        assert_eq!(
13268            machine.resolve_import(ModuleId::new(1), cid(1)),
13269            Ok(ImportTarget::Local(ModuleId::new(3)))
13270        );
13271    }
13272
13273    #[test]
13274    fn dynamic_import_of_a_missing_external_is_a_runtime_error() {
13275        let module = program_module(
13276            "root",
13277            vec![Constant::String(EcmaString::from_utf8("dynamic"))],
13278            vec![function(
13279                0,
13280                1,
13281                vec![
13282                    Instruction::Import {
13283                        dst: reg(0),
13284                        specifier: cid(1),
13285                    },
13286                    Instruction::Return { value: reg(0) },
13287                ],
13288                Vec::new(),
13289            )],
13290            vec![Edge {
13291                specifier: cid(1),
13292                target: EdgeTarget::External,
13293                kind: EdgeKind::Dynamic,
13294            }],
13295            Vec::new(),
13296            Vec::new(),
13297        );
13298        let program = linked(vec![module], 0);
13299        let mut host = TestHost;
13300        let error = Machine::new(&program, &mut host, Limits::default())
13301            .run()
13302            .unwrap_err();
13303        assert!(matches!(
13304            error.kind,
13305            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13306                if module == ModuleId::new(0) && edge == EdgeId::new(0)
13307        ));
13308    }
13309
13310    #[test]
13311    fn unbound_global_names_fall_back_to_the_realm_global_map() {
13312        let program = verified(
13313            vec![
13314                Constant::String(EcmaString::from_utf8("realmOnly")),
13315                Constant::Int32(7),
13316            ],
13317            vec![function(
13318                0,
13319                1,
13320                vec![
13321                    Instruction::LoadConst {
13322                        dst: reg(0),
13323                        constant: cid(1),
13324                    },
13325                    Instruction::StoreGlobal {
13326                        name: cid(0),
13327                        value: reg(0),
13328                    },
13329                    Instruction::LoadGlobal {
13330                        dst: reg(0),
13331                        name: cid(0),
13332                    },
13333                    Instruction::Return { value: reg(0) },
13334                ],
13335                Vec::new(),
13336            )],
13337        );
13338        assert_eq!(run_ok(&program).value, Value::int32(7));
13339    }
13340
13341    #[test]
13342    fn module_cell_limit_is_enforced_before_evaluation() {
13343        let module = program_module(
13344            "root",
13345            vec![Constant::String(EcmaString::from_utf8("x"))],
13346            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13347            Vec::new(),
13348            vec![Binding {
13349                name: cid(1),
13350                kind: BindingKind::Hoisted,
13351            }],
13352            Vec::new(),
13353        );
13354        let program = linked(vec![module], 0);
13355        let mut host = TestHost;
13356        let error = Machine::new(
13357            &program,
13358            &mut host,
13359            Limits {
13360                max_module_cells: 0,
13361                ..Limits::default()
13362            },
13363        )
13364        .run()
13365        .unwrap_err();
13366        assert!(matches!(
13367            error.kind,
13368            RuntimeErrorKind::ModuleCellLimitExceeded { limit: 0 }
13369        ));
13370    }
13371    #[test]
13372    fn imported_binding_store_throws_without_mutating_the_exporter() {
13373        let dependency = program_module(
13374            "dependency",
13375            vec![
13376                Constant::String(EcmaString::from_utf8("x")),
13377                Constant::Int32(1),
13378            ],
13379            vec![function(
13380                0,
13381                1,
13382                vec![
13383                    Instruction::LoadConst {
13384                        dst: reg(0),
13385                        constant: cid(2),
13386                    },
13387                    Instruction::StoreGlobal {
13388                        name: cid(1),
13389                        value: reg(0),
13390                    },
13391                    Instruction::Return { value: reg(0) },
13392                ],
13393                Vec::new(),
13394            )],
13395            Vec::new(),
13396            vec![Binding {
13397                name: cid(1),
13398                kind: BindingKind::Hoisted,
13399            }],
13400            vec![Export {
13401                name: cid(1),
13402                source: ExportSource::Local(BindingId::new(0)),
13403            }],
13404        );
13405        let root = program_module(
13406            "root",
13407            vec![
13408                Constant::String(EcmaString::from_utf8("x")),
13409                Constant::Int32(2),
13410                Constant::String(EcmaString::from_utf8("dep")),
13411            ],
13412            vec![function(
13413                0,
13414                1,
13415                vec![
13416                    Instruction::LoadConst {
13417                        dst: reg(0),
13418                        constant: cid(2),
13419                    },
13420                    Instruction::StoreGlobal {
13421                        name: cid(1),
13422                        value: reg(0),
13423                    },
13424                    Instruction::Return { value: reg(0) },
13425                ],
13426                Vec::new(),
13427            )],
13428            vec![Edge {
13429                specifier: cid(3),
13430                target: EdgeTarget::Local(ModuleId::new(0)),
13431                kind: EdgeKind::Static,
13432            }],
13433            vec![Binding {
13434                name: cid(1),
13435                kind: BindingKind::Imported {
13436                    edge: EdgeId::new(0),
13437                    name: cid(1),
13438                },
13439            }],
13440            Vec::new(),
13441        );
13442        let program = linked(vec![dependency, root], 1);
13443        let mut host = TestHost;
13444        let mut machine = Machine::new(&program, &mut host, Limits::default());
13445        machine.frames.clear();
13446        machine.live_registers = 0;
13447        machine.instantiate_modules().unwrap();
13448        assert!(machine.evaluate_module(ModuleId::new(1)).is_err());
13449        let exporter = machine.registry.modules[0].binding_cells[0].unwrap();
13450        assert_eq!(machine.registry.cells[exporter.0].value, Value::int32(1));
13451    }
13452
13453    #[test]
13454    fn namespace_descriptor_propagates_temporal_dead_zone() {
13455        let root = program_module(
13456            "root",
13457            vec![
13458                Constant::String(EcmaString::from_utf8("x")),
13459                Constant::Int32(1),
13460                Constant::String(EcmaString::from_utf8("dependency")),
13461            ],
13462            vec![function(
13463                0,
13464                1,
13465                vec![
13466                    Instruction::LoadConst {
13467                        dst: reg(0),
13468                        constant: cid(2),
13469                    },
13470                    Instruction::StoreGlobal {
13471                        name: cid(1),
13472                        value: reg(0),
13473                    },
13474                    Instruction::Return { value: reg(0) },
13475                ],
13476                Vec::new(),
13477            )],
13478            vec![Edge {
13479                specifier: cid(3),
13480                target: EdgeTarget::Local(ModuleId::new(1)),
13481                kind: EdgeKind::Static,
13482            }],
13483            vec![Binding {
13484                name: cid(1),
13485                kind: BindingKind::Lexical,
13486            }],
13487            vec![Export {
13488                name: cid(1),
13489                source: ExportSource::Local(BindingId::new(0)),
13490            }],
13491        );
13492        let dependency = program_module(
13493            "dependency",
13494            vec![
13495                Constant::String(EcmaString::from_utf8("ns")),
13496                Constant::String(EcmaString::from_utf8("root")),
13497                Constant::String(EcmaString::from_utf8("Object")),
13498                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13499                Constant::String(EcmaString::from_utf8("x")),
13500            ],
13501            vec![namespace_descriptor_entry()],
13502            vec![Edge {
13503                specifier: cid(2),
13504                target: EdgeTarget::Local(ModuleId::new(0)),
13505                kind: EdgeKind::Static,
13506            }],
13507            vec![Binding {
13508                name: cid(1),
13509                kind: BindingKind::Namespace {
13510                    edge: EdgeId::new(0),
13511                },
13512            }],
13513            Vec::new(),
13514        );
13515        let program = linked(vec![root, dependency], 0);
13516        let mut host = TestHost;
13517        let error = Machine::new(&program, &mut host, Limits::default())
13518            .run()
13519            .expect_err("descriptor reads uninitialized namespace export");
13520        assert!(matches!(
13521            error.kind,
13522            RuntimeErrorKind::TemporalDeadZone { module, binding }
13523                if module == ModuleId::new(0) && binding == BindingId::new(0)
13524        ));
13525    }
13526
13527    #[test]
13528    fn namespace_descriptor_propagates_external_linkage_error() {
13529        let exported = program_module(
13530            "exported",
13531            vec![
13532                Constant::String(EcmaString::from_utf8("x")),
13533                Constant::String(EcmaString::from_utf8("external")),
13534            ],
13535            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13536            vec![Edge {
13537                specifier: cid(2),
13538                target: EdgeTarget::External,
13539                kind: EdgeKind::Dynamic,
13540            }],
13541            Vec::new(),
13542            vec![Export {
13543                name: cid(1),
13544                source: ExportSource::Indirect {
13545                    edge: EdgeId::new(0),
13546                    name: cid(1),
13547                },
13548            }],
13549        );
13550        let importer = program_module(
13551            "importer",
13552            vec![
13553                Constant::String(EcmaString::from_utf8("ns")),
13554                Constant::String(EcmaString::from_utf8("exported")),
13555                Constant::String(EcmaString::from_utf8("Object")),
13556                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13557                Constant::String(EcmaString::from_utf8("x")),
13558            ],
13559            vec![namespace_descriptor_entry()],
13560            vec![Edge {
13561                specifier: cid(2),
13562                target: EdgeTarget::Local(ModuleId::new(0)),
13563                kind: EdgeKind::Static,
13564            }],
13565            vec![Binding {
13566                name: cid(1),
13567                kind: BindingKind::Namespace {
13568                    edge: EdgeId::new(0),
13569                },
13570            }],
13571            Vec::new(),
13572        );
13573        let program = linked(vec![exported, importer], 1);
13574        let mut host = TestHost;
13575        let error = Machine::new(&program, &mut host, Limits::default())
13576            .run()
13577            .expect_err("descriptor resolves external namespace export");
13578        assert!(matches!(
13579            error.kind,
13580            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13581                if module == ModuleId::new(0) && edge == EdgeId::new(0)
13582        ));
13583    }
13584
13585    #[test]
13586    fn installed_script_uses_machine_wide_id_and_keeps_its_code() {
13587        let root = verified(
13588            Vec::new(),
13589            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13590        );
13591        let script = Arc::new(verified(
13592            vec![Constant::Int32(42)],
13593            vec![function(
13594                0,
13595                1,
13596                vec![
13597                    Instruction::LoadConst {
13598                        dst: reg(0),
13599                        constant: cid(0),
13600                    },
13601                    Instruction::Return { value: reg(0) },
13602                ],
13603                Vec::new(),
13604            )],
13605        ));
13606        let mut host = TestHost;
13607        let mut machine = Machine::new(&root, &mut host, Limits::default());
13608        machine.instantiate_modules().unwrap();
13609        let module = machine.install_script_reserving(script, 0, 0).unwrap();
13610
13611        assert_eq!(module, ModuleId::new(root.modules().len() as u32));
13612        assert!(machine.program().module(module).is_none());
13613        assert_eq!(
13614            machine.module_code(module).constants()[0],
13615            Constant::Int32(42)
13616        );
13617
13618        let closure = machine
13619            .allocate(HeapEntry::Function {
13620                module,
13621                function: FunctionId::new(0),
13622                captures: Vec::new(),
13623                properties: PropertyMap::default(),
13624                prototype: Some(machine.intrinsics.function_prototype),
13625                extensible: true,
13626            })
13627            .unwrap();
13628        assert!(matches!(
13629            machine.call_value(closure, Value::UNDEFINED, &[]),
13630            Ok(value) if value == Value::int32(42)
13631        ));
13632    }
13633
13634    #[test]
13635    fn installed_script_rejects_non_classic_programs_and_enforces_limit() {
13636        let root = verified(
13637            Vec::new(),
13638            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13639        );
13640        let two_modules = Arc::new(linked(
13641            vec![
13642                program_module(
13643                    "first",
13644                    Vec::new(),
13645                    vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13646                    Vec::new(),
13647                    Vec::new(),
13648                    Vec::new(),
13649                ),
13650                program_module(
13651                    "second",
13652                    Vec::new(),
13653                    vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13654                    Vec::new(),
13655                    Vec::new(),
13656                    Vec::new(),
13657                ),
13658            ],
13659            0,
13660        ));
13661        let script = Arc::new(verified(
13662            Vec::new(),
13663            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13664        ));
13665        let mut host = TestHost;
13666        let mut machine = Machine::new(
13667            &root,
13668            &mut host,
13669            Limits {
13670                max_dynamic_modules: 1,
13671                ..Limits::default()
13672            },
13673        );
13674        machine.instantiate_modules().unwrap();
13675
13676        assert!(matches!(
13677            machine.install_script_reserving(two_modules, 0, 0),
13678            Err(RuntimeErrorKind::InvalidDynamicScript { .. })
13679        ));
13680        machine
13681            .install_script_reserving(script.clone(), 0, 0)
13682            .unwrap();
13683        assert!(matches!(
13684            machine.install_script_reserving(script, 0, 0),
13685            Err(RuntimeErrorKind::DynamicModuleLimitExceeded { limit: 1 })
13686        ));
13687    }
13688
13689    #[test]
13690    fn script_heap_cost_counts_scalar_constant_slots() {
13691        let entry = || vec![function(0, 1, vec![Instruction::Halt], Vec::new())];
13692        let empty = verified(Vec::new(), entry());
13693        let constants = vec![Constant::Int32(0); 128];
13694        let scalars = verified(constants.clone(), entry());
13695
13696        let added = Machine::<TestHost>::script_heap_cost(&scalars)
13697            - Machine::<TestHost>::script_heap_cost(&empty);
13698
13699        assert!(added >= constants.len() * std::mem::size_of::<Constant>());
13700    }
13701
13702    #[test]
13703    fn script_heap_cost_includes_verification_storage() {
13704        let small = verified(
13705            Vec::new(),
13706            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13707        );
13708        let large = verified(
13709            Vec::new(),
13710            vec![function(0, 130, vec![Instruction::Halt], Vec::new())],
13711        );
13712        let small_verification = small.modules()[0].code.verification_bytes();
13713        let large_verification = large.modules()[0].code.verification_bytes();
13714
13715        assert_eq!(
13716            Machine::<TestHost>::script_heap_cost(&large)
13717                - Machine::<TestHost>::script_heap_cost(&small),
13718            large_verification - small_verification
13719        );
13720    }
13721    #[test]
13722    fn promise_resolver_settles_once_and_reactions_wait_for_drain() {
13723        let program = verified(
13724            vec![
13725                Constant::String(EcmaString::from_utf8("resolve")),
13726                Constant::String(EcmaString::from_utf8("reject")),
13727                Constant::String(EcmaString::from_utf8("observed")),
13728            ],
13729            vec![
13730                function(0, 1, vec![Instruction::Halt], Vec::new()),
13731                function(
13732                    2,
13733                    2,
13734                    vec![
13735                        Instruction::StoreGlobal {
13736                            name: cid(0),
13737                            value: reg(0),
13738                        },
13739                        Instruction::StoreGlobal {
13740                            name: cid(1),
13741                            value: reg(1),
13742                        },
13743                        Instruction::Return { value: reg(0) },
13744                    ],
13745                    Vec::new(),
13746                ),
13747                function(
13748                    1,
13749                    1,
13750                    vec![
13751                        Instruction::StoreGlobal {
13752                            name: cid(2),
13753                            value: reg(0),
13754                        },
13755                        Instruction::Return { value: reg(0) },
13756                    ],
13757                    Vec::new(),
13758                ),
13759            ],
13760        );
13761        let mut host = TestHost;
13762        let mut machine = Machine::new(&program, &mut host, Limits::default());
13763        machine.frames.clear();
13764        machine.live_registers = 0;
13765        let executor = machine
13766            .allocate(HeapEntry::Function {
13767                module: ModuleId::new(0),
13768                function: FunctionId::new(1),
13769                captures: Vec::new(),
13770                properties: PropertyMap::default(),
13771                prototype: Some(machine.intrinsics.function_prototype),
13772                extensible: true,
13773            })
13774            .unwrap();
13775        let observer = machine
13776            .allocate(HeapEntry::Function {
13777                module: ModuleId::new(0),
13778                function: FunctionId::new(2),
13779                captures: Vec::new(),
13780                properties: PropertyMap::default(),
13781                prototype: Some(machine.intrinsics.function_prototype),
13782                extensible: true,
13783            })
13784            .unwrap();
13785        let constructor = machine.intrinsics.global("Promise").unwrap();
13786        let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13787        let HeapEntry::NativeFunction {
13788            callable: NativeCallable::Builtin(constructor_id),
13789            ..
13790        } = machine.heap[constructor_index]
13791        else {
13792            panic!("Promise must be a native constructor");
13793        };
13794        let BuiltinOutcome::Value(promise) = machine
13795            .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13796            .unwrap()
13797        else {
13798            panic!("Promise construction returns a Promise");
13799        };
13800        let then = machine.get_named_property(promise, "then").unwrap();
13801        machine
13802            .call_value(then, promise, &[observer])
13803            .expect("then returns a derived Promise");
13804        let resolve = machine
13805            .globals
13806            .get(&EcmaString::from_utf8("resolve"))
13807            .copied()
13808            .unwrap();
13809        let reject = machine
13810            .globals
13811            .get(&EcmaString::from_utf8("reject"))
13812            .copied()
13813            .unwrap();
13814        assert_eq!(
13815            machine
13816                .call_value(resolve, Value::UNDEFINED, &[Value::int32(1)])
13817                .unwrap(),
13818            Value::UNDEFINED
13819        );
13820        assert_eq!(
13821            machine
13822                .call_value(reject, Value::UNDEFINED, &[Value::int32(2)])
13823                .unwrap(),
13824            Value::UNDEFINED
13825        );
13826        assert!(
13827            !machine
13828                .globals
13829                .contains_key(&EcmaString::from_utf8("observed"))
13830        );
13831
13832        let drain = machine.drain_microtasks().unwrap();
13833        assert_eq!(drain.executed, 1);
13834        assert!(drain.uncaught.is_empty());
13835        assert_eq!(
13836            machine
13837                .globals
13838                .get(&EcmaString::from_utf8("observed"))
13839                .copied(),
13840            Some(Value::int32(1))
13841        );
13842    }
13843
13844    #[test]
13845    fn promise_resolution_adopts_thenables_with_a_fresh_resolver() {
13846        let program = verified(
13847            vec![
13848                Constant::String(EcmaString::from_utf8("resolve")),
13849                Constant::String(EcmaString::from_utf8("reject")),
13850                Constant::String(EcmaString::from_utf8("observed")),
13851                Constant::Int32(7),
13852                Constant::Int32(8),
13853                Constant::Int32(9),
13854                Constant::Undefined,
13855            ],
13856            vec![
13857                function(0, 1, vec![Instruction::Halt], Vec::new()),
13858                function(
13859                    2,
13860                    2,
13861                    vec![
13862                        Instruction::StoreGlobal {
13863                            name: cid(0),
13864                            value: reg(0),
13865                        },
13866                        Instruction::StoreGlobal {
13867                            name: cid(1),
13868                            value: reg(1),
13869                        },
13870                        Instruction::Return { value: reg(0) },
13871                    ],
13872                    Vec::new(),
13873                ),
13874                function(
13875                    1,
13876                    1,
13877                    vec![
13878                        Instruction::StoreGlobal {
13879                            name: cid(2),
13880                            value: reg(0),
13881                        },
13882                        Instruction::Return { value: reg(0) },
13883                    ],
13884                    Vec::new(),
13885                ),
13886                function(
13887                    2,
13888                    6,
13889                    vec![
13890                        Instruction::LoadConst {
13891                            dst: reg(2),
13892                            constant: cid(3),
13893                        },
13894                        Instruction::CreateArray { dst: reg(3) },
13895                        Instruction::ArrayPush {
13896                            array: reg(3),
13897                            value: reg(2),
13898                        },
13899                        Instruction::LoadConst {
13900                            dst: reg(4),
13901                            constant: cid(6),
13902                        },
13903                        Instruction::Call {
13904                            dst: reg(5),
13905                            callee: reg(0),
13906                            this_value: reg(4),
13907                            arguments: reg(3),
13908                        },
13909                        Instruction::LoadConst {
13910                            dst: reg(2),
13911                            constant: cid(4),
13912                        },
13913                        Instruction::CreateArray { dst: reg(3) },
13914                        Instruction::ArrayPush {
13915                            array: reg(3),
13916                            value: reg(2),
13917                        },
13918                        Instruction::Call {
13919                            dst: reg(5),
13920                            callee: reg(1),
13921                            this_value: reg(4),
13922                            arguments: reg(3),
13923                        },
13924                        Instruction::LoadConst {
13925                            dst: reg(2),
13926                            constant: cid(5),
13927                        },
13928                        Instruction::Throw { value: reg(2) },
13929                    ],
13930                    Vec::new(),
13931                ),
13932            ],
13933        );
13934        let mut host = TestHost;
13935        let mut machine = Machine::new(&program, &mut host, Limits::default());
13936        machine.frames.clear();
13937        machine.live_registers = 0;
13938        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
13939            machine
13940                .allocate(HeapEntry::Function {
13941                    module: ModuleId::new(0),
13942                    function: FunctionId::new(function),
13943                    captures: Vec::new(),
13944                    properties: PropertyMap::default(),
13945                    prototype: Some(machine.intrinsics.function_prototype),
13946                    extensible: true,
13947                })
13948                .unwrap()
13949        };
13950        let executor = runtime_function(&mut machine, 1);
13951        let observer = runtime_function(&mut machine, 2);
13952        let then_callback = runtime_function(&mut machine, 3);
13953        let thenable = machine
13954            .allocate(HeapEntry::Object {
13955                properties: PropertyMap::default(),
13956                prototype: Some(machine.intrinsics.object_prototype),
13957                boxed_primitive: None,
13958                extensible: true,
13959            })
13960            .unwrap();
13961        machine
13962            .set_data_property(thenable, "then", then_callback)
13963            .unwrap();
13964
13965        let constructor = machine.intrinsics.global("Promise").unwrap();
13966        let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13967        let HeapEntry::NativeFunction {
13968            callable: NativeCallable::Builtin(constructor_id),
13969            ..
13970        } = machine.heap[constructor_index]
13971        else {
13972            panic!("Promise must be a native constructor");
13973        };
13974        let BuiltinOutcome::Value(promise) = machine
13975            .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13976            .unwrap()
13977        else {
13978            panic!("Promise construction returns a Promise");
13979        };
13980        let resolve = machine
13981            .globals
13982            .get(&EcmaString::from_utf8("resolve"))
13983            .copied()
13984            .unwrap();
13985        let reject = machine
13986            .globals
13987            .get(&EcmaString::from_utf8("reject"))
13988            .copied()
13989            .unwrap();
13990        machine
13991            .call_value(resolve, Value::UNDEFINED, &[thenable])
13992            .unwrap();
13993        let then = machine.get_named_property(promise, "then").unwrap();
13994        machine.call_value(then, promise, &[observer]).unwrap();
13995        machine
13996            .call_value(reject, Value::UNDEFINED, &[Value::int32(9)])
13997            .unwrap();
13998        assert!(
13999            !machine
14000                .globals
14001                .contains_key(&EcmaString::from_utf8("observed"))
14002        );
14003
14004        let drain = machine.drain_microtasks().unwrap();
14005        assert_eq!(drain.executed, 2);
14006        assert!(drain.uncaught.is_empty());
14007        assert_eq!(
14008            machine
14009                .globals
14010                .get(&EcmaString::from_utf8("observed"))
14011                .copied(),
14012            Some(Value::int32(7))
14013        );
14014    }
14015
14016    #[test]
14017    fn queue_microtask_drains_fifo_including_jobs_added_during_drain() {
14018        let program = verified(
14019            vec![
14020                Constant::String(EcmaString::from_utf8("order")),
14021                Constant::String(EcmaString::from_utf8("queueMicrotask")),
14022                Constant::String(EcmaString::from_utf8("third")),
14023                Constant::Int32(1),
14024                Constant::Int32(2),
14025                Constant::Int32(3),
14026                Constant::Undefined,
14027            ],
14028            vec![
14029                function(0, 1, vec![Instruction::Halt], Vec::new()),
14030                function(
14031                    0,
14032                    7,
14033                    vec![
14034                        Instruction::LoadGlobal {
14035                            dst: reg(0),
14036                            name: cid(0),
14037                        },
14038                        Instruction::LoadConst {
14039                            dst: reg(1),
14040                            constant: cid(3),
14041                        },
14042                        Instruction::ArrayPush {
14043                            array: reg(0),
14044                            value: reg(1),
14045                        },
14046                        Instruction::LoadGlobal {
14047                            dst: reg(2),
14048                            name: cid(1),
14049                        },
14050                        Instruction::LoadGlobal {
14051                            dst: reg(3),
14052                            name: cid(2),
14053                        },
14054                        Instruction::CreateArray { dst: reg(4) },
14055                        Instruction::ArrayPush {
14056                            array: reg(4),
14057                            value: reg(3),
14058                        },
14059                        Instruction::LoadConst {
14060                            dst: reg(5),
14061                            constant: cid(6),
14062                        },
14063                        Instruction::Call {
14064                            dst: reg(6),
14065                            callee: reg(2),
14066                            this_value: reg(5),
14067                            arguments: reg(4),
14068                        },
14069                        Instruction::Return { value: reg(1) },
14070                    ],
14071                    Vec::new(),
14072                ),
14073                function(
14074                    0,
14075                    2,
14076                    vec![
14077                        Instruction::LoadGlobal {
14078                            dst: reg(0),
14079                            name: cid(0),
14080                        },
14081                        Instruction::LoadConst {
14082                            dst: reg(1),
14083                            constant: cid(4),
14084                        },
14085                        Instruction::ArrayPush {
14086                            array: reg(0),
14087                            value: reg(1),
14088                        },
14089                        Instruction::Return { value: reg(1) },
14090                    ],
14091                    Vec::new(),
14092                ),
14093                function(
14094                    0,
14095                    2,
14096                    vec![
14097                        Instruction::LoadGlobal {
14098                            dst: reg(0),
14099                            name: cid(0),
14100                        },
14101                        Instruction::LoadConst {
14102                            dst: reg(1),
14103                            constant: cid(5),
14104                        },
14105                        Instruction::ArrayPush {
14106                            array: reg(0),
14107                            value: reg(1),
14108                        },
14109                        Instruction::Return { value: reg(1) },
14110                    ],
14111                    Vec::new(),
14112                ),
14113            ],
14114        );
14115        let mut host = TestHost;
14116        let mut machine = Machine::new(&program, &mut host, Limits::default());
14117        machine.frames.clear();
14118        machine.live_registers = 0;
14119        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14120            machine
14121                .allocate(HeapEntry::Function {
14122                    module: ModuleId::new(0),
14123                    function: FunctionId::new(function),
14124                    captures: Vec::new(),
14125                    properties: PropertyMap::default(),
14126                    prototype: Some(machine.intrinsics.function_prototype),
14127                    extensible: true,
14128                })
14129                .unwrap()
14130        };
14131        let first = runtime_function(&mut machine, 1);
14132        let second = runtime_function(&mut machine, 2);
14133        let third = runtime_function(&mut machine, 3);
14134        let order = machine
14135            .allocate(HeapEntry::Array {
14136                elements: Vec::new(),
14137                properties: PropertyMap::default(),
14138                prototype: Some(machine.intrinsics.array_prototype),
14139                extensible: true,
14140                length_writable: true,
14141            })
14142            .unwrap();
14143        machine
14144            .globals
14145            .insert(EcmaString::from_utf8("order"), order);
14146        machine
14147            .globals
14148            .insert(EcmaString::from_utf8("third"), third);
14149        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14150        machine
14151            .call_value(queue, Value::UNDEFINED, &[first])
14152            .unwrap();
14153        machine
14154            .call_value(queue, Value::UNDEFINED, &[second])
14155            .unwrap();
14156
14157        let drain = machine.drain_microtasks().unwrap();
14158        assert_eq!(drain.executed, 3);
14159        assert!(drain.uncaught.is_empty());
14160        let index = machine.runtime_slot(order).unwrap().unwrap();
14161        let HeapEntry::Array { elements, .. } = &machine.heap[index] else {
14162            panic!("order remains an array");
14163        };
14164        assert_eq!(
14165            elements,
14166            &[Value::int32(1), Value::int32(2), Value::int32(3)]
14167        );
14168    }
14169
14170    #[test]
14171    fn queue_microtask_reports_callback_throws_and_continues() {
14172        let program = verified(
14173            vec![
14174                Constant::Int32(7),
14175                Constant::Int32(1),
14176                Constant::String(EcmaString::from_utf8("observed")),
14177            ],
14178            vec![
14179                function(0, 1, vec![Instruction::Halt], Vec::new()),
14180                function(
14181                    0,
14182                    1,
14183                    vec![
14184                        Instruction::LoadConst {
14185                            dst: reg(0),
14186                            constant: cid(0),
14187                        },
14188                        Instruction::Throw { value: reg(0) },
14189                    ],
14190                    Vec::new(),
14191                ),
14192                function(
14193                    0,
14194                    1,
14195                    vec![
14196                        Instruction::LoadConst {
14197                            dst: reg(0),
14198                            constant: cid(1),
14199                        },
14200                        Instruction::StoreGlobal {
14201                            name: cid(2),
14202                            value: reg(0),
14203                        },
14204                        Instruction::Return { value: reg(0) },
14205                    ],
14206                    Vec::new(),
14207                ),
14208            ],
14209        );
14210        let mut host = TestHost;
14211        let mut machine = Machine::new(&program, &mut host, Limits::default());
14212        machine.frames.clear();
14213        machine.live_registers = 0;
14214        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14215            machine
14216                .allocate(HeapEntry::Function {
14217                    module: ModuleId::new(0),
14218                    function: FunctionId::new(function),
14219                    captures: Vec::new(),
14220                    properties: PropertyMap::default(),
14221                    prototype: Some(machine.intrinsics.function_prototype),
14222                    extensible: true,
14223                })
14224                .unwrap()
14225        };
14226        let throwing = runtime_function(&mut machine, 1);
14227        let observer = runtime_function(&mut machine, 2);
14228        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14229        machine
14230            .call_value(queue, Value::UNDEFINED, &[throwing])
14231            .unwrap();
14232        machine
14233            .call_value(queue, Value::UNDEFINED, &[observer])
14234            .unwrap();
14235
14236        let drain = machine.drain_microtasks().unwrap();
14237        assert_eq!(drain.executed, 2);
14238        assert_eq!(
14239            drain.uncaught,
14240            vec![CallbackException {
14241                value: Value::int32(7),
14242                origin: ThrowOrigin::Bytecode,
14243            }]
14244        );
14245        assert_eq!(
14246            machine
14247                .globals
14248                .get(&EcmaString::from_utf8("observed"))
14249                .copied(),
14250            Some(Value::int32(1))
14251        );
14252    }
14253
14254    #[test]
14255    fn microtask_boundaries_preserve_the_queued_head() {
14256        let program = verified(
14257            vec![Constant::Undefined],
14258            vec![
14259                function(0, 1, vec![Instruction::Halt], Vec::new()),
14260                function(
14261                    0,
14262                    1,
14263                    vec![
14264                        Instruction::LoadConst {
14265                            dst: reg(0),
14266                            constant: cid(0),
14267                        },
14268                        Instruction::Return { value: reg(0) },
14269                    ],
14270                    Vec::new(),
14271                ),
14272            ],
14273        );
14274        let mut host = TestHost;
14275        let mut machine = Machine::new(
14276            &program,
14277            &mut host,
14278            Limits {
14279                max_microtasks: 1,
14280                ..Limits::default()
14281            },
14282        );
14283        machine.frames.clear();
14284        machine.live_registers = 0;
14285        let callback = machine
14286            .allocate(HeapEntry::Function {
14287                module: ModuleId::new(0),
14288                function: FunctionId::new(1),
14289                captures: Vec::new(),
14290                properties: PropertyMap::default(),
14291                prototype: Some(machine.intrinsics.function_prototype),
14292                extensible: true,
14293            })
14294            .unwrap();
14295        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14296        assert!(matches!(
14297            machine.call_value(queue, Value::UNDEFINED, &[Value::int32(1)]),
14298            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
14299        ));
14300        machine
14301            .call_value(queue, Value::UNDEFINED, &[callback])
14302            .unwrap();
14303        assert!(matches!(
14304            machine.call_value(queue, Value::UNDEFINED, &[callback]),
14305            Err(EvalFailure::Runtime(
14306                RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 1 }
14307            ))
14308        ));
14309
14310        let fuel = machine.fuel;
14311        machine.microtask_drain_active = true;
14312        let reentry = machine.drain_microtasks().unwrap_err();
14313        assert!(matches!(
14314            reentry.kind,
14315            RuntimeErrorKind::MicrotaskDrainReentry
14316        ));
14317        assert_eq!(machine.fuel, fuel);
14318        assert_eq!(machine.microtasks.len(), 1);
14319        machine.microtask_drain_active = false;
14320
14321        machine.fuel = 0;
14322        let exhausted = machine.drain_microtasks().unwrap_err();
14323        assert!(matches!(
14324            exhausted.kind,
14325            RuntimeErrorKind::FuelExhausted { .. }
14326        ));
14327        assert!(!machine.microtask_drain_active);
14328        assert_eq!(machine.microtasks.len(), 1);
14329
14330        machine.fuel = 100;
14331        let drain = machine.drain_microtasks().unwrap();
14332        assert_eq!(drain.executed, 1);
14333        assert!(machine.microtasks.is_empty());
14334    }
14335
14336    // ---- timers -----------------------------------------------------------
14337
14338    #[derive(Default)]
14339    struct ManualTimerState {
14340        live: std::collections::BTreeMap<u64, u64>,
14341        reports: std::collections::VecDeque<TimerWakeup>,
14342        scheduled: Vec<(u64, u32)>,
14343        cancelled: Vec<u64>,
14344        fail_schedule: bool,
14345        fail_poll: bool,
14346    }
14347
14348    #[derive(Clone, Default)]
14349    struct ManualTimerProvider {
14350        state: std::rc::Rc<std::cell::RefCell<ManualTimerState>>,
14351    }
14352
14353    impl TimerProvider for ManualTimerProvider {
14354        fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError> {
14355            let mut state = self.state.borrow_mut();
14356            state.scheduled.push((id, delay_ms));
14357            if state.fail_schedule {
14358                return Err(TimerError::new("manual schedule failure"));
14359            }
14360            let deadline = u64::from(delay_ms);
14361            state.live.insert(id, deadline);
14362            Ok(deadline)
14363        }
14364
14365        fn cancel(&mut self, id: u64) -> Result<bool, TimerError> {
14366            let mut state = self.state.borrow_mut();
14367            state.cancelled.push(id);
14368            Ok(state.live.remove(&id).is_some())
14369        }
14370
14371        fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError> {
14372            let mut state = self.state.borrow_mut();
14373            if state.fail_poll {
14374                return Err(TimerError::new("manual poll failure"));
14375            }
14376            output.extend(state.reports.drain(..));
14377            Ok(())
14378        }
14379
14380        fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError> {
14381            Ok(self.state.borrow_mut().reports.pop_front())
14382        }
14383
14384        fn has_pending(&self) -> bool {
14385            !self.state.borrow().live.is_empty()
14386        }
14387    }
14388
14389    #[derive(Default)]
14390    struct TimerTestHost {
14391        provider: ManualTimerProvider,
14392    }
14393
14394    impl Host for TimerTestHost {
14395        fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
14396            Some(&mut self.provider)
14397        }
14398    }
14399
14400    fn timer_program() -> Program<Verified> {
14401        verified(
14402            vec![
14403                Constant::String(EcmaString::from_utf8("a")),
14404                Constant::String(EcmaString::from_utf8("b")),
14405                Constant::String(EcmaString::from_utf8("this_seen")),
14406                Constant::String(EcmaString::from_utf8("arg_seen")),
14407                Constant::Int32(1),
14408                Constant::Int32(7),
14409            ],
14410            vec![
14411                function(0, 1, vec![Instruction::Halt], Vec::new()),
14412                function(
14413                    0,
14414                    1,
14415                    vec![
14416                        Instruction::LoadConst {
14417                            dst: reg(0),
14418                            constant: cid(4),
14419                        },
14420                        Instruction::StoreGlobal {
14421                            name: cid(0),
14422                            value: reg(0),
14423                        },
14424                        Instruction::Return { value: reg(0) },
14425                    ],
14426                    Vec::new(),
14427                ),
14428                function(
14429                    0,
14430                    1,
14431                    vec![
14432                        Instruction::LoadConst {
14433                            dst: reg(0),
14434                            constant: cid(4),
14435                        },
14436                        Instruction::StoreGlobal {
14437                            name: cid(1),
14438                            value: reg(0),
14439                        },
14440                        Instruction::Return { value: reg(0) },
14441                    ],
14442                    Vec::new(),
14443                ),
14444                function(
14445                    1,
14446                    2,
14447                    vec![
14448                        Instruction::LoadThis { dst: reg(1) },
14449                        Instruction::StoreGlobal {
14450                            name: cid(2),
14451                            value: reg(1),
14452                        },
14453                        Instruction::StoreGlobal {
14454                            name: cid(3),
14455                            value: reg(0),
14456                        },
14457                        Instruction::Return { value: reg(0) },
14458                    ],
14459                    Vec::new(),
14460                ),
14461                function(
14462                    0,
14463                    1,
14464                    vec![
14465                        Instruction::LoadConst {
14466                            dst: reg(0),
14467                            constant: cid(5),
14468                        },
14469                        Instruction::Throw { value: reg(0) },
14470                    ],
14471                    Vec::new(),
14472                ),
14473            ],
14474        )
14475    }
14476
14477    fn timer_fn(machine: &mut Machine<'_, TimerTestHost>, index: u32) -> Value {
14478        machine
14479            .allocate(HeapEntry::Function {
14480                module: ModuleId::new(0),
14481                function: FunctionId::new(index),
14482                captures: Vec::new(),
14483                properties: PropertyMap::default(),
14484                prototype: Some(machine.intrinsics.function_prototype),
14485                extensible: true,
14486            })
14487            .unwrap()
14488    }
14489
14490    fn read_global(machine: &Machine<'_, TimerTestHost>, name: &str) -> Option<Value> {
14491        machine.globals.get(&EcmaString::from_utf8(name)).copied()
14492    }
14493
14494    fn set_timeout_global(machine: &Machine<'_, TimerTestHost>) -> Value {
14495        machine
14496            .intrinsics
14497            .global("setTimeout")
14498            .expect("setTimeout is installed")
14499    }
14500
14501    fn schedule_nested_timer(
14502        machine: &mut Machine<'_, TimerTestHost>,
14503        _this: Value,
14504        _args: &[Value],
14505        _constructing: bool,
14506    ) -> Result<BuiltinOutcome, EvalFailure> {
14507        let callback = machine
14508            .globals
14509            .get(&EcmaString::from_utf8("nestedCallback"))
14510            .copied()
14511            .expect("test installs nested callback");
14512        let set_timeout = set_timeout_global(machine);
14513        machine.call_value(set_timeout, Value::UNDEFINED, &[callback, Value::int32(1)])?;
14514        Ok(BuiltinOutcome::Value(Value::UNDEFINED))
14515    }
14516
14517    fn timer_native(
14518        machine: &mut Machine<'_, TimerTestHost>,
14519        name: &'static str,
14520        handler: crate::intrinsics::BuiltinHandler<TimerTestHost>,
14521    ) -> Value {
14522        let id = machine
14523            .intrinsics
14524            .builtins
14525            .register(crate::intrinsics::BuiltinDef {
14526                name,
14527                length: 0,
14528                handler,
14529            });
14530        crate::intrinsics::native_function(&mut machine.heap, id, name, 0)
14531    }
14532
14533    #[test]
14534    fn timers_are_absent_without_the_capability() {
14535        let program = timer_program();
14536        let mut host = TestHost;
14537        let mut machine = Machine::new(&program, &mut host, Limits::default());
14538        machine.frames.clear();
14539        machine.live_registers = 0;
14540        assert!(machine.intrinsics.global("setTimeout").is_none());
14541        assert!(machine.intrinsics.global("clearTimeout").is_none());
14542        assert!(!machine.has_pending_timers());
14543        assert_eq!(
14544            machine.run_one_expired_timer().unwrap(),
14545            TimerRun::default()
14546        );
14547        assert!(!machine.wait_for_timer_expiry().unwrap());
14548    }
14549
14550    #[test]
14551    fn set_timeout_rejects_a_non_callable_callback_before_coercion() {
14552        let program = timer_program();
14553        let mut host = TimerTestHost::default();
14554        let shared = host.provider.state.clone();
14555        let mut machine = Machine::new(&program, &mut host, Limits::default());
14556        machine.frames.clear();
14557        machine.live_registers = 0;
14558        let set_timeout = set_timeout_global(&machine);
14559        let failure = machine
14560            .call_value(
14561                set_timeout,
14562                Value::UNDEFINED,
14563                &[Value::int32(3), Value::int32(5)],
14564            )
14565            .unwrap_err();
14566        assert!(matches!(
14567            failure,
14568            EvalFailure::Throw(ThrowOrigin::TypeError { .. })
14569        ));
14570        // Nothing was armed, so no delay coercion or provider call happened.
14571        assert!(shared.borrow().scheduled.is_empty());
14572        assert!(!machine.has_pending_timers());
14573    }
14574
14575    #[test]
14576    fn set_timeout_clamps_and_truncates_like_node() {
14577        let program = timer_program();
14578        let mut host = TimerTestHost::default();
14579        let shared = host.provider.state.clone();
14580        let mut machine = Machine::new(&program, &mut host, Limits::default());
14581        machine.frames.clear();
14582        machine.live_registers = 0;
14583        let set_timeout = set_timeout_global(&machine);
14584        let callback = timer_fn(&mut machine, 1);
14585        for delay in [
14586            Value::int32(0),
14587            Value::number(-5.0),
14588            Value::number(f64::NAN),
14589            Value::number(2_147_483_648.0),
14590            Value::int32(2_147_483_647),
14591            Value::number(3.9),
14592        ] {
14593            machine
14594                .call_value(set_timeout, Value::UNDEFINED, &[callback, delay])
14595                .unwrap();
14596        }
14597        let delays: Vec<u32> = shared.borrow().scheduled.iter().map(|(_, d)| *d).collect();
14598        assert_eq!(delays, vec![1, 1, 1, 1, 2_147_483_647, 3]);
14599        // Ids are minted monotonically from 1 and never reused.
14600        let ids: Vec<u64> = shared
14601            .borrow()
14602            .scheduled
14603            .iter()
14604            .map(|(id, _)| *id)
14605            .collect();
14606        assert_eq!(ids, vec![1, 2, 3, 4, 5, 6]);
14607    }
14608
14609    #[test]
14610    fn same_deadline_timers_run_in_registration_order_despite_reverse_reports() {
14611        let program = timer_program();
14612        let mut host = TimerTestHost::default();
14613        let shared = host.provider.state.clone();
14614        let mut machine = Machine::new(&program, &mut host, Limits::default());
14615        machine.frames.clear();
14616        machine.live_registers = 0;
14617        let set_timeout = set_timeout_global(&machine);
14618        let a = timer_fn(&mut machine, 1);
14619        let b = timer_fn(&mut machine, 2);
14620        machine
14621            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14622            .unwrap();
14623        machine
14624            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(5)])
14625            .unwrap();
14626        // Host reports the later registration first and in split batches.
14627        shared.borrow_mut().reports.push_back(TimerWakeup {
14628            id: 2,
14629            deadline_ms: 5,
14630        });
14631        let first = machine.run_one_expired_timer().unwrap();
14632        assert_eq!(first.executed, 1);
14633        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14634        assert_eq!(read_global(&machine, "b"), None);
14635        let second = machine.run_one_expired_timer().unwrap();
14636        assert_eq!(second.executed, 1);
14637        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14638        assert!(!machine.has_pending_timers());
14639    }
14640
14641    #[test]
14642    fn a_shorter_deadline_beats_an_older_sequence() {
14643        let program = timer_program();
14644        let mut host = TimerTestHost::default();
14645        let shared = host.provider.state.clone();
14646        let mut machine = Machine::new(&program, &mut host, Limits::default());
14647        machine.frames.clear();
14648        machine.live_registers = 0;
14649        let set_timeout = set_timeout_global(&machine);
14650        let a = timer_fn(&mut machine, 1);
14651        let b = timer_fn(&mut machine, 2);
14652        machine
14653            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14654            .unwrap();
14655        machine
14656            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14657            .unwrap();
14658        shared.borrow_mut().reports.push_back(TimerWakeup {
14659            id: 1,
14660            deadline_ms: 5,
14661        });
14662        machine.run_one_expired_timer().unwrap();
14663        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14664        assert_eq!(read_global(&machine, "a"), None);
14665    }
14666
14667    #[test]
14668    fn clear_timeout_prevents_a_ready_timer_and_ignores_stale_ids() {
14669        let program = timer_program();
14670        let mut host = TimerTestHost::default();
14671        let shared = host.provider.state.clone();
14672        let mut machine = Machine::new(&program, &mut host, Limits::default());
14673        machine.frames.clear();
14674        machine.live_registers = 0;
14675        let set_timeout = set_timeout_global(&machine);
14676        let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14677        let a = timer_fn(&mut machine, 1);
14678        let b = timer_fn(&mut machine, 2);
14679        let handle_a = machine
14680            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14681            .unwrap();
14682        machine
14683            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14684            .unwrap();
14685        // Clear the first timer even though the host already reported it.
14686        shared.borrow_mut().reports.push_back(TimerWakeup {
14687            id: 1,
14688            deadline_ms: 3,
14689        });
14690        machine
14691            .call_value(clear_timeout, Value::UNDEFINED, &[handle_a])
14692            .unwrap();
14693        assert!(shared.borrow().cancelled.contains(&1));
14694        // A stale positive-integer id must not cancel the surviving timer.
14695        machine
14696            .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14697            .unwrap();
14698        shared.borrow_mut().reports.push_back(TimerWakeup {
14699            id: 2,
14700            deadline_ms: 3,
14701        });
14702        let run = machine.run_one_expired_timer().unwrap();
14703        assert_eq!(run.executed, 1);
14704        assert_eq!(read_global(&machine, "a"), None);
14705        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14706    }
14707
14708    #[test]
14709    fn clear_timeout_accepts_a_direct_positive_integer_id() {
14710        let program = timer_program();
14711        let mut host = TimerTestHost::default();
14712        let shared = host.provider.state.clone();
14713        let mut machine = Machine::new(&program, &mut host, Limits::default());
14714        machine.frames.clear();
14715        machine.live_registers = 0;
14716        let set_timeout = set_timeout_global(&machine);
14717        let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14718        let a = timer_fn(&mut machine, 1);
14719        machine
14720            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14721            .unwrap();
14722        machine
14723            .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14724            .unwrap();
14725        assert!(!machine.has_pending_timers());
14726        shared.borrow_mut().reports.push_back(TimerWakeup {
14727            id: 1,
14728            deadline_ms: 3,
14729        });
14730        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 0);
14731
14732        machine.next_timer_id = Some(u64::MAX);
14733        let handle = machine
14734            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14735            .unwrap();
14736        machine
14737            .call_value(
14738                clear_timeout,
14739                Value::UNDEFINED,
14740                &[Value::number(u64::MAX as f64)],
14741            )
14742            .unwrap();
14743        assert!(machine.has_pending_timers());
14744        machine
14745            .call_value(clear_timeout, Value::UNDEFINED, &[handle])
14746            .unwrap();
14747        assert!(!machine.has_pending_timers());
14748        // A no-op clear of an unrelated value never coerces or errors.
14749        machine
14750            .call_value(clear_timeout, Value::UNDEFINED, &[Value::UNDEFINED])
14751            .unwrap();
14752    }
14753
14754    #[test]
14755    fn timer_callback_receives_trailing_args_and_the_handle_as_this() {
14756        let program = timer_program();
14757        let mut host = TimerTestHost::default();
14758        let shared = host.provider.state.clone();
14759        let mut machine = Machine::new(&program, &mut host, Limits::default());
14760        machine.frames.clear();
14761        machine.live_registers = 0;
14762        let set_timeout = set_timeout_global(&machine);
14763        let callback = timer_fn(&mut machine, 3);
14764        let handle = machine
14765            .call_value(
14766                set_timeout,
14767                Value::UNDEFINED,
14768                &[callback, Value::int32(1), Value::int32(42)],
14769            )
14770            .unwrap();
14771        shared.borrow_mut().reports.push_back(TimerWakeup {
14772            id: 1,
14773            deadline_ms: 1,
14774        });
14775        machine.run_one_expired_timer().unwrap();
14776        assert_eq!(read_global(&machine, "this_seen"), Some(handle));
14777        assert_eq!(read_global(&machine, "arg_seen"), Some(Value::int32(42)));
14778    }
14779
14780    #[test]
14781    fn a_callback_created_timer_waits_for_a_later_checkpoint() {
14782        let program = timer_program();
14783        let mut host = TimerTestHost::default();
14784        let shared = host.provider.state.clone();
14785        let mut machine = Machine::new(&program, &mut host, Limits::default());
14786        machine.frames.clear();
14787        machine.live_registers = 0;
14788        let set_timeout = set_timeout_global(&machine);
14789        let nested = timer_fn(&mut machine, 2);
14790        machine
14791            .globals
14792            .insert(EcmaString::from_utf8("nestedCallback"), nested);
14793        let creator = timer_native(&mut machine, "schedule nested", schedule_nested_timer);
14794        machine
14795            .call_value(set_timeout, Value::UNDEFINED, &[creator, Value::int32(1)])
14796            .unwrap();
14797        shared.borrow_mut().reports.push_back(TimerWakeup {
14798            id: 1,
14799            deadline_ms: 1,
14800        });
14801        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14802        assert_eq!(read_global(&machine, "b"), None);
14803        assert!(machine.has_pending_timers());
14804        // Even if the provider can report it immediately, it runs only in a
14805        // later explicit timer checkpoint.
14806        shared.borrow_mut().reports.push_back(TimerWakeup {
14807            id: 2,
14808            deadline_ms: 1,
14809        });
14810        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14811        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14812    }
14813
14814    #[test]
14815    fn timer_callback_throw_is_reported_and_a_runtime_failure_propagates() {
14816        let program = timer_program();
14817        let mut host = TimerTestHost::default();
14818        let shared = host.provider.state.clone();
14819        let mut machine = Machine::new(&program, &mut host, Limits::default());
14820        machine.frames.clear();
14821        machine.live_registers = 0;
14822        let set_timeout = set_timeout_global(&machine);
14823        let thrower = timer_fn(&mut machine, 4);
14824        machine
14825            .call_value(set_timeout, Value::UNDEFINED, &[thrower, Value::int32(1)])
14826            .unwrap();
14827        shared.borrow_mut().reports.push_back(TimerWakeup {
14828            id: 1,
14829            deadline_ms: 1,
14830        });
14831        let run = machine.run_one_expired_timer().unwrap();
14832        assert_eq!(run.executed, 1);
14833        assert_eq!(
14834            run.uncaught,
14835            vec![CallbackException {
14836                value: Value::int32(7),
14837                origin: ThrowOrigin::Bytecode
14838            }]
14839        );
14840
14841        // A runtime failure inside the callback stops the checkpoint.
14842        let another = timer_fn(&mut machine, 1);
14843        machine
14844            .call_value(set_timeout, Value::UNDEFINED, &[another, Value::int32(1)])
14845            .unwrap();
14846        shared.borrow_mut().reports.push_back(TimerWakeup {
14847            id: 2,
14848            deadline_ms: 1,
14849        });
14850        machine.fuel = 1;
14851        let error = machine.run_one_expired_timer().unwrap_err();
14852        assert!(matches!(error.kind, RuntimeErrorKind::FuelExhausted { .. }));
14853    }
14854
14855    #[test]
14856    fn a_timer_checkpoint_never_drains_microtasks() {
14857        let program = timer_program();
14858        let mut host = TimerTestHost::default();
14859        let shared = host.provider.state.clone();
14860        let mut machine = Machine::new(&program, &mut host, Limits::default());
14861        machine.frames.clear();
14862        machine.live_registers = 0;
14863        let set_timeout = set_timeout_global(&machine);
14864        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14865        let a = timer_fn(&mut machine, 1);
14866        let b = timer_fn(&mut machine, 2);
14867        machine
14868            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14869            .unwrap();
14870        machine.call_value(queue, Value::UNDEFINED, &[b]).unwrap();
14871        shared.borrow_mut().reports.push_back(TimerWakeup {
14872            id: 1,
14873            deadline_ms: 1,
14874        });
14875        let run = machine.run_one_expired_timer().unwrap();
14876        assert_eq!(run.executed, 1);
14877        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14878        assert_eq!(read_global(&machine, "b"), None);
14879        assert_eq!(machine.microtasks.len(), 1);
14880        machine.drain_microtasks().unwrap();
14881        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14882    }
14883
14884    #[test]
14885    fn timer_reentry_capacity_and_fuel_preserve_state() {
14886        let program = timer_program();
14887        let mut host = TimerTestHost::default();
14888        let shared = host.provider.state.clone();
14889        let mut machine = Machine::new(
14890            &program,
14891            &mut host,
14892            Limits {
14893                max_timers: 1,
14894                ..Limits::default()
14895            },
14896        );
14897        machine.frames.clear();
14898        machine.live_registers = 0;
14899        let set_timeout = set_timeout_global(&machine);
14900        let a = timer_fn(&mut machine, 1);
14901        let b = timer_fn(&mut machine, 2);
14902        machine
14903            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14904            .unwrap();
14905        // Capacity is enforced before any provider or table mutation.
14906        let capacity = machine
14907            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(1)])
14908            .unwrap_err();
14909        assert!(matches!(
14910            capacity,
14911            EvalFailure::Runtime(RuntimeErrorKind::TimerCapacityExceeded { limit: 1 })
14912        ));
14913        assert_eq!(shared.borrow().scheduled.len(), 1);
14914
14915        // Reentry fails without consuming fuel or touching the ready timer.
14916        shared.borrow_mut().reports.push_back(TimerWakeup {
14917            id: 1,
14918            deadline_ms: 1,
14919        });
14920        machine.timer_checkpoint_active = true;
14921        let fuel = machine.fuel;
14922        let reentry = machine.run_one_expired_timer().unwrap_err();
14923        assert!(matches!(
14924            reentry.kind,
14925            RuntimeErrorKind::TimerCheckpointReentry
14926        ));
14927        assert_eq!(machine.fuel, fuel);
14928        machine.timer_checkpoint_active = false;
14929
14930        // Fuel is charged before the live record is removed.
14931        machine.fuel = 0;
14932        let exhausted = machine.run_one_expired_timer().unwrap_err();
14933        assert!(matches!(
14934            exhausted.kind,
14935            RuntimeErrorKind::FuelExhausted { .. }
14936        ));
14937        assert!(machine.has_pending_timers());
14938        machine.fuel = 100;
14939        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14940        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14941    }
14942
14943    #[test]
14944    fn a_failed_schedule_never_reuses_its_timer_id() {
14945        let program = timer_program();
14946        let mut host = TimerTestHost::default();
14947        let shared = host.provider.state.clone();
14948        let mut machine = Machine::new(&program, &mut host, Limits::default());
14949        machine.frames.clear();
14950        machine.live_registers = 0;
14951        let set_timeout = set_timeout_global(&machine);
14952        let a = timer_fn(&mut machine, 1);
14953        shared.borrow_mut().fail_schedule = true;
14954        let failure = machine
14955            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14956            .unwrap_err();
14957        assert!(matches!(
14958            failure,
14959            EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure { .. })
14960        ));
14961        shared.borrow_mut().fail_schedule = false;
14962        machine
14963            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14964            .unwrap();
14965        let ids: Vec<u64> = shared
14966            .borrow()
14967            .scheduled
14968            .iter()
14969            .map(|(id, _)| *id)
14970            .collect();
14971        assert_eq!(ids, vec![1, 2]);
14972    }
14973
14974    #[test]
14975    fn wait_for_timer_expiry_promotes_a_reported_timer() {
14976        let program = timer_program();
14977        let mut host = TimerTestHost::default();
14978        let shared = host.provider.state.clone();
14979        let mut machine = Machine::new(&program, &mut host, Limits::default());
14980        machine.frames.clear();
14981        machine.live_registers = 0;
14982        assert!(!machine.wait_for_timer_expiry().unwrap());
14983        let set_timeout = set_timeout_global(&machine);
14984        let a = timer_fn(&mut machine, 1);
14985        machine
14986            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14987            .unwrap();
14988        shared.borrow_mut().reports.push_back(TimerWakeup {
14989            id: 1,
14990            deadline_ms: 1,
14991        });
14992        assert!(machine.wait_for_timer_expiry().unwrap());
14993        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14994        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14995    }
14996
14997    // ---- automatic event loop ---------------------------------------------
14998
14999    /// A program whose entry queues the global `"job"` microtask, with helper
15000    /// functions that push a marker onto the global `"order"` array. Function 1
15001    /// also queues `"job"` so a microtask can be created during a drain or a
15002    /// timer turn. The entry function is only exercised by `evaluate`.
15003    fn loop_test_program() -> Program<Verified> {
15004        verified(
15005            vec![
15006                Constant::String(EcmaString::from_utf8("order")), // 0
15007                Constant::Int32(1),                                // 1
15008                Constant::Int32(2),                                // 2
15009                Constant::Int32(3),                                // 3
15010                Constant::Int32(4),                                // 4
15011                Constant::String(EcmaString::from_utf8("queueMicrotask")), // 5
15012                Constant::String(EcmaString::from_utf8("job")),   // 6
15013                Constant::Undefined,                              // 7
15014            ],
15015            vec![
15016                function(
15017                    0,
15018                    7,
15019                    vec![
15020                        Instruction::LoadGlobal {
15021                            dst: reg(0),
15022                            name: cid(5),
15023                        },
15024                        Instruction::LoadGlobal {
15025                            dst: reg(1),
15026                            name: cid(6),
15027                        },
15028                        Instruction::CreateArray { dst: reg(2) },
15029                        Instruction::ArrayPush {
15030                            array: reg(2),
15031                            value: reg(1),
15032                        },
15033                        Instruction::LoadConst {
15034                            dst: reg(3),
15035                            constant: cid(7),
15036                        },
15037                        Instruction::Call {
15038                            dst: reg(4),
15039                            callee: reg(0),
15040                            this_value: reg(3),
15041                            arguments: reg(2),
15042                        },
15043                        Instruction::Return { value: reg(3) },
15044                    ],
15045                    Vec::new(),
15046                ),
15047                function(
15048                    0,
15049                    7,
15050                    vec![
15051                        Instruction::LoadGlobal {
15052                            dst: reg(0),
15053                            name: cid(0),
15054                        },
15055                        Instruction::LoadConst {
15056                            dst: reg(1),
15057                            constant: cid(1),
15058                        },
15059                        Instruction::ArrayPush {
15060                            array: reg(0),
15061                            value: reg(1),
15062                        },
15063                        Instruction::LoadGlobal {
15064                            dst: reg(2),
15065                            name: cid(5),
15066                        },
15067                        Instruction::LoadGlobal {
15068                            dst: reg(3),
15069                            name: cid(6),
15070                        },
15071                        Instruction::CreateArray { dst: reg(4) },
15072                        Instruction::ArrayPush {
15073                            array: reg(4),
15074                            value: reg(3),
15075                        },
15076                        Instruction::LoadConst {
15077                            dst: reg(5),
15078                            constant: cid(7),
15079                        },
15080                        Instruction::Call {
15081                            dst: reg(6),
15082                            callee: reg(2),
15083                            this_value: reg(5),
15084                            arguments: reg(4),
15085                        },
15086                        Instruction::Return { value: reg(1) },
15087                    ],
15088                    Vec::new(),
15089                ),
15090                function(
15091                    0,
15092                    2,
15093                    vec![
15094                        Instruction::LoadGlobal {
15095                            dst: reg(0),
15096                            name: cid(0),
15097                        },
15098                        Instruction::LoadConst {
15099                            dst: reg(1),
15100                            constant: cid(2),
15101                        },
15102                        Instruction::ArrayPush {
15103                            array: reg(0),
15104                            value: reg(1),
15105                        },
15106                        Instruction::Return { value: reg(1) },
15107                    ],
15108                    Vec::new(),
15109                ),
15110                function(
15111                    0,
15112                    2,
15113                    vec![
15114                        Instruction::LoadGlobal {
15115                            dst: reg(0),
15116                            name: cid(0),
15117                        },
15118                        Instruction::LoadConst {
15119                            dst: reg(1),
15120                            constant: cid(3),
15121                        },
15122                        Instruction::ArrayPush {
15123                            array: reg(0),
15124                            value: reg(1),
15125                        },
15126                        Instruction::Return { value: reg(1) },
15127                    ],
15128                    Vec::new(),
15129                ),
15130                function(
15131                    0,
15132                    2,
15133                    vec![
15134                        Instruction::LoadGlobal {
15135                            dst: reg(0),
15136                            name: cid(0),
15137                        },
15138                        Instruction::LoadConst {
15139                            dst: reg(1),
15140                            constant: cid(4),
15141                        },
15142                        Instruction::ArrayPush {
15143                            array: reg(0),
15144                            value: reg(1),
15145                        },
15146                        Instruction::Return { value: reg(1) },
15147                    ],
15148                    Vec::new(),
15149                ),
15150            ],
15151        )
15152    }
15153
15154    fn promise_throw_program() -> Program<Verified> {
15155        verified(
15156            vec![
15157                Constant::String(EcmaString::from_utf8("resolve")), // 0
15158                Constant::String(EcmaString::from_utf8("reject")),  // 1
15159                Constant::String(EcmaString::from_utf8("observed")), // 2
15160                Constant::Int32(7),                                 // 3
15161            ],
15162            vec![
15163                function(0, 1, vec![Instruction::Halt], Vec::new()),
15164                function(
15165                    2,
15166                    2,
15167                    vec![
15168                        Instruction::StoreGlobal {
15169                            name: cid(0),
15170                            value: reg(0),
15171                        },
15172                        Instruction::StoreGlobal {
15173                            name: cid(1),
15174                            value: reg(1),
15175                        },
15176                        Instruction::Return { value: reg(0) },
15177                    ],
15178                    Vec::new(),
15179                ),
15180                function(
15181                    1,
15182                    1,
15183                    vec![
15184                        Instruction::LoadConst {
15185                            dst: reg(0),
15186                            constant: cid(3),
15187                        },
15188                        Instruction::Throw { value: reg(0) },
15189                    ],
15190                    Vec::new(),
15191                ),
15192                function(
15193                    1,
15194                    1,
15195                    vec![
15196                        Instruction::StoreGlobal {
15197                            name: cid(2),
15198                            value: reg(0),
15199                        },
15200                        Instruction::Return { value: reg(0) },
15201                    ],
15202                    Vec::new(),
15203                ),
15204            ],
15205        )
15206    }
15207
15208    fn install_order_array(machine: &mut Machine<'_, TimerTestHost>) -> Value {
15209        let order = machine
15210            .allocate(HeapEntry::Array {
15211                elements: Vec::new(),
15212                properties: PropertyMap::default(),
15213                prototype: Some(machine.intrinsics.array_prototype),
15214                extensible: true,
15215                length_writable: true,
15216            })
15217            .unwrap();
15218        machine
15219            .globals
15220            .insert(EcmaString::from_utf8("order"), order);
15221        order
15222    }
15223
15224    fn order_markers(machine: &Machine<'_, TimerTestHost>) -> Vec<Value> {
15225        let order = machine
15226            .globals
15227            .get(&EcmaString::from_utf8("order"))
15228            .copied()
15229            .expect("order array is installed");
15230        let index = machine
15231            .runtime_slot(order)
15232            .expect("order resolves")
15233            .expect("order is a heap value");
15234        let HeapEntry::Array { elements, .. } = &machine.heap[index] else {
15235            panic!("order remains an array");
15236        };
15237        elements.clone()
15238    }
15239
15240    fn schedule_global_job(
15241        machine: &mut Machine<'_, TimerTestHost>,
15242        global: &str,
15243    ) -> Result<BuiltinOutcome, EvalFailure> {
15244        let job = machine
15245            .globals
15246            .get(&EcmaString::from_utf8(global))
15247            .copied()
15248            .unwrap_or_else(|| panic!("test installs the {global} job"));
15249        let queue = machine
15250            .intrinsics
15251            .global("queueMicrotask")
15252            .expect("queueMicrotask is installed");
15253        machine.call_value(queue, Value::UNDEFINED, &[job])?;
15254        Ok(BuiltinOutcome::Value(Value::UNDEFINED))
15255    }
15256
15257    fn queue_job_then_throw(
15258        machine: &mut Machine<'_, TimerTestHost>,
15259        _this: Value,
15260        _args: &[Value],
15261        _constructing: bool,
15262    ) -> Result<BuiltinOutcome, EvalFailure> {
15263        schedule_global_job(machine, "nestedCallback")?;
15264        Err(EvalFailure::ThrowValue(Value::int32(7)))
15265    }
15266
15267    fn respawn_job(
15268        machine: &mut Machine<'_, TimerTestHost>,
15269        _this: Value,
15270        _args: &[Value],
15271        _constructing: bool,
15272    ) -> Result<BuiltinOutcome, EvalFailure> {
15273        schedule_global_job(machine, "nestedCallback")
15274    }
15275
15276
15277    #[test]
15278    fn automatic_loop_leaves_an_idle_machine_untouched() {
15279        let program = timer_program();
15280        let mut host = TimerTestHost::default();
15281        let mut machine = Machine::new(&program, &mut host, Limits::default());
15282        machine.frames.clear();
15283        machine.live_registers = 0;
15284        let fuel = machine.fuel;
15285        machine.run_to_quiescence().unwrap();
15286        assert_eq!(machine.fuel, fuel);
15287        assert!(machine.microtasks.is_empty());
15288        assert!(!machine.has_pending_timers());
15289        assert!(!machine.microtask_drain_active);
15290        assert!(!machine.timer_checkpoint_active);
15291    }
15292
15293    #[test]
15294    fn run_returns_the_synchronous_execution_snapshot() {
15295        let program = loop_test_program();
15296        let mut host = TimerTestHost::default();
15297        let mut first = Machine::new(&program, &mut host, Limits::default());
15298        first.frames.clear();
15299        first.live_registers = 0;
15300        install_order_array(&mut first);
15301        first
15302            .globals
15303            .insert(EcmaString::from_utf8("job"), timer_fn(&mut first, 2));
15304        let snapshot = first.evaluate().unwrap();
15305        assert!(order_markers(&first).is_empty());
15306        first.run_to_quiescence().unwrap();
15307        assert_eq!(order_markers(&first), vec![Value::int32(2)]);
15308        drop(first);
15309
15310        let mut host = TimerTestHost::default();
15311        let mut second = Machine::new(&program, &mut host, Limits::default());
15312        second.frames.clear();
15313        second.live_registers = 0;
15314        install_order_array(&mut second);
15315        second
15316            .globals
15317            .insert(EcmaString::from_utf8("job"), timer_fn(&mut second, 2));
15318        let execution = second.run().unwrap();
15319        assert_eq!(execution, snapshot);
15320    }
15321
15322    #[test]
15323    fn automatic_loop_drains_nested_microtasks_in_fifo_order() {
15324        let program = loop_test_program();
15325        let mut host = TimerTestHost::default();
15326        let mut machine = Machine::new(&program, &mut host, Limits::default());
15327        machine.frames.clear();
15328        machine.live_registers = 0;
15329        install_order_array(&mut machine);
15330        let first = timer_fn(&mut machine, 1); // pushes 1, queues "job"
15331        let second = timer_fn(&mut machine, 2); // pushes 2
15332        let third = timer_fn(&mut machine, 3); // pushes 3
15333        machine
15334            .globals
15335            .insert(EcmaString::from_utf8("job"), third);
15336        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
15337        machine
15338            .call_value(queue, Value::UNDEFINED, &[first])
15339            .unwrap();
15340        machine
15341            .call_value(queue, Value::UNDEFINED, &[second])
15342            .unwrap();
15343
15344        machine.run_to_quiescence().unwrap();
15345        assert_eq!(
15346            order_markers(&machine),
15347            vec![Value::int32(1), Value::int32(2), Value::int32(3)]
15348        );
15349        assert!(machine.microtasks.is_empty());
15350    }
15351
15352    #[test]
15353    fn automatic_loop_runs_two_timers_with_a_full_drain_between_turns() {
15354        let program = loop_test_program();
15355        let mut host = TimerTestHost::default();
15356        let shared = host.provider.state.clone();
15357        let mut machine = Machine::new(&program, &mut host, Limits::default());
15358        machine.frames.clear();
15359        machine.live_registers = 0;
15360        install_order_array(&mut machine);
15361        let first = timer_fn(&mut machine, 1); // pushes 1, queues "job"
15362        let second = timer_fn(&mut machine, 3); // pushes 3
15363        let microtask = timer_fn(&mut machine, 2); // pushes 2
15364        machine
15365            .globals
15366            .insert(EcmaString::from_utf8("job"), microtask);
15367        let set_timeout = set_timeout_global(&machine);
15368        machine
15369            .call_value(set_timeout, Value::UNDEFINED, &[first, Value::int32(5)])
15370            .unwrap();
15371        machine
15372            .call_value(set_timeout, Value::UNDEFINED, &[second, Value::int32(5)])
15373            .unwrap();
15374        shared.borrow_mut().reports.push_back(TimerWakeup {
15375            id: 1,
15376            deadline_ms: 5,
15377        });
15378        shared.borrow_mut().reports.push_back(TimerWakeup {
15379            id: 2,
15380            deadline_ms: 5,
15381        });
15382
15383        machine.run_to_quiescence().unwrap();
15384        // The first timer's microtask (2) runs before the second timer (3).
15385        assert_eq!(
15386            order_markers(&machine),
15387            vec![Value::int32(1), Value::int32(2), Value::int32(3)]
15388        );
15389        assert!(machine.microtasks.is_empty());
15390        assert!(!machine.has_pending_timers());
15391    }
15392
15393    #[test]
15394    fn automatic_loop_runs_a_timer_created_timer_in_a_later_turn() {
15395        let program = timer_program();
15396        let mut host = TimerTestHost::default();
15397        let shared = host.provider.state.clone();
15398        let mut machine = Machine::new(&program, &mut host, Limits::default());
15399        machine.frames.clear();
15400        machine.live_registers = 0;
15401        let nested = timer_fn(&mut machine, 2);
15402        machine
15403            .globals
15404            .insert(EcmaString::from_utf8("nestedCallback"), nested);
15405        let creator = timer_native(&mut machine, "schedule nested", schedule_nested_timer);
15406        let set_timeout = set_timeout_global(&machine);
15407        machine
15408            .call_value(set_timeout, Value::UNDEFINED, &[creator, Value::int32(1)])
15409            .unwrap();
15410        shared.borrow_mut().reports.push_back(TimerWakeup {
15411            id: 1,
15412            deadline_ms: 1,
15413        });
15414        shared.borrow_mut().reports.push_back(TimerWakeup {
15415            id: 2,
15416            deadline_ms: 1,
15417        });
15418
15419        machine.run_to_quiescence().unwrap();
15420        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
15421        assert!(!machine.has_pending_timers());
15422        assert!(machine.microtasks.is_empty());
15423    }
15424
15425    #[test]
15426    fn automatic_loop_ignores_stale_and_premature_reports_until_real_expiry() {
15427        let program = timer_program();
15428        let mut host = TimerTestHost::default();
15429        let shared = host.provider.state.clone();
15430        let mut machine = Machine::new(&program, &mut host, Limits::default());
15431        machine.frames.clear();
15432        machine.live_registers = 0;
15433        let a = timer_fn(&mut machine, 1);
15434        let set_timeout = set_timeout_global(&machine);
15435        machine
15436            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(50)])
15437            .unwrap();
15438        // A stale wakeup for an unknown id must not terminate the loop.
15439        shared.borrow_mut().reports.push_back(TimerWakeup {
15440            id: 999,
15441            deadline_ms: 10,
15442        });
15443        // A premature wakeup for the live timer cannot fire it before its deadline.
15444        shared.borrow_mut().reports.push_back(TimerWakeup {
15445            id: 1,
15446            deadline_ms: 10,
15447        });
15448        // The real expiry report finally fires the callback.
15449        shared.borrow_mut().reports.push_back(TimerWakeup {
15450            id: 1,
15451            deadline_ms: 50,
15452        });
15453
15454        machine.run_to_quiescence().unwrap();
15455        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
15456        assert!(!machine.has_pending_timers());
15457    }
15458
15459    #[test]
15460    fn automatic_loop_fails_with_typed_error_when_provider_loses_a_live_timer() {
15461        let program = timer_program();
15462        let mut host = TimerTestHost::default();
15463        let shared = host.provider.state.clone();
15464        let mut machine = Machine::new(&program, &mut host, Limits::default());
15465        machine.frames.clear();
15466        machine.live_registers = 0;
15467        let a = timer_fn(&mut machine, 1);
15468        let set_timeout = set_timeout_global(&machine);
15469        machine
15470            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
15471            .unwrap();
15472        // Simulate the provider losing the armed timer without reporting it.
15473        shared.borrow_mut().live.remove(&1);
15474
15475        let error = machine.run_to_quiescence().unwrap_err();
15476        assert!(matches!(
15477            error.kind,
15478            RuntimeErrorKind::TimerProviderFailure { .. }
15479        ));
15480        // The machine-owned live record and flags survive the failure.
15481        assert!(machine.has_pending_timers());
15482        assert!(!machine.microtask_drain_active);
15483        assert!(!machine.timer_checkpoint_active);
15484    }
15485
15486    #[test]
15487    fn automatic_loop_maps_the_first_uncaught_microtask_throw_and_stops() {
15488        let program = timer_program();
15489        let mut host = TimerTestHost::default();
15490        let mut machine = Machine::new(&program, &mut host, Limits::default());
15491        machine.frames.clear();
15492        machine.live_registers = 0;
15493        let throwing = timer_fn(&mut machine, 4); // throws 7
15494        let observer = timer_fn(&mut machine, 1); // stores a = 1
15495        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
15496        machine
15497            .call_value(queue, Value::UNDEFINED, &[throwing])
15498            .unwrap();
15499        machine
15500            .call_value(queue, Value::UNDEFINED, &[observer])
15501            .unwrap();
15502
15503        let error = machine.run_to_quiescence().unwrap_err();
15504        assert_eq!(
15505            error.kind,
15506            RuntimeErrorKind::UncaughtThrow {
15507                value: Value::int32(7),
15508                origin: ThrowOrigin::Bytecode,
15509            }
15510        );
15511        // The later job stays queued and unrun.
15512        assert_eq!(read_global(&machine, "a"), None);
15513        assert_eq!(machine.microtasks.len(), 1);
15514        assert!(!machine.microtask_drain_active);
15515    }
15516
15517    #[test]
15518    fn automatic_loop_timer_throw_suppresses_queued_microtasks_and_later_timers() {
15519        let program = timer_program();
15520        let mut host = TimerTestHost::default();
15521        let shared = host.provider.state.clone();
15522        let mut machine = Machine::new(&program, &mut host, Limits::default());
15523        machine.frames.clear();
15524        machine.live_registers = 0;
15525        let suppressed_microtask = timer_fn(&mut machine, 2); // stores b = 1
15526        machine
15527            .globals
15528            .insert(EcmaString::from_utf8("nestedCallback"), suppressed_microtask);
15529        let thrower = timer_native(&mut machine, "queue then throw", queue_job_then_throw);
15530        let later_timer = timer_fn(&mut machine, 1); // stores a = 1
15531        let set_timeout = set_timeout_global(&machine);
15532        machine
15533            .call_value(set_timeout, Value::UNDEFINED, &[thrower, Value::int32(1)])
15534            .unwrap();
15535        machine
15536            .call_value(set_timeout, Value::UNDEFINED, &[later_timer, Value::int32(2)])
15537            .unwrap();
15538        shared.borrow_mut().reports.push_back(TimerWakeup {
15539            id: 1,
15540            deadline_ms: 1,
15541        });
15542        shared.borrow_mut().reports.push_back(TimerWakeup {
15543            id: 2,
15544            deadline_ms: 2,
15545        });
15546
15547        let error = machine.run_to_quiescence().unwrap_err();
15548        assert_eq!(
15549            error.kind,
15550            RuntimeErrorKind::UncaughtThrow {
15551                value: Value::int32(7),
15552                origin: ThrowOrigin::Bytecode,
15553            }
15554        );
15555        // The exception converts before any drain, so the queued microtask and
15556        // the later timer both stay pending.
15557        assert_eq!(read_global(&machine, "b"), None);
15558        assert_eq!(read_global(&machine, "a"), None);
15559        assert_eq!(machine.microtasks.len(), 1);
15560        assert!(machine.has_pending_timers());
15561        assert!(!machine.microtask_drain_active);
15562        assert!(!machine.timer_checkpoint_active);
15563    }
15564
15565    #[test]
15566    fn automatic_loop_settles_derived_promises_when_a_handler_throws() {
15567        let program = promise_throw_program();
15568        let mut host = TimerTestHost::default();
15569        let mut machine = Machine::new(&program, &mut host, Limits::default());
15570        machine.frames.clear();
15571        machine.live_registers = 0;
15572        let executor = timer_fn(&mut machine, 1);
15573        let throwing = timer_fn(&mut machine, 2);
15574        let observer = timer_fn(&mut machine, 3);
15575        let constructor = machine.intrinsics.global("Promise").unwrap();
15576        let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
15577        let HeapEntry::NativeFunction {
15578            callable: NativeCallable::Builtin(constructor_id),
15579            ..
15580        } = machine.heap[constructor_index]
15581        else {
15582            panic!("Promise must be a native constructor");
15583        };
15584        let BuiltinOutcome::Value(promise) = machine
15585            .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
15586            .unwrap()
15587        else {
15588            panic!("Promise construction returns a Promise");
15589        };
15590        let resolve = machine
15591            .globals
15592            .get(&EcmaString::from_utf8("resolve"))
15593            .copied()
15594            .unwrap();
15595        machine
15596            .call_value(resolve, Value::UNDEFINED, &[Value::int32(1)])
15597            .unwrap();
15598        let then = machine.get_named_property(promise, "then").unwrap();
15599        let derived = machine.call_value(then, promise, &[throwing]).unwrap();
15600        let then_again = machine.get_named_property(derived, "then").unwrap();
15601        machine
15602            .call_value(then_again, derived, &[Value::UNDEFINED, observer])
15603            .unwrap();
15604
15605        machine.run_to_quiescence().unwrap();
15606        // The thrown handler rejects the derived promise, whose rejection
15607        // observer runs instead of aborting the loop.
15608        assert_eq!(
15609            machine
15610                .globals
15611                .get(&EcmaString::from_utf8("observed"))
15612                .copied(),
15613            Some(Value::int32(7))
15614        );
15615        assert!(machine.microtasks.is_empty());
15616    }
15617
15618    #[test]
15619    fn recursive_microtask_work_reaches_existing_fuel() {
15620        let program = timer_program();
15621        let mut host = TimerTestHost::default();
15622        let mut machine = Machine::new(&program, &mut host, Limits::default());
15623        machine.frames.clear();
15624        machine.live_registers = 0;
15625        let respawn = timer_native(&mut machine, "respawn", respawn_job);
15626        machine
15627            .globals
15628            .insert(EcmaString::from_utf8("nestedCallback"), respawn);
15629        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
15630        machine
15631            .call_value(queue, Value::UNDEFINED, &[respawn])
15632            .unwrap();
15633        machine.fuel = 16;
15634
15635        let error = machine.run_to_quiescence().unwrap_err();
15636        assert!(matches!(
15637            error.kind,
15638            RuntimeErrorKind::FuelExhausted { .. }
15639        ));
15640        // Recursive work never reaches quiescence; fuel was fully spent.
15641        assert_eq!(machine.fuel, 0);
15642        assert!(!machine.microtask_drain_active);
15643    }
15644
15645    #[test]
15646    fn manual_drain_still_collects_every_throw_and_continues() {
15647        let program = timer_program();
15648        let mut host = TimerTestHost::default();
15649        let mut machine = Machine::new(&program, &mut host, Limits::default());
15650        machine.frames.clear();
15651        machine.live_registers = 0;
15652        let throwing = timer_fn(&mut machine, 4); // throws 7
15653        let observer = timer_fn(&mut machine, 1); // stores a = 1
15654        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
15655        machine
15656            .call_value(queue, Value::UNDEFINED, &[throwing])
15657            .unwrap();
15658        machine
15659            .call_value(queue, Value::UNDEFINED, &[observer])
15660            .unwrap();
15661        machine
15662            .call_value(queue, Value::UNDEFINED, &[throwing])
15663            .unwrap();
15664
15665        let drain = machine.drain_microtasks().unwrap();
15666        assert_eq!(drain.executed, 3);
15667        assert_eq!(
15668            drain.uncaught,
15669            vec![
15670                CallbackException {
15671                    value: Value::int32(7),
15672                    origin: ThrowOrigin::Bytecode,
15673                },
15674                CallbackException {
15675                    value: Value::int32(7),
15676                    origin: ThrowOrigin::Bytecode,
15677                },
15678            ]
15679        );
15680        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
15681        assert!(machine.microtasks.is_empty());
15682    }
15683}