Skip to main content

bamts_runtime/
lib.rs

1//! Deterministic register interpreter for verified production BamTS bytecode.
2//!
3//! Persisted constants never carry runtime identity. This interpreter therefore
4//! owns a slot heap for strings, bigints, objects, arrays, closures, private
5//! names, regular expressions, and iterators, and exposes only
6//! `bamts_native::Value` words at host boundaries. Bytecode heap slots use
7//! segment 1; other segments remain host-owned.
8//!
9//! The 36-op dynamic-computation ISA is executed locally: register-keyed
10//! property access (with data properties, accessor descriptors, and private-name
11//! identity), prototype chains, closures with explicit capture environments,
12//! arguments arrays, `this`/`arguments`/`new.target`, globals, arrays with
13//! spread, object spread, iterators (sync/async/keys) with the two-write
14//! `IteratorNext`, and generator/async `Suspend`-resume. The [`Host`] trait owns
15//! only *external* module and builtin operations (foreign calls/constructs,
16//! foreign property access, `import`, `export`, and the suspension driver);
17//! internal objects, arrays, functions, closures, prototypes, and private names
18//! never leave the interpreter.
19
20#![forbid(unsafe_code)]
21
22use std::borrow::Cow;
23use std::cmp::Ordering;
24use std::collections::BTreeMap;
25use std::collections::BTreeSet;
26use std::collections::VecDeque;
27use std::error::Error;
28use std::fmt;
29use std::sync::Arc;
30
31use bamts_bytecode::{
32    AccessorKind, BinaryOp, BindingId, BindingKind, Constant, ConstantId, EcmaString,
33    EcmaStringBuilder, EdgeId, EdgeTarget, Function, FunctionId, Instruction, IteratorKind, Module,
34    ModuleId, Pc, Program, ProgramModule, ResolvedExport, UnaryOp, Verified,
35};
36use bamts_native::{Decoded, SlotId, Value};
37
38mod external_modules;
39mod host_objects;
40mod intrinsics;
41mod native;
42mod vm;
43
44pub use native::{NativeEngine, NativeError, run_linked_program};
45
46const RUNTIME_HEAP_SEGMENT: u16 = 1;
47
48/// The observable result of a terminated execution.
49#[derive(Clone, Debug, Eq, PartialEq)]
50pub struct ExecutionOutcome {
51    /// Reserved for output produced by host builtins.
52    pub stdout: Vec<u8>,
53    /// `0` for normal bytecode termination.
54    pub exit_code: i32,
55}
56
57/// A terminated execution and the entry activation's observable state.
58#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct Execution {
60    pub outcome: ExecutionOutcome,
61    /// The explicit entry return value, or `undefined` for `Halt`.
62    pub value: Value,
63    /// Backward-compatible name for the final returned value.
64    pub link: Value,
65    pub entry_registers: Vec<Value>,
66}
67
68/// Deterministic execution and allocation ceilings.
69#[derive(Clone, Debug, Eq, PartialEq)]
70pub struct Limits {
71    pub fuel: u64,
72    pub max_call_depth: usize,
73    pub max_total_registers: usize,
74    /// Ceiling on the length of a single call's arguments array.
75    pub max_argument_count: u32,
76    pub max_heap_slots: usize,
77    pub max_heap_bytes: usize,
78    /// Ceiling on engine-owned module binding cells.
79    pub max_module_cells: usize,
80    /// Ceiling on host-compiled scripts retained by one machine.
81    pub max_dynamic_modules: usize,
82    /// Ceiling on queued microtasks.
83    pub max_microtasks: usize,
84    /// Ceiling on live timers armed at once.
85    pub max_timers: usize,
86}
87
88impl Default for Limits {
89    fn default() -> Self {
90        Self {
91            fuel: 1_000_000,
92            max_call_depth: 64,
93            max_total_registers: 1 << 20,
94            max_argument_count: 1 << 16,
95            max_heap_slots: 1 << 20,
96            max_heap_bytes: 64 << 20,
97            max_module_cells: 1 << 20,
98            max_dynamic_modules: 1 << 10,
99            max_microtasks: 1 << 20,
100            max_timers: 1 << 20,
101        }
102    }
103}
104
105/// The exact source of one classic script. Source and resource name preserve
106/// UTF-16 code units verbatim, including unpaired surrogates.
107pub struct ScriptSource<'a> {
108    pub source: &'a [u16],
109    pub name: &'a [u16],
110}
111
112/// Why a host compiler refused a classic script.
113#[derive(Clone, Debug, Eq, PartialEq)]
114pub enum ScriptCompileError {
115    IllFormedSource {
116        unit_offset: usize,
117    },
118    Syntax {
119        message: String,
120        line: u32,
121        column: u32,
122    },
123    Unsupported {
124        message: String,
125        line: u32,
126        column: u32,
127    },
128    Capacity {
129        message: String,
130    },
131}
132
133/// A host capability for compiling a classic script without observing machine state.
134pub trait CompileProvider {
135    fn compile_script(
136        &mut self,
137        source: ScriptSource<'_>,
138    ) -> Result<Arc<Program<Verified>>, ScriptCompileError>;
139}
140
141/// External capabilities available to the JavaScript runtime.
142///
143/// Runtime values never cross this boundary. The engine owns all JavaScript
144/// objects and value semantics; hosts provide byte sinks and process services.
145pub trait Host {
146    fn write_stdout(&mut self, _bytes: &[u8]) {}
147
148    fn write_stderr(&mut self, _bytes: &[u8]) {}
149
150    fn exit_code(&self) -> i32 {
151        0
152    }
153
154    fn set_exit_code(&mut self, _exit_code: i32) {}
155
156    fn argv(&self) -> &[String] {
157        &[]
158    }
159
160    fn env(&self, _name: &str) -> Option<&str> {
161        None
162    }
163
164    fn set_env(&mut self, _name: &str, _value: &str) {}
165
166    fn delete_env(&mut self, _name: &str) -> bool {
167        false
168    }
169
170    fn now_ms(&mut self) -> u64 {
171        0
172    }
173
174    fn monotonic_ns(&mut self) -> u64 {
175        0
176    }
177
178    fn random(&mut self) -> f64 {
179        0.0
180    }
181
182    fn hash(&mut self, _algorithm: &str, _data: &[u8]) -> Option<Vec<u8>> {
183        None
184    }
185
186    /// This host's script compiler, or `None` when it provides none.
187    ///
188    /// Presence MUST remain stable for the lifetime of one machine because it
189    /// determines whether `node:vm` is installed during construction.
190    fn script_compiler(&mut self) -> Option<&mut (dyn CompileProvider + 'static)> {
191        None
192    }
193
194    /// This host's timer scheduler, or `None` when it provides none.
195    ///
196    /// Presence MUST remain stable for the lifetime of one machine because it
197    /// determines whether `setTimeout`/`clearTimeout` are installed during
198    /// construction.
199    fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
200        None
201    }
202}
203
204/// A single expired timer reported by a [`TimerProvider`].
205///
206/// `id` echoes the machine-owned JavaScript identifier passed to
207/// [`TimerProvider::schedule`]; `deadline_ms` is the provider's monotonic
208/// absolute-millisecond deadline used as the promotion watermark.
209#[derive(Clone, Copy, Debug, Eq, PartialEq)]
210pub struct TimerWakeup {
211    pub id: u64,
212    pub deadline_ms: u64,
213}
214
215/// An owned failure message produced by a [`TimerProvider`].
216#[derive(Clone, Debug, Eq, PartialEq)]
217pub struct TimerError {
218    message: String,
219}
220
221impl TimerError {
222    #[must_use]
223    pub fn new(message: impl Into<String>) -> Self {
224        Self {
225            message: message.into(),
226        }
227    }
228
229    #[must_use]
230    pub fn message(&self) -> &str {
231        &self.message
232    }
233}
234
235impl fmt::Display for TimerError {
236    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
237        formatter.write_str(&self.message)
238    }
239}
240
241impl Error for TimerError {}
242
243/// A host capability that schedules real time without observing machine state.
244///
245/// The provider owns only opaque `u64` identifiers and monotonic millisecond
246/// deadlines; it never stores a JavaScript `Value`. The machine owns visible
247/// ordering and every callback identity.
248pub trait TimerProvider {
249    /// Arms a timer for `id` after at least `delay_ms` milliseconds, returning
250    /// the provider-monotonic absolute-millisecond deadline.
251    fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError>;
252
253    /// Cancels the armed timer for `id`, returning whether one was removed.
254    fn cancel(&mut self, id: u64) -> Result<bool, TimerError>;
255
256    /// Drains every currently expired timer into `output` without blocking.
257    fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError>;
258
259    /// Blocks until the next timer expires, or returns `None` when none pend.
260    fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError>;
261
262    /// Reports whether the provider has any armed timer.
263    fn has_pending(&self) -> bool;
264}
265
266#[derive(Clone, Copy, Debug, Eq, PartialEq)]
267pub enum ThrowOrigin {
268    Bytecode,
269    TypeError { operation: &'static str },
270    RangeError { operation: &'static str },
271    ReferenceError { operation: &'static str },
272    UriError { operation: &'static str },
273}
274
275/// Source metadata attached to every machine failure.
276#[derive(Clone, Debug, Eq, PartialEq)]
277pub struct RuntimeSource {
278    pub function_name: Option<EcmaString>,
279    pub instruction: Instruction,
280}
281
282#[derive(Clone, Debug, Eq, PartialEq)]
283pub struct RuntimeError {
284    pub kind: RuntimeErrorKind,
285    pub function: FunctionId,
286    pub pc: Pc,
287    pub source: RuntimeSource,
288}
289
290#[derive(Clone, Debug, Eq, PartialEq)]
291pub enum RuntimeErrorKind {
292    UncaughtThrow {
293        value: Value,
294        origin: ThrowOrigin,
295    },
296    FuelExhausted {
297        limit: u64,
298    },
299    CallDepthExceeded {
300        limit: usize,
301    },
302    RegisterLimitExceeded {
303        limit: usize,
304    },
305    ArgumentLimitExceeded {
306        limit: u32,
307        requested: u32,
308    },
309    HeapSlotLimitExceeded {
310        limit: usize,
311    },
312    HeapByteLimitExceeded {
313        limit: usize,
314    },
315    ModuleCellLimitExceeded {
316        limit: usize,
317    },
318    DynamicModuleLimitExceeded {
319        limit: usize,
320    },
321    MicrotaskQueueLimitExceeded {
322        limit: usize,
323    },
324    MicrotaskDrainReentry,
325    TimerProviderFailure {
326        message: String,
327    },
328    TimerCapacityExceeded {
329        limit: usize,
330    },
331    TimerCheckpointReentry,
332    InvalidDynamicScript {
333        reason: &'static str,
334    },
335    TemporalDeadZone {
336        module: ModuleId,
337        binding: BindingId,
338    },
339    ExternalModuleUnavailable {
340        module: ModuleId,
341        edge: EdgeId,
342    },
343    DynamicImportEdgeMissing {
344        module: ModuleId,
345        specifier: ConstantId,
346    },
347    InvalidVerifiedProgram {
348        module: ModuleId,
349        instruction: Instruction,
350    },
351    InvalidValue {
352        value: Value,
353    },
354    InvalidRuntimeHeapReference {
355        slot: u32,
356    },
357}
358
359impl fmt::Display for RuntimeError {
360    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
361        write!(
362            formatter,
363            "runtime error in function {} at pc {}",
364            self.function.get(),
365            self.pc.get()
366        )?;
367        if let Some(name) = &self.source.function_name {
368            write!(formatter, " ({})", name.to_utf8_lossy())?;
369        }
370        write!(formatter, ": ")?;
371        match &self.kind {
372            RuntimeErrorKind::UncaughtThrow { value, origin } => write!(
373                formatter,
374                "uncaught {origin:?} throw (value {:#018x})",
375                value.to_bits()
376            ),
377            RuntimeErrorKind::FuelExhausted { limit } => {
378                write!(formatter, "fuel exhausted after {limit} instructions")
379            }
380            RuntimeErrorKind::CallDepthExceeded { limit } => {
381                write!(formatter, "call depth limit {limit} exceeded")
382            }
383            RuntimeErrorKind::RegisterLimitExceeded { limit } => {
384                write!(formatter, "live register limit {limit} exceeded")
385            }
386            RuntimeErrorKind::ArgumentLimitExceeded { limit, requested } => write!(
387                formatter,
388                "argument count {requested} exceeds runtime limit {limit}"
389            ),
390            RuntimeErrorKind::HeapSlotLimitExceeded { limit } => {
391                write!(formatter, "heap slot limit {limit} exceeded")
392            }
393            RuntimeErrorKind::HeapByteLimitExceeded { limit } => {
394                write!(formatter, "heap byte limit {limit} exceeded")
395            }
396            RuntimeErrorKind::ModuleCellLimitExceeded { limit } => {
397                write!(formatter, "module cell limit {limit} exceeded")
398            }
399            RuntimeErrorKind::DynamicModuleLimitExceeded { limit } => {
400                write!(formatter, "dynamic module limit {limit} exceeded")
401            }
402            RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit } => {
403                write!(formatter, "microtask queue limit {limit} exceeded")
404            }
405            RuntimeErrorKind::MicrotaskDrainReentry => {
406                write!(formatter, "microtask drain is already active")
407            }
408            RuntimeErrorKind::TimerProviderFailure { message } => {
409                write!(formatter, "timer provider failure: {message}")
410            }
411            RuntimeErrorKind::TimerCapacityExceeded { limit } => {
412                write!(formatter, "timer capacity {limit} exceeded")
413            }
414            RuntimeErrorKind::TimerCheckpointReentry => {
415                write!(formatter, "timer checkpoint is already active")
416            }
417            RuntimeErrorKind::InvalidDynamicScript { reason } => {
418                write!(formatter, "invalid dynamic script: {reason}")
419            }
420            RuntimeErrorKind::TemporalDeadZone { module, binding } => write!(
421                formatter,
422                "module {} binding {} read before initialization",
423                module.get(),
424                binding.get()
425            ),
426            RuntimeErrorKind::ExternalModuleUnavailable { module, edge } => write!(
427                formatter,
428                "external module at module {} edge {} is unavailable",
429                module.get(),
430                edge.get()
431            ),
432            RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier } => write!(
433                formatter,
434                "verified module {} has no dynamic edge for constant {}",
435                module.get(),
436                specifier.get()
437            ),
438            RuntimeErrorKind::InvalidVerifiedProgram {
439                module,
440                instruction,
441            } => write!(
442                formatter,
443                "verified module {} contains impossible instruction {instruction:?}",
444                module.get()
445            ),
446            RuntimeErrorKind::InvalidValue { value } => {
447                write!(
448                    formatter,
449                    "malformed or foreign value {:#018x}",
450                    value.to_bits()
451                )
452            }
453            RuntimeErrorKind::InvalidRuntimeHeapReference { slot } => {
454                write!(formatter, "runtime heap slot {slot} does not exist")
455            }
456        }
457    }
458}
459
460impl Error for RuntimeError {}
461
462/// Converts constants that need no runtime identity to ABI values. Strings and
463/// bigints are materialized by `LoadConst` in the machine's slot heap.
464#[must_use]
465pub fn constant_value(constant: &Constant) -> Option<Value> {
466    match constant {
467        Constant::Number(bits) => Some(Value::number(bits.to_f64())),
468        Constant::Int32(value) => Some(Value::int32(*value as u32)),
469        Constant::String(_) | Constant::BigInt(_) => None,
470        Constant::Boolean(value) => Some(Value::boolean(*value)),
471        Constant::Null => Some(Value::NULL),
472        Constant::Undefined => Some(Value::UNDEFINED),
473    }
474}
475
476/// A normalized property key: a string name (which may parse as an array index),
477/// a public symbol, or a language private name, each identified by its heap slot.
478/// Two identity-bearing allocations with the same description remain distinct keys.
479#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
480enum PropertyKey {
481    Named(EcmaString),
482    Symbol(u32),
483    Private(u32),
484}
485
486impl PropertyKey {
487    fn as_string(&self) -> Option<&EcmaString> {
488        match self {
489            PropertyKey::Named(text) => Some(text),
490            PropertyKey::Symbol(_) | PropertyKey::Private(_) => None,
491        }
492    }
493
494    fn eq_ascii(&self, ascii: &str) -> bool {
495        matches!(self, PropertyKey::Named(text) if text.eq_ascii(ascii))
496    }
497
498    fn charge_bytes(&self) -> usize {
499        match self {
500            PropertyKey::Named(text) => text.len_units().saturating_mul(2).saturating_add(8),
501            PropertyKey::Symbol(_) | PropertyKey::Private(_) => 16,
502        }
503    }
504}
505
506/// A stored property: a data value or an accessor descriptor.
507#[derive(Clone, Debug)]
508enum Property {
509    Data {
510        value: Value,
511        writable: bool,
512        enumerable: bool,
513        configurable: bool,
514    },
515    Accessor {
516        getter: Option<Value>,
517        setter: Option<Value>,
518        enumerable: bool,
519        configurable: bool,
520    },
521}
522impl Property {
523    fn enumerable(&self) -> bool {
524        match self {
525            Self::Data { enumerable, .. } | Self::Accessor { enumerable, .. } => *enumerable,
526        }
527    }
528
529    fn configurable(&self) -> bool {
530        match self {
531            Self::Data { configurable, .. } | Self::Accessor { configurable, .. } => *configurable,
532        }
533    }
534}
535
536/// Own properties in creation order. ECMAScript enumerates array-index keys
537/// separately, but preserves this order for all other string and symbol keys.
538#[derive(Clone, Debug, Default)]
539struct PropertyMap(Vec<(PropertyKey, Property)>);
540
541impl PropertyMap {
542    fn get(&self, key: &PropertyKey) -> Option<&Property> {
543        self.0
544            .iter()
545            .find_map(|(candidate, property)| (candidate == key).then_some(property))
546    }
547
548    fn get_mut(&mut self, key: &PropertyKey) -> Option<&mut Property> {
549        self.0
550            .iter_mut()
551            .find_map(|(candidate, property)| (candidate == key).then_some(property))
552    }
553
554    fn contains_key(&self, key: &PropertyKey) -> bool {
555        self.get(key).is_some()
556    }
557
558    fn get_ascii(&self, ascii: &str) -> Option<&Property> {
559        debug_assert!(ascii.is_ascii());
560        self.0
561            .iter()
562            .find_map(|(key, property)| key.eq_ascii(ascii).then_some(property))
563    }
564
565    fn insert(&mut self, key: PropertyKey, property: Property) -> Option<Property> {
566        if let Some(existing) = self.get_mut(&key) {
567            return Some(std::mem::replace(existing, property));
568        }
569        self.0.push((key, property));
570        None
571    }
572
573    fn remove(&mut self, key: &PropertyKey) -> Option<Property> {
574        let index = self.0.iter().position(|(candidate, _)| candidate == key)?;
575        Some(self.0.remove(index).1)
576    }
577
578    fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Property)> {
579        self.0.iter().map(|(key, property)| (key, property))
580    }
581    fn charge_bytes(&self) -> usize {
582        self.0.iter().fold(0, |bytes, (key, _)| {
583            bytes.saturating_add(key.charge_bytes())
584        })
585    }
586}
587
588impl<'a> IntoIterator for &'a PropertyMap {
589    type Item = (&'a PropertyKey, &'a Property);
590    type IntoIter = std::iter::Map<
591        std::slice::Iter<'a, (PropertyKey, Property)>,
592        fn(&(PropertyKey, Property)) -> (&PropertyKey, &Property),
593    >;
594
595    fn into_iter(self) -> Self::IntoIter {
596        fn pair_refs(pair: &(PropertyKey, Property)) -> (&PropertyKey, &Property) {
597            (&pair.0, &pair.1)
598        }
599        self.0.iter().map(pair_refs)
600    }
601}
602
603#[derive(Clone, Copy, Debug, Eq, PartialEq)]
604enum IterationKind {
605    Key,
606    Value,
607    Entry,
608}
609
610#[derive(Clone, Copy, Debug)]
611struct CollectionEntry {
612    order: u64,
613    key: Value,
614    value: Value,
615}
616
617impl CollectionEntry {
618    const BYTES: usize = std::mem::size_of::<Self>();
619}
620
621#[derive(Clone, Copy, Debug)]
622pub(crate) enum IteratorNextPrepared {
623    Ready { done: bool, value: Value },
624    Call { callee: Value, this_value: Value },
625}
626
627#[derive(Clone, Debug)]
628enum IteratorState {
629    Keys { index: usize, keys: Vec<EcmaString> },
630    Protocol { iterator: Value, next: Value },
631}
632
633#[derive(Clone, Debug)]
634pub(crate) struct GeneratorStart {
635    pub(crate) target: RuntimeFunction,
636    pub(crate) captures: Vec<Value>,
637    pub(crate) this_value: Value,
638    pub(crate) new_target: Value,
639    pub(crate) args: Vec<Value>,
640}
641
642#[derive(Clone, Debug)]
643pub(crate) struct SuspendedActivation {
644    pub(crate) target: RuntimeFunction,
645    pub(crate) registers: Vec<Value>,
646    pub(crate) this_value: Value,
647    pub(crate) new_target: Value,
648    pub(crate) args: Vec<Value>,
649    pub(crate) arguments_object: Option<Value>,
650    pub(crate) resume_token: u32,
651}
652
653#[derive(Clone, Debug)]
654pub(crate) enum GeneratorState {
655    SuspendedStart(GeneratorStart),
656    Executing,
657    Suspended(SuspendedActivation),
658    Completed,
659}
660
661#[derive(Clone, Debug)]
662pub(crate) enum GeneratorResume {
663    Yield {
664        value: Value,
665        activation: SuspendedActivation,
666    },
667    Return(Value),
668    Throw {
669        value: Value,
670        origin: ThrowOrigin,
671    },
672}
673
674/// One step of driving a detached async-function activation: it awaited a
675/// value (and must suspend), it returned, or it threw an uncaught value.
676#[derive(Clone, Debug)]
677enum AsyncStep {
678    Suspend {
679        awaited: Value,
680        activation: SuspendedActivation,
681    },
682    Return(Value),
683    Throw {
684        value: Value,
685        origin: ThrowOrigin,
686    },
687}
688
689#[derive(Clone, Debug)]
690pub(crate) enum PromiseState {
691    Pending {
692        fulfill_reactions: Vec<PromiseReaction>,
693        reject_reactions: Vec<PromiseReaction>,
694    },
695    Fulfilled {
696        value: Value,
697    },
698    Rejected {
699        reason: Value,
700        origin: ThrowOrigin,
701    },
702}
703
704#[derive(Clone, Copy, Debug)]
705pub(crate) enum PromiseCompletion {
706    Fulfilled,
707    Rejected,
708}
709
710#[derive(Clone, Debug)]
711pub(crate) enum PromiseReaction {
712    Fulfilled {
713        handler: Value,
714        derived: Value,
715    },
716    Rejected {
717        handler: Value,
718        derived: Value,
719    },
720    Finally {
721        handler: Value,
722        derived: Value,
723        completion: PromiseCompletion,
724    },
725    /// Resumes a suspended async activation on fulfillment. Carries only the
726    /// heap `AsyncActivation` record handle; the awaited value arrives as the
727    /// reaction value.
728    AsyncFulfill {
729        activation: Value,
730    },
731    /// Resumes a suspended async activation on rejection. Carries only the
732    /// heap `AsyncActivation` record handle; the rejection reason arrives as
733    /// the reaction value.
734    AsyncReject {
735        activation: Value,
736    },
737}
738
739#[derive(Clone, Debug)]
740pub(crate) enum MicrotaskJob {
741    Reaction {
742        reaction: PromiseReaction,
743        value: Value,
744        origin: ThrowOrigin,
745    },
746    Thenable {
747        promise: Value,
748        thenable: Value,
749        then: Value,
750    },
751    Callback {
752        callback: Value,
753    },
754}
755
756/// An exception reported by a microtask or timer callback.
757#[derive(Clone, Debug, Eq, PartialEq)]
758pub struct CallbackException {
759    /// The value thrown by the callback.
760    pub value: Value,
761    /// The operation that produced the throw.
762    pub origin: ThrowOrigin,
763}
764
765/// The result of one explicit microtask checkpoint.
766#[derive(Clone, Debug, Default, Eq, PartialEq)]
767pub struct MicrotaskDrain {
768    /// The number of jobs removed from the queue and run.
769    pub executed: usize,
770    /// Callback exceptions, in FIFO execution order.
771    pub uncaught: Vec<CallbackException>,
772}
773
774/// The result of one explicit timer checkpoint. At most one callback runs.
775#[derive(Clone, Debug, Default, Eq, PartialEq)]
776pub struct TimerRun {
777    /// The number of timer callbacks run: `0` or `1`.
778    pub executed: usize,
779    /// Callback exceptions from the run callback, if it threw.
780    pub uncaught: Vec<CallbackException>,
781}
782
783#[derive(Clone, Debug)]
784enum HeapEntry {
785    String(EcmaString),
786    BigInt(String),
787    Object {
788        properties: PropertyMap,
789        prototype: Option<Value>,
790        boxed_primitive: Option<Value>,
791        extensible: bool,
792    },
793    Array {
794        elements: Vec<Value>,
795        properties: PropertyMap,
796        prototype: Option<Value>,
797        extensible: bool,
798        length_writable: bool,
799    },
800    Function {
801        module: ModuleId,
802        function: FunctionId,
803        captures: Vec<Value>,
804        properties: PropertyMap,
805        prototype: Option<Value>,
806        extensible: bool,
807    },
808    /// A `vm.Script` whose entry function retains its machine-owned dynamic module.
809    Script {
810        entry: Value,
811        properties: PropertyMap,
812        prototype: Option<Value>,
813        extensible: bool,
814    },
815    ModuleNamespace {
816        module: ModuleId,
817    },
818    ExternalModuleNamespace {
819        specifier: EcmaString,
820    },
821    HashState {
822        algorithm: String,
823        data: Vec<u8>,
824        digested: bool,
825        update: Value,
826        digest: Value,
827    },
828    Symbol {
829        description: EcmaString,
830    },
831    PrivateName {
832        description: EcmaString,
833    },
834    RegExp {
835        pattern: EcmaString,
836        flags: EcmaString,
837        properties: PropertyMap,
838        prototype: Option<Value>,
839        extensible: bool,
840    },
841    Date {
842        time: f64,
843        properties: PropertyMap,
844        prototype: Option<Value>,
845        extensible: bool,
846    },
847    /// Entries stay in insertion order. Iterators track order IDs, so deletion
848    /// can remove storage without moving an iterator's logical cursor.
849    /// Weak collections share this strong storage until the runtime has a collector.
850    Collection {
851        entries: Vec<CollectionEntry>,
852        next_order: u64,
853        properties: PropertyMap,
854        prototype: Option<Value>,
855        extensible: bool,
856    },
857    BuiltinIterator {
858        source: Value,
859        kind: IterationKind,
860        position: Option<u64>,
861        properties: PropertyMap,
862        prototype: Option<Value>,
863        extensible: bool,
864    },
865    Iterator {
866        state: IteratorState,
867    },
868    Generator {
869        state: GeneratorState,
870        properties: PropertyMap,
871        prototype: Option<Value>,
872        extensible: bool,
873    },
874    ProcessEnv {
875        prototype: Option<Value>,
876        extensible: bool,
877    },
878    Promise {
879        state: PromiseState,
880        properties: PropertyMap,
881        prototype: Option<Value>,
882        extensible: bool,
883    },
884    /// A Node-compatible `Timeout` handle carrying its monotonic timer id and
885    /// ordinary object property/prototype fields.
886    Timeout {
887        id: u64,
888        properties: PropertyMap,
889        prototype: Option<Value>,
890        extensible: bool,
891    },
892    PromiseResolver {
893        promise: Value,
894        used: bool,
895    },
896    PromiseFinally {
897        derived: Value,
898        value: Value,
899        origin: ThrowOrigin,
900        completion: PromiseCompletion,
901    },
902    PromiseAll {
903        promise: Value,
904        values: Vec<Value>,
905        remaining: usize,
906        settled: bool,
907    },
908    PromiseAllElement {
909        aggregate: Value,
910        index: usize,
911        called: bool,
912    },
913    /// One suspended async-function activation together with the implicit
914    /// result Promise it settles. `activation` is taken on resume so a second
915    /// resume is a hard invalid-state error. This record is internal and never
916    /// escapes to user code.
917    AsyncActivation {
918        activation: Option<SuspendedActivation>,
919        promise: Value,
920    },
921    NativeFunction {
922        callable: NativeCallable,
923        properties: PropertyMap,
924        extensible: bool,
925    },
926}
927
928#[derive(Clone, Debug)]
929pub(crate) enum NativeCallable {
930    Builtin(intrinsics::BuiltinId),
931    Bound(Box<BoundCallable>),
932}
933
934#[derive(Clone, Debug)]
935pub(crate) struct BoundCallable {
936    pub(crate) target: Value,
937    pub(crate) this_value: Value,
938    pub(crate) arguments: Vec<Value>,
939}
940
941impl HeapEntry {
942    fn initial_bytes(&self) -> usize {
943        match self {
944            Self::String(text)
945            | Self::Symbol { description: text }
946            | Self::PrivateName { description: text } => text.len_units().saturating_mul(2),
947            Self::BigInt(text) => text.len(),
948            Self::RegExp { pattern, flags, .. } => pattern
949                .len_units()
950                .saturating_add(flags.len_units())
951                .saturating_mul(2),
952            Self::HashState {
953                algorithm, data, ..
954            } => algorithm.len() + data.len(),
955            Self::Collection { entries, .. } => entries
956                .len()
957                .saturating_mul(CollectionEntry::BYTES)
958                .saturating_add(1),
959            Self::NativeFunction { callable, .. } => match callable {
960                NativeCallable::Builtin(_) => 1,
961                NativeCallable::Bound(bound) => bound.arguments.len().saturating_add(1),
962            },
963            Self::Generator { state, .. } => match state {
964                GeneratorState::SuspendedStart(start) => start
965                    .captures
966                    .len()
967                    .saturating_add(start.args.len())
968                    .saturating_add(1),
969                GeneratorState::Suspended(activation) => activation
970                    .registers
971                    .len()
972                    .saturating_add(activation.args.len())
973                    .saturating_add(1),
974                GeneratorState::Executing | GeneratorState::Completed => 1,
975            },
976            Self::Object { properties, .. } | Self::Timeout { properties, .. } => {
977                properties.charge_bytes().saturating_add(1)
978            }
979            Self::Array { .. }
980            | Self::Function { .. }
981            | Self::Script { .. }
982            | Self::ModuleNamespace { .. }
983            | Self::ExternalModuleNamespace { .. }
984            | Self::ProcessEnv { .. }
985            | Self::Date { .. }
986            | Self::BuiltinIterator { .. }
987            | Self::Iterator { .. }
988            | Self::Promise { .. }
989            | Self::PromiseResolver { .. }
990            | Self::PromiseFinally { .. }
991            | Self::PromiseAll { .. }
992            | Self::AsyncActivation { .. }
993            | Self::PromiseAllElement { .. } => 1,
994        }
995    }
996}
997
998#[derive(Clone, Copy, Debug)]
999struct ReturnTo {
1000    /// The caller register receiving the result, or `None` to discard it (an
1001    /// accessor setter invocation produces no observable value).
1002    destination: Option<usize>,
1003    call_pc: usize,
1004    constructed: Option<Value>,
1005}
1006
1007struct CallRequest<'a> {
1008    callee: Value,
1009    this_value: Value,
1010    arguments: &'a [Value],
1011    destination: Option<u32>,
1012    call_pc: usize,
1013    constructed: Option<Value>,
1014    new_target: Value,
1015}
1016
1017pub(crate) struct BoundCall {
1018    pub(crate) target: Value,
1019    pub(crate) this_value: Value,
1020    pub(crate) arguments: Vec<Value>,
1021}
1022
1023#[derive(Clone, Copy, Debug)]
1024pub(crate) struct RuntimeFunction {
1025    pub(crate) module: ModuleId,
1026    pub(crate) function: FunctionId,
1027}
1028
1029#[derive(Clone, Debug)]
1030struct Frame {
1031    module: ModuleId,
1032    function: usize,
1033    pc: usize,
1034    registers: Vec<Value>,
1035    return_to: Option<ReturnTo>,
1036    this_value: Value,
1037    new_target: Value,
1038    args: Vec<Value>,
1039    arguments_object: Option<Value>,
1040}
1041
1042impl Frame {
1043    fn new(
1044        target: RuntimeFunction,
1045        metadata: &Function,
1046        captures: &[Value],
1047        this_value: Value,
1048        new_target: Value,
1049        arguments: &[Value],
1050        return_to: Option<ReturnTo>,
1051    ) -> Self {
1052        let mut registers = vec![Value::UNINITIALIZED; metadata.register_count() as usize];
1053        let capture_count = metadata.capture_count() as usize;
1054        for (index, slot) in registers.iter_mut().take(capture_count).enumerate() {
1055            *slot = captures.get(index).copied().unwrap_or(Value::UNDEFINED);
1056        }
1057        for (index, slot) in registers
1058            .iter_mut()
1059            .skip(capture_count)
1060            .take(metadata.parameter_count() as usize)
1061            .enumerate()
1062        {
1063            *slot = arguments.get(index).copied().unwrap_or(Value::UNDEFINED);
1064        }
1065        Self {
1066            module: target.module,
1067            function: target.function.get() as usize,
1068            pc: 0,
1069            registers,
1070            return_to,
1071            this_value,
1072            new_target,
1073            args: arguments.to_vec(),
1074            arguments_object: None,
1075        }
1076    }
1077}
1078
1079#[derive(Clone, Debug)]
1080pub(crate) enum EvalFailure {
1081    Throw(ThrowOrigin),
1082    ThrowValue(Value),
1083    Runtime(RuntimeErrorKind),
1084    ThrowValueOrigin { value: Value, origin: ThrowOrigin },
1085}
1086
1087pub(crate) fn import_failure(error: &RuntimeError) -> EvalFailure {
1088    match &error.kind {
1089        RuntimeErrorKind::UncaughtThrow { value, .. } => EvalFailure::ThrowValue(*value),
1090        kind => EvalFailure::Runtime(kind.clone()),
1091    }
1092}
1093
1094/// The outcome of resolving a property read.
1095#[derive(Clone, Debug)]
1096enum GetOutcome {
1097    /// A ready ABI value.
1098    Value(Value),
1099    /// Text that must be interned as a fresh heap string.
1100    Text(EcmaString),
1101    /// An accessor getter to invoke with the receiver as `this`.
1102    Getter(Value),
1103}
1104
1105/// The outcome of resolving a property write.
1106#[derive(Clone, Debug)]
1107enum SetOutcome {
1108    Done,
1109    /// An accessor setter to invoke with the receiver as `this` and the value as
1110    /// its sole argument.
1111    Setter(Value),
1112}
1113
1114/// Own-property lookup result during a prototype-chain walk.
1115#[derive(Clone, Debug)]
1116enum Found {
1117    Value(Value),
1118    Text(EcmaString),
1119    Getter(Value),
1120    Failure(RuntimeErrorKind),
1121    /// An accessor property with no getter resolves to `undefined`.
1122    NoGetter,
1123}
1124
1125/// A classified callee, shared by the interpreter and the native engine so both
1126/// route calls through one decision. The native engine reimplements the frame
1127/// push but consumes this classifier verbatim.
1128#[derive(Clone, Debug)]
1129enum CalleeKind {
1130    Runtime {
1131        target: RuntimeFunction,
1132        captures: Vec<Value>,
1133    },
1134    Builtin {
1135        id: intrinsics::BuiltinId,
1136    },
1137    Bound,
1138    NotCallable,
1139}
1140
1141#[derive(Clone, Debug)]
1142struct TimerRecord {
1143    callback: Value,
1144    arguments: Vec<Value>,
1145    handle: Value,
1146    deadline_ms: u64,
1147    sequence: u64,
1148}
1149
1150/// The production bytecode interpreter.
1151pub struct Machine<'a, H: Host> {
1152    program: Option<&'a Program<Verified>>,
1153    module: &'a Module<Verified>,
1154    host: &'a mut H,
1155    limits: Limits,
1156    frames: Vec<Frame>,
1157    heap: Vec<HeapEntry>,
1158    intrinsic_slots: usize,
1159    heap_bytes: usize,
1160    live_registers: usize,
1161    native_depth: usize,
1162    fuel: u64,
1163    globals: BTreeMap<EcmaString, Value>,
1164    last_completion: Option<Value>,
1165    /// Frame depths owned by native-to-runtime callback evaluations. A throw
1166    /// crossing this boundary returns to the native caller so the enclosing
1167    /// bytecode instruction resolves it at its own program counter.
1168    callback_boundaries: Vec<usize>,
1169    generator_boundaries: Vec<usize>,
1170    pending_generator_resume: Option<GeneratorResume>,
1171    async_boundaries: Vec<usize>,
1172    pending_async_suspend: Option<(Value, SuspendedActivation)>,
1173    microtasks: VecDeque<MicrotaskJob>,
1174    microtask_drain_active: bool,
1175    next_timer_id: Option<u64>,
1176    next_timer_sequence: Option<u64>,
1177    timers: BTreeMap<u64, TimerRecord>,
1178    ready_timers: BTreeSet<(u64, u64)>,
1179    timer_watermark: Option<u64>,
1180    timer_checkpoint_active: bool,
1181    intrinsics: intrinsics::Intrinsics<H>,
1182    current_builtin_id: Option<intrinsics::BuiltinId>,
1183    registry: ModuleRegistry,
1184    /// First machine-wide module ID reserved for host-compiled script modules.
1185    dynamic_base: usize,
1186    /// Host-compiled programs retained for the machine lifetime so their
1187    /// closures remain executable after the originating Script object dies.
1188    dynamic: Vec<DynamicModule>,
1189}
1190
1191#[derive(Clone, Debug)]
1192struct DynamicModule {
1193    program: Arc<Program<Verified>>,
1194    bytes: usize,
1195}
1196
1197#[derive(Clone, Debug, Default)]
1198struct ModuleRegistry {
1199    modules: Vec<ModuleInstance>,
1200    cells: Vec<Cell>,
1201    external: BTreeMap<EcmaString, ExternalModuleInstance>,
1202}
1203
1204#[derive(Clone, Debug)]
1205struct ExternalModuleInstance {
1206    namespace: Value,
1207    exports: BTreeMap<EcmaString, ExternalExport>,
1208    internals: BTreeMap<&'static str, Value>,
1209}
1210
1211#[derive(Clone, Copy, Debug)]
1212struct ExternalExport {
1213    value: Value,
1214    cell: Option<CellId>,
1215}
1216
1217#[derive(Clone, Debug)]
1218struct ModuleInstance {
1219    binding_cells: Vec<Option<CellId>>,
1220    constant_cells: Vec<Option<CellId>>,
1221    namespace: Option<Value>,
1222    state: ModuleState,
1223}
1224
1225#[derive(Clone, Debug)]
1226enum ModuleState {
1227    Unevaluated,
1228    Evaluating,
1229    Evaluated(Result<(), RuntimeError>),
1230}
1231
1232#[derive(Clone, Debug)]
1233pub(crate) enum ModuleEvaluation {
1234    Cycle,
1235    Evaluated(Result<(), RuntimeError>),
1236    Ready(Vec<ModuleId>),
1237}
1238
1239#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1240pub(crate) enum ImportTarget {
1241    Local(ModuleId),
1242    External(EdgeId),
1243}
1244
1245#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1246struct CellId(usize);
1247
1248#[derive(Clone, Copy, Debug)]
1249struct Cell {
1250    value: Value,
1251}
1252
1253pub fn run<H: Host>(
1254    program: &Program<Verified>,
1255    host: &mut H,
1256    limits: &Limits,
1257) -> Result<ExecutionOutcome, RuntimeError> {
1258    Machine::new(program, host, limits.clone())
1259        .run()
1260        .map(|execution| execution.outcome)
1261}
1262
1263impl<'a, H: Host> Machine<'a, H> {
1264    #[must_use]
1265    pub fn new(program: &'a Program<Verified>, host: &'a mut H, limits: Limits) -> Self {
1266        let module = &program
1267            .module(program.entry())
1268            .expect("verified program entry exists")
1269            .code;
1270        Self::build(Some(program), module, host, limits)
1271    }
1272
1273    fn build(
1274        program: Option<&'a Program<Verified>>,
1275        module: &'a Module<Verified>,
1276        host: &'a mut H,
1277        limits: Limits,
1278    ) -> Self {
1279        let entry = module.entry().get() as usize;
1280        let module_id = program.map_or(ModuleId::new(0), Program::entry);
1281        let frame = Frame::new(
1282            RuntimeFunction {
1283                module: module_id,
1284                function: FunctionId::new(entry as u32),
1285            },
1286            &module.functions()[entry],
1287            &[],
1288            Value::UNDEFINED,
1289            Value::UNDEFINED,
1290            &[],
1291            None,
1292        );
1293        let live_registers = frame.registers.len();
1294        let mut heap = Vec::new();
1295        let timers_available = host.timers().is_some();
1296        let mut intrinsics = intrinsics::Intrinsics::<H>::initialize(&mut heap, timers_available);
1297        let script_compiler = host.script_compiler().is_some();
1298        let installed_external = external_modules::install(
1299            &mut heap,
1300            &mut intrinsics.builtins,
1301            intrinsics.object_prototype,
1302            script_compiler,
1303        );
1304        let argv_text = host.argv().to_vec();
1305        let argv_values: Vec<Value> = argv_text
1306            .into_iter()
1307            .map(|text| {
1308                intrinsics::push(&mut heap, HeapEntry::String(EcmaString::from_utf8(&text)))
1309            })
1310            .collect();
1311        let process = intrinsics
1312            .global("process")
1313            .expect("host objects install process");
1314        let Some(Decoded::HeapRef(process_id)) = process.decode() else {
1315            unreachable!("process is an engine object");
1316        };
1317        let process_index = process_id.slot() as usize - 1;
1318        let HeapEntry::Object { properties, .. } = &heap[process_index] else {
1319            unreachable!("process is an ordinary object");
1320        };
1321        let Some(Property::Data { value: argv, .. }) = properties.get_ascii("argv") else {
1322            unreachable!("process owns argv");
1323        };
1324        let Some(Decoded::HeapRef(argv_id)) = argv.decode() else {
1325            unreachable!("process.argv is an engine array");
1326        };
1327        let argv_index = argv_id.slot() as usize - 1;
1328        let HeapEntry::Array { elements, .. } = &mut heap[argv_index] else {
1329            unreachable!("process.argv is an array");
1330        };
1331        *elements = argv_values;
1332        let intrinsic_slots = heap.len();
1333        let fuel = limits.fuel;
1334        Self {
1335            program,
1336            module,
1337            host,
1338            limits,
1339            fuel,
1340            frames: vec![frame],
1341            heap,
1342            heap_bytes: 0,
1343            intrinsic_slots,
1344            live_registers,
1345            native_depth: 0,
1346            last_completion: None,
1347            callback_boundaries: Vec::new(),
1348            generator_boundaries: Vec::new(),
1349            pending_generator_resume: None,
1350            async_boundaries: Vec::new(),
1351            pending_async_suspend: None,
1352            microtasks: VecDeque::new(),
1353            microtask_drain_active: false,
1354            next_timer_id: Some(1),
1355            next_timer_sequence: Some(0),
1356            timers: BTreeMap::new(),
1357            ready_timers: BTreeSet::new(),
1358            timer_watermark: None,
1359            timer_checkpoint_active: false,
1360            globals: BTreeMap::new(),
1361            registry: ModuleRegistry {
1362                external: installed_external
1363                    .into_iter()
1364                    .map(|module| {
1365                        let mut exports: BTreeMap<_, _> = module
1366                            .exports
1367                            .into_iter()
1368                            .map(|(name, value)| (name, ExternalExport { value, cell: None }))
1369                            .collect();
1370                        exports.insert(
1371                            EcmaString::from_utf8("default"),
1372                            ExternalExport {
1373                                value: module.namespace,
1374                                cell: None,
1375                            },
1376                        );
1377                        (
1378                            module.specifier,
1379                            ExternalModuleInstance {
1380                                namespace: module.namespace,
1381                                exports,
1382                                internals: module.internals,
1383                            },
1384                        )
1385                    })
1386                    .collect(),
1387                ..ModuleRegistry::default()
1388            },
1389            dynamic_base: program.map_or(1, |program| program.modules().len()),
1390            dynamic: Vec::new(),
1391            current_builtin_id: None,
1392            intrinsics,
1393        }
1394    }
1395
1396    pub fn run(mut self) -> Result<Execution, RuntimeError> {
1397        self.evaluate()
1398    }
1399
1400    /// Evaluates the program without draining queued microtasks.
1401    ///
1402    /// The machine remains available for an explicit [`Self::drain_microtasks`]
1403    /// checkpoint.
1404    pub fn evaluate(&mut self) -> Result<Execution, RuntimeError> {
1405        if let Some(program) = self.program {
1406            let entry = program.entry();
1407            self.frames.clear();
1408            self.live_registers = 0;
1409            self.instantiate_modules()?;
1410            return self.evaluate_module(entry)?.ok_or_else(|| {
1411                self.program_error(
1412                    entry,
1413                    RuntimeErrorKind::InvalidVerifiedProgram {
1414                        module: entry,
1415                        instruction: Instruction::Halt,
1416                    },
1417                )
1418            });
1419        }
1420        Ok(self
1421            .run_loop(0)?
1422            .expect("the entry frame completes before the run loop stops"))
1423    }
1424
1425    /// Runs at most one expired live timer callback.
1426    ///
1427    /// This explicit checkpoint never drains microtasks. Expiry reports only
1428    /// advance a monotonic watermark; visible delivery is ordered by the
1429    /// machine-owned `(deadline, sequence)` key.
1430    pub fn run_one_expired_timer(&mut self) -> Result<TimerRun, RuntimeError> {
1431        if self.timer_checkpoint_active {
1432            return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1433        }
1434        self.timer_checkpoint_active = true;
1435        let result = (|| {
1436            self.poll_timer_expiries()
1437                .map_err(|kind| self.checkpoint_error(kind))?;
1438            let Some(order) = self.ready_timers.first().copied() else {
1439                return Ok(TimerRun::default());
1440            };
1441            let Some(id) = self.timers.iter().find_map(|(id, timer)| {
1442                ((timer.deadline_ms, timer.sequence) == order).then_some(*id)
1443            }) else {
1444                return Err(self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1445                    value: Value::UNDEFINED,
1446                }));
1447            };
1448            // Preserve both the ready key and live record when fuel is empty.
1449            self.consume_fuel(1)
1450                .map_err(|kind| self.checkpoint_error(kind))?;
1451            self.ready_timers.remove(&order);
1452            let timer = self
1453                .timers
1454                .remove(&id)
1455                .expect("ready timer remains live until after fuel charging");
1456            let mut report = TimerRun {
1457                executed: 1,
1458                uncaught: Vec::new(),
1459            };
1460            match self.call_value(timer.callback, timer.handle, &timer.arguments) {
1461                Ok(_) => {}
1462                Err(EvalFailure::Runtime(kind)) => {
1463                    return Err(self.checkpoint_error(kind));
1464                }
1465                Err(failure) => {
1466                    let (value, origin) =
1467                        self.promise_rejection_value(failure)
1468                            .map_err(|failure| match failure {
1469                                EvalFailure::Runtime(kind) => self.checkpoint_error(kind),
1470                                _ => self.checkpoint_error(RuntimeErrorKind::InvalidValue {
1471                                    value: timer.callback,
1472                                }),
1473                            })?;
1474                    report.uncaught.try_reserve(1).map_err(|_| {
1475                        self.checkpoint_error(RuntimeErrorKind::HeapByteLimitExceeded {
1476                            limit: self.limits.max_heap_bytes,
1477                        })
1478                    })?;
1479                    report.uncaught.push(CallbackException { value, origin });
1480                }
1481            }
1482            Ok(report)
1483        })();
1484        self.timer_checkpoint_active = false;
1485        result
1486    }
1487
1488    /// Blocks in the host provider until an expiry report is available.
1489    /// Returns whether at least one live timer became ready.
1490    pub fn wait_for_timer_expiry(&mut self) -> Result<bool, RuntimeError> {
1491        if self.timer_checkpoint_active {
1492            return Err(self.checkpoint_error(RuntimeErrorKind::TimerCheckpointReentry));
1493        }
1494        if !self.ready_timers.is_empty() {
1495            return Ok(true);
1496        }
1497        self.timer_checkpoint_active = true;
1498        let result = (|| {
1499            let wakeup = match self.host.timers() {
1500                Some(provider) => provider.wait_expired(),
1501                None => return Ok(false),
1502            }
1503            .map_err(|error| {
1504                self.checkpoint_error(RuntimeErrorKind::TimerProviderFailure {
1505                    message: error.to_string(),
1506                })
1507            })?;
1508            if let Some(wakeup) = wakeup {
1509                self.promote_timer_wakeup(wakeup);
1510            }
1511            Ok(!self.ready_timers.is_empty())
1512        })();
1513        self.timer_checkpoint_active = false;
1514        result
1515    }
1516
1517    /// Returns whether any machine-owned live timer remains.
1518    #[must_use]
1519    pub fn has_pending_timers(&self) -> bool {
1520        !self.timers.is_empty()
1521    }
1522
1523    pub(crate) fn schedule_timeout(
1524        &mut self,
1525        callback: Value,
1526        delay_ms: u32,
1527        arguments: Vec<Value>,
1528    ) -> Result<Value, EvalFailure> {
1529        if self.timers.len() >= self.limits.max_timers {
1530            return Err(EvalFailure::Runtime(
1531                RuntimeErrorKind::TimerCapacityExceeded {
1532                    limit: self.limits.max_timers,
1533                },
1534            ));
1535        }
1536        let id = self.next_timer_id.take().ok_or(EvalFailure::Runtime(
1537            RuntimeErrorKind::TimerCapacityExceeded {
1538                limit: self.limits.max_timers,
1539            },
1540        ))?;
1541        self.next_timer_id = id.checked_add(1);
1542        let sequence = self.next_timer_sequence.take().ok_or(EvalFailure::Runtime(
1543            RuntimeErrorKind::TimerCapacityExceeded {
1544                limit: self.limits.max_timers,
1545            },
1546        ))?;
1547        self.next_timer_sequence = sequence.checked_add(1);
1548        let deadline_ms = self
1549            .host
1550            .timers()
1551            .ok_or(EvalFailure::Runtime(
1552                RuntimeErrorKind::TimerProviderFailure {
1553                    message: "timer capability is unavailable".to_owned(),
1554                },
1555            ))?
1556            .schedule(id, delay_ms)
1557            .map_err(|error| {
1558                EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1559                    message: error.to_string(),
1560                })
1561            })?;
1562        let handle = match self.allocate(HeapEntry::Timeout {
1563            id,
1564            properties: PropertyMap::default(),
1565            prototype: Some(self.intrinsics.object_prototype),
1566            extensible: true,
1567        }) {
1568            Ok(handle) => handle,
1569            Err(kind) => {
1570                if let Some(provider) = self.host.timers() {
1571                    let _ = provider.cancel(id);
1572                }
1573                return Err(EvalFailure::Runtime(kind));
1574            }
1575        };
1576        self.timers.insert(
1577            id,
1578            TimerRecord {
1579                callback,
1580                arguments,
1581                handle,
1582                deadline_ms,
1583                sequence,
1584            },
1585        );
1586        Ok(handle)
1587    }
1588
1589    pub(crate) fn clear_timeout(&mut self, handle: Value) -> Result<(), EvalFailure> {
1590        let id = match handle.decode() {
1591            Some(Decoded::Int32(raw)) if (raw as i32) > 0 => Some(u64::from(raw)),
1592            Some(Decoded::Number(number))
1593                if number.is_finite()
1594                    && number > 0.0
1595                    && number.fract() == 0.0
1596                    && number < u64::MAX as f64 =>
1597            {
1598                Some(number as u64)
1599            }
1600            Some(Decoded::HeapRef(_)) => {
1601                self.runtime_slot(handle)
1602                    .ok()
1603                    .flatten()
1604                    .and_then(|index| match &self.heap[index] {
1605                        HeapEntry::Timeout { id, .. } => Some(*id),
1606                        _ => None,
1607                    })
1608            }
1609            _ => None,
1610        };
1611        let Some(id) = id else {
1612            return Ok(());
1613        };
1614        let Some(timer) = self.timers.remove(&id) else {
1615            return Ok(());
1616        };
1617        self.ready_timers
1618            .remove(&(timer.deadline_ms, timer.sequence));
1619        if let Some(provider) = self.host.timers() {
1620            provider.cancel(id).map_err(|error| {
1621                EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure {
1622                    message: error.to_string(),
1623                })
1624            })?;
1625        }
1626        Ok(())
1627    }
1628
1629    fn poll_timer_expiries(&mut self) -> Result<(), RuntimeErrorKind> {
1630        let mut wakeups = Vec::new();
1631        let Some(provider) = self.host.timers() else {
1632            return Ok(());
1633        };
1634        provider.poll_expired(&mut wakeups).map_err(|error| {
1635            RuntimeErrorKind::TimerProviderFailure {
1636                message: error.to_string(),
1637            }
1638        })?;
1639        // One live report authorizes every earlier deadline. Collapse a host
1640        // batch to one watermark instead of rescanning all live timers per report.
1641        if let Some(wakeup) = wakeups
1642            .into_iter()
1643            .filter(|wakeup| self.timers.contains_key(&wakeup.id))
1644            .max_by_key(|wakeup| wakeup.deadline_ms)
1645        {
1646            self.promote_timer_wakeup(wakeup);
1647        }
1648        Ok(())
1649    }
1650
1651    fn promote_timer_wakeup(&mut self, wakeup: TimerWakeup) {
1652        // Unknown IDs are stale cancellation races and carry no authority to
1653        // advance the watermark.
1654        if !self.timers.contains_key(&wakeup.id) {
1655            return;
1656        }
1657        let watermark = self.timer_watermark.map_or(wakeup.deadline_ms, |current| {
1658            current.max(wakeup.deadline_ms)
1659        });
1660        self.timer_watermark = Some(watermark);
1661        for timer in self.timers.values() {
1662            if timer.deadline_ms <= watermark {
1663                self.ready_timers
1664                    .insert((timer.deadline_ms, timer.sequence));
1665            }
1666        }
1667    }
1668
1669    /// Runs queued microtasks in FIFO order until the queue is empty.
1670    ///
1671    /// Jobs queued by another job run in the same checkpoint. Promise callback
1672    /// throws settle their derived promises. Throws from `queueMicrotask`
1673    /// callbacks are returned in [`MicrotaskDrain::uncaught`].
1674    pub fn drain_microtasks(&mut self) -> Result<MicrotaskDrain, RuntimeError> {
1675        if self.microtask_drain_active {
1676            return Err(self.checkpoint_error(RuntimeErrorKind::MicrotaskDrainReentry));
1677        }
1678        self.microtask_drain_active = true;
1679        let result = (|| {
1680            let mut report = MicrotaskDrain::default();
1681            while self.microtasks.front().is_some() {
1682                self.consume_fuel(1)
1683                    .map_err(|kind| self.checkpoint_error(kind))?;
1684                let job = self
1685                    .microtasks
1686                    .pop_front()
1687                    .expect("the queued microtask remains present after fuel charging");
1688                report.executed = report.executed.saturating_add(1);
1689                self.execute_microtask_job(job, &mut report)
1690                    .map_err(|kind| self.checkpoint_error(kind))?;
1691            }
1692            Ok(report)
1693        })();
1694        self.microtask_drain_active = false;
1695        result
1696    }
1697
1698    fn checkpoint_error(&self, kind: RuntimeErrorKind) -> RuntimeError {
1699        let function = self.module.entry();
1700        let instruction = self.module.functions()[function.get() as usize]
1701            .code()
1702            .first()
1703            .copied()
1704            .unwrap_or(Instruction::Halt);
1705        RuntimeError {
1706            kind,
1707            function,
1708            pc: Pc::new(0),
1709            source: RuntimeSource {
1710                function_name: None,
1711                instruction,
1712            },
1713        }
1714    }
1715
1716    fn execute_microtask_job(
1717        &mut self,
1718        job: MicrotaskJob,
1719        report: &mut MicrotaskDrain,
1720    ) -> Result<(), RuntimeErrorKind> {
1721        match job {
1722            MicrotaskJob::Reaction {
1723                reaction,
1724                value,
1725                origin,
1726            } => self.execute_promise_reaction(reaction, value, origin),
1727            MicrotaskJob::Thenable {
1728                promise,
1729                thenable,
1730                then,
1731            } => self.execute_thenable_job(promise, thenable, then),
1732            MicrotaskJob::Callback { callback } => {
1733                self.execute_callback_microtask(callback, report)
1734            }
1735        }
1736    }
1737
1738    fn execute_callback_microtask(
1739        &mut self,
1740        callback: Value,
1741        report: &mut MicrotaskDrain,
1742    ) -> Result<(), RuntimeErrorKind> {
1743        match self.call_value(callback, Value::UNDEFINED, &[]) {
1744            Ok(_) => Ok(()),
1745            Err(EvalFailure::Runtime(kind)) => Err(kind),
1746            Err(failure) => {
1747                let (value, origin) =
1748                    self.promise_rejection_value(failure)
1749                        .map_err(|failure| match failure {
1750                            EvalFailure::Runtime(kind) => kind,
1751                            _ => RuntimeErrorKind::InvalidValue { value: callback },
1752                        })?;
1753                report.uncaught.try_reserve(1).map_err(|_| {
1754                    RuntimeErrorKind::HeapByteLimitExceeded {
1755                        limit: self.limits.max_heap_bytes,
1756                    }
1757                })?;
1758                report.uncaught.push(CallbackException { value, origin });
1759                Ok(())
1760            }
1761        }
1762    }
1763
1764    fn execute_thenable_job(
1765        &mut self,
1766        promise: Value,
1767        thenable: Value,
1768        then: Value,
1769    ) -> Result<(), RuntimeErrorKind> {
1770        let record = self
1771            .create_promise_resolver(promise)
1772            .map_err(|failure| match failure {
1773                EvalFailure::Runtime(kind) => kind,
1774                _ => RuntimeErrorKind::InvalidValue { value: promise },
1775            })?;
1776        let (resolve_target, reject_target) = self.intrinsics.builtins.promise_resolver_targets();
1777        let resolve = self
1778            .create_promise_resolver_function(resolve_target, record)
1779            .map_err(|failure| match failure {
1780                EvalFailure::Runtime(kind) => kind,
1781                _ => RuntimeErrorKind::InvalidValue { value: record },
1782            })?;
1783        let reject = self
1784            .create_promise_resolver_function(reject_target, record)
1785            .map_err(|failure| match failure {
1786                EvalFailure::Runtime(kind) => kind,
1787                _ => RuntimeErrorKind::InvalidValue { value: record },
1788            })?;
1789        match self.call_value(then, thenable, &[resolve, reject]) {
1790            Ok(_) => Ok(()),
1791            Err(EvalFailure::Runtime(kind)) => Err(kind),
1792            Err(failure) => self
1793                .reject_promise_resolver_failure(record, failure)
1794                .map_err(|failure| match failure {
1795                    EvalFailure::Runtime(kind) => kind,
1796                    _ => RuntimeErrorKind::InvalidValue { value: record },
1797                }),
1798        }
1799    }
1800
1801    fn execute_promise_reaction(
1802        &mut self,
1803        reaction: PromiseReaction,
1804        value: Value,
1805        origin: ThrowOrigin,
1806    ) -> Result<(), RuntimeErrorKind> {
1807        match reaction {
1808            PromiseReaction::Fulfilled { handler, derived } => self.execute_promise_handler(
1809                handler,
1810                derived,
1811                value,
1812                origin,
1813                PromiseCompletion::Fulfilled,
1814            ),
1815            PromiseReaction::Rejected { handler, derived } => self.execute_promise_handler(
1816                handler,
1817                derived,
1818                value,
1819                origin,
1820                PromiseCompletion::Rejected,
1821            ),
1822            PromiseReaction::Finally {
1823                handler,
1824                derived,
1825                completion,
1826            } => self.execute_promise_finally(handler, derived, value, origin, completion),
1827            PromiseReaction::AsyncFulfill { activation } => {
1828                self.resume_async(activation, value, None)
1829            }
1830            PromiseReaction::AsyncReject { activation } => {
1831                self.resume_async(activation, value, Some(origin))
1832            }
1833        }
1834    }
1835
1836    fn execute_promise_handler(
1837        &mut self,
1838        handler: Value,
1839        derived: Value,
1840        value: Value,
1841        origin: ThrowOrigin,
1842        completion: PromiseCompletion,
1843    ) -> Result<(), RuntimeErrorKind> {
1844        if !self.is_callable(handler).map_err(|failure| match failure {
1845            EvalFailure::Runtime(kind) => kind,
1846            _ => RuntimeErrorKind::InvalidValue { value: handler },
1847        })? {
1848            return match completion {
1849                PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1850                PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1851            };
1852        }
1853        match self.call_value(handler, Value::UNDEFINED, &[value]) {
1854            Ok(result) => self.resolve_promise(derived, result),
1855            Err(EvalFailure::Runtime(kind)) => Err(kind),
1856            Err(failure) => self
1857                .reject_promise_failure(derived, failure)
1858                .map_err(|failure| match failure {
1859                    EvalFailure::Runtime(kind) => kind,
1860                    _ => RuntimeErrorKind::InvalidValue { value: derived },
1861                }),
1862        }
1863    }
1864
1865    fn execute_promise_finally(
1866        &mut self,
1867        handler: Value,
1868        derived: Value,
1869        value: Value,
1870        origin: ThrowOrigin,
1871        completion: PromiseCompletion,
1872    ) -> Result<(), RuntimeErrorKind> {
1873        if !self.is_callable(handler).map_err(|failure| match failure {
1874            EvalFailure::Runtime(kind) => kind,
1875            _ => RuntimeErrorKind::InvalidValue { value: handler },
1876        })? {
1877            return match completion {
1878                PromiseCompletion::Fulfilled => self.resolve_promise(derived, value),
1879                PromiseCompletion::Rejected => self.reject_promise(derived, value, origin),
1880            };
1881        }
1882        let cleanup = self.create_promise().map_err(|failure| match failure {
1883            EvalFailure::Runtime(kind) => kind,
1884            _ => RuntimeErrorKind::InvalidValue { value: derived },
1885        })?;
1886        let record = self
1887            .create_promise_finally(derived, value, origin, completion)
1888            .map_err(|failure| match failure {
1889                EvalFailure::Runtime(kind) => kind,
1890                _ => RuntimeErrorKind::InvalidValue { value: derived },
1891            })?;
1892        let (on_fulfilled, on_rejected) = self.intrinsics.builtins.promise_finally_targets();
1893        let on_fulfilled = self
1894            .create_promise_resolver_function(on_fulfilled, record)
1895            .map_err(|failure| match failure {
1896                EvalFailure::Runtime(kind) => kind,
1897                _ => RuntimeErrorKind::InvalidValue { value: record },
1898            })?;
1899        let on_rejected = self
1900            .create_promise_resolver_function(on_rejected, record)
1901            .map_err(|failure| match failure {
1902                EvalFailure::Runtime(kind) => kind,
1903                _ => RuntimeErrorKind::InvalidValue { value: record },
1904            })?;
1905        self.promise_then(cleanup, on_fulfilled, on_rejected)
1906            .map_err(|failure| match failure {
1907                EvalFailure::Runtime(kind) => kind,
1908                _ => RuntimeErrorKind::InvalidValue { value: cleanup },
1909            })?;
1910        match self.call_value(handler, Value::UNDEFINED, &[]) {
1911            Ok(result) => self.resolve_promise(cleanup, result),
1912            Err(EvalFailure::Runtime(kind)) => Err(kind),
1913            Err(failure) => self
1914                .reject_promise_failure(cleanup, failure)
1915                .map_err(|failure| match failure {
1916                    EvalFailure::Runtime(kind) => kind,
1917                    _ => RuntimeErrorKind::InvalidValue { value: cleanup },
1918                }),
1919        }
1920    }
1921
1922    pub(crate) fn enqueue_microtask_callback(
1923        &mut self,
1924        callback: Value,
1925    ) -> Result<(), EvalFailure> {
1926        self.ensure_microtask_capacity(1)
1927            .map_err(EvalFailure::Runtime)?;
1928        self.microtasks
1929            .push_back(MicrotaskJob::Callback { callback });
1930        Ok(())
1931    }
1932
1933    fn ensure_microtask_capacity(&mut self, additional: usize) -> Result<(), RuntimeErrorKind> {
1934        if self
1935            .microtasks
1936            .len()
1937            .checked_add(additional)
1938            .is_none_or(|length| length > self.limits.max_microtasks)
1939        {
1940            return Err(RuntimeErrorKind::MicrotaskQueueLimitExceeded {
1941                limit: self.limits.max_microtasks,
1942            });
1943        }
1944        self.microtasks.try_reserve(additional).map_err(|_| {
1945            RuntimeErrorKind::HeapByteLimitExceeded {
1946                limit: self.limits.max_heap_bytes,
1947            }
1948        })
1949    }
1950
1951    pub(crate) fn create_promise(&mut self) -> Result<Value, EvalFailure> {
1952        self.allocate(HeapEntry::Promise {
1953            state: PromiseState::Pending {
1954                fulfill_reactions: Vec::new(),
1955                reject_reactions: Vec::new(),
1956            },
1957            properties: PropertyMap::default(),
1958            prototype: Some(self.intrinsics.builtins.promise_prototype()),
1959            extensible: true,
1960        })
1961        .map_err(EvalFailure::Runtime)
1962    }
1963
1964    pub(crate) fn create_promise_resolver(&mut self, promise: Value) -> Result<Value, EvalFailure> {
1965        self.allocate(HeapEntry::PromiseResolver {
1966            promise,
1967            used: false,
1968        })
1969        .map_err(EvalFailure::Runtime)
1970    }
1971
1972    pub(crate) fn create_promise_resolver_function(
1973        &mut self,
1974        target: Value,
1975        record: Value,
1976    ) -> Result<Value, EvalFailure> {
1977        self.allocate(HeapEntry::NativeFunction {
1978            callable: NativeCallable::Bound(Box::new(BoundCallable {
1979                target,
1980                this_value: Value::UNDEFINED,
1981                arguments: vec![record],
1982            })),
1983            properties: PropertyMap::default(),
1984            extensible: true,
1985        })
1986        .map_err(EvalFailure::Runtime)
1987    }
1988
1989    pub(crate) fn resolve_promise_resolver(
1990        &mut self,
1991        record: Value,
1992        value: Value,
1993    ) -> Result<(), EvalFailure> {
1994        if let Some(promise) = self.use_promise_resolver(record)? {
1995            self.resolve_promise(promise, value)
1996                .map_err(EvalFailure::Runtime)?;
1997        }
1998        Ok(())
1999    }
2000
2001    pub(crate) fn reject_promise_resolver(
2002        &mut self,
2003        record: Value,
2004        reason: Value,
2005    ) -> Result<(), EvalFailure> {
2006        if let Some(promise) = self.use_promise_resolver(record)? {
2007            self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2008                .map_err(EvalFailure::Runtime)?;
2009        }
2010        Ok(())
2011    }
2012
2013    pub(crate) fn reject_promise_resolver_failure(
2014        &mut self,
2015        record: Value,
2016        failure: EvalFailure,
2017    ) -> Result<(), EvalFailure> {
2018        if let Some(promise) = self.use_promise_resolver(record)? {
2019            self.reject_promise_failure(promise, failure)?;
2020        }
2021        Ok(())
2022    }
2023
2024    fn use_promise_resolver(&mut self, record: Value) -> Result<Option<Value>, EvalFailure> {
2025        let index = self
2026            .runtime_slot(record)
2027            .map_err(EvalFailure::Runtime)?
2028            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2029                operation: "Promise resolver",
2030            }))?;
2031        let HeapEntry::PromiseResolver { promise, used } = &mut self.heap[index] else {
2032            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2033                operation: "Promise resolver",
2034            }));
2035        };
2036        if *used {
2037            return Ok(None);
2038        }
2039        *used = true;
2040        Ok(Some(*promise))
2041    }
2042
2043    fn charge_promise_reactions(&mut self, count: usize) -> Result<(), EvalFailure> {
2044        let bytes = std::mem::size_of::<PromiseReaction>()
2045            .checked_mul(count)
2046            .ok_or(EvalFailure::Runtime(
2047                RuntimeErrorKind::HeapByteLimitExceeded {
2048                    limit: self.limits.max_heap_bytes,
2049                },
2050            ))?;
2051        self.charge_heap(bytes).map_err(EvalFailure::Runtime)
2052    }
2053
2054    pub(crate) fn promise_then(
2055        &mut self,
2056        promise: Value,
2057        on_fulfilled: Value,
2058        on_rejected: Value,
2059    ) -> Result<Value, EvalFailure> {
2060        let index = self
2061            .runtime_slot(promise)
2062            .map_err(EvalFailure::Runtime)?
2063            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2064                operation: "Promise.prototype.then",
2065            }))?;
2066        let settled = match &self.heap[index] {
2067            HeapEntry::Promise {
2068                state: PromiseState::Pending { .. },
2069                ..
2070            } => None,
2071            HeapEntry::Promise {
2072                state: PromiseState::Fulfilled { value },
2073                ..
2074            } => Some((true, *value, ThrowOrigin::Bytecode)),
2075            HeapEntry::Promise {
2076                state: PromiseState::Rejected { reason, origin },
2077                ..
2078            } => Some((false, *reason, *origin)),
2079            _ => {
2080                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2081                    operation: "Promise.prototype.then",
2082                }));
2083            }
2084        };
2085        let derived = self.create_promise()?;
2086        if let Some((fulfilled, value, origin)) = settled {
2087            self.ensure_microtask_capacity(1)
2088                .map_err(EvalFailure::Runtime)?;
2089            let reaction = if fulfilled {
2090                PromiseReaction::Fulfilled {
2091                    handler: on_fulfilled,
2092                    derived,
2093                }
2094            } else {
2095                PromiseReaction::Rejected {
2096                    handler: on_rejected,
2097                    derived,
2098                }
2099            };
2100            self.microtasks.push_back(MicrotaskJob::Reaction {
2101                reaction,
2102                value,
2103                origin,
2104            });
2105            return Ok(derived);
2106        }
2107        self.charge_promise_reactions(2)?;
2108        let HeapEntry::Promise {
2109            state:
2110                PromiseState::Pending {
2111                    fulfill_reactions,
2112                    reject_reactions,
2113                },
2114            ..
2115        } = &mut self.heap[index]
2116        else {
2117            unreachable!("pending Promise state was checked before derived allocation");
2118        };
2119        fulfill_reactions.push(PromiseReaction::Fulfilled {
2120            handler: on_fulfilled,
2121            derived,
2122        });
2123        reject_reactions.push(PromiseReaction::Rejected {
2124            handler: on_rejected,
2125            derived,
2126        });
2127        Ok(derived)
2128    }
2129
2130    pub(crate) fn promise_finally(
2131        &mut self,
2132        promise: Value,
2133        handler: Value,
2134    ) -> Result<Value, EvalFailure> {
2135        let index = self
2136            .runtime_slot(promise)
2137            .map_err(EvalFailure::Runtime)?
2138            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2139                operation: "Promise.prototype.finally",
2140            }))?;
2141        let settled = match &self.heap[index] {
2142            HeapEntry::Promise {
2143                state: PromiseState::Pending { .. },
2144                ..
2145            } => None,
2146            HeapEntry::Promise {
2147                state: PromiseState::Fulfilled { value },
2148                ..
2149            } => Some((true, *value, ThrowOrigin::Bytecode)),
2150            HeapEntry::Promise {
2151                state: PromiseState::Rejected { reason, origin },
2152                ..
2153            } => Some((false, *reason, *origin)),
2154            _ => {
2155                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2156                    operation: "Promise.prototype.finally",
2157                }));
2158            }
2159        };
2160        let derived = self.create_promise()?;
2161        let reaction = |completion| PromiseReaction::Finally {
2162            handler,
2163            derived,
2164            completion,
2165        };
2166        if let Some((fulfilled, value, origin)) = settled {
2167            self.ensure_microtask_capacity(1)
2168                .map_err(EvalFailure::Runtime)?;
2169            self.microtasks.push_back(MicrotaskJob::Reaction {
2170                reaction: reaction(if fulfilled {
2171                    PromiseCompletion::Fulfilled
2172                } else {
2173                    PromiseCompletion::Rejected
2174                }),
2175                value,
2176                origin,
2177            });
2178            return Ok(derived);
2179        }
2180        self.charge_promise_reactions(2)?;
2181        let HeapEntry::Promise {
2182            state:
2183                PromiseState::Pending {
2184                    fulfill_reactions,
2185                    reject_reactions,
2186                },
2187            ..
2188        } = &mut self.heap[index]
2189        else {
2190            unreachable!("pending Promise state was checked before derived allocation");
2191        };
2192        fulfill_reactions.push(reaction(PromiseCompletion::Fulfilled));
2193        reject_reactions.push(reaction(PromiseCompletion::Rejected));
2194        Ok(derived)
2195    }
2196
2197    pub(crate) fn create_promise_finally(
2198        &mut self,
2199        derived: Value,
2200        value: Value,
2201        origin: ThrowOrigin,
2202        completion: PromiseCompletion,
2203    ) -> Result<Value, EvalFailure> {
2204        self.allocate(HeapEntry::PromiseFinally {
2205            derived,
2206            value,
2207            origin,
2208            completion,
2209        })
2210        .map_err(EvalFailure::Runtime)
2211    }
2212
2213    pub(crate) fn fulfill_promise_finally(&mut self, record: Value) -> Result<(), EvalFailure> {
2214        let (derived, value, origin, completion) = self.promise_finally_record(record)?;
2215        match completion {
2216            PromiseCompletion::Fulfilled => self
2217                .resolve_promise(derived, value)
2218                .map_err(EvalFailure::Runtime),
2219            PromiseCompletion::Rejected => self
2220                .reject_promise(derived, value, origin)
2221                .map_err(EvalFailure::Runtime),
2222        }
2223    }
2224
2225    pub(crate) fn reject_promise_finally(
2226        &mut self,
2227        record: Value,
2228        reason: Value,
2229    ) -> Result<(), EvalFailure> {
2230        let (derived, _, _, _) = self.promise_finally_record(record)?;
2231        self.reject_promise(derived, reason, ThrowOrigin::Bytecode)
2232            .map_err(EvalFailure::Runtime)
2233    }
2234
2235    fn promise_finally_record(
2236        &mut self,
2237        record: Value,
2238    ) -> Result<(Value, Value, ThrowOrigin, PromiseCompletion), EvalFailure> {
2239        let index = self
2240            .runtime_slot(record)
2241            .map_err(EvalFailure::Runtime)?
2242            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2243                operation: "Promise finally target",
2244            }))?;
2245        let HeapEntry::PromiseFinally {
2246            derived,
2247            value,
2248            origin,
2249            completion,
2250        } = &self.heap[index]
2251        else {
2252            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2253                operation: "Promise finally target",
2254            }));
2255        };
2256        Ok((*derived, *value, *origin, *completion))
2257    }
2258
2259    pub(crate) fn promise_resolve(&mut self, value: Value) -> Result<Value, EvalFailure> {
2260        if matches!(self.runtime_slot(value).map_err(EvalFailure::Runtime)?, Some(index) if matches!(self.heap[index], HeapEntry::Promise { .. }))
2261        {
2262            return Ok(value);
2263        }
2264        let promise = self.create_promise()?;
2265        self.resolve_promise(promise, value)
2266            .map_err(EvalFailure::Runtime)?;
2267        Ok(promise)
2268    }
2269
2270    pub(crate) fn promise_reject(&mut self, reason: Value) -> Result<Value, EvalFailure> {
2271        let promise = self.create_promise()?;
2272        self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2273            .map_err(EvalFailure::Runtime)?;
2274        Ok(promise)
2275    }
2276
2277    pub(crate) fn promise_all(&mut self, iterable: Value) -> Result<Value, EvalFailure> {
2278        let promise = self.create_promise()?;
2279        let aggregate = self
2280            .allocate(HeapEntry::PromiseAll {
2281                promise,
2282                values: Vec::new(),
2283                remaining: 1,
2284                settled: false,
2285            })
2286            .map_err(EvalFailure::Runtime)?;
2287        let iterator = match self.create_iterator(iterable, IteratorKind::Sync) {
2288            Ok(iterator) => iterator,
2289            Err(failure) => {
2290                self.mark_promise_all_settled(aggregate)?;
2291                self.reject_promise_failure(promise, failure)?;
2292                return Ok(promise);
2293            }
2294        };
2295        loop {
2296            let value = match self.iterator_next(iterator) {
2297                Ok((true, _)) => break,
2298                Ok((false, value)) => value,
2299                Err(failure) => {
2300                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2301                }
2302            };
2303            let index = match self.add_promise_all_element(aggregate) {
2304                Ok(index) => index,
2305                Err(failure) => {
2306                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2307                }
2308            };
2309            let element = match self
2310                .allocate(HeapEntry::PromiseAllElement {
2311                    aggregate,
2312                    index,
2313                    called: false,
2314                })
2315                .map_err(EvalFailure::Runtime)
2316            {
2317                Ok(element) => element,
2318                Err(failure) => {
2319                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2320                }
2321            };
2322            let (fulfill_target, reject_target) = self.intrinsics.builtins.promise_all_targets();
2323            let on_fulfilled = match self.create_promise_resolver_function(fulfill_target, element)
2324            {
2325                Ok(callback) => callback,
2326                Err(failure) => {
2327                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2328                }
2329            };
2330            let on_rejected = match self.create_promise_resolver_function(reject_target, element) {
2331                Ok(callback) => callback,
2332                Err(failure) => {
2333                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2334                }
2335            };
2336            let resolved = match self.promise_resolve(value) {
2337                Ok(resolved) => resolved,
2338                Err(failure) => {
2339                    return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2340                }
2341            };
2342            if let Err(failure) = self.promise_then(resolved, on_fulfilled, on_rejected) {
2343                return self.reject_promise_all_abrupt(aggregate, promise, iterator, failure);
2344            }
2345        }
2346        if let Some(values) = self.finish_promise_all(aggregate)? {
2347            let array = self.create_array(values)?;
2348            self.fulfill_promise(promise, array)
2349                .map_err(EvalFailure::Runtime)?;
2350        }
2351        Ok(promise)
2352    }
2353
2354    fn reject_promise_all_abrupt(
2355        &mut self,
2356        aggregate: Value,
2357        promise: Value,
2358        iterator: Value,
2359        failure: EvalFailure,
2360    ) -> Result<Value, EvalFailure> {
2361        self.mark_promise_all_settled(aggregate)?;
2362        if let Err(EvalFailure::Runtime(kind)) = self.close_iterator(iterator) {
2363            return Err(EvalFailure::Runtime(kind));
2364        }
2365        self.reject_promise_failure(promise, failure)?;
2366        Ok(promise)
2367    }
2368
2369    fn close_iterator(&mut self, iterator: Value) -> Result<(), EvalFailure> {
2370        let Some(index) = self.runtime_slot(iterator).map_err(EvalFailure::Runtime)? else {
2371            return Ok(());
2372        };
2373        let HeapEntry::Iterator {
2374            state: IteratorState::Protocol { iterator, .. },
2375        } = &self.heap[index]
2376        else {
2377            return Ok(());
2378        };
2379        let iterator = *iterator;
2380        let close = self.get_named_property(iterator, "return")?;
2381        if self.is_callable(close)? {
2382            let _ = self.call_value(close, iterator, &[])?;
2383        }
2384        Ok(())
2385    }
2386
2387    fn mark_promise_all_settled(&mut self, aggregate: Value) -> Result<bool, EvalFailure> {
2388        let index = self
2389            .runtime_slot(aggregate)
2390            .map_err(EvalFailure::Runtime)?
2391            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2392                operation: "Promise.all target",
2393            }))?;
2394        let HeapEntry::PromiseAll { settled, .. } = &mut self.heap[index] else {
2395            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2396                operation: "Promise.all target",
2397            }));
2398        };
2399        let changed = !*settled;
2400        *settled = true;
2401        Ok(changed)
2402    }
2403
2404    fn add_promise_all_element(&mut self, aggregate: Value) -> Result<usize, EvalFailure> {
2405        let index = self
2406            .runtime_slot(aggregate)
2407            .map_err(EvalFailure::Runtime)?
2408            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2409                operation: "Promise.all target",
2410            }))?;
2411        let next_remaining = match &self.heap[index] {
2412            HeapEntry::PromiseAll {
2413                remaining,
2414                settled: false,
2415                ..
2416            } => remaining.checked_add(1).ok_or(EvalFailure::Runtime(
2417                RuntimeErrorKind::HeapByteLimitExceeded {
2418                    limit: self.limits.max_heap_bytes,
2419                },
2420            ))?,
2421            HeapEntry::PromiseAll { .. } => {
2422                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2423                    operation: "Promise.all target",
2424                }));
2425            }
2426            _ => {
2427                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2428                    operation: "Promise.all target",
2429                }));
2430            }
2431        };
2432        self.charge_heap(std::mem::size_of::<Value>())
2433            .map_err(EvalFailure::Runtime)?;
2434        let HeapEntry::PromiseAll {
2435            values, remaining, ..
2436        } = &mut self.heap[index]
2437        else {
2438            unreachable!("Promise.all aggregate was checked before its heap charge");
2439        };
2440        values.try_reserve(1).map_err(|_| {
2441            EvalFailure::Runtime(RuntimeErrorKind::HeapByteLimitExceeded {
2442                limit: self.limits.max_heap_bytes,
2443            })
2444        })?;
2445        let index = values.len();
2446        values.push(Value::UNDEFINED);
2447        *remaining = next_remaining;
2448        Ok(index)
2449    }
2450
2451    fn finish_promise_all(&mut self, aggregate: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
2452        let index = self
2453            .runtime_slot(aggregate)
2454            .map_err(EvalFailure::Runtime)?
2455            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2456                operation: "Promise.all target",
2457            }))?;
2458        let HeapEntry::PromiseAll {
2459            values,
2460            remaining,
2461            settled,
2462            ..
2463        } = &mut self.heap[index]
2464        else {
2465            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2466                operation: "Promise.all target",
2467            }));
2468        };
2469        if *settled {
2470            return Ok(None);
2471        }
2472        *remaining -= 1;
2473        if *remaining != 0 {
2474            return Ok(None);
2475        }
2476        *settled = true;
2477        Ok(Some(std::mem::take(values)))
2478    }
2479
2480    pub(crate) fn resolve_promise_all_element(
2481        &mut self,
2482        element: Value,
2483        value: Value,
2484    ) -> Result<(), EvalFailure> {
2485        let index = self
2486            .runtime_slot(element)
2487            .map_err(EvalFailure::Runtime)?
2488            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2489                operation: "Promise.all target",
2490            }))?;
2491        let (aggregate, output_index) = {
2492            let HeapEntry::PromiseAllElement {
2493                aggregate,
2494                index: output_index,
2495                called,
2496            } = &mut self.heap[index]
2497            else {
2498                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2499                    operation: "Promise.all target",
2500                }));
2501            };
2502            if *called {
2503                return Ok(());
2504            }
2505            *called = true;
2506            (*aggregate, *output_index)
2507        };
2508        let aggregate_index = self
2509            .runtime_slot(aggregate)
2510            .map_err(EvalFailure::Runtime)?
2511            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2512                operation: "Promise.all target",
2513            }))?;
2514        let (promise, values) = {
2515            let HeapEntry::PromiseAll {
2516                promise,
2517                values,
2518                remaining,
2519                settled,
2520            } = &mut self.heap[aggregate_index]
2521            else {
2522                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2523                    operation: "Promise.all target",
2524                }));
2525            };
2526            if *settled {
2527                return Ok(());
2528            }
2529            values[output_index] = value;
2530            *remaining -= 1;
2531            let values = (*remaining == 0).then(|| {
2532                *settled = true;
2533                std::mem::take(values)
2534            });
2535            (*promise, values)
2536        };
2537        if let Some(values) = values {
2538            let array = self.create_array(values)?;
2539            self.fulfill_promise(promise, array)
2540                .map_err(EvalFailure::Runtime)?;
2541        }
2542        Ok(())
2543    }
2544
2545    pub(crate) fn reject_promise_all_element(
2546        &mut self,
2547        element: Value,
2548        reason: Value,
2549    ) -> Result<(), EvalFailure> {
2550        let index = self
2551            .runtime_slot(element)
2552            .map_err(EvalFailure::Runtime)?
2553            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2554                operation: "Promise.all target",
2555            }))?;
2556        let aggregate = {
2557            let HeapEntry::PromiseAllElement {
2558                aggregate, called, ..
2559            } = &mut self.heap[index]
2560            else {
2561                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2562                    operation: "Promise.all target",
2563                }));
2564            };
2565            if *called {
2566                return Ok(());
2567            }
2568            *called = true;
2569            *aggregate
2570        };
2571        let aggregate_index = self
2572            .runtime_slot(aggregate)
2573            .map_err(EvalFailure::Runtime)?
2574            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
2575                operation: "Promise.all target",
2576            }))?;
2577        let HeapEntry::PromiseAll { promise, .. } = &self.heap[aggregate_index] else {
2578            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
2579                operation: "Promise.all target",
2580            }));
2581        };
2582        let promise = *promise;
2583        if !self.mark_promise_all_settled(aggregate)? {
2584            return Ok(());
2585        }
2586        self.reject_promise(promise, reason, ThrowOrigin::Bytecode)
2587            .map_err(EvalFailure::Runtime)
2588    }
2589
2590    fn create_array(&mut self, elements: Vec<Value>) -> Result<Value, EvalFailure> {
2591        self.allocate(HeapEntry::Array {
2592            elements,
2593            properties: PropertyMap::default(),
2594            prototype: Some(self.intrinsics.array_prototype),
2595            extensible: true,
2596            length_writable: true,
2597        })
2598        .map_err(EvalFailure::Runtime)
2599    }
2600
2601    fn resolve_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2602        if promise == value {
2603            return self
2604                .reject_promise_failure(
2605                    promise,
2606                    EvalFailure::Throw(ThrowOrigin::TypeError {
2607                        operation: "Promise cannot resolve itself",
2608                    }),
2609                )
2610                .map_err(|failure| match failure {
2611                    EvalFailure::Runtime(kind) => kind,
2612                    _ => RuntimeErrorKind::InvalidValue { value: promise },
2613                });
2614        }
2615        if !self.is_object(value) {
2616            return self.fulfill_promise(promise, value);
2617        }
2618        let then = match self.get_named_property(value, "then") {
2619            Ok(then) => then,
2620            Err(EvalFailure::Runtime(kind)) => return Err(kind),
2621            Err(failure) => {
2622                return self.reject_promise_failure(promise, failure).map_err(
2623                    |failure| match failure {
2624                        EvalFailure::Runtime(kind) => kind,
2625                        _ => RuntimeErrorKind::InvalidValue { value: promise },
2626                    },
2627                );
2628            }
2629        };
2630        if !self.is_callable(then).map_err(|failure| match failure {
2631            EvalFailure::Runtime(kind) => kind,
2632            _ => RuntimeErrorKind::InvalidValue { value: then },
2633        })? {
2634            return self.fulfill_promise(promise, value);
2635        }
2636        self.ensure_microtask_capacity(1)?;
2637        self.microtasks.push_back(MicrotaskJob::Thenable {
2638            promise,
2639            thenable: value,
2640            then,
2641        });
2642        Ok(())
2643    }
2644
2645    fn reject_promise(
2646        &mut self,
2647        promise: Value,
2648        reason: Value,
2649        origin: ThrowOrigin,
2650    ) -> Result<(), RuntimeErrorKind> {
2651        self.settle_promise(promise, PromiseState::Rejected { reason, origin })
2652    }
2653
2654    fn fulfill_promise(&mut self, promise: Value, value: Value) -> Result<(), RuntimeErrorKind> {
2655        self.settle_promise(promise, PromiseState::Fulfilled { value })
2656    }
2657
2658    fn settle_promise(
2659        &mut self,
2660        promise: Value,
2661        terminal: PromiseState,
2662    ) -> Result<(), RuntimeErrorKind> {
2663        let index = self
2664            .runtime_slot(promise)?
2665            .ok_or(RuntimeErrorKind::InvalidValue { value: promise })?;
2666        let reaction_count = match &self.heap[index] {
2667            HeapEntry::Promise {
2668                state:
2669                    PromiseState::Pending {
2670                        fulfill_reactions,
2671                        reject_reactions,
2672                    },
2673                ..
2674            } => match &terminal {
2675                PromiseState::Fulfilled { .. } => fulfill_reactions.len(),
2676                PromiseState::Rejected { .. } => reject_reactions.len(),
2677                PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2678            },
2679            HeapEntry::Promise { .. } => return Ok(()),
2680            _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2681        };
2682        self.ensure_microtask_capacity(reaction_count)?;
2683        let reactions = match &mut self.heap[index] {
2684            HeapEntry::Promise { state, .. } => {
2685                let reactions = match state {
2686                    PromiseState::Pending {
2687                        fulfill_reactions,
2688                        reject_reactions,
2689                    } => match &terminal {
2690                        PromiseState::Fulfilled { .. } => std::mem::take(fulfill_reactions),
2691                        PromiseState::Rejected { .. } => std::mem::take(reject_reactions),
2692                        PromiseState::Pending { .. } => {
2693                            unreachable!("Promise settlement is terminal")
2694                        }
2695                    },
2696                    _ => return Ok(()),
2697                };
2698                *state = terminal.clone();
2699                reactions
2700            }
2701            _ => return Err(RuntimeErrorKind::InvalidValue { value: promise }),
2702        };
2703        let (value, origin) = match terminal {
2704            PromiseState::Fulfilled { value } => (value, ThrowOrigin::Bytecode),
2705            PromiseState::Rejected { reason, origin } => (reason, origin),
2706            PromiseState::Pending { .. } => unreachable!("Promise settlement is terminal"),
2707        };
2708        for reaction in reactions {
2709            self.microtasks.push_back(MicrotaskJob::Reaction {
2710                reaction,
2711                value,
2712                origin,
2713            });
2714        }
2715        Ok(())
2716    }
2717
2718    fn reject_promise_failure(
2719        &mut self,
2720        promise: Value,
2721        failure: EvalFailure,
2722    ) -> Result<(), EvalFailure> {
2723        let (reason, origin) = self.promise_rejection_value(failure)?;
2724        self.reject_promise(promise, reason, origin)
2725            .map_err(EvalFailure::Runtime)
2726    }
2727
2728    fn promise_rejection_value(
2729        &mut self,
2730        failure: EvalFailure,
2731    ) -> Result<(Value, ThrowOrigin), EvalFailure> {
2732        match failure {
2733            EvalFailure::ThrowValue(value) => Ok((value, ThrowOrigin::Bytecode)),
2734            EvalFailure::ThrowValueOrigin { value, origin } => Ok((value, origin)),
2735            EvalFailure::Throw(ThrowOrigin::Bytecode) => {
2736                Ok((Value::UNDEFINED, ThrowOrigin::Bytecode))
2737            }
2738            EvalFailure::Throw(origin) => {
2739                let (name, message) = match origin {
2740                    ThrowOrigin::TypeError { operation } => ("TypeError", operation),
2741                    ThrowOrigin::RangeError { operation } => ("RangeError", operation),
2742                    ThrowOrigin::ReferenceError { operation } => ("ReferenceError", operation),
2743                    ThrowOrigin::UriError { operation } => ("URIError", operation),
2744                    ThrowOrigin::Bytecode => unreachable!("handled above"),
2745                };
2746                let id = self
2747                    .intrinsics
2748                    .builtins
2749                    .id_named(name)
2750                    .expect("error constructor is installed");
2751                match self.throw_error(id, message.to_owned()) {
2752                    EvalFailure::ThrowValue(value) => Ok((value, origin)),
2753                    EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2754                    _ => unreachable!("error materialization returns a thrown value"),
2755                }
2756            }
2757            EvalFailure::Runtime(kind) => Err(EvalFailure::Runtime(kind)),
2758        }
2759    }
2760
2761    fn program(&self) -> &Program<Verified> {
2762        self.program
2763            .expect("module registry operations require a whole program")
2764    }
2765
2766    fn module_code(&self, module: ModuleId) -> &Module<Verified> {
2767        let index = module.get() as usize;
2768        if index >= self.dynamic_base {
2769            return &self.dynamic[index - self.dynamic_base].program.modules()[0].code;
2770        }
2771        match self.program {
2772            Some(program) => {
2773                &program
2774                    .module(module)
2775                    .expect("verified module id remains in bounds")
2776                    .code
2777            }
2778            None => self.module,
2779        }
2780    }
2781
2782    fn program_module(&self, module: ModuleId) -> &ProgramModule<Verified> {
2783        let index = module.get() as usize;
2784        if index >= self.dynamic_base {
2785            return &self.dynamic[index - self.dynamic_base].program.modules()[0];
2786        }
2787        self.program
2788            .and_then(|program| program.module(module))
2789            .expect("verified module id remains in bounds")
2790    }
2791
2792    /// Validates host-provided code against this machine's classic-script realm.
2793    fn validate_dynamic_script(program: &Program<Verified>) -> Result<(), &'static str> {
2794        if program.modules().len() != 1 {
2795            return Err("script program must contain exactly one module");
2796        }
2797        if program.entry() != ModuleId::new(0) {
2798            return Err("script program entry must be module zero");
2799        }
2800        let module = &program.modules()[0];
2801        if !module.edges.is_empty() || !module.bindings.is_empty() || !module.exports.is_empty() {
2802            return Err("script program must not contain linkage metadata");
2803        }
2804        if module
2805            .code
2806            .functions()
2807            .iter()
2808            .flat_map(|function| function.code())
2809            .any(|instruction| {
2810                matches!(
2811                    instruction,
2812                    Instruction::Import { .. } | Instruction::Export { .. }
2813                )
2814            })
2815        {
2816            return Err("script program must not contain import or export instructions");
2817        }
2818        Ok(())
2819    }
2820
2821    fn script_heap_cost(program: &Program<Verified>) -> usize {
2822        const MODULE_BYTES: usize = 64;
2823        const FUNCTION_BYTES: usize = 32;
2824        program.modules().iter().fold(0usize, |total, module| {
2825            let constant_bytes = module
2826                .code
2827                .constants()
2828                .iter()
2829                .fold(0usize, |bytes, constant| {
2830                    let payload = match constant {
2831                        Constant::String(text) => text.len_units().saturating_mul(2),
2832                        Constant::BigInt(value) => value.as_str().len(),
2833                        Constant::Number(_)
2834                        | Constant::Int32(_)
2835                        | Constant::Boolean(_)
2836                        | Constant::Null
2837                        | Constant::Undefined => 0,
2838                    };
2839                    bytes
2840                        .saturating_add(std::mem::size_of::<Constant>())
2841                        .saturating_add(payload)
2842                });
2843            let function_bytes =
2844                module
2845                    .code
2846                    .functions()
2847                    .iter()
2848                    .fold(0usize, |bytes, function| {
2849                        bytes
2850                            .saturating_add(FUNCTION_BYTES)
2851                            .saturating_add(
2852                                function
2853                                    .code()
2854                                    .len()
2855                                    .saturating_mul(std::mem::size_of::<Instruction>()),
2856                            )
2857                            .saturating_add(function.handlers().len().saturating_mul(
2858                                std::mem::size_of::<bamts_bytecode::ExceptionHandler>(),
2859                            ))
2860                    });
2861            total
2862                .saturating_add(MODULE_BYTES)
2863                .saturating_add(constant_bytes)
2864                .saturating_add(function_bytes)
2865                .saturating_add(module.code.verification_bytes())
2866        })
2867    }
2868
2869    fn install_script_reserving(
2870        &mut self,
2871        program: Arc<Program<Verified>>,
2872        reserved_slots: usize,
2873        reserved_bytes: usize,
2874    ) -> Result<ModuleId, RuntimeErrorKind> {
2875        Self::validate_dynamic_script(&program)
2876            .map_err(|reason| RuntimeErrorKind::InvalidDynamicScript { reason })?;
2877        if self.dynamic.len() >= self.limits.max_dynamic_modules {
2878            return Err(RuntimeErrorKind::DynamicModuleLimitExceeded {
2879                limit: self.limits.max_dynamic_modules,
2880            });
2881        }
2882        let bytes = Self::script_heap_cost(&program);
2883        let retained_bytes =
2884            bytes
2885                .checked_add(reserved_bytes)
2886                .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
2887                    limit: self.limits.max_heap_bytes,
2888                })?;
2889        self.ensure_allocation_capacity(reserved_slots, retained_bytes)?;
2890        self.charge_heap(bytes)?;
2891        let index = self.dynamic_base.checked_add(self.dynamic.len()).ok_or(
2892            RuntimeErrorKind::DynamicModuleLimitExceeded {
2893                limit: self.limits.max_dynamic_modules,
2894            },
2895        )?;
2896        let module = ModuleId::new(u32::try_from(index).map_err(|_| {
2897            RuntimeErrorKind::DynamicModuleLimitExceeded {
2898                limit: self.limits.max_dynamic_modules,
2899            }
2900        })?);
2901        self.dynamic.push(DynamicModule { program, bytes });
2902        self.registry.modules.push(ModuleInstance {
2903            binding_cells: Vec::new(),
2904            constant_cells: Vec::new(),
2905            namespace: None,
2906            state: ModuleState::Unevaluated,
2907        });
2908        debug_assert_eq!(
2909            self.dynamic
2910                .last()
2911                .expect("installed script remains retained")
2912                .bytes,
2913            bytes
2914        );
2915        debug_assert_eq!(
2916            self.registry.modules.len(),
2917            self.dynamic_base + self.dynamic.len()
2918        );
2919        Ok(module)
2920    }
2921
2922    fn allocate_cell(&mut self, value: Value, module: ModuleId) -> Result<CellId, RuntimeError> {
2923        if self.registry.cells.len() >= self.limits.max_module_cells {
2924            return Err(self.program_error(
2925                module,
2926                RuntimeErrorKind::ModuleCellLimitExceeded {
2927                    limit: self.limits.max_module_cells,
2928                },
2929            ));
2930        }
2931        let id = CellId(self.registry.cells.len());
2932        self.registry.cells.push(Cell { value });
2933        Ok(id)
2934    }
2935
2936    pub(crate) fn instantiate_modules(&mut self) -> Result<(), RuntimeError> {
2937        debug_assert!(
2938            self.dynamic.is_empty(),
2939            "module instantiation precedes dynamic script installation"
2940        );
2941        let program = self
2942            .program
2943            .expect("module registry operations require a whole program");
2944        self.registry.modules = program
2945            .modules()
2946            .iter()
2947            .map(|module| ModuleInstance {
2948                binding_cells: vec![None; module.bindings.len()],
2949                constant_cells: vec![None; module.code.constants().len()],
2950                namespace: None,
2951                state: ModuleState::Unevaluated,
2952            })
2953            .collect();
2954
2955        for module_index in 0..program.modules().len() {
2956            let module_id = ModuleId::new(module_index as u32);
2957            let bindings = program.modules()[module_index].bindings.clone();
2958            for (binding_index, binding) in bindings.into_iter().enumerate() {
2959                let initial = match binding.kind {
2960                    BindingKind::Hoisted => Some(Value::UNDEFINED),
2961                    BindingKind::Lexical => Some(Value::UNINITIALIZED),
2962                    BindingKind::Imported { .. } | BindingKind::Namespace { .. } => None,
2963                };
2964                if let Some(value) = initial {
2965                    let cell = self.allocate_cell(value, module_id)?;
2966                    self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
2967                }
2968            }
2969        }
2970
2971        for module_index in 0..program.modules().len() {
2972            let module_id = ModuleId::new(module_index as u32);
2973            let bindings = program.modules()[module_index].bindings.clone();
2974            for (binding_index, binding) in bindings.into_iter().enumerate() {
2975                let cell = match binding.kind {
2976                    BindingKind::Hoisted | BindingKind::Lexical => continue,
2977                    BindingKind::Imported { edge, name } => {
2978                        let dependency = program.modules()[module_index].edges[edge.get() as usize];
2979                        match dependency.target {
2980                            EdgeTarget::External => {
2981                                let name = self.constant_text(module_id, name).clone();
2982                                self.external_export_cell(module_id, edge, &name)?
2983                            }
2984                            EdgeTarget::Local(target) => match program
2985                                .resolve_export(target, self.constant_text(module_id, name))
2986                            {
2987                                Some(ResolvedExport::Local { module, binding }) => {
2988                                    self.registry.modules[module.get() as usize].binding_cells
2989                                        [binding.get() as usize]
2990                                        .expect("own cells are allocated before aliases link")
2991                                }
2992                                Some(ResolvedExport::External { module, edge, name }) => {
2993                                    let name = self.constant_text(module, name).clone();
2994                                    self.external_export_cell(module, edge, &name)?
2995                                }
2996                                None => {
2997                                    return Err(self.program_error(
2998                                        module_id,
2999                                        RuntimeErrorKind::InvalidVerifiedProgram {
3000                                            module: module_id,
3001                                            instruction: Instruction::Import {
3002                                                dst: bamts_bytecode::Register::new(0),
3003                                                specifier: name,
3004                                            },
3005                                        },
3006                                    ));
3007                                }
3008                            },
3009                        }
3010                    }
3011                    BindingKind::Namespace { edge } => {
3012                        let dependency = program.modules()[module_index].edges[edge.get() as usize];
3013                        let namespace = match dependency.target {
3014                            EdgeTarget::Local(target) => {
3015                                self.module_namespace(target, module_id)?
3016                            }
3017                            EdgeTarget::External => self.external_namespace(module_id, edge)?,
3018                        };
3019                        self.allocate_cell(namespace, module_id)?
3020                    }
3021                };
3022                self.registry.modules[module_index].binding_cells[binding_index] = Some(cell);
3023            }
3024        }
3025
3026        for module_index in 0..program.modules().len() {
3027            let bindings = &program.modules()[module_index].bindings;
3028            let constants = program.modules()[module_index].code.constants();
3029            for (constant_index, constant) in constants.iter().enumerate() {
3030                let Constant::String(name) = constant else {
3031                    continue;
3032                };
3033                if let Some((binding_index, _)) =
3034                    bindings.iter().enumerate().find(|(_, binding)| {
3035                        self.constant_text(ModuleId::new(module_index as u32), binding.name) == name
3036                    })
3037                {
3038                    self.registry.modules[module_index].constant_cells[constant_index] =
3039                        self.registry.modules[module_index].binding_cells[binding_index];
3040                }
3041            }
3042        }
3043        Ok(())
3044    }
3045
3046    fn module_namespace(
3047        &mut self,
3048        target: ModuleId,
3049        requester: ModuleId,
3050    ) -> Result<Value, RuntimeError> {
3051        if let Some(value) = self.registry.modules[target.get() as usize].namespace {
3052            return Ok(value);
3053        }
3054        let exported_names: Vec<EcmaString> = self
3055            .program_module(target)
3056            .exports
3057            .iter()
3058            .map(|export| self.constant_text(target, export.name).clone())
3059            .collect();
3060        for exported_name in exported_names {
3061            if let Some(ResolvedExport::External { module, edge, name }) =
3062                self.program().resolve_export(target, &exported_name)
3063            {
3064                let name = self.constant_text(module, name).clone();
3065                self.external_export_cell(module, edge, &name)?;
3066            }
3067        }
3068        let value = self
3069            .allocate(HeapEntry::ModuleNamespace { module: target })
3070            .map_err(|kind| self.program_error(requester, kind))?;
3071        self.registry.modules[target.get() as usize].namespace = Some(value);
3072        Ok(value)
3073    }
3074
3075    fn external_specifier(&self, module: ModuleId, edge: EdgeId) -> Option<EcmaString> {
3076        let dependency = self.program_module(module).edges[edge.get() as usize];
3077        let specifier = self.constant_text(module, dependency.specifier);
3078        self.registry
3079            .external
3080            .contains_key(specifier)
3081            .then(|| specifier.clone())
3082    }
3083
3084    fn external_namespace(
3085        &mut self,
3086        module: ModuleId,
3087        edge: EdgeId,
3088    ) -> Result<Value, RuntimeError> {
3089        let Some(specifier) = self.external_specifier(module, edge) else {
3090            return Err(self.program_error(
3091                module,
3092                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3093            ));
3094        };
3095        let export_names: Vec<EcmaString> = self.registry.external[&specifier]
3096            .exports
3097            .keys()
3098            .cloned()
3099            .collect();
3100        for name in export_names {
3101            self.external_export_cell(module, edge, &name)?;
3102        }
3103        Ok(self.registry.external[&specifier].namespace)
3104    }
3105
3106    fn external_export_cell(
3107        &mut self,
3108        module: ModuleId,
3109        edge: EdgeId,
3110        name: &EcmaString,
3111    ) -> Result<CellId, RuntimeError> {
3112        let Some(specifier) = self.external_specifier(module, edge) else {
3113            return Err(self.program_error(
3114                module,
3115                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3116            ));
3117        };
3118        let Some(export) = self.registry.external[&specifier]
3119            .exports
3120            .get(name)
3121            .copied()
3122        else {
3123            return Err(self.program_error(
3124                module,
3125                RuntimeErrorKind::ExternalModuleUnavailable { module, edge },
3126            ));
3127        };
3128        if let Some(cell) = export.cell {
3129            return Ok(cell);
3130        }
3131        let cell = self.allocate_cell(export.value, module)?;
3132        self.registry
3133            .external
3134            .get_mut(&specifier)
3135            .expect("external module remains registered")
3136            .exports
3137            .get_mut(name)
3138            .expect("external export remains registered")
3139            .cell = Some(cell);
3140        Ok(cell)
3141    }
3142
3143    pub(crate) fn resolve_import(
3144        &self,
3145        module: ModuleId,
3146        specifier: ConstantId,
3147    ) -> Result<ImportTarget, RuntimeErrorKind> {
3148        let name = self.constant_text(module, specifier);
3149        self.program_module(module)
3150            .edges
3151            .iter()
3152            .enumerate()
3153            .find(|(_, edge)| {
3154                edge.kind.has_dynamic() && self.constant_text(module, edge.specifier) == name
3155            })
3156            .map(|(index, edge)| match edge.target {
3157                EdgeTarget::Local(target) => ImportTarget::Local(target),
3158                EdgeTarget::External => ImportTarget::External(EdgeId::new(index as u32)),
3159            })
3160            .ok_or(RuntimeErrorKind::DynamicImportEdgeMissing { module, specifier })
3161    }
3162
3163    pub(crate) fn imported_namespace(
3164        &mut self,
3165        requester: ModuleId,
3166        target: ImportTarget,
3167    ) -> Result<Value, RuntimeErrorKind> {
3168        match target {
3169            ImportTarget::Local(target) => self.module_namespace(target, requester),
3170            ImportTarget::External(edge) => self.external_namespace(requester, edge),
3171        }
3172        .map_err(|error| error.kind)
3173    }
3174
3175    fn run_import_entry(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3176        let function = self.module_code(module).entry();
3177        let stop_depth = self.frames.len();
3178        self.push_frame(
3179            RuntimeFunction { module, function },
3180            &[],
3181            Value::UNDEFINED,
3182            Value::UNDEFINED,
3183            &[],
3184            None,
3185        )?;
3186        let result = self.run_loop(stop_depth).and_then(|execution| {
3187            execution.map(|_| ()).ok_or_else(|| {
3188                self.program_error(
3189                    module,
3190                    RuntimeErrorKind::InvalidVerifiedProgram {
3191                        module,
3192                        instruction: Instruction::Halt,
3193                    },
3194                )
3195            })
3196        });
3197        if result.is_err() {
3198            self.unwind_frames_to(stop_depth);
3199        }
3200        result
3201    }
3202
3203    fn evaluate_import(&mut self, module: ModuleId) -> Result<(), RuntimeError> {
3204        let dependencies = match self.begin_module_evaluation(module)? {
3205            ModuleEvaluation::Cycle => return Ok(()),
3206            ModuleEvaluation::Evaluated(result) => return result,
3207            ModuleEvaluation::Ready(dependencies) => dependencies,
3208        };
3209        for dependency in dependencies {
3210            if let Err(error) = self.evaluate_import(dependency) {
3211                self.settle_module_evaluation(module, Err(error.clone()));
3212                return Err(error);
3213            }
3214        }
3215        let result = self.run_import_entry(module);
3216        self.settle_module_evaluation(module, result.clone());
3217        result
3218    }
3219
3220    fn import_namespace(
3221        &mut self,
3222        requester: ModuleId,
3223        specifier: ConstantId,
3224    ) -> Result<Value, EvalFailure> {
3225        let target = self
3226            .resolve_import(requester, specifier)
3227            .map_err(EvalFailure::Runtime)?;
3228        if let ImportTarget::Local(module) = target {
3229            self.evaluate_import(module)
3230                .map_err(|error| import_failure(&error))?;
3231        }
3232        self.imported_namespace(requester, target)
3233            .map_err(EvalFailure::Runtime)
3234    }
3235    fn evaluate_module(&mut self, module: ModuleId) -> Result<Option<Execution>, RuntimeError> {
3236        let dependencies = match self.begin_module_evaluation(module)? {
3237            ModuleEvaluation::Cycle => return Ok(None),
3238            ModuleEvaluation::Evaluated(result) => return result.map(|()| None),
3239            ModuleEvaluation::Ready(dependencies) => dependencies,
3240        };
3241        for dependency in dependencies {
3242            if let Err(error) = self.evaluate_module(dependency) {
3243                return self.finish_module_evaluation(module, Err(error)).map(Some);
3244            }
3245        }
3246
3247        let code = self.module_code(module);
3248        let function = code.entry().get() as usize;
3249        let metadata = &code.functions()[function];
3250        let register_count = metadata.register_count() as usize;
3251        let result = if self.limits.max_call_depth < 1 {
3252            Err(self.program_error(
3253                module,
3254                RuntimeErrorKind::CallDepthExceeded {
3255                    limit: self.limits.max_call_depth,
3256                },
3257            ))
3258        } else if register_count > self.limits.max_total_registers {
3259            Err(self.program_error(
3260                module,
3261                RuntimeErrorKind::RegisterLimitExceeded {
3262                    limit: self.limits.max_total_registers,
3263                },
3264            ))
3265        } else {
3266            self.frames.push(Frame::new(
3267                RuntimeFunction {
3268                    module,
3269                    function: FunctionId::new(function as u32),
3270                },
3271                metadata,
3272                &[],
3273                Value::UNDEFINED,
3274                Value::UNDEFINED,
3275                &[],
3276                None,
3277            ));
3278            self.live_registers = register_count;
3279            self.run_loop(0).and_then(|execution| {
3280                execution.ok_or_else(|| {
3281                    self.program_error(
3282                        module,
3283                        RuntimeErrorKind::InvalidVerifiedProgram {
3284                            module,
3285                            instruction: Instruction::Halt,
3286                        },
3287                    )
3288                })
3289            })
3290        };
3291        self.finish_module_evaluation(module, result).map(Some)
3292    }
3293
3294    pub(crate) fn begin_module_evaluation(
3295        &mut self,
3296        module: ModuleId,
3297    ) -> Result<ModuleEvaluation, RuntimeError> {
3298        match self.registry.modules[module.get() as usize].state.clone() {
3299            ModuleState::Evaluating => return Ok(ModuleEvaluation::Cycle),
3300            ModuleState::Evaluated(result) => return Ok(ModuleEvaluation::Evaluated(result)),
3301            ModuleState::Unevaluated => {}
3302        }
3303        self.registry.modules[module.get() as usize].state = ModuleState::Evaluating;
3304
3305        let mut dependencies = Vec::new();
3306        for (edge_index, edge) in self
3307            .program_module(module)
3308            .edges
3309            .iter()
3310            .copied()
3311            .enumerate()
3312        {
3313            if !edge.kind.has_static() {
3314                continue;
3315            }
3316            match edge.target {
3317                EdgeTarget::Local(dependency) => dependencies.push(dependency),
3318                EdgeTarget::External
3319                    if self
3320                        .external_specifier(module, EdgeId::new(edge_index as u32))
3321                        .is_some() => {}
3322                EdgeTarget::External => {
3323                    let error = self.program_error(
3324                        module,
3325                        RuntimeErrorKind::ExternalModuleUnavailable {
3326                            module,
3327                            edge: EdgeId::new(edge_index as u32),
3328                        },
3329                    );
3330                    self.settle_module_evaluation(module, Err(error.clone()));
3331                    return Err(error);
3332                }
3333            }
3334        }
3335        Ok(ModuleEvaluation::Ready(dependencies))
3336    }
3337
3338    pub(crate) fn finish_module_evaluation(
3339        &mut self,
3340        module: ModuleId,
3341        result: Result<Execution, RuntimeError>,
3342    ) -> Result<Execution, RuntimeError> {
3343        if result.is_err() {
3344            self.frames.clear();
3345            self.live_registers = 0;
3346        }
3347        let stored = result.as_ref().map(|_| ()).map_err(Clone::clone);
3348        self.settle_module_evaluation(module, stored);
3349        result
3350    }
3351
3352    pub(crate) fn settle_module_evaluation(
3353        &mut self,
3354        module: ModuleId,
3355        result: Result<(), RuntimeError>,
3356    ) {
3357        match result {
3358            Ok(()) => {
3359                self.registry.modules[module.get() as usize].state = ModuleState::Evaluated(Ok(()));
3360            }
3361            Err(error) if matches!(error.kind, RuntimeErrorKind::UncaughtThrow { .. }) => {
3362                self.registry.modules[module.get() as usize].state =
3363                    ModuleState::Evaluated(Err(error));
3364            }
3365            Err(_) => self.abort_module_evaluation(module),
3366        }
3367    }
3368
3369    pub(crate) fn abort_module_evaluation(&mut self, module: ModuleId) {
3370        if matches!(
3371            self.registry.modules[module.get() as usize].state,
3372            ModuleState::Evaluating
3373        ) {
3374            self.registry.modules[module.get() as usize].state = ModuleState::Unevaluated;
3375        }
3376    }
3377
3378    pub(crate) fn constant_text(&self, module: ModuleId, id: ConstantId) -> &EcmaString {
3379        match &self.module_code(module).constants()[id.get() as usize] {
3380            Constant::String(text) => text,
3381            _ => unreachable!("verified module names are strings"),
3382        }
3383    }
3384
3385    fn program_error(&self, module: ModuleId, kind: RuntimeErrorKind) -> RuntimeError {
3386        let code = self.module_code(module);
3387        let function = code.entry().get() as usize;
3388        let instruction = code.functions()[function]
3389            .code()
3390            .first()
3391            .copied()
3392            .unwrap_or(Instruction::Halt);
3393        RuntimeError {
3394            kind,
3395            function: FunctionId::new(function as u32),
3396            pc: Pc::new(0),
3397            source: RuntimeSource {
3398                function_name: None,
3399                instruction,
3400            },
3401        }
3402    }
3403
3404    fn run_loop(&mut self, stop_depth: usize) -> Result<Option<Execution>, RuntimeError> {
3405        if self.frames.len().saturating_add(self.native_depth) > self.limits.max_call_depth {
3406            return Err(self.error_here(RuntimeErrorKind::CallDepthExceeded {
3407                limit: self.limits.max_call_depth,
3408            }));
3409        }
3410        if self.live_registers > self.limits.max_total_registers {
3411            return Err(self.error_here(RuntimeErrorKind::RegisterLimitExceeded {
3412                limit: self.limits.max_total_registers,
3413            }));
3414        }
3415
3416        loop {
3417            let frame_index = self.frames.len() - 1;
3418            let (module_id, function_index, pc) = {
3419                let frame = &self.frames[frame_index];
3420                (frame.module, frame.function, frame.pc)
3421            };
3422            if let Err(kind) = self.consume_fuel(1) {
3423                return Err(self.error_at(kind, function_index, pc));
3424            }
3425            let instruction = self.module_code(module_id).functions()[function_index].code()[pc];
3426
3427            match instruction {
3428                Instruction::LoadConst { dst, constant } => {
3429                    let value = self.load_constant(constant, function_index, pc)?;
3430                    self.write_register(frame_index, dst.get(), value);
3431                    self.frames[frame_index].pc = pc + 1;
3432                }
3433                Instruction::Move { dst, src } => {
3434                    let value = self.read_register(frame_index, src.get());
3435                    self.write_register(frame_index, dst.get(), value);
3436                    self.frames[frame_index].pc = pc + 1;
3437                }
3438                Instruction::Unary { dst, op, operand } => {
3439                    let value = self.read_register(frame_index, operand.get());
3440                    match self.eval_unary(op, value) {
3441                        Ok(result) => {
3442                            self.write_register(frame_index, dst.get(), result);
3443                            self.frames[frame_index].pc = pc + 1;
3444                        }
3445                        Err(failure) => self.resolve_failure(failure, pc)?,
3446                    }
3447                }
3448                Instruction::Binary {
3449                    dst,
3450                    op,
3451                    left,
3452                    right,
3453                } => {
3454                    let left = self.read_register(frame_index, left.get());
3455                    let right = self.read_register(frame_index, right.get());
3456                    match self.eval_binary(op, left, right) {
3457                        Ok(result) => {
3458                            self.write_register(frame_index, dst.get(), result);
3459                            self.frames[frame_index].pc = pc + 1;
3460                        }
3461                        Err(failure) => self.resolve_failure(failure, pc)?,
3462                    }
3463                }
3464                Instruction::CreateObject { dst } => {
3465                    let value = self
3466                        .allocate(HeapEntry::Object {
3467                            properties: PropertyMap::default(),
3468                            prototype: Some(self.intrinsics.object_prototype),
3469                            boxed_primitive: None,
3470                            extensible: true,
3471                        })
3472                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3473                    self.write_register(frame_index, dst.get(), value);
3474                    self.frames[frame_index].pc = pc + 1;
3475                }
3476                Instruction::CreateArray { dst } => {
3477                    let value = self
3478                        .allocate(HeapEntry::Array {
3479                            elements: Vec::new(),
3480                            properties: PropertyMap::default(),
3481                            prototype: Some(self.intrinsics.array_prototype),
3482                            extensible: true,
3483                            length_writable: true,
3484                        })
3485                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3486                    self.write_register(frame_index, dst.get(), value);
3487                    self.frames[frame_index].pc = pc + 1;
3488                }
3489                Instruction::CreateCell { dst } => {
3490                    let value = self
3491                        .allocate(HeapEntry::Array {
3492                            elements: vec![Value::UNINITIALIZED],
3493                            properties: PropertyMap::default(),
3494                            prototype: Some(self.intrinsics.array_prototype),
3495                            extensible: true,
3496                            length_writable: true,
3497                        })
3498                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3499                    self.write_register(frame_index, dst.get(), value);
3500                    self.frames[frame_index].pc = pc + 1;
3501                }
3502                Instruction::CreateClosure {
3503                    dst,
3504                    function,
3505                    captures,
3506                } => match self.read_captures(frame_index, captures.get(), function) {
3507                    Ok(captures) => {
3508                        let value = self
3509                            .allocate(HeapEntry::Function {
3510                                module: module_id,
3511                                function,
3512                                captures,
3513                                properties: PropertyMap::default(),
3514                                prototype: Some(self.intrinsics.function_prototype),
3515                                extensible: true,
3516                            })
3517                            .map_err(|kind| self.error_at(kind, function_index, pc))?;
3518                        self.write_register(frame_index, dst.get(), value);
3519                        self.frames[frame_index].pc = pc + 1;
3520                    }
3521                    Err(failure) => self.resolve_failure(failure, pc)?,
3522                },
3523                Instruction::GetProperty { dst, object, key } => {
3524                    let object = self.read_register(frame_index, object.get());
3525                    let key_value = self.read_register(frame_index, key.get());
3526                    let key = match self.to_property_key(key_value) {
3527                        Ok(key) => key,
3528                        Err(failure) => {
3529                            self.resolve_failure(failure, pc)?;
3530                            continue;
3531                        }
3532                    };
3533                    match self.resolve_get(object, &key) {
3534                        Ok(GetOutcome::Value(value)) => {
3535                            self.write_register(frame_index, dst.get(), value);
3536                            self.frames[frame_index].pc = pc + 1;
3537                        }
3538                        Ok(GetOutcome::Text(text)) => {
3539                            let value = self
3540                                .allocate(HeapEntry::String(text))
3541                                .map_err(|kind| self.error_at(kind, function_index, pc))?;
3542                            self.write_register(frame_index, dst.get(), value);
3543                            self.frames[frame_index].pc = pc + 1;
3544                        }
3545                        Ok(GetOutcome::Getter(getter)) => {
3546                            self.frames[frame_index].pc = pc + 1;
3547                            self.execute_call(CallRequest {
3548                                callee: getter,
3549                                this_value: object,
3550                                arguments: &[],
3551                                destination: Some(dst.get()),
3552                                call_pc: pc,
3553                                constructed: None,
3554                                new_target: Value::UNDEFINED,
3555                            })?;
3556                        }
3557                        Err(failure) => self.resolve_failure(failure, pc)?,
3558                    }
3559                }
3560                Instruction::SetProperty { object, key, value } => {
3561                    let object = self.read_register(frame_index, object.get());
3562                    let value = self.read_register(frame_index, value.get());
3563                    let key_value = self.read_register(frame_index, key.get());
3564                    let key = match self.to_property_key(key_value) {
3565                        Ok(key) => key,
3566                        Err(failure) => {
3567                            self.resolve_failure(failure, pc)?;
3568                            continue;
3569                        }
3570                    };
3571                    match self.resolve_set(object, key, value) {
3572                        Ok(SetOutcome::Done) => self.frames[frame_index].pc = pc + 1,
3573                        Ok(SetOutcome::Setter(setter)) => {
3574                            self.frames[frame_index].pc = pc + 1;
3575                            self.execute_call(CallRequest {
3576                                callee: setter,
3577                                this_value: object,
3578                                arguments: &[value],
3579                                destination: None,
3580                                call_pc: pc,
3581                                constructed: None,
3582                                new_target: Value::UNDEFINED,
3583                            })?;
3584                        }
3585                        Err(failure) => self.resolve_failure(failure, pc)?,
3586                    }
3587                }
3588                Instruction::DeleteProperty { dst, object, key } => {
3589                    let object = self.read_register(frame_index, object.get());
3590                    let key_value = self.read_register(frame_index, key.get());
3591                    let key = match self.to_property_key(key_value) {
3592                        Ok(key) => key,
3593                        Err(failure) => {
3594                            self.resolve_failure(failure, pc)?;
3595                            continue;
3596                        }
3597                    };
3598                    match self.delete_property(object, &key) {
3599                        Ok(deleted) => {
3600                            self.write_register(frame_index, dst.get(), Value::boolean(deleted));
3601                            self.frames[frame_index].pc = pc + 1;
3602                        }
3603                        Err(failure) => self.resolve_failure(failure, pc)?,
3604                    }
3605                }
3606                Instruction::DefineAccessor {
3607                    object,
3608                    key,
3609                    accessor,
3610                    kind,
3611                } => {
3612                    let object = self.read_register(frame_index, object.get());
3613                    let accessor = self.read_register(frame_index, accessor.get());
3614                    let key_value = self.read_register(frame_index, key.get());
3615                    let key = match self.to_property_key(key_value) {
3616                        Ok(key) => key,
3617                        Err(failure) => {
3618                            self.resolve_failure(failure, pc)?;
3619                            continue;
3620                        }
3621                    };
3622                    match self.define_accessor(object, key, accessor, kind) {
3623                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3624                        Err(failure) => self.resolve_failure(failure, pc)?,
3625                    }
3626                }
3627                Instruction::Call {
3628                    dst,
3629                    callee,
3630                    this_value,
3631                    arguments,
3632                } => {
3633                    let callee = self.read_register(frame_index, callee.get());
3634                    let this_value = self.read_register(frame_index, this_value.get());
3635                    match self.read_arguments(frame_index, arguments.get()) {
3636                        Ok(arguments) => {
3637                            self.frames[frame_index].pc = pc + 1;
3638                            self.execute_call(CallRequest {
3639                                callee,
3640                                this_value,
3641                                arguments: &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::Construct {
3652                    dst,
3653                    callee,
3654                    arguments,
3655                } => {
3656                    let callee = self.read_register(frame_index, callee.get());
3657                    match self.read_arguments(frame_index, arguments.get()) {
3658                        Ok(arguments) => {
3659                            self.frames[frame_index].pc = pc + 1;
3660                            self.execute_construct(callee, &arguments, dst.get(), pc)?;
3661                        }
3662                        Err(failure) => self.resolve_failure(failure, pc)?,
3663                    }
3664                }
3665                Instruction::LoadGlobal { dst, name } => match self.load_global(module_id, name) {
3666                    Ok(Some(value)) => {
3667                        self.write_register(frame_index, dst.get(), value);
3668                        self.frames[frame_index].pc = pc + 1;
3669                    }
3670                    Ok(None) => self.throw(
3671                        Value::UNDEFINED,
3672                        ThrowOrigin::ReferenceError {
3673                            operation: "global is not defined",
3674                        },
3675                        pc,
3676                    )?,
3677                    Err(kind) => return Err(self.error_here_at(kind, pc)),
3678                },
3679                Instruction::StoreGlobal { name, value } => {
3680                    let value = self.read_register(frame_index, value.get());
3681                    match self.store_global(module_id, name, value) {
3682                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3683                        Err(failure) => self.resolve_failure(failure, pc)?,
3684                    }
3685                }
3686                Instruction::TypeOfGlobal { dst, name } => {
3687                    let text = match self.load_global(module_id, name) {
3688                        Ok(value) => value.map_or("undefined", |value| self.type_of(value)),
3689                        Err(kind) => return Err(self.error_here_at(kind, pc)),
3690                    };
3691                    let value = self
3692                        .allocate(HeapEntry::String(EcmaString::from_utf8(text)))
3693                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3694                    self.write_register(frame_index, dst.get(), value);
3695                    self.frames[frame_index].pc = pc + 1;
3696                }
3697                Instruction::LoadThis { dst } => {
3698                    let value = self.frames[frame_index].this_value;
3699                    self.write_register(frame_index, dst.get(), value);
3700                    self.frames[frame_index].pc = pc + 1;
3701                }
3702                Instruction::LoadArguments { dst } => {
3703                    let value = self.materialize_arguments(frame_index, function_index, pc)?;
3704                    self.write_register(frame_index, dst.get(), value);
3705                    self.frames[frame_index].pc = pc + 1;
3706                }
3707                Instruction::LoadNewTarget { dst } => {
3708                    let value = self.frames[frame_index].new_target;
3709                    self.write_register(frame_index, dst.get(), value);
3710                    self.frames[frame_index].pc = pc + 1;
3711                }
3712                Instruction::ArrayPush { array, value } => {
3713                    let array = self.read_register(frame_index, array.get());
3714                    let value = self.read_register(frame_index, value.get());
3715                    match self.array_push(array, value) {
3716                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3717                        Err(failure) => self.resolve_failure(failure, pc)?,
3718                    }
3719                }
3720                Instruction::ArrayExtend { array, iterable } => {
3721                    let array = self.read_register(frame_index, array.get());
3722                    let iterable = self.read_register(frame_index, iterable.get());
3723                    match self.array_extend(array, iterable) {
3724                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3725                        Err(failure) => self.resolve_failure(failure, pc)?,
3726                    }
3727                }
3728                Instruction::ObjectSpread { target, source } => {
3729                    let target = self.read_register(frame_index, target.get());
3730                    let source = self.read_register(frame_index, source.get());
3731                    match self.object_spread(target, source) {
3732                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3733                        Err(failure) => self.resolve_failure(failure, pc)?,
3734                    }
3735                }
3736                Instruction::SetPrototype { object, prototype } => {
3737                    let object = self.read_register(frame_index, object.get());
3738                    let prototype = self.read_register(frame_index, prototype.get());
3739                    match self.set_prototype(object, prototype) {
3740                        Ok(()) => self.frames[frame_index].pc = pc + 1,
3741                        Err(failure) => self.resolve_failure(failure, pc)?,
3742                    }
3743                }
3744                Instruction::CreatePrivateName { dst, description } => {
3745                    let description = self.constant_string(description).clone();
3746                    let value = self
3747                        .allocate(HeapEntry::PrivateName { description })
3748                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3749                    self.write_register(frame_index, dst.get(), value);
3750                    self.frames[frame_index].pc = pc + 1;
3751                }
3752                Instruction::CreateRegExp {
3753                    dst,
3754                    pattern,
3755                    flags,
3756                } => {
3757                    let pattern = self.constant_string(pattern).clone();
3758                    let flags = self.constant_string(flags).clone();
3759                    let value = self
3760                        .allocate(HeapEntry::RegExp {
3761                            pattern,
3762                            flags,
3763                            properties: PropertyMap::default(),
3764                            prototype: Some(self.intrinsics.regexp_prototype()),
3765                            extensible: true,
3766                        })
3767                        .map_err(|kind| self.error_at(kind, function_index, pc))?;
3768                    self.write_register(frame_index, dst.get(), value);
3769                    self.frames[frame_index].pc = pc + 1;
3770                }
3771                Instruction::GetIterator { dst, src, kind } => {
3772                    let src = self.read_register(frame_index, src.get());
3773                    match self.create_iterator(src, kind) {
3774                        Ok(value) => {
3775                            self.write_register(frame_index, dst.get(), value);
3776                            self.frames[frame_index].pc = pc + 1;
3777                        }
3778                        Err(failure) => self.resolve_failure(failure, pc)?,
3779                    }
3780                }
3781                Instruction::IteratorNext {
3782                    done,
3783                    value,
3784                    iterator,
3785                } => {
3786                    let iterator = self.read_register(frame_index, iterator.get());
3787                    match self.iterator_next(iterator) {
3788                        Ok((is_done, produced)) => {
3789                            self.write_register(frame_index, done.get(), Value::boolean(is_done));
3790                            self.write_register(frame_index, value.get(), produced);
3791                            self.frames[frame_index].pc = pc + 1;
3792                        }
3793                        Err(failure) => self.resolve_failure(failure, pc)?,
3794                    }
3795                }
3796                Instruction::Jump { target } => {
3797                    self.frames[frame_index].pc = target.get() as usize;
3798                }
3799                Instruction::JumpIfTrue { condition, target } => {
3800                    let condition = self.read_register(frame_index, condition.get());
3801                    self.frames[frame_index].pc = if self.truthy(condition) {
3802                        target.get() as usize
3803                    } else {
3804                        pc + 1
3805                    };
3806                }
3807                Instruction::JumpIfFalse { condition, target } => {
3808                    let condition = self.read_register(frame_index, condition.get());
3809                    self.frames[frame_index].pc = if self.truthy(condition) {
3810                        pc + 1
3811                    } else {
3812                        target.get() as usize
3813                    };
3814                }
3815                Instruction::Return { value } => {
3816                    let value = self.read_register(frame_index, value.get());
3817                    if let Some(execution) = self.complete_frame(value) {
3818                        return Ok(Some(execution));
3819                    }
3820                    if self.frames.len() == stop_depth {
3821                        return Ok(None);
3822                    }
3823                }
3824                Instruction::Throw { value } => {
3825                    let value = self.read_register(frame_index, value.get());
3826                    self.throw(value, ThrowOrigin::Bytecode, pc)?;
3827                }
3828                Instruction::Suspend { src, .. }
3829                    if self
3830                        .async_boundaries
3831                        .last()
3832                        .is_some_and(|boundary| *boundary == frame_index) =>
3833                {
3834                    let awaited = self.read_register(frame_index, src.get());
3835                    let frame = self.frames.pop().expect("async activation is executing");
3836                    self.pending_async_suspend = Some((
3837                        awaited,
3838                        SuspendedActivation {
3839                            target: RuntimeFunction {
3840                                module: frame.module,
3841                                function: FunctionId::new(frame.function as u32),
3842                            },
3843                            registers: frame.registers,
3844                            this_value: frame.this_value,
3845                            new_target: frame.new_target,
3846                            args: frame.args,
3847                            arguments_object: frame.arguments_object,
3848                            resume_token: pc as u32 + 1,
3849                        },
3850                    ));
3851                    return Ok(None);
3852                }
3853                Instruction::Suspend { src, .. }
3854                    if self
3855                        .generator_boundaries
3856                        .last()
3857                        .is_some_and(|boundary| *boundary == frame_index) =>
3858                {
3859                    let value = self.read_register(frame_index, src.get());
3860                    let frame = self
3861                        .frames
3862                        .pop()
3863                        .expect("generator activation is executing");
3864                    self.pending_generator_resume = Some(GeneratorResume::Yield {
3865                        value,
3866                        activation: SuspendedActivation {
3867                            target: RuntimeFunction {
3868                                module: frame.module,
3869                                function: FunctionId::new(frame.function as u32),
3870                            },
3871                            registers: frame.registers,
3872                            this_value: frame.this_value,
3873                            new_target: frame.new_target,
3874                            args: frame.args,
3875                            arguments_object: frame.arguments_object,
3876                            resume_token: pc as u32 + 1,
3877                        },
3878                    });
3879                    return Ok(None);
3880                }
3881                Instruction::Suspend { .. } => {
3882                    self.throw_type("suspend outside an engine-owned event loop", pc)?;
3883                }
3884                Instruction::Import { dst, specifier } => {
3885                    match self.import_namespace(module_id, specifier) {
3886                        Ok(namespace) => {
3887                            self.write_register(frame_index, dst.get(), namespace);
3888                            self.frames[frame_index].pc = pc + 1;
3889                        }
3890                        Err(failure) => self.resolve_failure(failure, pc)?,
3891                    }
3892                }
3893                Instruction::Export { .. } => {
3894                    return Err(self.error_here_at(
3895                        RuntimeErrorKind::InvalidVerifiedProgram {
3896                            module: module_id,
3897                            instruction,
3898                        },
3899                        pc,
3900                    ));
3901                }
3902                Instruction::Halt => {
3903                    if let Some(execution) = self.complete_frame(Value::UNDEFINED) {
3904                        return Ok(Some(execution));
3905                    }
3906                    if self.frames.len() == stop_depth {
3907                        return Ok(None);
3908                    }
3909                }
3910            }
3911        }
3912    }
3913
3914    fn read_register(&self, frame: usize, register: u32) -> Value {
3915        self.frames[frame].registers[register as usize]
3916    }
3917
3918    fn write_register(&mut self, frame: usize, register: u32, value: Value) {
3919        self.frames[frame].registers[register as usize] = value;
3920    }
3921
3922    fn constant_string(&self, id: ConstantId) -> &EcmaString {
3923        self.constant_text(self.active_module_id(), id)
3924    }
3925
3926    fn load_constant(
3927        &mut self,
3928        id: ConstantId,
3929        function: usize,
3930        pc: usize,
3931    ) -> Result<Value, RuntimeError> {
3932        self.load_constant_value(self.active_module_id(), id)
3933            .map_err(|kind| self.error_at(kind, function, pc))
3934    }
3935
3936    fn allocate(&mut self, entry: HeapEntry) -> Result<Value, RuntimeErrorKind> {
3937        let bytes = entry.initial_bytes();
3938        self.ensure_allocation_capacity(1, bytes)?;
3939        self.heap_bytes += bytes;
3940        let slot = self.heap.len() as u32 + 1;
3941        self.heap.push(entry);
3942        let id = SlotId::from_parts(RUNTIME_HEAP_SEGMENT, slot)
3943            .expect("runtime segment and one-based slot are nonzero");
3944        Ok(Value::heap_ref(id))
3945    }
3946
3947    fn ensure_allocation_capacity(
3948        &self,
3949        additional_slots: usize,
3950        additional_bytes: usize,
3951    ) -> Result<(), RuntimeErrorKind> {
3952        let used_slots = self.heap.len().saturating_sub(self.intrinsic_slots);
3953        let slots_fit_limit = used_slots
3954            .checked_add(additional_slots)
3955            .is_some_and(|total| total <= self.limits.max_heap_slots);
3956        let slots_fit_value = self
3957            .heap
3958            .len()
3959            .checked_add(additional_slots)
3960            .is_some_and(|total| total <= u32::MAX as usize);
3961        if !slots_fit_limit || !slots_fit_value {
3962            return Err(RuntimeErrorKind::HeapSlotLimitExceeded {
3963                limit: self.limits.max_heap_slots,
3964            });
3965        }
3966        let bytes_fit = self
3967            .heap_bytes
3968            .checked_add(additional_bytes)
3969            .is_some_and(|total| total <= self.limits.max_heap_bytes);
3970        if !bytes_fit {
3971            return Err(RuntimeErrorKind::HeapByteLimitExceeded {
3972                limit: self.limits.max_heap_bytes,
3973            });
3974        }
3975        Ok(())
3976    }
3977
3978    fn ensure_object_property_capacity(
3979        &self,
3980        property_bytes: usize,
3981    ) -> Result<(), RuntimeErrorKind> {
3982        let bytes =
3983            property_bytes
3984                .checked_add(1)
3985                .ok_or(RuntimeErrorKind::HeapByteLimitExceeded {
3986                    limit: self.limits.max_heap_bytes,
3987                })?;
3988        self.ensure_allocation_capacity(1, bytes)
3989    }
3990    fn charge_heap(&mut self, bytes: usize) -> Result<(), RuntimeErrorKind> {
3991        self.ensure_allocation_capacity(0, bytes)?;
3992        self.heap_bytes += bytes;
3993        Ok(())
3994    }
3995
3996    fn runtime_slot(&self, value: Value) -> Result<Option<usize>, RuntimeErrorKind> {
3997        let Some(decoded) = value.decode() else {
3998            return Err(RuntimeErrorKind::InvalidValue { value });
3999        };
4000        let Decoded::HeapRef(id) = decoded else {
4001            return Ok(None);
4002        };
4003        if id.segment() != RUNTIME_HEAP_SEGMENT {
4004            return Err(RuntimeErrorKind::InvalidValue { value });
4005        }
4006        let index = id.slot() as usize - 1;
4007        if index >= self.heap.len() {
4008            return Err(RuntimeErrorKind::InvalidRuntimeHeapReference { slot: id.slot() });
4009        }
4010        Ok(Some(index))
4011    }
4012
4013    fn active_module_id(&self) -> ModuleId {
4014        self.frames
4015            .last()
4016            .map_or(ModuleId::new(0), |frame| frame.module)
4017    }
4018
4019    pub(crate) fn load_global(
4020        &self,
4021        module: ModuleId,
4022        name: ConstantId,
4023    ) -> Result<Option<Value>, RuntimeErrorKind> {
4024        if let Some(cell) = self
4025            .registry
4026            .modules
4027            .get(module.get() as usize)
4028            .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4029            .copied()
4030            .flatten()
4031        {
4032            let value = self.registry.cells[cell.0].value;
4033            if value.is_uninitialized() {
4034                let binding = self.registry.modules[module.get() as usize]
4035                    .binding_cells
4036                    .iter()
4037                    .position(|candidate| *candidate == Some(cell))
4038                    .map(|index| BindingId::new(index as u32))
4039                    .expect("linked cell belongs to a binding");
4040                return Err(RuntimeErrorKind::TemporalDeadZone { module, binding });
4041            }
4042            return Ok(Some(value));
4043        }
4044        Ok(self.resolve_global_binding(self.constant_text(module, name)))
4045    }
4046
4047    pub(crate) fn store_global(
4048        &mut self,
4049        module: ModuleId,
4050        name: ConstantId,
4051        value: Value,
4052    ) -> Result<(), EvalFailure> {
4053        let cell = self
4054            .registry
4055            .modules
4056            .get(module.get() as usize)
4057            .and_then(|instance| instance.constant_cells.get(name.get() as usize))
4058            .copied()
4059            .flatten();
4060        if let Some(cell) = cell {
4061            let binding = self.registry.modules[module.get() as usize]
4062                .binding_cells
4063                .iter()
4064                .position(|candidate| *candidate == Some(cell))
4065                .expect("mapped module cell belongs to a binding");
4066            if matches!(
4067                self.program_module(module).bindings[binding].kind,
4068                BindingKind::Imported { .. } | BindingKind::Namespace { .. }
4069            ) {
4070                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4071                    operation: "assign to immutable module binding",
4072                }));
4073            }
4074            self.registry.cells[cell.0].value = value;
4075        } else {
4076            let name = self.constant_text(module, name).to_owned();
4077            if let Some(global_this) = self.intrinsics.global("globalThis") {
4078                let key = PropertyKey::Named(name.clone());
4079                if matches!(
4080                    self.own_descriptor(global_this, &key)?,
4081                    Some(
4082                        Property::Data {
4083                            writable: false,
4084                            ..
4085                        } | Property::Accessor { setter: None, .. }
4086                    )
4087                ) {
4088                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4089                        operation: "assign to non-writable global property",
4090                    }));
4091                }
4092            }
4093            self.globals.insert(name, value);
4094        }
4095        Ok(())
4096    }
4097
4098    /// Resolves a true realm global after module bindings have been considered.
4099    fn resolve_global_binding(&self, name: &EcmaString) -> Option<Value> {
4100        self.globals.get(name).copied().or_else(|| {
4101            self.intrinsics
4102                .globals
4103                .iter()
4104                .find_map(|(candidate, value)| (candidate == name).then_some(*value))
4105        })
4106    }
4107
4108    /// Classifies a callee into the shared dispatch categories.
4109    fn callee_kind(&self, callee: Value) -> Result<CalleeKind, RuntimeErrorKind> {
4110        match self.runtime_slot(callee)? {
4111            Some(index) => match &self.heap[index] {
4112                HeapEntry::Function {
4113                    module,
4114                    function,
4115                    captures,
4116                    ..
4117                } => Ok(CalleeKind::Runtime {
4118                    target: RuntimeFunction {
4119                        module: *module,
4120                        function: *function,
4121                    },
4122                    captures: captures.clone(),
4123                }),
4124                HeapEntry::NativeFunction { callable, .. } => match callable {
4125                    NativeCallable::Builtin(id) => Ok(CalleeKind::Builtin { id: *id }),
4126                    NativeCallable::Bound(_) => Ok(CalleeKind::Bound),
4127                },
4128                _ => Ok(CalleeKind::NotCallable),
4129            },
4130            None => Ok(CalleeKind::NotCallable),
4131        }
4132    }
4133
4134    pub(crate) fn flatten_bound(
4135        &self,
4136        callee: Value,
4137        this_value: Value,
4138        arguments: &[Value],
4139    ) -> Result<BoundCall, RuntimeErrorKind> {
4140        let mut target = callee;
4141        let mut receiver = this_value;
4142        let mut segments = Vec::new();
4143        let mut total = arguments.len();
4144        while let Some(index) = self.runtime_slot(target)? {
4145            let HeapEntry::NativeFunction {
4146                callable: NativeCallable::Bound(bound),
4147                ..
4148            } = &self.heap[index]
4149            else {
4150                break;
4151            };
4152            total = total.checked_add(bound.arguments.len()).ok_or(
4153                RuntimeErrorKind::ArgumentLimitExceeded {
4154                    limit: self.limits.max_argument_count,
4155                    requested: u32::MAX,
4156                },
4157            )?;
4158            if total > self.limits.max_argument_count as usize {
4159                return Err(RuntimeErrorKind::ArgumentLimitExceeded {
4160                    limit: self.limits.max_argument_count,
4161                    requested: u32::try_from(total).unwrap_or(u32::MAX),
4162                });
4163            }
4164            segments.push(bound.arguments.as_slice());
4165            receiver = bound.this_value;
4166            target = bound.target;
4167        }
4168        let mut flattened = Vec::with_capacity(total);
4169        for segment in segments.iter().rev() {
4170            flattened.extend_from_slice(segment);
4171        }
4172        flattened.extend_from_slice(arguments);
4173        Ok(BoundCall {
4174            target,
4175            this_value: receiver,
4176            arguments: flattened,
4177        })
4178    }
4179
4180    fn bound_target(&self, mut value: Value) -> Result<Value, RuntimeErrorKind> {
4181        loop {
4182            let Some(index) = self.runtime_slot(value)? else {
4183                return Ok(value);
4184            };
4185            let HeapEntry::NativeFunction {
4186                callable: NativeCallable::Bound(bound),
4187                ..
4188            } = &self.heap[index]
4189            else {
4190                return Ok(value);
4191            };
4192            value = bound.target;
4193        }
4194    }
4195
4196    /// Materializes a constant into an ABI value, interning strings and bigints
4197    /// into the slot heap. Shared with the native engine.
4198    pub(crate) fn load_constant_value(
4199        &mut self,
4200        module: ModuleId,
4201        id: ConstantId,
4202    ) -> Result<Value, RuntimeErrorKind> {
4203        match &self.module_code(module).constants()[id.get() as usize] {
4204            Constant::String(text) => self.allocate(HeapEntry::String(text.clone())),
4205            Constant::BigInt(value) => self.allocate(HeapEntry::BigInt(value.as_str().to_owned())),
4206            constant => Ok(constant_value(constant).expect("non-heap constant")),
4207        }
4208    }
4209
4210    /// Reads a call/construct arguments array from a register: it must hold a
4211    /// runtime array, whose length is capped by `max_argument_count`.
4212    fn read_arguments(&self, frame: usize, register: u32) -> Result<Vec<Value>, EvalFailure> {
4213        let value = self.read_register(frame, register);
4214        self.arguments_from_array(value)
4215    }
4216
4217    /// Validates a call/construct arguments array value: it must be a runtime
4218    /// array whose length is capped by `max_argument_count`, with holes read as
4219    /// `undefined`. Shared with the native engine.
4220    fn arguments_from_array(&self, arguments: Value) -> Result<Vec<Value>, EvalFailure> {
4221        match self.runtime_slot(arguments).map_err(EvalFailure::Runtime)? {
4222            Some(index) => match &self.heap[index] {
4223                HeapEntry::Array { elements, .. } => {
4224                    if elements.len() as u64 > u64::from(self.limits.max_argument_count) {
4225                        return Err(EvalFailure::Runtime(
4226                            RuntimeErrorKind::ArgumentLimitExceeded {
4227                                limit: self.limits.max_argument_count,
4228                                requested: u32::try_from(elements.len()).unwrap_or(u32::MAX),
4229                            },
4230                        ));
4231                    }
4232                    Ok(elements
4233                        .iter()
4234                        .map(|value| {
4235                            if *value == Value::HOLE {
4236                                Value::UNDEFINED
4237                            } else {
4238                                *value
4239                            }
4240                        })
4241                        .collect())
4242                }
4243                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4244                    operation: "call arguments are not an array",
4245                })),
4246            },
4247            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4248                operation: "call arguments are not an array",
4249            })),
4250        }
4251    }
4252
4253    /// Reads a `CreateClosure` captures array: it must hold a runtime array whose
4254    /// length matches the target function's capture count.
4255    fn read_captures(
4256        &self,
4257        frame: usize,
4258        register: u32,
4259        function: FunctionId,
4260    ) -> Result<Vec<Value>, EvalFailure> {
4261        let value = self.read_register(frame, register);
4262        self.captures_from_array(self.active_module_id(), value, function)
4263    }
4264
4265    /// Validates a `CreateClosure` captures array value: it must be a runtime
4266    /// array whose length matches the target function's capture count, with
4267    /// holes read as `undefined`. Shared with the native engine.
4268    pub(crate) fn captures_from_array(
4269        &self,
4270        module: ModuleId,
4271        captures: Value,
4272        function: FunctionId,
4273    ) -> Result<Vec<Value>, EvalFailure> {
4274        let expected =
4275            self.module_code(module).functions()[function.get() as usize].capture_count() as usize;
4276        match self.runtime_slot(captures).map_err(EvalFailure::Runtime)? {
4277            Some(index) => match &self.heap[index] {
4278                HeapEntry::Array { elements, .. } => {
4279                    if elements.len() != expected {
4280                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4281                            operation: "closure capture array arity",
4282                        }));
4283                    }
4284                    Ok(elements
4285                        .iter()
4286                        .map(|value| {
4287                            if *value == Value::HOLE {
4288                                Value::UNDEFINED
4289                            } else {
4290                                *value
4291                            }
4292                        })
4293                        .collect())
4294                }
4295                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4296                    operation: "closure captures are not an array",
4297                })),
4298            },
4299            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4300                operation: "closure captures are not an array",
4301            })),
4302        }
4303    }
4304
4305    pub(crate) fn materialize_arguments(
4306        &mut self,
4307        frame: usize,
4308        function: usize,
4309        pc: usize,
4310    ) -> Result<Value, RuntimeError> {
4311        if let Some(existing) = self.frames[frame].arguments_object {
4312            return Ok(existing);
4313        }
4314        let args = self.frames[frame].args.clone();
4315        let value = self
4316            .allocate(HeapEntry::Array {
4317                elements: args,
4318                properties: PropertyMap::default(),
4319                prototype: Some(self.intrinsics.array_prototype),
4320                extensible: true,
4321                length_writable: true,
4322            })
4323            .map_err(|kind| self.error_at(kind, function, pc))?;
4324        self.frames[frame].arguments_object = Some(value);
4325        Ok(value)
4326    }
4327
4328    fn push_frame(
4329        &mut self,
4330        target: RuntimeFunction,
4331        captures: &[Value],
4332        this_value: Value,
4333        new_target: Value,
4334        arguments: &[Value],
4335        return_to: Option<ReturnTo>,
4336    ) -> Result<(), RuntimeError> {
4337        let function_index = target.function.get() as usize;
4338        let metadata = &self.module_code(target.module).functions()[function_index];
4339        let limit_error = |kind| match (self.frames.last(), return_to) {
4340            (Some(caller), Some(return_to)) => {
4341                self.error_at_in_module(kind, caller.module, caller.function, return_to.call_pc)
4342            }
4343            (_, None) => self.error_at_in_module(kind, target.module, function_index, 0),
4344            (None, Some(_)) => unreachable!("a returning frame has a caller"),
4345        };
4346        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4347            return Err(limit_error(RuntimeErrorKind::CallDepthExceeded {
4348                limit: self.limits.max_call_depth,
4349            }));
4350        }
4351        let next_registers = metadata.register_count() as usize;
4352        if self.live_registers.saturating_add(next_registers) > self.limits.max_total_registers {
4353            return Err(limit_error(RuntimeErrorKind::RegisterLimitExceeded {
4354                limit: self.limits.max_total_registers,
4355            }));
4356        }
4357        let frame = Frame::new(
4358            target, metadata, captures, this_value, new_target, arguments, return_to,
4359        );
4360        self.live_registers += next_registers;
4361        self.frames.push(frame);
4362        Ok(())
4363    }
4364
4365    pub(crate) fn consume_fuel(&mut self, amount: u64) -> Result<(), RuntimeErrorKind> {
4366        if self.fuel < amount {
4367            self.fuel = 0;
4368            return Err(RuntimeErrorKind::FuelExhausted {
4369                limit: self.limits.fuel,
4370            });
4371        }
4372        self.fuel -= amount;
4373        Ok(())
4374    }
4375
4376    pub(crate) fn reserve_native_activation(
4377        &mut self,
4378        register_count: usize,
4379    ) -> Result<(), RuntimeErrorKind> {
4380        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4381            return Err(RuntimeErrorKind::CallDepthExceeded {
4382                limit: self.limits.max_call_depth,
4383            });
4384        }
4385        if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4386            return Err(RuntimeErrorKind::RegisterLimitExceeded {
4387                limit: self.limits.max_total_registers,
4388            });
4389        }
4390        self.native_depth += 1;
4391        self.live_registers += register_count;
4392        Ok(())
4393    }
4394
4395    pub(crate) fn release_native_activation(&mut self, register_count: usize) {
4396        self.native_depth -= 1;
4397        self.live_registers -= register_count;
4398    }
4399
4400    pub(crate) fn reserve_suspended_activation_registers(
4401        &mut self,
4402        register_count: usize,
4403    ) -> Result<(), RuntimeErrorKind> {
4404        if self.live_registers.saturating_add(register_count) > self.limits.max_total_registers {
4405            return Err(RuntimeErrorKind::RegisterLimitExceeded {
4406                limit: self.limits.max_total_registers,
4407            });
4408        }
4409        self.live_registers += register_count;
4410        Ok(())
4411    }
4412
4413    pub(crate) fn release_suspended_activation_registers(&mut self, register_count: usize) {
4414        self.live_registers -= register_count;
4415    }
4416
4417    pub(crate) fn enter_native_generator(&mut self) -> Result<(), RuntimeErrorKind> {
4418        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
4419            return Err(RuntimeErrorKind::CallDepthExceeded {
4420                limit: self.limits.max_call_depth,
4421            });
4422        }
4423        self.native_depth += 1;
4424        Ok(())
4425    }
4426
4427    pub(crate) fn leave_native_generator(&mut self) {
4428        self.native_depth -= 1;
4429    }
4430
4431    fn execute_call(&mut self, request: CallRequest<'_>) -> Result<(), RuntimeError> {
4432        let CallRequest {
4433            callee,
4434            this_value,
4435            arguments,
4436            destination,
4437            call_pc,
4438            constructed,
4439            new_target,
4440        } = request;
4441        let mut callee = callee;
4442        let mut this_value = this_value;
4443        let mut arguments = Cow::Borrowed(arguments);
4444        loop {
4445            match self.callee_kind(callee) {
4446                Ok(CalleeKind::Runtime { target, captures }) => {
4447                    let flags = self.module_code(target.module).functions()
4448                        [target.function.get() as usize]
4449                        .flags();
4450                    if flags.is_generator && !flags.is_async {
4451                        let generator = self
4452                            .create_generator(GeneratorStart {
4453                                target,
4454                                captures,
4455                                this_value,
4456                                new_target,
4457                                args: arguments.as_ref().to_vec(),
4458                            })
4459                            .map_err(|kind| self.error_here_at(kind, call_pc))?;
4460                        if let Some(register) = destination {
4461                            self.write_register(self.frames.len() - 1, register, generator);
4462                        }
4463                        return Ok(());
4464                    }
4465                    if flags.is_async && !flags.is_generator {
4466                        return match self.start_async_call(
4467                            target,
4468                            &captures,
4469                            this_value,
4470                            new_target,
4471                            arguments.as_ref(),
4472                        ) {
4473                            Ok(promise) => {
4474                                if let Some(register) = destination {
4475                                    self.write_register(self.frames.len() - 1, register, promise);
4476                                }
4477                                Ok(())
4478                            }
4479                            Err(failure) => self.resolve_failure(failure, call_pc),
4480                        };
4481                    }
4482                    return self.push_frame(
4483                        target,
4484                        &captures,
4485                        this_value,
4486                        new_target,
4487                        arguments.as_ref(),
4488                        Some(ReturnTo {
4489                            destination: destination.map(|register| register as usize),
4490                            call_pc,
4491                            constructed,
4492                        }),
4493                    );
4494                }
4495                Ok(CalleeKind::Builtin { id }) => {
4496                    match self.call_builtin(id, this_value, arguments.as_ref(), false) {
4497                        Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4498                            if let Some(register) = destination {
4499                                self.write_register(self.frames.len() - 1, register, value);
4500                            }
4501                            return Ok(());
4502                        }
4503                        Ok(intrinsics::BuiltinOutcome::Call {
4504                            callee: next,
4505                            this_value: next_this,
4506                            arguments: next_arguments,
4507                        }) => {
4508                            callee = next;
4509                            this_value = next_this;
4510                            arguments = Cow::Owned(next_arguments);
4511                        }
4512                        Ok(intrinsics::BuiltinOutcome::GeneratorNext {
4513                            generator,
4514                            resume_value,
4515                        }) => match self.resume_generator(generator, resume_value) {
4516                            Ok(value) => {
4517                                if let Some(register) = destination {
4518                                    self.write_register(self.frames.len() - 1, register, value);
4519                                }
4520                                return Ok(());
4521                            }
4522                            Err(failure) => return self.resolve_failure(failure, call_pc),
4523                        },
4524                        Ok(intrinsics::BuiltinOutcome::ConstructCall { .. }) => {
4525                            return self.throw_type("call", call_pc);
4526                        }
4527                        Err(failure) => return self.resolve_failure(failure, call_pc),
4528                    }
4529                }
4530                Ok(CalleeKind::Bound) => {
4531                    let bound = self
4532                        .flatten_bound(callee, this_value, arguments.as_ref())
4533                        .map_err(|kind| self.error_here_at(kind, call_pc))?;
4534                    callee = bound.target;
4535                    if constructed.is_none() {
4536                        this_value = bound.this_value;
4537                    }
4538                    arguments = Cow::Owned(bound.arguments);
4539                }
4540                Ok(CalleeKind::NotCallable) => return self.throw_type("call", call_pc),
4541                Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4542            }
4543        }
4544    }
4545
4546    fn execute_construct(
4547        &mut self,
4548        callee: Value,
4549        arguments: &[Value],
4550        destination: u32,
4551        call_pc: usize,
4552    ) -> Result<(), RuntimeError> {
4553        let mut callee = callee;
4554        let mut arguments = Cow::Borrowed(arguments);
4555        if matches!(self.callee_kind(callee), Ok(CalleeKind::Bound)) {
4556            let bound = self
4557                .flatten_bound(callee, Value::UNDEFINED, arguments.as_ref())
4558                .map_err(|kind| self.error_here_at(kind, call_pc))?;
4559            callee = bound.target;
4560            arguments = Cow::Owned(bound.arguments);
4561        }
4562        let index = match self.runtime_slot(callee) {
4563            Ok(Some(index)) => index,
4564            Ok(None) => return self.throw_type("construct", call_pc),
4565            Err(kind) => return Err(self.error_here_at(kind, call_pc)),
4566        };
4567        let builtin = match &self.heap[index] {
4568            HeapEntry::NativeFunction {
4569                callable: NativeCallable::Builtin(id),
4570                ..
4571            } => Some(*id),
4572            _ => None,
4573        };
4574        if let Some(id) = builtin {
4575            return match self.call_builtin(id, Value::UNDEFINED, arguments.as_ref(), true) {
4576                Ok(intrinsics::BuiltinOutcome::Value(value)) => {
4577                    self.write_register(self.frames.len() - 1, destination, value);
4578                    Ok(())
4579                }
4580                Ok(
4581                    intrinsics::BuiltinOutcome::Call { .. }
4582                    | intrinsics::BuiltinOutcome::GeneratorNext { .. },
4583                ) => self.throw_type("construct", call_pc),
4584                Ok(intrinsics::BuiltinOutcome::ConstructCall {
4585                    callee: continuation,
4586                    this_value,
4587                    arguments: continuation_arguments,
4588                    prototype,
4589                }) => {
4590                    let object = self
4591                        .allocate_constructed_receiver_with(prototype)
4592                        .map_err(|kind| self.error_here_at(kind, call_pc))?;
4593                    self.execute_call(CallRequest {
4594                        callee: continuation,
4595                        this_value,
4596                        arguments: &continuation_arguments,
4597                        destination: Some(destination),
4598                        call_pc,
4599                        constructed: Some(object),
4600                        new_target: callee,
4601                    })
4602                }
4603                Err(failure) => self.resolve_failure(failure, call_pc),
4604            };
4605        }
4606        if !matches!(
4607            self.heap[index],
4608            HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
4609        ) {
4610            return self.throw_type("construct", call_pc);
4611        }
4612        if let HeapEntry::Function {
4613            module, function, ..
4614        } = self.heap[index]
4615        {
4616            if self.module_code(module).functions()[function.get() as usize]
4617                .flags()
4618                .is_async
4619            {
4620                return self.throw_type("construct", call_pc);
4621            }
4622        }
4623        let object = self
4624            .allocate_constructed_receiver(callee)
4625            .map_err(|kind| self.error_here_at(kind, call_pc))?;
4626        self.execute_call(CallRequest {
4627            callee,
4628            this_value: object,
4629            arguments: arguments.as_ref(),
4630            destination: Some(destination),
4631            call_pc,
4632            constructed: Some(object),
4633            new_target: callee,
4634        })
4635    }
4636
4637    fn constructed_prototype(&self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4638        let index = self
4639            .runtime_slot(callee)?
4640            .ok_or(RuntimeErrorKind::InvalidValue { value: callee })?;
4641        Ok(match self.own_data_property(index, "prototype") {
4642            Some(value) if self.is_object(value) => value,
4643            _ => self.intrinsics.object_prototype,
4644        })
4645    }
4646
4647    fn allocate_constructed_receiver(&mut self, callee: Value) -> Result<Value, RuntimeErrorKind> {
4648        let prototype = self.constructed_prototype(callee)?;
4649        self.allocate_constructed_receiver_with(prototype)
4650    }
4651
4652    fn allocate_constructed_receiver_with(
4653        &mut self,
4654        prototype: Value,
4655    ) -> Result<Value, RuntimeErrorKind> {
4656        self.allocate(HeapEntry::Object {
4657            properties: PropertyMap::default(),
4658            prototype: Some(prototype),
4659            boxed_primitive: None,
4660            extensible: true,
4661        })
4662    }
4663
4664    pub(crate) fn array_elements(&self, value: Value) -> Result<Option<Vec<Value>>, EvalFailure> {
4665        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4666            return Ok(None);
4667        };
4668        match &self.heap[index] {
4669            HeapEntry::Array { elements, .. } => Ok(Some(elements.clone())),
4670            _ => Ok(None),
4671        }
4672    }
4673
4674    pub(crate) fn array_length(&self, value: Value) -> Result<usize, EvalFailure> {
4675        self.array_elements(value)?
4676            .map(|elements| elements.len())
4677            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
4678                operation: "array method called on incompatible receiver",
4679            }))
4680    }
4681
4682    pub(crate) fn replace_array_elements(
4683        &mut self,
4684        value: Value,
4685        elements: Vec<Value>,
4686    ) -> Result<(), EvalFailure> {
4687        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4688            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4689                operation: "array method called on incompatible receiver",
4690            }));
4691        };
4692        let HeapEntry::Array {
4693            elements: current, ..
4694        } = &mut self.heap[index]
4695        else {
4696            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4697                operation: "array method called on incompatible receiver",
4698            }));
4699        };
4700        *current = elements;
4701        Ok(())
4702    }
4703
4704    pub(crate) fn string_value(&self, value: Value) -> Option<EcmaString> {
4705        let index = self.runtime_slot(value).ok().flatten()?;
4706        match &self.heap[index] {
4707            HeapEntry::String(text) => Some(text.clone()),
4708            _ => None,
4709        }
4710    }
4711
4712    pub(crate) fn get_named_property(
4713        &mut self,
4714        object: Value,
4715        name: &str,
4716    ) -> Result<Value, EvalFailure> {
4717        self.get_property_ascii(object, name)
4718    }
4719
4720    fn get_property_ascii(&mut self, object: Value, name: &str) -> Result<Value, EvalFailure> {
4721        debug_assert!(name.is_ascii());
4722        match self.resolve_get_ascii(object, name)? {
4723            GetOutcome::Value(value) => Ok(value),
4724            GetOutcome::Text(text) => self
4725                .allocate(HeapEntry::String(text))
4726                .map_err(EvalFailure::Runtime),
4727            GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4728        }
4729    }
4730
4731    pub(crate) fn get_property_key(
4732        &mut self,
4733        object: Value,
4734        key: &PropertyKey,
4735    ) -> Result<Value, EvalFailure> {
4736        match self.resolve_get(object, key)? {
4737            GetOutcome::Value(value) => Ok(value),
4738            GetOutcome::Text(text) => self
4739                .allocate(HeapEntry::String(text))
4740                .map_err(EvalFailure::Runtime),
4741            GetOutcome::Getter(getter) => self.call_value(getter, object, &[]),
4742        }
4743    }
4744
4745    pub(crate) fn set_data_property(
4746        &mut self,
4747        object: Value,
4748        name: &str,
4749        value: Value,
4750    ) -> Result<(), EvalFailure> {
4751        self.set_data_property_key(
4752            object,
4753            PropertyKey::Named(EcmaString::from_utf8(name)),
4754            value,
4755        )
4756    }
4757
4758    pub(crate) fn set_data_property_key(
4759        &mut self,
4760        object: Value,
4761        key: PropertyKey,
4762        value: Value,
4763    ) -> Result<(), EvalFailure> {
4764        match self.resolve_set(object, key, value)? {
4765            SetOutcome::Done => Ok(()),
4766            SetOutcome::Setter(setter) => {
4767                self.call_value(setter, object, &[value])?;
4768                Ok(())
4769            }
4770        }
4771    }
4772
4773    pub(crate) fn is_callable(&self, value: Value) -> Result<bool, EvalFailure> {
4774        Ok(!matches!(
4775            self.callee_kind(value).map_err(EvalFailure::Runtime)?,
4776            CalleeKind::NotCallable
4777        ))
4778    }
4779
4780    pub(crate) fn box_primitive(&mut self, value: Value) -> Result<Value, EvalFailure> {
4781        let prototype = match value.decode() {
4782            Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
4783            Some(Decoded::Number(_) | Decoded::Int32(_)) => self.intrinsics.number_prototype,
4784            Some(Decoded::HeapRef(_)) if self.string_value(value).is_some() => {
4785                self.intrinsics.string_prototype
4786            }
4787            _ => self.intrinsics.object_prototype,
4788        };
4789        self.allocate(HeapEntry::Object {
4790            properties: PropertyMap::default(),
4791            prototype: Some(prototype),
4792            boxed_primitive: Some(value),
4793            extensible: true,
4794        })
4795        .map_err(EvalFailure::Runtime)
4796    }
4797
4798    pub(crate) fn unbox_primitive_or_self(&self, value: Value) -> Result<Value, EvalFailure> {
4799        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
4800            return Ok(value);
4801        };
4802        match self.heap[index] {
4803            HeapEntry::Object {
4804                boxed_primitive: Some(primitive),
4805                ..
4806            } => Ok(primitive),
4807            _ => Ok(value),
4808        }
4809    }
4810
4811    pub(crate) fn unbox_primitive(
4812        &self,
4813        value: Value,
4814        operation: &'static str,
4815    ) -> Result<Value, EvalFailure> {
4816        let unboxed = self.unbox_primitive_or_self(value)?;
4817        if unboxed == value && self.is_object(value) {
4818            Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }))
4819        } else {
4820            Ok(unboxed)
4821        }
4822    }
4823
4824    pub(crate) fn current_builtin_id(&self) -> Option<intrinsics::BuiltinId> {
4825        self.current_builtin_id
4826    }
4827
4828    pub(crate) fn throw_error(
4829        &mut self,
4830        id: intrinsics::BuiltinId,
4831        message: String,
4832    ) -> EvalFailure {
4833        let message = match self.allocate(HeapEntry::String(EcmaString::from_utf8(&message))) {
4834            Ok(value) => value,
4835            Err(kind) => return EvalFailure::Runtime(kind),
4836        };
4837        let mut properties = PropertyMap::default();
4838        properties.insert(
4839            PropertyKey::Named(EcmaString::from_utf8("message")),
4840            Property::Data {
4841                value: message,
4842                writable: true,
4843                enumerable: true,
4844                configurable: true,
4845            },
4846        );
4847        match self.allocate(HeapEntry::Object {
4848            properties,
4849            prototype: Some(self.intrinsics.error_prototype(id)),
4850            boxed_primitive: None,
4851            extensible: true,
4852        }) {
4853            Ok(value) => EvalFailure::ThrowValue(value),
4854            Err(kind) => EvalFailure::Runtime(kind),
4855        }
4856    }
4857
4858    pub(crate) fn has_own_property_key(
4859        &self,
4860        object: Value,
4861        key: &PropertyKey,
4862    ) -> Result<bool, EvalFailure> {
4863        let Some(index) = self.runtime_slot(object).map_err(EvalFailure::Runtime)? else {
4864            return Ok(false);
4865        };
4866        Ok(self.own_get(index, key).is_some())
4867    }
4868
4869    pub(crate) fn call_value(
4870        &mut self,
4871        callee: Value,
4872        this_value: Value,
4873        arguments: &[Value],
4874    ) -> Result<Value, EvalFailure> {
4875        let mut callee = callee;
4876        let mut this_value = this_value;
4877        let mut arguments = Cow::Borrowed(arguments);
4878        loop {
4879            match self.callee_kind(callee).map_err(EvalFailure::Runtime)? {
4880                CalleeKind::Builtin { id } => {
4881                    match self.call_builtin(id, this_value, arguments.as_ref(), false)? {
4882                        intrinsics::BuiltinOutcome::Value(value) => return Ok(value),
4883                        intrinsics::BuiltinOutcome::Call {
4884                            callee: next,
4885                            this_value: next_this,
4886                            arguments: next_arguments,
4887                        } => {
4888                            callee = next;
4889                            this_value = next_this;
4890                            arguments = Cow::Owned(next_arguments);
4891                        }
4892                        intrinsics::BuiltinOutcome::GeneratorNext {
4893                            generator,
4894                            resume_value,
4895                        } => return self.resume_generator(generator, resume_value),
4896                        intrinsics::BuiltinOutcome::ConstructCall { .. } => {
4897                            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4898                                operation: "call",
4899                            }));
4900                        }
4901                    }
4902                }
4903                CalleeKind::Runtime { target, captures } => {
4904                    let flags = self.module_code(target.module).functions()
4905                        [target.function.get() as usize]
4906                        .flags();
4907                    if flags.is_generator && !flags.is_async {
4908                        return self
4909                            .create_generator(GeneratorStart {
4910                                target,
4911                                captures,
4912                                this_value,
4913                                new_target: Value::UNDEFINED,
4914                                args: arguments.as_ref().to_vec(),
4915                            })
4916                            .map_err(EvalFailure::Runtime);
4917                    }
4918                    if flags.is_async && !flags.is_generator {
4919                        return self.start_async_call(
4920                            target,
4921                            &captures,
4922                            this_value,
4923                            Value::UNDEFINED,
4924                            arguments.as_ref(),
4925                        );
4926                    }
4927                    let stop_depth = self.frames.len();
4928                    let return_to = self.frames.last().map(|frame| ReturnTo {
4929                        destination: None,
4930                        call_pc: frame.pc,
4931                        constructed: None,
4932                    });
4933                    self.push_frame(
4934                        target,
4935                        &captures,
4936                        this_value,
4937                        Value::UNDEFINED,
4938                        arguments.as_ref(),
4939                        return_to,
4940                    )
4941                    .map_err(|error| EvalFailure::Runtime(error.kind))?;
4942                    self.callback_boundaries.push(stop_depth);
4943                    let result = self.run_loop(stop_depth);
4944                    self.callback_boundaries
4945                        .pop()
4946                        .expect("nested runtime callback owns its unwind boundary");
4947                    return match result {
4948                        Ok(None) => self.last_completion.take().ok_or(EvalFailure::Runtime(
4949                            RuntimeErrorKind::InvalidValue {
4950                                value: Value::UNDEFINED,
4951                            },
4952                        )),
4953                        Ok(Some(execution)) => Ok(execution.value),
4954                        Err(error) => {
4955                            self.unwind_frames_to(stop_depth);
4956                            match error.kind {
4957                                RuntimeErrorKind::UncaughtThrow { value, .. } => {
4958                                    Err(EvalFailure::ThrowValue(value))
4959                                }
4960                                kind => Err(EvalFailure::Runtime(kind)),
4961                            }
4962                        }
4963                    };
4964                }
4965                CalleeKind::Bound => {
4966                    let bound = self
4967                        .flatten_bound(callee, this_value, arguments.as_ref())
4968                        .map_err(EvalFailure::Runtime)?;
4969                    callee = bound.target;
4970                    this_value = bound.this_value;
4971                    arguments = Cow::Owned(bound.arguments);
4972                }
4973                CalleeKind::NotCallable => {
4974                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
4975                        operation: "call",
4976                    }));
4977                }
4978            }
4979        }
4980    }
4981
4982    fn unwind_frames_to(&mut self, depth: usize) {
4983        while self.frames.len() > depth {
4984            let frame = self.frames.pop().expect("frame depth was checked");
4985            self.live_registers -= frame.registers.len();
4986        }
4987    }
4988
4989    fn complete_frame(&mut self, returned: Value) -> Option<Execution> {
4990        let frame = self.frames.pop().expect("an activation is executing");
4991        self.live_registers -= frame.registers.len();
4992        match frame.return_to {
4993            None => {
4994                let outcome = ExecutionOutcome {
4995                    stdout: Vec::new(),
4996                    exit_code: 0,
4997                };
4998                Some(Execution {
4999                    outcome,
5000                    value: returned,
5001                    link: returned,
5002                    entry_registers: frame.registers,
5003                })
5004            }
5005            Some(return_to) => {
5006                let value = match return_to.constructed {
5007                    Some(object) if !self.is_object(returned) => object,
5008                    _ => returned,
5009                };
5010                if let Some(destination) = return_to.destination {
5011                    self.frames.last_mut().expect("callee has caller").registers[destination] =
5012                        value;
5013                } else {
5014                    self.last_completion = Some(value);
5015                }
5016                None
5017            }
5018        }
5019    }
5020
5021    fn resolve_failure(&mut self, failure: EvalFailure, pc: usize) -> Result<(), RuntimeError> {
5022        match failure {
5023            EvalFailure::Throw(origin) => self.throw(Value::UNDEFINED, origin, pc),
5024            EvalFailure::ThrowValue(value) => self.throw(value, ThrowOrigin::Bytecode, pc),
5025            EvalFailure::ThrowValueOrigin { value, origin } => self.throw(value, origin, pc),
5026            EvalFailure::Runtime(kind) => Err(self.error_here_at(kind, pc)),
5027        }
5028    }
5029
5030    fn throw_type(&mut self, operation: &'static str, pc: usize) -> Result<(), RuntimeError> {
5031        self.throw(Value::UNDEFINED, ThrowOrigin::TypeError { operation }, pc)
5032    }
5033
5034    fn throw(
5035        &mut self,
5036        value: Value,
5037        origin: ThrowOrigin,
5038        faulting_pc: usize,
5039    ) -> Result<(), RuntimeError> {
5040        let site_module = self
5041            .frames
5042            .last()
5043            .expect("an activation is executing")
5044            .module;
5045        let site_function = self
5046            .frames
5047            .last()
5048            .expect("an activation is executing")
5049            .function;
5050        let mut search_pc = faulting_pc;
5051        loop {
5052            if self
5053                .callback_boundaries
5054                .last()
5055                .is_some_and(|boundary| self.frames.len() == *boundary)
5056            {
5057                return Err(self.error_at_in_module(
5058                    RuntimeErrorKind::UncaughtThrow { value, origin },
5059                    site_module,
5060                    site_function,
5061                    faulting_pc,
5062                ));
5063            }
5064            let frame_index = self.frames.len() - 1;
5065            let function_index = self.frames[frame_index].function;
5066            let module = self.frames[frame_index].module;
5067            let function = &self.module_code(module).functions()[function_index];
5068            if let Some(handler) = innermost_handler(function, search_pc) {
5069                let frame = &mut self.frames[frame_index];
5070                frame.registers[handler.catch_register.get() as usize] = value;
5071                frame.pc = handler.handler.get() as usize;
5072                return Ok(());
5073            }
5074            let frame = self.frames.pop().expect("throw walks live frames");
5075            self.live_registers -= frame.registers.len();
5076            match frame.return_to {
5077                Some(return_to) => search_pc = return_to.call_pc,
5078                None => {
5079                    return Err(self.error_at_in_module(
5080                        RuntimeErrorKind::UncaughtThrow { value, origin },
5081                        site_module,
5082                        site_function,
5083                        faulting_pc,
5084                    ));
5085                }
5086            }
5087        }
5088    }
5089
5090    fn error_here(&self, kind: RuntimeErrorKind) -> RuntimeError {
5091        let frame = self.frames.last().expect("an activation is executing");
5092        self.error_at(kind, frame.function, frame.pc)
5093    }
5094
5095    fn error_here_at(&self, kind: RuntimeErrorKind, pc: usize) -> RuntimeError {
5096        let function = self
5097            .frames
5098            .last()
5099            .expect("an activation is executing")
5100            .function;
5101        self.error_at(kind, function, pc)
5102    }
5103
5104    fn error_at(&self, kind: RuntimeErrorKind, function: usize, pc: usize) -> RuntimeError {
5105        self.error_at_in_module(kind, self.active_module_id(), function, pc)
5106    }
5107
5108    pub(crate) fn error_at_in_module(
5109        &self,
5110        kind: RuntimeErrorKind,
5111        module: ModuleId,
5112        function: usize,
5113        pc: usize,
5114    ) -> RuntimeError {
5115        let code = self.module_code(module);
5116        let metadata = &code.functions()[function];
5117        let function_name =
5118            metadata
5119                .name()
5120                .and_then(|id| match &code.constants()[id.get() as usize] {
5121                    Constant::String(name) => Some(name.clone()),
5122                    _ => None,
5123                });
5124        RuntimeError {
5125            kind,
5126            function: FunctionId::new(function as u32),
5127            pc: Pc::new(pc as u32),
5128            source: RuntimeSource {
5129                function_name,
5130                instruction: metadata.code()[pc],
5131            },
5132        }
5133    }
5134
5135    // ---- property keys -----------------------------------------------------
5136
5137    /// Normalizes a register value into a property key. A runtime string borrows
5138    /// its text; a private name yields its slot identity; everything else is
5139    /// coerced with `ToString`.
5140    fn to_property_key(&self, value: Value) -> Result<PropertyKey, EvalFailure> {
5141        match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
5142            Some(index) => match &self.heap[index] {
5143                HeapEntry::String(text) => Ok(PropertyKey::Named(text.clone())),
5144                HeapEntry::Symbol { .. } => Ok(PropertyKey::Symbol(index as u32)),
5145                HeapEntry::PrivateName { .. } => Ok(PropertyKey::Private(index as u32)),
5146                _ => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5147            },
5148            None => Ok(PropertyKey::Named(self.value_to_string(value, 0)?)),
5149        }
5150    }
5151
5152    // ---- property get ------------------------------------------------------
5153
5154    fn resolve_get(&mut self, object: Value, key: &PropertyKey) -> Result<GetOutcome, EvalFailure> {
5155        let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5156        let start = match slot {
5157            Some(index) => {
5158                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5159                    let PropertyKey::Named(name) = key else {
5160                        return Ok(GetOutcome::Value(Value::UNDEFINED));
5161                    };
5162                    let text = name
5163                        .to_utf8_strict()
5164                        .ok()
5165                        .and_then(|name| self.host.env(&name))
5166                        .map(EcmaString::from_utf8);
5167                    return match text {
5168                        Some(text) => self
5169                            .allocate(HeapEntry::String(text))
5170                            .map(GetOutcome::Value)
5171                            .map_err(EvalFailure::Runtime),
5172                        None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5173                    };
5174                }
5175                if let Some(found) = self.primitive_get(index, key) {
5176                    return self.found_outcome(found);
5177                }
5178                match self.heap[index] {
5179                    HeapEntry::String(_) => self
5180                        .runtime_slot(self.intrinsics.string_prototype)
5181                        .map_err(EvalFailure::Runtime)?,
5182                    HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5183                        .runtime_slot(self.intrinsics.object_prototype)
5184                        .map_err(EvalFailure::Runtime)?,
5185                    HeapEntry::Symbol { .. } => self
5186                        .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5187                        .map_err(EvalFailure::Runtime)?,
5188                    _ => Some(index),
5189                }
5190            }
5191            None => {
5192                let prototype = match object.decode() {
5193                    Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5194                    Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5195                        self.intrinsics.number_prototype
5196                    }
5197                    _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5198                };
5199                self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5200            }
5201        };
5202        let Some(mut node) = start else {
5203            return Ok(GetOutcome::Value(Value::UNDEFINED));
5204        };
5205        for _ in 0..=self.heap.len() {
5206            if let Some(found) = self.own_get(node, key) {
5207                return self.found_outcome(found);
5208            }
5209            match self.prototype_index(node)? {
5210                Some(next) => node = next,
5211                None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5212            }
5213        }
5214        Ok(GetOutcome::Value(Value::UNDEFINED))
5215    }
5216
5217    fn resolve_get_ascii(&mut self, object: Value, name: &str) -> Result<GetOutcome, EvalFailure> {
5218        debug_assert!(name.is_ascii());
5219        let slot = self.runtime_slot(object).map_err(EvalFailure::Runtime)?;
5220        let start = match slot {
5221            Some(index) => {
5222                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5223                    return match self.host.env(name).map(EcmaString::from_utf8) {
5224                        Some(text) => self
5225                            .allocate(HeapEntry::String(text))
5226                            .map(GetOutcome::Value)
5227                            .map_err(EvalFailure::Runtime),
5228                        None => Ok(GetOutcome::Value(Value::UNDEFINED)),
5229                    };
5230                }
5231                if let HeapEntry::String(text) = &self.heap[index] {
5232                    if name == "length" {
5233                        return Ok(GetOutcome::Value(number_value(text.len_units() as f64)));
5234                    }
5235                    if let Some(offset) = array_index_ascii(name)
5236                        && let Some(unit) = text.unit_at(offset as usize)
5237                    {
5238                        return Ok(GetOutcome::Text(EcmaString::from_units(&[unit])));
5239                    }
5240                }
5241                match self.heap[index] {
5242                    HeapEntry::String(_) => self
5243                        .runtime_slot(self.intrinsics.string_prototype)
5244                        .map_err(EvalFailure::Runtime)?,
5245                    HeapEntry::BigInt(_) | HeapEntry::PrivateName { .. } => self
5246                        .runtime_slot(self.intrinsics.object_prototype)
5247                        .map_err(EvalFailure::Runtime)?,
5248                    HeapEntry::Symbol { .. } => self
5249                        .runtime_slot(self.intrinsics.builtins.symbol_prototype())
5250                        .map_err(EvalFailure::Runtime)?,
5251                    _ => Some(index),
5252                }
5253            }
5254            None => {
5255                let prototype = match object.decode() {
5256                    Some(Decoded::Boolean(_)) => self.intrinsics.boolean_prototype,
5257                    Some(Decoded::Number(_) | Decoded::Int32(_)) => {
5258                        self.intrinsics.number_prototype
5259                    }
5260                    _ => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5261                };
5262                self.runtime_slot(prototype).map_err(EvalFailure::Runtime)?
5263            }
5264        };
5265        let Some(mut node) = start else {
5266            return Ok(GetOutcome::Value(Value::UNDEFINED));
5267        };
5268        for _ in 0..=self.heap.len() {
5269            if let Some(found) = self.own_get_ascii(node, name) {
5270                return self.found_outcome(found);
5271            }
5272            match self.prototype_index(node)? {
5273                Some(next) => node = next,
5274                None => return Ok(GetOutcome::Value(Value::UNDEFINED)),
5275            }
5276        }
5277        Ok(GetOutcome::Value(Value::UNDEFINED))
5278    }
5279
5280    fn found_outcome(&mut self, found: Found) -> Result<GetOutcome, EvalFailure> {
5281        match found {
5282            Found::Value(Value::UNINITIALIZED) => {
5283                let id = self
5284                    .intrinsics
5285                    .builtins
5286                    .id_named("ReferenceError")
5287                    .expect("ReferenceError intrinsic is installed");
5288                match self.throw_error(
5289                    id,
5290                    "Cannot access lexical binding before initialization".into(),
5291                ) {
5292                    EvalFailure::ThrowValue(value) => Err(EvalFailure::ThrowValueOrigin {
5293                        value,
5294                        origin: ThrowOrigin::ReferenceError {
5295                            operation: "lexical binding is uninitialized",
5296                        },
5297                    }),
5298                    failure => Err(failure),
5299                }
5300            }
5301            Found::Value(value) => Ok(GetOutcome::Value(value)),
5302            Found::Text(text) => Ok(GetOutcome::Text(text)),
5303            Found::Getter(getter) => Ok(GetOutcome::Getter(getter)),
5304            Found::Failure(kind) => Err(EvalFailure::Runtime(kind)),
5305            Found::NoGetter => Ok(GetOutcome::Value(Value::UNDEFINED)),
5306        }
5307    }
5308
5309    fn primitive_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5310        if let HeapEntry::String(text) = &self.heap[index]
5311            && let PropertyKey::Named(name) = key
5312        {
5313            if name.eq_ascii("length") {
5314                return Some(Found::Value(number_value(text.len_units() as f64)));
5315            }
5316            if let Some(offset) = array_index(name)
5317                && let Some(unit) = text.unit_at(offset as usize)
5318            {
5319                return Some(Found::Text(EcmaString::from_units(&[unit])));
5320            }
5321        }
5322        None
5323    }
5324    fn own_get_ascii(&self, index: usize, name: &str) -> Option<Found> {
5325        debug_assert!(name.is_ascii());
5326        let slot = |value| self.runtime_slot(value).ok().flatten();
5327        if slot(self.intrinsics.object_prototype) == Some(index) && name == "toString" {
5328            return Some(Found::Value(self.intrinsics.object_to_string()));
5329        }
5330        match &self.heap[index] {
5331            HeapEntry::Object { properties, .. }
5332            | HeapEntry::Generator { properties, .. }
5333            | HeapEntry::Script { properties, .. }
5334            | HeapEntry::NativeFunction { properties, .. }
5335            | HeapEntry::Date { properties, .. }
5336            | HeapEntry::BuiltinIterator { properties, .. }
5337            | HeapEntry::Collection { properties, .. }
5338            | HeapEntry::Promise { properties, .. }
5339            | HeapEntry::Timeout { properties, .. } => property_lookup_ascii(properties, name),
5340            HeapEntry::Array {
5341                elements,
5342                properties,
5343                ..
5344            } => {
5345                if name == "length" {
5346                    return Some(Found::Value(number_value(elements.len() as f64)));
5347                }
5348                if let Some(offset) = array_index_ascii(name)
5349                    && let Some(element) = elements.get(offset as usize)
5350                    && *element != Value::HOLE
5351                {
5352                    return Some(Found::Value(*element));
5353                }
5354                property_lookup_ascii(properties, name)
5355            }
5356            HeapEntry::Function {
5357                module,
5358                function,
5359                properties,
5360                ..
5361            } => {
5362                if let Some(found) = property_lookup_ascii(properties, name) {
5363                    return Some(found);
5364                }
5365                let metadata = &self.module_code(*module).functions()[function.get() as usize];
5366                if name == "length" {
5367                    return Some(Found::Value(
5368                        number_value(metadata.parameter_count() as f64),
5369                    ));
5370                }
5371                if name == "name" {
5372                    return Some(Found::Text(
5373                        metadata
5374                            .name()
5375                            .map(|id| self.constant_text(*module, id).clone())
5376                            .unwrap_or_default(),
5377                    ));
5378                }
5379                None
5380            }
5381            HeapEntry::ModuleNamespace { module } => {
5382                let key = self
5383                    .program_module(*module)
5384                    .exports
5385                    .iter()
5386                    .map(|export| self.constant_text(*module, export.name))
5387                    .find(|candidate| candidate.eq_ascii(name))?
5388                    .clone();
5389                match self.namespace_export(*module, &key) {
5390                    Ok(Some(value)) => Some(Found::Value(value)),
5391                    Ok(None) => None,
5392                    Err(kind) => Some(Found::Failure(kind)),
5393                }
5394            }
5395            HeapEntry::ExternalModuleNamespace { specifier } => {
5396                let export = self.registry.external[specifier]
5397                    .exports
5398                    .iter()
5399                    .find_map(|(candidate, export)| candidate.eq_ascii(name).then_some(export))?;
5400                let cell = export
5401                    .cell
5402                    .expect("external namespace exports link before evaluation");
5403                Some(Found::Value(self.registry.cells[cell.0].value))
5404            }
5405            HeapEntry::RegExp {
5406                pattern,
5407                flags,
5408                properties,
5409                ..
5410            } => {
5411                if let Some(found) = property_lookup_ascii(properties, name) {
5412                    return Some(found);
5413                }
5414                let flag = |unit| {
5415                    Found::Value(Value::boolean(flags.as_units().contains(&u16::from(unit))))
5416                };
5417                match name {
5418                    "source" => Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5419                        pattern,
5420                    ))),
5421                    "flags" => Some(Found::Text(flags.clone())),
5422                    "global" => Some(flag(b'g')),
5423                    "ignoreCase" => Some(flag(b'i')),
5424                    "multiline" => Some(flag(b'm')),
5425                    "sticky" => Some(flag(b'y')),
5426                    "unicode" => Some(flag(b'u')),
5427                    "dotAll" => Some(flag(b's')),
5428                    "lastIndex" => Some(Found::Value(Value::int32(0))),
5429                    _ => None,
5430                }
5431            }
5432            HeapEntry::HashState { update, digest, .. } => match name {
5433                "update" => Some(Found::Value(*update)),
5434                "digest" => Some(Found::Value(*digest)),
5435                _ => None,
5436            },
5437            HeapEntry::ProcessEnv { .. }
5438            | HeapEntry::String(_)
5439            | HeapEntry::BigInt(_)
5440            | HeapEntry::Symbol { .. }
5441            | HeapEntry::PrivateName { .. }
5442            | HeapEntry::Iterator { .. }
5443            | HeapEntry::PromiseResolver { .. }
5444            | HeapEntry::PromiseFinally { .. }
5445            | HeapEntry::PromiseAll { .. }
5446            | HeapEntry::AsyncActivation { .. }
5447            | HeapEntry::PromiseAllElement { .. } => None,
5448        }
5449    }
5450
5451    /// Looks up an own property of the heap entry at `index`, returning `None`
5452    /// when the key is absent so the caller may continue up the prototype chain.
5453    fn own_get(&self, index: usize, key: &PropertyKey) -> Option<Found> {
5454        if let PropertyKey::Named(name) = key {
5455            let slot = |value| self.runtime_slot(value).ok().flatten();
5456            if slot(self.intrinsics.object_prototype) == Some(index) && name.eq_ascii("toString") {
5457                return Some(Found::Value(self.intrinsics.object_to_string()));
5458            }
5459        }
5460        match &self.heap[index] {
5461            HeapEntry::Object { properties, .. }
5462            | HeapEntry::Generator { properties, .. }
5463            | HeapEntry::Script { properties, .. }
5464            | HeapEntry::Date { properties, .. }
5465            | HeapEntry::BuiltinIterator { properties, .. }
5466            | HeapEntry::Collection { properties, .. }
5467            | HeapEntry::Promise { properties, .. }
5468            | HeapEntry::Timeout { properties, .. } => property_lookup(properties, key),
5469            HeapEntry::Array {
5470                elements,
5471                properties,
5472                ..
5473            } => {
5474                if let PropertyKey::Named(name) = key {
5475                    if name.eq_ascii("length") {
5476                        return Some(Found::Value(number_value(elements.len() as f64)));
5477                    }
5478                    if let Some(offset) = array_index(name)
5479                        && let Some(element) = elements.get(offset as usize)
5480                        && *element != Value::HOLE
5481                    {
5482                        return Some(Found::Value(*element));
5483                    }
5484                }
5485                property_lookup(properties, key)
5486            }
5487            HeapEntry::Function {
5488                module,
5489                function,
5490                properties,
5491                ..
5492            } => {
5493                if let Some(found) = property_lookup(properties, key) {
5494                    return Some(found);
5495                }
5496                if let PropertyKey::Named(name) = key {
5497                    let metadata = &self.module_code(*module).functions()[function.get() as usize];
5498                    if name.eq_ascii("length") {
5499                        return Some(Found::Value(
5500                            number_value(metadata.parameter_count() as f64),
5501                        ));
5502                    }
5503                    if name.eq_ascii("name") {
5504                        return Some(Found::Text(
5505                            metadata
5506                                .name()
5507                                .map(|id| self.constant_text(*module, id).clone())
5508                                .unwrap_or_default(),
5509                        ));
5510                    }
5511                }
5512                None
5513            }
5514            HeapEntry::ModuleNamespace { module } => {
5515                let PropertyKey::Named(name) = key else {
5516                    return None;
5517                };
5518                match self.namespace_export(*module, name) {
5519                    Ok(Some(value)) => Some(Found::Value(value)),
5520                    Ok(None) => None,
5521                    Err(kind) => Some(Found::Failure(kind)),
5522                }
5523            }
5524            HeapEntry::ExternalModuleNamespace { specifier } => {
5525                let PropertyKey::Named(name) = key else {
5526                    return None;
5527                };
5528                let export = self.registry.external[specifier].exports.get(name)?;
5529                Some(Found::Value(export.cell.map_or(export.value, |cell| {
5530                    self.registry.cells[cell.0].value
5531                })))
5532            }
5533            HeapEntry::NativeFunction { properties, .. } => property_lookup(properties, key),
5534            HeapEntry::RegExp {
5535                pattern,
5536                flags,
5537                properties,
5538                ..
5539            } => {
5540                if let Some(found) = property_lookup(properties, key) {
5541                    return Some(found);
5542                }
5543                if let PropertyKey::Named(name) = key {
5544                    let flag = |ascii: &str| {
5545                        Found::Value(Value::boolean(
5546                            flags.as_units().contains(&u16::from(ascii.as_bytes()[0])),
5547                        ))
5548                    };
5549                    if name.eq_ascii("source") {
5550                        return Some(Found::Text(crate::intrinsics::builtins::canonical_source(
5551                            pattern,
5552                        )));
5553                    }
5554                    if name.eq_ascii("flags") {
5555                        return Some(Found::Text(flags.clone()));
5556                    }
5557                    if name.eq_ascii("global") {
5558                        return Some(flag("g"));
5559                    }
5560                    if name.eq_ascii("ignoreCase") {
5561                        return Some(flag("i"));
5562                    }
5563                    if name.eq_ascii("multiline") {
5564                        return Some(flag("m"));
5565                    }
5566                    if name.eq_ascii("sticky") {
5567                        return Some(flag("y"));
5568                    }
5569                    if name.eq_ascii("unicode") {
5570                        return Some(flag("u"));
5571                    }
5572                    if name.eq_ascii("dotAll") {
5573                        return Some(flag("s"));
5574                    }
5575                    if name.eq_ascii("lastIndex") {
5576                        return Some(Found::Value(Value::int32(0)));
5577                    }
5578                }
5579                None
5580            }
5581            HeapEntry::HashState { update, digest, .. } => {
5582                let PropertyKey::Named(name) = key else {
5583                    return None;
5584                };
5585                if name.eq_ascii("update") {
5586                    Some(Found::Value(*update))
5587                } else if name.eq_ascii("digest") {
5588                    Some(Found::Value(*digest))
5589                } else {
5590                    None
5591                }
5592            }
5593            HeapEntry::ProcessEnv { .. }
5594            | HeapEntry::String(_)
5595            | HeapEntry::BigInt(_)
5596            | HeapEntry::Symbol { .. }
5597            | HeapEntry::PrivateName { .. }
5598            | HeapEntry::Iterator { .. }
5599            | HeapEntry::PromiseResolver { .. }
5600            | HeapEntry::PromiseFinally { .. }
5601            | HeapEntry::PromiseAll { .. }
5602            | HeapEntry::AsyncActivation { .. }
5603            | HeapEntry::PromiseAllElement { .. } => None,
5604        }
5605    }
5606
5607    fn namespace_export(
5608        &self,
5609        module: ModuleId,
5610        name: &EcmaString,
5611    ) -> Result<Option<Value>, RuntimeErrorKind> {
5612        if module.get() as usize >= self.dynamic_base {
5613            return Ok(None);
5614        }
5615        match self.program().resolve_export(module, name) {
5616            Some(ResolvedExport::Local { module, binding }) => {
5617                let cell = self.registry.modules[module.get() as usize].binding_cells
5618                    [binding.get() as usize]
5619                    .expect("verified export resolves to a linked cell");
5620                let value = self.registry.cells[cell.0].value;
5621                if value.is_uninitialized() {
5622                    Err(RuntimeErrorKind::TemporalDeadZone { module, binding })
5623                } else {
5624                    Ok(Some(value))
5625                }
5626            }
5627            Some(ResolvedExport::External { module, edge, name }) => {
5628                let Some(specifier) = self.external_specifier(module, edge) else {
5629                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5630                };
5631                let name = self.constant_text(module, name);
5632                let Some(export) = self.registry.external[&specifier].exports.get(name) else {
5633                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5634                };
5635                let Some(cell) = export.cell else {
5636                    return Err(RuntimeErrorKind::ExternalModuleUnavailable { module, edge });
5637                };
5638                Ok(Some(self.registry.cells[cell.0].value))
5639            }
5640            None => Ok(None),
5641        }
5642    }
5643
5644    fn own_data_property(&self, index: usize, name: &str) -> Option<Value> {
5645        let properties = match &self.heap[index] {
5646            HeapEntry::Object { properties, .. }
5647            | HeapEntry::Generator { properties, .. }
5648            | HeapEntry::Script { properties, .. }
5649            | HeapEntry::Array { properties, .. }
5650            | HeapEntry::Function { properties, .. }
5651            | HeapEntry::NativeFunction { properties, .. }
5652            | HeapEntry::RegExp { properties, .. }
5653            | HeapEntry::Date { properties, .. }
5654            | HeapEntry::BuiltinIterator { properties, .. }
5655            | HeapEntry::Collection { properties, .. }
5656            | HeapEntry::Promise { properties, .. }
5657            | HeapEntry::Timeout { properties, .. } => properties,
5658            _ => return None,
5659        };
5660        match properties.get_ascii(name) {
5661            Some(Property::Data { value, .. }) => Some(*value),
5662            _ => None,
5663        }
5664    }
5665
5666    fn prototype_index(&self, index: usize) -> Result<Option<usize>, EvalFailure> {
5667        let prototype = match &self.heap[index] {
5668            HeapEntry::Object { prototype, .. }
5669            | HeapEntry::Generator { prototype, .. }
5670            | HeapEntry::Script { prototype, .. }
5671            | HeapEntry::Array { prototype, .. }
5672            | HeapEntry::Function { prototype, .. }
5673            | HeapEntry::RegExp { prototype, .. }
5674            | HeapEntry::Date { prototype, .. }
5675            | HeapEntry::BuiltinIterator { prototype, .. }
5676            | HeapEntry::Collection { prototype, .. }
5677            | HeapEntry::Promise { prototype, .. }
5678            | HeapEntry::Timeout { prototype, .. }
5679            | HeapEntry::ProcessEnv { prototype, .. } => *prototype,
5680            HeapEntry::NativeFunction { .. } => Some(self.intrinsics.function_prototype),
5681            _ => None,
5682        };
5683        match prototype {
5684            Some(value) => self.runtime_slot(value).map_err(EvalFailure::Runtime),
5685            None => Ok(None),
5686        }
5687    }
5688
5689    pub(crate) fn inherits_from_prototype(
5690        &self,
5691        value: Value,
5692        prototype: Value,
5693    ) -> Result<bool, EvalFailure> {
5694        let Some(mut current) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
5695            return Ok(false);
5696        };
5697        let Some(target) = self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? else {
5698            return Ok(false);
5699        };
5700        let mut traversed = 0;
5701        while let Some(next) = self.prototype_index(current)? {
5702            if next == target {
5703                return Ok(true);
5704            }
5705            current = next;
5706            traversed += 1;
5707            if traversed > self.heap.len() {
5708                return Ok(false);
5709            }
5710        }
5711        Ok(false)
5712    }
5713
5714    // ---- property set ------------------------------------------------------
5715
5716    fn resolve_set(
5717        &mut self,
5718        object: Value,
5719        key: PropertyKey,
5720        value: Value,
5721    ) -> Result<SetOutcome, EvalFailure> {
5722        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
5723            Some(index) => {
5724                if matches!(self.heap[index], HeapEntry::ModuleNamespace { .. }) {
5725                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5726                        operation: "assign to module namespace",
5727                    }));
5728                }
5729                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
5730                    let PropertyKey::Named(name) = &key else {
5731                        return Ok(SetOutcome::Done);
5732                    };
5733                    let Ok(name) = name.to_utf8_strict() else {
5734                        return Ok(SetOutcome::Done);
5735                    };
5736                    let text = self.to_string(value)?;
5737                    let text = crate::host_objects::env_value_text_lossy(&text);
5738                    self.host.set_env(&name, &text);
5739                    return Ok(SetOutcome::Done);
5740                }
5741                if let Some(setter) = self.find_setter(index, &key)? {
5742                    return Ok(match setter {
5743                        Some(setter) => SetOutcome::Setter(setter),
5744                        None => SetOutcome::Done,
5745                    });
5746                }
5747                self.set_own_data(index, key, value)?;
5748                Ok(SetOutcome::Done)
5749            }
5750            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5751                operation: "set property on primitive",
5752            })),
5753        }
5754    }
5755
5756    fn find_setter(
5757        &self,
5758        index: usize,
5759        key: &PropertyKey,
5760    ) -> Result<Option<Option<Value>>, EvalFailure> {
5761        if self.own_has_non_accessor(index, key) {
5762            return Ok(None);
5763        }
5764        let mut node = index;
5765        let mut guard = 0;
5766        loop {
5767            let accessor = match &self.heap[node] {
5768                HeapEntry::Object { properties, .. }
5769                | HeapEntry::Generator { properties, .. }
5770                | HeapEntry::Script { properties, .. }
5771                | HeapEntry::Array { properties, .. }
5772                | HeapEntry::Function { properties, .. }
5773                | HeapEntry::NativeFunction { properties, .. }
5774                | HeapEntry::RegExp { properties, .. }
5775                | HeapEntry::Date { properties, .. }
5776                | HeapEntry::BuiltinIterator { properties, .. }
5777                | HeapEntry::Collection { properties, .. }
5778                | HeapEntry::Promise { properties, .. }
5779                | HeapEntry::Timeout { properties, .. } => match properties.get(key) {
5780                    Some(Property::Accessor { setter, .. }) => Some(Some(*setter)),
5781                    Some(Property::Data { .. }) => Some(None),
5782                    None => None,
5783                },
5784                _ => None,
5785            };
5786            match accessor {
5787                Some(Some(setter)) => return Ok(Some(setter)),
5788                Some(None) => return Ok(None),
5789                None => {}
5790            }
5791            match self.prototype_index(node)? {
5792                Some(next) => {
5793                    node = next;
5794                    guard += 1;
5795                    if guard > self.heap.len() + 1 {
5796                        return Ok(None);
5797                    }
5798                }
5799                None => return Ok(None),
5800            }
5801        }
5802    }
5803
5804    fn own_has_non_accessor(&self, index: usize, key: &PropertyKey) -> bool {
5805        match &self.heap[index] {
5806            HeapEntry::Array { elements, .. } => {
5807                if let PropertyKey::Named(name) = key {
5808                    if name.eq_ascii("length") {
5809                        return true;
5810                    }
5811                    if let Some(offset) = array_index(name) {
5812                        return elements
5813                            .get(offset as usize)
5814                            .is_some_and(|element| *element != Value::HOLE);
5815                    }
5816                }
5817                false
5818            }
5819            HeapEntry::Function { .. } => {
5820                (key.eq_ascii("length") || key.eq_ascii("name"))
5821                    && match key {
5822                        PropertyKey::Named(name) if name.eq_ascii("length") => {
5823                            self.own_data_property(index, "length").is_none()
5824                        }
5825                        PropertyKey::Named(_) => self.own_data_property(index, "name").is_none(),
5826                        _ => false,
5827                    }
5828            }
5829            _ => false,
5830        }
5831    }
5832
5833    fn set_own_data(
5834        &mut self,
5835        index: usize,
5836        key: PropertyKey,
5837        value: Value,
5838    ) -> Result<(), EvalFailure> {
5839        if matches!(key, PropertyKey::Named(ref name) if name.eq_ascii("length"))
5840            && matches!(self.heap[index], HeapEntry::Array { .. })
5841        {
5842            let HeapEntry::Array {
5843                elements,
5844                properties,
5845                length_writable,
5846                ..
5847            } = &mut self.heap[index]
5848            else {
5849                unreachable!("array checked above");
5850            };
5851            return array_set_length(
5852                elements,
5853                properties,
5854                *length_writable,
5855                value,
5856                "set array length",
5857            );
5858        }
5859        if let HeapEntry::Array {
5860            elements,
5861            length_writable,
5862            ..
5863        } = &self.heap[index]
5864            && let Some(offset) = key.as_string().and_then(array_index)
5865            && offset as usize >= elements.len()
5866            && !*length_writable
5867        {
5868            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5869                operation: "add index beyond non-writable array length",
5870            }));
5871        }
5872        let (properties, extensible, virtual_exists) = match &self.heap[index] {
5873            HeapEntry::Object {
5874                properties,
5875                extensible,
5876                ..
5877            }
5878            | HeapEntry::Generator {
5879                properties,
5880                extensible,
5881                ..
5882            }
5883            | HeapEntry::Script {
5884                properties,
5885                extensible,
5886                ..
5887            }
5888            | HeapEntry::Function {
5889                properties,
5890                extensible,
5891                ..
5892            }
5893            | HeapEntry::NativeFunction {
5894                properties,
5895                extensible,
5896                ..
5897            }
5898            | HeapEntry::RegExp {
5899                properties,
5900                extensible,
5901                ..
5902            }
5903            | HeapEntry::Date {
5904                properties,
5905                extensible,
5906                ..
5907            }
5908            | HeapEntry::BuiltinIterator {
5909                properties,
5910                extensible,
5911                ..
5912            }
5913            | HeapEntry::Collection {
5914                properties,
5915                extensible,
5916                ..
5917            }
5918            | HeapEntry::Promise {
5919                properties,
5920                extensible,
5921                ..
5922            } => (Some(properties), *extensible, false),
5923            HeapEntry::Array {
5924                elements,
5925                properties,
5926                extensible,
5927                ..
5928            } => {
5929                let virtual_exists = key.as_string().is_some_and(|name| {
5930                    name.eq_ascii("length")
5931                        || array_index(name).is_some_and(|offset| {
5932                            elements
5933                                .get(offset as usize)
5934                                .is_some_and(|element| *element != Value::HOLE)
5935                        })
5936                });
5937                (Some(properties), *extensible, virtual_exists)
5938            }
5939            _ => (None, true, false),
5940        };
5941        if let Some(property) = properties.and_then(|properties| properties.get(&key)) {
5942            match property {
5943                Property::Data {
5944                    writable: false, ..
5945                }
5946                | Property::Accessor { .. } => {
5947                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5948                        operation: "assign to read only property",
5949                    }));
5950                }
5951                Property::Data { writable: true, .. } => {}
5952            }
5953        } else if !extensible && !virtual_exists {
5954            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5955                operation: "add property to non-extensible object",
5956            }));
5957        }
5958
5959        let growth = match &self.heap[index] {
5960            HeapEntry::Object { properties, .. }
5961            | HeapEntry::Generator { properties, .. }
5962            | HeapEntry::Script { properties, .. }
5963            | HeapEntry::Function { properties, .. }
5964            | HeapEntry::NativeFunction { properties, .. }
5965            | HeapEntry::RegExp { properties, .. }
5966            | HeapEntry::Date { properties, .. }
5967            | HeapEntry::BuiltinIterator { properties, .. }
5968            | HeapEntry::Collection { properties, .. }
5969            | HeapEntry::Promise { properties, .. }
5970            | HeapEntry::Timeout { properties, .. } => {
5971                usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5972            }
5973            HeapEntry::Array {
5974                elements,
5975                properties,
5976                ..
5977            } => match &key {
5978                PropertyKey::Named(name) if name.eq_ascii("length") => 0,
5979                PropertyKey::Named(name) => {
5980                    if let Some(offset) = array_index(name) {
5981                        (offset as usize + 1).saturating_sub(elements.len()) * 8
5982                    } else {
5983                        usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5984                    }
5985                }
5986                PropertyKey::Symbol(_) | PropertyKey::Private(_) => {
5987                    usize::from(!properties.contains_key(&key)) * key.charge_bytes()
5988                }
5989            },
5990            HeapEntry::String(_) | HeapEntry::BigInt(_) => {
5991                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
5992                    operation: "set property on primitive",
5993                }));
5994            }
5995            HeapEntry::Symbol { .. }
5996            | HeapEntry::PrivateName { .. }
5997            | HeapEntry::Iterator { .. }
5998            | HeapEntry::PromiseResolver { .. }
5999            | HeapEntry::PromiseFinally { .. }
6000            | HeapEntry::PromiseAll { .. }
6001            | HeapEntry::AsyncActivation { .. }
6002            | HeapEntry::PromiseAllElement { .. } => {
6003                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6004                    operation: "set property on non-object",
6005                }));
6006            }
6007            HeapEntry::ProcessEnv { .. } => {
6008                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6009                    operation: "set internal process environment",
6010                }));
6011            }
6012            HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6013                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6014                    operation: "assign to module namespace",
6015                }));
6016            }
6017            HeapEntry::HashState { .. } => {
6018                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6019                    operation: "assign to hash state",
6020                }));
6021            }
6022        };
6023        self.charge_heap(growth).map_err(EvalFailure::Runtime)?;
6024        match &mut self.heap[index] {
6025            HeapEntry::Object { properties, .. }
6026            | HeapEntry::Generator { properties, .. }
6027            | HeapEntry::Script { properties, .. }
6028            | HeapEntry::Function { properties, .. }
6029            | HeapEntry::NativeFunction { properties, .. }
6030            | HeapEntry::RegExp { properties, .. }
6031            | HeapEntry::Date { properties, .. }
6032            | HeapEntry::BuiltinIterator { properties, .. }
6033            | HeapEntry::Collection { properties, .. }
6034            | HeapEntry::Promise { properties, .. }
6035            | HeapEntry::Timeout { properties, .. } => {
6036                properties.insert(
6037                    key,
6038                    Property::Data {
6039                        value,
6040                        writable: true,
6041                        enumerable: true,
6042                        configurable: true,
6043                    },
6044                );
6045                Ok(())
6046            }
6047            HeapEntry::Array {
6048                elements,
6049                properties,
6050                length_writable,
6051                ..
6052            } => {
6053                match key {
6054                    PropertyKey::Named(name) => {
6055                        if let Some(offset) = array_index(&name) {
6056                            let offset = offset as usize;
6057                            if elements.len() <= offset {
6058                                array_set_length(
6059                                    elements,
6060                                    properties,
6061                                    *length_writable,
6062                                    number_value((offset + 1) as f64),
6063                                    "set array index",
6064                                )?;
6065                            }
6066                            elements[offset] = value;
6067                        } else {
6068                            properties.insert(
6069                                PropertyKey::Named(name),
6070                                Property::Data {
6071                                    value,
6072                                    writable: true,
6073                                    enumerable: true,
6074                                    configurable: true,
6075                                },
6076                            );
6077                        }
6078                    }
6079                    identity @ (PropertyKey::Symbol(_) | PropertyKey::Private(_)) => {
6080                        properties.insert(
6081                            identity,
6082                            Property::Data {
6083                                value,
6084                                writable: true,
6085                                enumerable: true,
6086                                configurable: true,
6087                            },
6088                        );
6089                    }
6090                }
6091                Ok(())
6092            }
6093            HeapEntry::ProcessEnv { .. } => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6094                operation: "set internal process environment",
6095            })),
6096            _ => unreachable!("primitive and identity entries rejected above"),
6097        }
6098    }
6099
6100    fn define_accessor(
6101        &mut self,
6102        object: Value,
6103        key: PropertyKey,
6104        accessor: Value,
6105        kind: AccessorKind,
6106    ) -> Result<(), EvalFailure> {
6107        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6108            Some(index) => {
6109                self.charge_heap(key.charge_bytes() + 8)
6110                    .map_err(EvalFailure::Runtime)?;
6111                let (properties, extensible) = match &mut self.heap[index] {
6112                    HeapEntry::Object {
6113                        properties,
6114                        extensible,
6115                        ..
6116                    }
6117                    | HeapEntry::Generator {
6118                        properties,
6119                        extensible,
6120                        ..
6121                    }
6122                    | HeapEntry::Script {
6123                        properties,
6124                        extensible,
6125                        ..
6126                    }
6127                    | HeapEntry::Array {
6128                        properties,
6129                        extensible,
6130                        ..
6131                    }
6132                    | HeapEntry::Function {
6133                        properties,
6134                        extensible,
6135                        ..
6136                    }
6137                    | HeapEntry::NativeFunction {
6138                        properties,
6139                        extensible,
6140                        ..
6141                    }
6142                    | HeapEntry::RegExp {
6143                        properties,
6144                        extensible,
6145                        ..
6146                    }
6147                    | HeapEntry::Date {
6148                        properties,
6149                        extensible,
6150                        ..
6151                    }
6152                    | HeapEntry::BuiltinIterator {
6153                        properties,
6154                        extensible,
6155                        ..
6156                    }
6157                    | HeapEntry::Collection {
6158                        properties,
6159                        extensible,
6160                        ..
6161                    }
6162                    | HeapEntry::Promise {
6163                        properties,
6164                        extensible,
6165                        ..
6166                    } => (properties, *extensible),
6167                    _ => {
6168                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6169                            operation: "define accessor on primitive",
6170                        }));
6171                    }
6172                };
6173                if properties
6174                    .get(&key)
6175                    .is_some_and(|property| !property.configurable())
6176                    || (!properties.contains_key(&key) && !extensible)
6177                {
6178                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6179                        operation: "define accessor on non-configurable object",
6180                    }));
6181                }
6182                let property = properties.get_mut(&key);
6183                match property {
6184                    Some(Property::Accessor { getter, setter, .. }) => match kind {
6185                        AccessorKind::Getter => *getter = Some(accessor),
6186                        AccessorKind::Setter => *setter = Some(accessor),
6187                    },
6188                    Some(Property::Data { .. }) | None => {
6189                        let (getter, setter) = match kind {
6190                            AccessorKind::Getter => (Some(accessor), None),
6191                            AccessorKind::Setter => (None, Some(accessor)),
6192                        };
6193                        properties.insert(
6194                            key,
6195                            Property::Accessor {
6196                                getter,
6197                                setter,
6198                                enumerable: true,
6199                                configurable: true,
6200                            },
6201                        );
6202                    }
6203                }
6204                Ok(())
6205            }
6206            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6207                operation: "define accessor on host object",
6208            })),
6209        }
6210    }
6211
6212    fn delete_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6213        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6214            Some(index) => match &mut self.heap[index] {
6215                HeapEntry::Object { properties, .. }
6216                | HeapEntry::Generator { properties, .. }
6217                | HeapEntry::Script { properties, .. }
6218                | HeapEntry::Function { properties, .. }
6219                | HeapEntry::NativeFunction { properties, .. }
6220                | HeapEntry::RegExp { properties, .. }
6221                | HeapEntry::Date { properties, .. }
6222                | HeapEntry::BuiltinIterator { properties, .. }
6223                | HeapEntry::Collection { properties, .. }
6224                | HeapEntry::Promise { properties, .. }
6225                | HeapEntry::Timeout { properties, .. } => {
6226                    if properties
6227                        .get(key)
6228                        .is_some_and(|property| !property.configurable())
6229                    {
6230                        return Ok(false);
6231                    }
6232                    properties.remove(key);
6233                    Ok(true)
6234                }
6235                HeapEntry::Array {
6236                    elements,
6237                    properties,
6238                    ..
6239                } => {
6240                    if properties
6241                        .get(key)
6242                        .is_some_and(|property| !property.configurable())
6243                    {
6244                        return Ok(false);
6245                    }
6246                    if properties.remove(key).is_some() {
6247                        return Ok(true);
6248                    }
6249                    if let PropertyKey::Named(name) = key {
6250                        if name.eq_ascii("length") {
6251                            return Ok(false);
6252                        }
6253                        if let Some(offset) = array_index(name) {
6254                            if let Some(element) = elements.get_mut(offset as usize) {
6255                                *element = Value::HOLE;
6256                            }
6257                            return Ok(true);
6258                        }
6259                    }
6260                    Ok(true)
6261                }
6262                HeapEntry::ProcessEnv { .. } => {
6263                    let PropertyKey::Named(name) = key else {
6264                        return Ok(true);
6265                    };
6266                    Ok(name
6267                        .to_utf8_strict()
6268                        .is_ok_and(|name| self.host.delete_env(&name)))
6269                }
6270                HeapEntry::String(_)
6271                | HeapEntry::BigInt(_)
6272                | HeapEntry::Symbol { .. }
6273                | HeapEntry::PrivateName { .. }
6274                | HeapEntry::Iterator { .. }
6275                | HeapEntry::PromiseResolver { .. }
6276                | HeapEntry::PromiseFinally { .. }
6277                | HeapEntry::PromiseAll { .. }
6278                | HeapEntry::AsyncActivation { .. }
6279                | HeapEntry::PromiseAllElement { .. }
6280                | HeapEntry::HashState { .. } => Ok(true),
6281                HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
6282                    Ok(false)
6283                }
6284            },
6285            None => Ok(true),
6286        }
6287    }
6288
6289    fn has_property(&mut self, object: Value, key: &PropertyKey) -> Result<bool, EvalFailure> {
6290        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6291            Some(index) => {
6292                if matches!(self.heap[index], HeapEntry::ProcessEnv { .. }) {
6293                    let PropertyKey::Named(name) = key else {
6294                        return Ok(false);
6295                    };
6296                    return Ok(name
6297                        .to_utf8_strict()
6298                        .is_ok_and(|name| self.host.env(&name).is_some()));
6299                }
6300                if matches!(key, PropertyKey::Private(_)) {
6301                    return Ok(self.own_get(index, key).is_some());
6302                }
6303                let mut node = index;
6304                let mut guard = 0;
6305                loop {
6306                    if self.own_get(node, key).is_some() {
6307                        return Ok(true);
6308                    }
6309                    match self.prototype_index(node)? {
6310                        Some(next) => {
6311                            node = next;
6312                            guard += 1;
6313                            if guard > self.heap.len() + 1 {
6314                                return Ok(false);
6315                            }
6316                        }
6317                        None => return Ok(false),
6318                    }
6319                }
6320            }
6321            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6322                operation: "in",
6323            })),
6324        }
6325    }
6326
6327    // ---- aggregates & prototypes ------------------------------------------
6328
6329    pub(crate) fn array_push(&mut self, array: Value, value: Value) -> Result<(), EvalFailure> {
6330        match self.runtime_slot(array).map_err(EvalFailure::Runtime)? {
6331            Some(index) => {
6332                if !matches!(self.heap[index], HeapEntry::Array { .. }) {
6333                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6334                        operation: "push on non-array",
6335                    }));
6336                }
6337                self.charge_heap(8).map_err(EvalFailure::Runtime)?;
6338                if let HeapEntry::Array {
6339                    elements,
6340                    properties,
6341                    length_writable,
6342                    ..
6343                } = &mut self.heap[index]
6344                {
6345                    let offset = elements.len();
6346                    array_set_length(
6347                        elements,
6348                        properties,
6349                        *length_writable,
6350                        number_value((offset + 1) as f64),
6351                        "push beyond non-writable array length",
6352                    )?;
6353                    elements[offset] = value;
6354                }
6355                Ok(())
6356            }
6357            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6358                operation: "push on non-array",
6359            })),
6360        }
6361    }
6362
6363    fn array_extend(&mut self, array: Value, iterable: Value) -> Result<(), EvalFailure> {
6364        let iterator = self.create_iterator(iterable, IteratorKind::Sync)?;
6365        loop {
6366            let (done, value) = self.iterator_next(iterator)?;
6367            if done {
6368                return Ok(());
6369            }
6370            self.array_push(array, value)?;
6371        }
6372    }
6373
6374    fn object_spread(&mut self, target: Value, source: Value) -> Result<(), EvalFailure> {
6375        let target_index = match self.runtime_slot(target).map_err(EvalFailure::Runtime)? {
6376            Some(index)
6377                if matches!(
6378                    self.heap[index],
6379                    HeapEntry::Object { .. }
6380                        | HeapEntry::Generator { .. }
6381                        | HeapEntry::Script { .. }
6382                        | HeapEntry::Array { .. }
6383                        | HeapEntry::Promise { .. }
6384                ) =>
6385            {
6386                index
6387            }
6388            _ => {
6389                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6390                    operation: "object spread target is not an object",
6391                }));
6392            }
6393        };
6394        let keys = self.own_property_keys(source)?;
6395        for key in keys {
6396            if !self.own_property_is_enumerable(source, &key)? {
6397                continue;
6398            }
6399            let value = self.get_property_key(source, &key)?;
6400            self.set_own_data(target_index, key, value)?;
6401        }
6402        Ok(())
6403    }
6404
6405    fn set_prototype(&mut self, object: Value, prototype: Value) -> Result<(), EvalFailure> {
6406        let prototype = match self.runtime_slot(prototype).map_err(EvalFailure::Runtime)? {
6407            Some(_) => Some(prototype),
6408            None => match prototype.decode() {
6409                Some(Decoded::Null) => None,
6410                Some(Decoded::HeapRef(_)) => Some(prototype),
6411                _ => {
6412                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6413                        operation: "set prototype to non-object",
6414                    }));
6415                }
6416            },
6417        };
6418        match self.runtime_slot(object).map_err(EvalFailure::Runtime)? {
6419            Some(index) => match &mut self.heap[index] {
6420                HeapEntry::Object {
6421                    prototype: slot, ..
6422                }
6423                | HeapEntry::Generator {
6424                    prototype: slot, ..
6425                }
6426                | HeapEntry::Script {
6427                    prototype: slot, ..
6428                }
6429                | HeapEntry::Array {
6430                    prototype: slot, ..
6431                }
6432                | HeapEntry::Function {
6433                    prototype: slot, ..
6434                }
6435                | HeapEntry::RegExp {
6436                    prototype: slot, ..
6437                }
6438                | HeapEntry::Date {
6439                    prototype: slot, ..
6440                }
6441                | HeapEntry::BuiltinIterator {
6442                    prototype: slot, ..
6443                }
6444                | HeapEntry::Collection {
6445                    prototype: slot, ..
6446                }
6447                | HeapEntry::Promise {
6448                    prototype: slot, ..
6449                } => {
6450                    *slot = prototype;
6451                    Ok(())
6452                }
6453                _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6454                    operation: "set prototype on primitive",
6455                })),
6456            },
6457            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6458                operation: "set prototype on host object",
6459            })),
6460        }
6461    }
6462
6463    pub(crate) fn create_generator(
6464        &mut self,
6465        start: GeneratorStart,
6466    ) -> Result<Value, RuntimeErrorKind> {
6467        self.allocate(HeapEntry::Generator {
6468            state: GeneratorState::SuspendedStart(start),
6469            properties: PropertyMap::default(),
6470            prototype: Some(self.intrinsics.builtins.generator_prototype()),
6471            extensible: true,
6472        })
6473    }
6474
6475    fn resume_generator(
6476        &mut self,
6477        generator: Value,
6478        resume_value: Value,
6479    ) -> Result<Value, EvalFailure> {
6480        let state = self.take_generator_state(generator)?;
6481        if matches!(&state, GeneratorState::Completed) {
6482            return self.iterator_result(Value::UNDEFINED, true);
6483        }
6484
6485        let stop_depth = self.frames.len();
6486        let return_to = self.frames.last().map(|frame| ReturnTo {
6487            destination: None,
6488            call_pc: frame.pc,
6489            constructed: None,
6490        });
6491        let prepared = match state {
6492            GeneratorState::SuspendedStart(start) => self
6493                .push_frame(
6494                    start.target,
6495                    &start.captures,
6496                    start.this_value,
6497                    start.new_target,
6498                    &start.args,
6499                    return_to,
6500                )
6501                .map_err(|error| EvalFailure::Runtime(error.kind)),
6502            GeneratorState::Suspended(activation) => {
6503                self.push_resumed_generator_frame(activation, resume_value, return_to)
6504            }
6505            GeneratorState::Executing | GeneratorState::Completed => unreachable!(),
6506        };
6507        if let Err(failure) = prepared {
6508            self.settle_generator_completed(generator)?;
6509            return Err(failure);
6510        }
6511
6512        let resumed = self.run_generator_activation(stop_depth);
6513        match resumed {
6514            Ok(GeneratorResume::Yield { value, activation }) => {
6515                self.settle_generator_yield(generator, value, activation)
6516            }
6517            Ok(GeneratorResume::Return(value)) => {
6518                self.settle_generator_completed(generator)?;
6519                self.iterator_result(value, true)
6520            }
6521            Ok(GeneratorResume::Throw { value, origin }) => {
6522                self.settle_generator_completed(generator)?;
6523                Err(EvalFailure::ThrowValueOrigin { value, origin })
6524            }
6525            Err(failure) => {
6526                self.settle_generator_completed(generator)?;
6527                Err(failure)
6528            }
6529        }
6530    }
6531
6532    fn push_resumed_generator_frame(
6533        &mut self,
6534        activation: SuspendedActivation,
6535        resume_value: Value,
6536        return_to: Option<ReturnTo>,
6537    ) -> Result<(), EvalFailure> {
6538        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6539            self.release_suspended_activation_registers(activation.registers.len());
6540            return Err(EvalFailure::Runtime(RuntimeErrorKind::CallDepthExceeded {
6541                limit: self.limits.max_call_depth,
6542            }));
6543        }
6544        let suspend_pc = activation
6545            .resume_token
6546            .checked_sub(1)
6547            .expect("suspended generator token is nonzero") as usize;
6548        let instruction = self.module_code(activation.target.module).functions()
6549            [activation.target.function.get() as usize]
6550            .code()[suspend_pc];
6551        let Instruction::Suspend { dst, resume, .. } = instruction else {
6552            unreachable!("generator resume token names a suspend instruction");
6553        };
6554        let mut frame = Frame {
6555            module: activation.target.module,
6556            function: activation.target.function.get() as usize,
6557            pc: resume.get() as usize,
6558            registers: activation.registers,
6559            return_to,
6560            this_value: activation.this_value,
6561            new_target: activation.new_target,
6562            args: activation.args,
6563            arguments_object: activation.arguments_object,
6564        };
6565        frame.registers[dst.get() as usize] = resume_value;
6566        self.frames.push(frame);
6567        Ok(())
6568    }
6569
6570    fn run_generator_activation(
6571        &mut self,
6572        stop_depth: usize,
6573    ) -> Result<GeneratorResume, EvalFailure> {
6574        self.last_completion = None;
6575        self.pending_generator_resume = None;
6576        self.callback_boundaries.push(stop_depth);
6577        self.generator_boundaries.push(stop_depth);
6578        let result = self.run_loop(stop_depth);
6579        self.generator_boundaries
6580            .pop()
6581            .expect("generator execution owns its suspend boundary");
6582        self.callback_boundaries
6583            .pop()
6584            .expect("generator execution owns its unwind boundary");
6585
6586        match result {
6587            Ok(Some(execution)) => Ok(GeneratorResume::Return(execution.value)),
6588            Ok(None) => {
6589                if let Some(resume) = self.pending_generator_resume.take() {
6590                    return Ok(resume);
6591                }
6592                let value = self.last_completion.take().unwrap_or(Value::UNDEFINED);
6593                Ok(GeneratorResume::Return(value))
6594            }
6595            Err(error) => {
6596                self.unwind_frames_to(stop_depth);
6597                match error.kind {
6598                    RuntimeErrorKind::UncaughtThrow { value, origin } => {
6599                        Ok(GeneratorResume::Throw { value, origin })
6600                    }
6601                    kind => Err(EvalFailure::Runtime(kind)),
6602                }
6603            }
6604        }
6605    }
6606
6607    pub(crate) fn take_generator_state(
6608        &mut self,
6609        generator: Value,
6610    ) -> Result<GeneratorState, EvalFailure> {
6611        let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6612            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6613                operation: "Generator.prototype.next called on incompatible receiver",
6614            }));
6615        };
6616        let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6617            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6618                operation: "Generator.prototype.next called on incompatible receiver",
6619            }));
6620        };
6621        match std::mem::replace(state, GeneratorState::Executing) {
6622            GeneratorState::Executing => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
6623                operation: "generator is already running",
6624            })),
6625            GeneratorState::Completed => {
6626                *state = GeneratorState::Completed;
6627                Ok(GeneratorState::Completed)
6628            }
6629            state => Ok(state),
6630        }
6631    }
6632
6633    pub(crate) fn settle_generator_yield(
6634        &mut self,
6635        generator: Value,
6636        value: Value,
6637        activation: SuspendedActivation,
6638    ) -> Result<Value, EvalFailure> {
6639        let register_count = activation.registers.len();
6640        let result = match self.iterator_result(value, false) {
6641            Ok(result) => result,
6642            Err(failure) => {
6643                self.release_suspended_activation_registers(register_count);
6644                self.replace_executing_generator(generator, GeneratorState::Completed)?;
6645                return Err(failure);
6646            }
6647        };
6648        if let Err(failure) =
6649            self.replace_executing_generator(generator, GeneratorState::Suspended(activation))
6650        {
6651            self.release_suspended_activation_registers(register_count);
6652            return Err(failure);
6653        }
6654        Ok(result)
6655    }
6656
6657    pub(crate) fn settle_generator_completed(
6658        &mut self,
6659        generator: Value,
6660    ) -> Result<(), EvalFailure> {
6661        self.replace_executing_generator(generator, GeneratorState::Completed)
6662    }
6663
6664    fn replace_executing_generator(
6665        &mut self,
6666        generator: Value,
6667        next: GeneratorState,
6668    ) -> Result<(), EvalFailure> {
6669        let Some(index) = self.runtime_slot(generator).map_err(EvalFailure::Runtime)? else {
6670            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6671                value: generator,
6672            }));
6673        };
6674        let HeapEntry::Generator { state, .. } = &mut self.heap[index] else {
6675            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6676                value: generator,
6677            }));
6678        };
6679        if !matches!(state, GeneratorState::Executing) {
6680            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6681                value: generator,
6682            }));
6683        }
6684        *state = next;
6685        Ok(())
6686    }
6687
6688    /// Starts an ordinary async function: creates the implicit result Promise,
6689    /// drives the body synchronously to its first `await` or completion under a
6690    /// detached suspend boundary, and returns the Promise. A `return` resolves
6691    /// it and an escaping throw rejects it; a runtime limit failure stays fatal.
6692    pub(crate) fn start_async_call(
6693        &mut self,
6694        target: RuntimeFunction,
6695        captures: &[Value],
6696        this_value: Value,
6697        new_target: Value,
6698        arguments: &[Value],
6699    ) -> Result<Value, EvalFailure> {
6700        let promise = self.create_promise()?;
6701        let record = self.create_async_activation(promise)?;
6702        let stop_depth = self.frames.len();
6703        let return_to = self.frames.last().map(|frame| ReturnTo {
6704            destination: None,
6705            call_pc: frame.pc,
6706            constructed: None,
6707        });
6708        self.push_frame(
6709            target, captures, this_value, new_target, arguments, return_to,
6710        )
6711        .map_err(|error| EvalFailure::Runtime(error.kind))?;
6712        let step = self.drive_async_activation(stop_depth, None);
6713        self.settle_async_step(record, promise, step)?;
6714        Ok(promise)
6715    }
6716
6717    /// Resumes a suspended async activation inside its Promise reaction job. On
6718    /// fulfillment the awaited value is written to `Suspend.dst`; on rejection
6719    /// the reason is thrown at the `Suspend` pc so a covering `try`/`catch`
6720    /// runs. The activation is one-shot; a second resume is a hard error.
6721    fn resume_async(
6722        &mut self,
6723        record: Value,
6724        value: Value,
6725        rejection: Option<ThrowOrigin>,
6726    ) -> Result<(), RuntimeErrorKind> {
6727        let promise = self.async_activation_promise(record)?;
6728        let activation = self.take_async_activation(record)?;
6729        let register_count = activation.registers.len();
6730        if self.frames.len().saturating_add(self.native_depth) >= self.limits.max_call_depth {
6731            self.release_suspended_activation_registers(register_count);
6732            return Err(RuntimeErrorKind::CallDepthExceeded {
6733                limit: self.limits.max_call_depth,
6734            });
6735        }
6736        let suspend_pc = activation
6737            .resume_token
6738            .checked_sub(1)
6739            .expect("suspended async token is nonzero") as usize;
6740        let instruction = self.module_code(activation.target.module).functions()
6741            [activation.target.function.get() as usize]
6742            .code()[suspend_pc];
6743        let Instruction::Suspend { dst, resume, .. } = instruction else {
6744            unreachable!("async resume token names a suspend instruction");
6745        };
6746        let stop_depth = self.frames.len();
6747        let return_to = self.frames.last().map(|frame| ReturnTo {
6748            destination: None,
6749            call_pc: frame.pc,
6750            constructed: None,
6751        });
6752        let mut frame = Frame {
6753            module: activation.target.module,
6754            function: activation.target.function.get() as usize,
6755            pc: resume.get() as usize,
6756            registers: activation.registers,
6757            return_to,
6758            this_value: activation.this_value,
6759            new_target: activation.new_target,
6760            args: activation.args,
6761            arguments_object: activation.arguments_object,
6762        };
6763        let inject = match rejection {
6764            None => {
6765                frame.registers[dst.get() as usize] = value;
6766                None
6767            }
6768            Some(origin) => Some((value, origin, suspend_pc)),
6769        };
6770        self.frames.push(frame);
6771        let step = self.drive_async_activation(stop_depth, inject);
6772        match self.settle_async_step(record, promise, step) {
6773            Ok(()) => Ok(()),
6774            Err(EvalFailure::Runtime(kind)) => Err(kind),
6775            Err(_) => Err(RuntimeErrorKind::InvalidValue { value: record }),
6776        }
6777    }
6778
6779    /// Runs the interpreter loop for a detached async activation under one
6780    /// suspend and one unwind boundary, optionally injecting a rejection at the
6781    /// resumed `Suspend` pc first. It reports the awaited value on suspension,
6782    /// the returned value on completion, or an uncaught throw; runtime limit
6783    /// failures propagate as fatal `EvalFailure::Runtime`.
6784    fn drive_async_activation(
6785        &mut self,
6786        stop_depth: usize,
6787        inject: Option<(Value, ThrowOrigin, usize)>,
6788    ) -> Result<AsyncStep, EvalFailure> {
6789        self.last_completion = None;
6790        self.pending_async_suspend = None;
6791        self.callback_boundaries.push(stop_depth);
6792        self.async_boundaries.push(stop_depth);
6793        let result = match inject {
6794            None => self.run_loop(stop_depth),
6795            Some((value, origin, faulting_pc)) => match self.throw(value, origin, faulting_pc) {
6796                Ok(()) => self.run_loop(stop_depth),
6797                Err(error) => Err(error),
6798            },
6799        };
6800        self.async_boundaries
6801            .pop()
6802            .expect("async execution owns its suspend boundary");
6803        self.callback_boundaries
6804            .pop()
6805            .expect("async execution owns its unwind boundary");
6806        match result {
6807            Ok(Some(execution)) => Ok(AsyncStep::Return(execution.value)),
6808            Ok(None) => {
6809                if let Some((awaited, activation)) = self.pending_async_suspend.take() {
6810                    Ok(AsyncStep::Suspend {
6811                        awaited,
6812                        activation,
6813                    })
6814                } else {
6815                    Ok(AsyncStep::Return(
6816                        self.last_completion.take().unwrap_or(Value::UNDEFINED),
6817                    ))
6818                }
6819            }
6820            Err(error) => {
6821                self.unwind_frames_to(stop_depth);
6822                match error.kind {
6823                    RuntimeErrorKind::UncaughtThrow { value, origin } => {
6824                        Ok(AsyncStep::Throw { value, origin })
6825                    }
6826                    kind => Err(EvalFailure::Runtime(kind)),
6827                }
6828            }
6829        }
6830    }
6831
6832    /// Settles the result Promise (or arms the next await) for one async step.
6833    fn settle_async_step(
6834        &mut self,
6835        record: Value,
6836        promise: Value,
6837        step: Result<AsyncStep, EvalFailure>,
6838    ) -> Result<(), EvalFailure> {
6839        match step {
6840            Ok(AsyncStep::Suspend {
6841                awaited,
6842                activation,
6843            }) => {
6844                let register_count = activation.registers.len();
6845                let result = self
6846                    .store_async_activation(record, activation)
6847                    .and_then(|()| self.await_promise(awaited, record));
6848                if result.is_err() {
6849                    let released = self
6850                        .take_async_activation(record)
6851                        .map_or(register_count, |stored| stored.registers.len());
6852                    self.release_suspended_activation_registers(released);
6853                }
6854                result
6855            }
6856            Ok(AsyncStep::Return(value)) => self
6857                .resolve_promise(promise, value)
6858                .map_err(EvalFailure::Runtime),
6859            Ok(AsyncStep::Throw { value, origin }) => self
6860                .reject_promise(promise, value, origin)
6861                .map_err(EvalFailure::Runtime),
6862            Err(failure) => Err(failure),
6863        }
6864    }
6865
6866    /// Resolves the awaited value through Promise resolution and attaches the
6867    /// two direct resume reactions that point only at the activation record. An
6868    /// already-settled Promise costs exactly one microtask tick.
6869    fn await_promise(&mut self, awaited: Value, record: Value) -> Result<(), EvalFailure> {
6870        let promise = self.promise_resolve(awaited)?;
6871        let index = self
6872            .runtime_slot(promise)
6873            .map_err(EvalFailure::Runtime)?
6874            .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6875                value: promise,
6876            }))?;
6877        let settled = match &self.heap[index] {
6878            HeapEntry::Promise {
6879                state: PromiseState::Pending { .. },
6880                ..
6881            } => None,
6882            HeapEntry::Promise {
6883                state: PromiseState::Fulfilled { value },
6884                ..
6885            } => Some((true, *value, ThrowOrigin::Bytecode)),
6886            HeapEntry::Promise {
6887                state: PromiseState::Rejected { reason, origin },
6888                ..
6889            } => Some((false, *reason, *origin)),
6890            _ => {
6891                return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6892                    value: promise,
6893                }));
6894            }
6895        };
6896        if let Some((fulfilled, value, origin)) = settled {
6897            self.ensure_microtask_capacity(1)
6898                .map_err(EvalFailure::Runtime)?;
6899            let reaction = if fulfilled {
6900                PromiseReaction::AsyncFulfill { activation: record }
6901            } else {
6902                PromiseReaction::AsyncReject { activation: record }
6903            };
6904            self.microtasks.push_back(MicrotaskJob::Reaction {
6905                reaction,
6906                value,
6907                origin,
6908            });
6909            return Ok(());
6910        }
6911        self.charge_promise_reactions(2)?;
6912        let HeapEntry::Promise {
6913            state:
6914                PromiseState::Pending {
6915                    fulfill_reactions,
6916                    reject_reactions,
6917                },
6918            ..
6919        } = &mut self.heap[index]
6920        else {
6921            unreachable!("pending Promise state was checked before reaction registration");
6922        };
6923        fulfill_reactions.push(PromiseReaction::AsyncFulfill { activation: record });
6924        reject_reactions.push(PromiseReaction::AsyncReject { activation: record });
6925        Ok(())
6926    }
6927
6928    fn create_async_activation(&mut self, promise: Value) -> Result<Value, EvalFailure> {
6929        self.allocate(HeapEntry::AsyncActivation {
6930            activation: None,
6931            promise,
6932        })
6933        .map_err(EvalFailure::Runtime)
6934    }
6935
6936    fn store_async_activation(
6937        &mut self,
6938        record: Value,
6939        activation: SuspendedActivation,
6940    ) -> Result<(), EvalFailure> {
6941        let index = self
6942            .runtime_slot(record)
6943            .map_err(EvalFailure::Runtime)?
6944            .ok_or(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6945                value: record,
6946            }))?;
6947        let HeapEntry::AsyncActivation {
6948            activation: slot, ..
6949        } = &mut self.heap[index]
6950        else {
6951            return Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
6952                value: record,
6953            }));
6954        };
6955        *slot = Some(activation);
6956        Ok(())
6957    }
6958
6959    /// Takes the one suspended activation out of the record, making resume
6960    /// one-shot. A second take (a second resume) is a hard invalid-state error.
6961    fn take_async_activation(
6962        &mut self,
6963        record: Value,
6964    ) -> Result<SuspendedActivation, RuntimeErrorKind> {
6965        let index = self
6966            .runtime_slot(record)?
6967            .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
6968        let HeapEntry::AsyncActivation {
6969            activation: slot, ..
6970        } = &mut self.heap[index]
6971        else {
6972            return Err(RuntimeErrorKind::InvalidValue { value: record });
6973        };
6974        slot.take()
6975            .ok_or(RuntimeErrorKind::InvalidValue { value: record })
6976    }
6977
6978    fn async_activation_promise(&self, record: Value) -> Result<Value, RuntimeErrorKind> {
6979        let index = self
6980            .runtime_slot(record)?
6981            .ok_or(RuntimeErrorKind::InvalidValue { value: record })?;
6982        let HeapEntry::AsyncActivation { promise, .. } = &self.heap[index] else {
6983            return Err(RuntimeErrorKind::InvalidValue { value: record });
6984        };
6985        Ok(*promise)
6986    }
6987
6988    pub(crate) fn iterator_result(
6989        &mut self,
6990        value: Value,
6991        done: bool,
6992    ) -> Result<Value, EvalFailure> {
6993        let result = self
6994            .allocate(HeapEntry::Object {
6995                properties: PropertyMap::default(),
6996                prototype: Some(self.intrinsics.object_prototype),
6997                boxed_primitive: None,
6998                extensible: true,
6999            })
7000            .map_err(EvalFailure::Runtime)?;
7001        self.set_data_property(result, "value", value)?;
7002        self.set_data_property(result, "done", Value::boolean(done))?;
7003        Ok(result)
7004    }
7005
7006    // ---- iterators ---------------------------------------------------------
7007
7008    fn create_iterator(&mut self, src: Value, kind: IteratorKind) -> Result<Value, EvalFailure> {
7009        if kind == IteratorKind::Keys {
7010            let keys = self.enumerable_keys(src)?;
7011            return self
7012                .allocate(HeapEntry::Iterator {
7013                    state: IteratorState::Keys { index: 0, keys },
7014                })
7015                .map_err(EvalFailure::Runtime);
7016        }
7017
7018        let iterator_symbol = self.intrinsics.builtins.symbol_iterator();
7019        let iterator_key = self.to_property_key(iterator_symbol)?;
7020        let method = self.get_property_key(src, &iterator_key)?;
7021        if !self.is_callable(method)? {
7022            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7023                operation: "value is not iterable",
7024            }));
7025        }
7026        let iterator = self.call_value(method, src, &[])?;
7027        if !self.is_object(iterator) {
7028            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7029                operation: "iterator method returned a non-object",
7030            }));
7031        }
7032        let next = self.get_named_property(iterator, "next")?;
7033        self.create_protocol_iterator(iterator, next)
7034    }
7035
7036    pub(crate) fn create_protocol_iterator(
7037        &mut self,
7038        iterator: Value,
7039        next: Value,
7040    ) -> Result<Value, EvalFailure> {
7041        self.allocate(HeapEntry::Iterator {
7042            state: IteratorState::Protocol { iterator, next },
7043        })
7044        .map_err(EvalFailure::Runtime)
7045    }
7046
7047    fn own_property_keys(&self, src: Value) -> Result<Vec<PropertyKey>, EvalFailure> {
7048        match self.runtime_slot(src).map_err(EvalFailure::Runtime)? {
7049            Some(index) => match &self.heap[index] {
7050                HeapEntry::Object { properties, .. }
7051                | HeapEntry::Generator { properties, .. }
7052                | HeapEntry::Script { properties, .. }
7053                | HeapEntry::Function { properties, .. }
7054                | HeapEntry::NativeFunction { properties, .. }
7055                | HeapEntry::RegExp { properties, .. }
7056                | HeapEntry::Date { properties, .. }
7057                | HeapEntry::BuiltinIterator { properties, .. }
7058                | HeapEntry::Collection { properties, .. }
7059                | HeapEntry::Promise { properties, .. }
7060                | HeapEntry::Timeout { properties, .. } => Ok(ordered_property_keys(properties)),
7061                HeapEntry::Array {
7062                    elements,
7063                    properties,
7064                    ..
7065                } => {
7066                    let mut indices: Vec<(usize, PropertyKey)> = elements
7067                        .iter()
7068                        .enumerate()
7069                        .filter(|(_, element)| **element != Value::HOLE)
7070                        .map(|(offset, _)| {
7071                            (
7072                                offset,
7073                                PropertyKey::Named(EcmaString::from_utf8(&offset.to_string())),
7074                            )
7075                        })
7076                        .collect();
7077                    let mut suffix = Vec::new();
7078                    for key in ordered_property_keys(properties) {
7079                        let Some(offset) = key.as_string().and_then(array_index) else {
7080                            suffix.push(key);
7081                            continue;
7082                        };
7083                        let offset = offset as usize;
7084                        if elements
7085                            .get(offset)
7086                            .is_some_and(|element| *element != Value::HOLE)
7087                        {
7088                            continue;
7089                        }
7090                        indices.push((offset, key));
7091                    }
7092                    indices.sort_unstable_by_key(|(offset, _)| *offset);
7093                    Ok(indices
7094                        .into_iter()
7095                        .map(|(_, key)| key)
7096                        .chain(suffix)
7097                        .collect())
7098                }
7099                HeapEntry::String(text) => Ok((0..text.len_units())
7100                    .map(|index| PropertyKey::Named(EcmaString::from_utf8(&index.to_string())))
7101                    .collect()),
7102                HeapEntry::ModuleNamespace { module } => {
7103                    let mut names: Vec<EcmaString> = self
7104                        .program_module(*module)
7105                        .exports
7106                        .iter()
7107                        .map(|export| self.constant_text(*module, export.name).clone())
7108                        .collect();
7109                    names.sort();
7110                    Ok(names.into_iter().map(PropertyKey::Named).collect())
7111                }
7112                HeapEntry::ExternalModuleNamespace { specifier } => Ok(self.registry.external
7113                    [specifier]
7114                    .exports
7115                    .keys()
7116                    .cloned()
7117                    .map(PropertyKey::Named)
7118                    .collect()),
7119                HeapEntry::ProcessEnv { .. }
7120                | HeapEntry::BigInt(_)
7121                | HeapEntry::Symbol { .. }
7122                | HeapEntry::PrivateName { .. }
7123                | HeapEntry::HashState { .. }
7124                | HeapEntry::Iterator { .. }
7125                | HeapEntry::PromiseResolver { .. }
7126                | HeapEntry::PromiseFinally { .. }
7127                | HeapEntry::PromiseAll { .. }
7128                | HeapEntry::AsyncActivation { .. }
7129                | HeapEntry::PromiseAllElement { .. } => Ok(Vec::new()),
7130            },
7131            None => Ok(Vec::new()),
7132        }
7133    }
7134
7135    fn own_property_is_enumerable(
7136        &self,
7137        src: Value,
7138        key: &PropertyKey,
7139    ) -> Result<bool, EvalFailure> {
7140        let Some(index) = self.runtime_slot(src).map_err(EvalFailure::Runtime)? else {
7141            return Ok(false);
7142        };
7143        Ok(match &self.heap[index] {
7144            HeapEntry::Array {
7145                elements,
7146                properties,
7147                ..
7148            } => properties.get(key).map_or_else(
7149                || {
7150                    key.as_string().is_some_and(|name| {
7151                        array_index(name).is_some_and(|offset| {
7152                            elements
7153                                .get(offset as usize)
7154                                .is_some_and(|element| *element != Value::HOLE)
7155                        })
7156                    })
7157                },
7158                Property::enumerable,
7159            ),
7160            HeapEntry::String(text) => key.as_string().is_some_and(|name| {
7161                array_index(name).is_some_and(|offset| (offset as usize) < text.len_units())
7162            }),
7163            HeapEntry::ModuleNamespace { .. } | HeapEntry::ExternalModuleNamespace { .. } => {
7164                matches!(key, PropertyKey::Named(_))
7165            }
7166            HeapEntry::Object { properties, .. }
7167            | HeapEntry::Generator { properties, .. }
7168            | HeapEntry::Script { properties, .. }
7169            | HeapEntry::Function { properties, .. }
7170            | HeapEntry::NativeFunction { properties, .. }
7171            | HeapEntry::RegExp { properties, .. }
7172            | HeapEntry::Date { properties, .. }
7173            | HeapEntry::BuiltinIterator { properties, .. }
7174            | HeapEntry::Collection { properties, .. }
7175            | HeapEntry::Promise { properties, .. }
7176            | HeapEntry::Timeout { properties, .. } => {
7177                properties.get(key).is_some_and(Property::enumerable)
7178            }
7179            _ => false,
7180        })
7181    }
7182
7183    fn enumerable_keys(&self, src: Value) -> Result<Vec<EcmaString>, EvalFailure> {
7184        let mut names = Vec::new();
7185        for key in self.own_property_keys(src)? {
7186            if !self.own_property_is_enumerable(src, &key)? {
7187                continue;
7188            }
7189            if let PropertyKey::Named(name) = key {
7190                names.push(name);
7191            }
7192        }
7193        Ok(names)
7194    }
7195
7196    fn iterator_next(&mut self, iterator: Value) -> Result<(bool, Value), EvalFailure> {
7197        let (callee, this_value) = match self.prepare_iterator_next(iterator)? {
7198            IteratorNextPrepared::Ready { done, value } => return Ok((done, value)),
7199            IteratorNextPrepared::Call { callee, this_value } => (callee, this_value),
7200        };
7201
7202        let result = self.call_value(callee, this_value, &[])?;
7203        if !self.is_object(result) {
7204            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7205                operation: "iterator next returned a non-object",
7206            }));
7207        }
7208        let done = self.get_named_property(result, "done")?;
7209        if self.truthy(done) {
7210            return Ok((true, Value::UNDEFINED));
7211        }
7212        let value = self.get_named_property(result, "value")?;
7213        Ok((false, value))
7214    }
7215
7216    pub(crate) fn prepare_iterator_next(
7217        &mut self,
7218        iterator: Value,
7219    ) -> Result<IteratorNextPrepared, EvalFailure> {
7220        let iterator_index = self
7221            .runtime_slot(iterator)
7222            .map_err(EvalFailure::Runtime)?
7223            .ok_or(EvalFailure::Throw(ThrowOrigin::TypeError {
7224                operation: "iterator next on non-iterator",
7225            }))?;
7226        match &self.heap[iterator_index] {
7227            HeapEntry::Iterator {
7228                state: IteratorState::Keys { index, keys },
7229            } => {
7230                let Some(text) = keys.get(*index).cloned() else {
7231                    return Ok(IteratorNextPrepared::Ready {
7232                        done: true,
7233                        value: Value::UNDEFINED,
7234                    });
7235                };
7236                let value = self
7237                    .allocate(HeapEntry::String(text))
7238                    .map_err(EvalFailure::Runtime)?;
7239                self.advance_iterator(iterator_index);
7240                Ok(IteratorNextPrepared::Ready { done: false, value })
7241            }
7242            HeapEntry::Iterator {
7243                state: IteratorState::Protocol { iterator, next },
7244            } => Ok(IteratorNextPrepared::Call {
7245                callee: *next,
7246                this_value: *iterator,
7247            }),
7248            _ => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7249                operation: "iterator next on non-iterator",
7250            })),
7251        }
7252    }
7253
7254    pub(crate) fn iterable_values(&mut self, source: Value) -> Result<Vec<Value>, EvalFailure> {
7255        let iterator = self.create_iterator(source, IteratorKind::Sync)?;
7256        let mut values = Vec::new();
7257        loop {
7258            let (done, value) = self.iterator_next(iterator)?;
7259            if done {
7260                return Ok(values);
7261            }
7262            let bytes = values
7263                .len()
7264                .checked_add(1)
7265                .and_then(|length| length.checked_mul(std::mem::size_of::<Value>()))
7266                .ok_or(EvalFailure::Runtime(
7267                    RuntimeErrorKind::HeapByteLimitExceeded {
7268                        limit: self.limits.max_heap_bytes,
7269                    },
7270                ))?;
7271            self.ensure_allocation_capacity(1, bytes)
7272                .map_err(EvalFailure::Runtime)?;
7273            values.push(value);
7274        }
7275    }
7276
7277    fn advance_iterator(&mut self, iterator_index: usize) {
7278        if let HeapEntry::Iterator {
7279            state: IteratorState::Keys { index, .. },
7280        } = &mut self.heap[iterator_index]
7281        {
7282            *index += 1;
7283        }
7284    }
7285
7286    // ---- operators & coercions --------------------------------------------
7287
7288    fn eval_unary(&mut self, op: UnaryOp, operand: Value) -> Result<Value, EvalFailure> {
7289        match op {
7290            UnaryOp::Void => Ok(Value::UNDEFINED),
7291            UnaryOp::TypeOf => {
7292                let text = EcmaString::from_utf8(self.type_of(operand));
7293                self.allocate(HeapEntry::String(text))
7294                    .map_err(EvalFailure::Runtime)
7295            }
7296            UnaryOp::Plus => self.to_number(operand),
7297            UnaryOp::Negate => {
7298                if let Some(text) = self.bigint_text(operand) {
7299                    let negated = if text == "0" {
7300                        "0".to_owned()
7301                    } else if let Some(positive) = text.strip_prefix('-') {
7302                        positive.to_owned()
7303                    } else {
7304                        format!("-{text}")
7305                    };
7306                    return self
7307                        .allocate(HeapEntry::BigInt(negated))
7308                        .map_err(EvalFailure::Runtime);
7309                }
7310                let number =
7311                    numeric_f64(self.to_number(operand)?).expect("ToNumber returns numeric");
7312                Ok(number_value(-number))
7313            }
7314            UnaryOp::BitwiseNot => {
7315                if let Some(text) = self.bigint_text(operand) {
7316                    let value = text.parse::<i128>().map_err(|_| {
7317                        EvalFailure::Throw(ThrowOrigin::RangeError {
7318                            operation: "bigint bitwise not",
7319                        })
7320                    })?;
7321                    return self
7322                        .allocate(HeapEntry::BigInt((!value).to_string()))
7323                        .map_err(EvalFailure::Runtime);
7324                }
7325                Ok(Value::int32(
7326                    (!to_int32(numeric_f64(self.to_number(operand)?).unwrap())) as u32,
7327                ))
7328            }
7329            UnaryOp::LogicalNot => Ok(Value::boolean(!self.truthy(operand))),
7330        }
7331    }
7332
7333    fn eval_binary(
7334        &mut self,
7335        op: BinaryOp,
7336        left: Value,
7337        right: Value,
7338    ) -> Result<Value, EvalFailure> {
7339        match op {
7340            BinaryOp::StrictEqual => Ok(Value::boolean(self.strict_equal(left, right))),
7341            BinaryOp::StrictNotEqual => Ok(Value::boolean(!self.strict_equal(left, right))),
7342            BinaryOp::Equal | BinaryOp::NotEqual => {
7343                let equal = self.abstract_equal(left, right)?;
7344                Ok(Value::boolean(if op == BinaryOp::Equal {
7345                    equal
7346                } else {
7347                    !equal
7348                }))
7349            }
7350            BinaryOp::LessThan
7351            | BinaryOp::LessThanOrEqual
7352            | BinaryOp::GreaterThan
7353            | BinaryOp::GreaterThanOrEqual => {
7354                let ordering = self.relational_compare(left, right)?;
7355                let result = match (op, ordering) {
7356                    (_, None) => false,
7357                    (BinaryOp::LessThan, Some(order)) => order == Ordering::Less,
7358                    (BinaryOp::LessThanOrEqual, Some(order)) => order != Ordering::Greater,
7359                    (BinaryOp::GreaterThan, Some(order)) => order == Ordering::Greater,
7360                    (BinaryOp::GreaterThanOrEqual, Some(order)) => order != Ordering::Less,
7361                    _ => unreachable!(),
7362                };
7363                Ok(Value::boolean(result))
7364            }
7365            BinaryOp::InstanceOf => self.instance_of(left, right).map(Value::boolean),
7366            BinaryOp::In => {
7367                let key = self.to_property_key(left)?;
7368                self.has_property(right, &key).map(Value::boolean)
7369            }
7370            BinaryOp::Add => self.add(left, right),
7371            BinaryOp::Subtract
7372            | BinaryOp::Multiply
7373            | BinaryOp::Divide
7374            | BinaryOp::Remainder
7375            | BinaryOp::Exponent
7376            | BinaryOp::BitAnd
7377            | BinaryOp::BitOr
7378            | BinaryOp::BitXor
7379            | BinaryOp::ShiftLeft
7380            | BinaryOp::ShiftRight
7381            | BinaryOp::UnsignedShiftRight => self.numeric_binary(op, left, right),
7382        }
7383    }
7384
7385    fn add(&mut self, left: Value, right: Value) -> Result<Value, EvalFailure> {
7386        let left = self.to_primitive_default(left)?;
7387        let right = self.to_primitive_default(right)?;
7388        let left_string = self.string_text(left).cloned();
7389        let right_string = self.string_text(right).cloned();
7390        if left_string.is_some() || right_string.is_some() {
7391            let left = match left_string {
7392                Some(text) => text,
7393                None => self.to_string(left)?,
7394            };
7395            let right = match right_string {
7396                Some(text) => text,
7397                None => self.to_string(right)?,
7398            };
7399            let mut builder = EcmaStringBuilder::with_capacity(
7400                left.len_units().saturating_add(right.len_units()),
7401            );
7402            for &unit in left.as_units() {
7403                builder.push_unit(unit);
7404            }
7405            for &unit in right.as_units() {
7406                builder.push_unit(unit);
7407            }
7408            return self
7409                .allocate(HeapEntry::String(builder.finish()))
7410                .map_err(EvalFailure::Runtime);
7411        }
7412        let left_bigint = self.bigint_text(left).map(str::to_owned);
7413        let right_bigint = self.bigint_text(right).map(str::to_owned);
7414        match (left_bigint, right_bigint) {
7415            (Some(left), Some(right)) => {
7416                let sum = bigint_i128(&left)?
7417                    .checked_add(bigint_i128(&right)?)
7418                    .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError {
7419                        operation: "bigint add overflow",
7420                    }))?;
7421                return self
7422                    .allocate(HeapEntry::BigInt(sum.to_string()))
7423                    .map_err(EvalFailure::Runtime);
7424            }
7425            (Some(_), None) | (None, Some(_)) => {
7426                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7427                    operation: "add bigint and number",
7428                }));
7429            }
7430            (None, None) => {}
7431        }
7432        let left = numeric_f64(self.to_number(left)?).unwrap();
7433        let right = numeric_f64(self.to_number(right)?).unwrap();
7434        Ok(number_value(left + right))
7435    }
7436
7437    fn numeric_binary(
7438        &mut self,
7439        op: BinaryOp,
7440        left: Value,
7441        right: Value,
7442    ) -> Result<Value, EvalFailure> {
7443        let left_bigint = self.bigint_text(left).map(str::to_owned);
7444        let right_bigint = self.bigint_text(right).map(str::to_owned);
7445        if left_bigint.is_some() || right_bigint.is_some() {
7446            let (Some(left), Some(right)) = (left_bigint, right_bigint) else {
7447                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7448                    operation: "mix bigint and number",
7449                }));
7450            };
7451            let result = bigint_binary(op, &left, &right)?;
7452            return self
7453                .allocate(HeapEntry::BigInt(result))
7454                .map_err(EvalFailure::Runtime);
7455        }
7456        let left = numeric_f64(self.to_number(left)?).unwrap();
7457        let right = numeric_f64(self.to_number(right)?).unwrap();
7458        let value = match op {
7459            BinaryOp::Subtract => number_value(left - right),
7460            BinaryOp::Multiply => number_value(left * right),
7461            BinaryOp::Divide => Value::number(left / right),
7462            BinaryOp::Remainder => Value::number(left % right),
7463            BinaryOp::Exponent => Value::number(left.powf(right)),
7464            BinaryOp::BitAnd => Value::int32((to_int32(left) & to_int32(right)) as u32),
7465            BinaryOp::BitOr => Value::int32((to_int32(left) | to_int32(right)) as u32),
7466            BinaryOp::BitXor => Value::int32((to_int32(left) ^ to_int32(right)) as u32),
7467            BinaryOp::ShiftLeft => {
7468                Value::int32(to_int32(left).wrapping_shl(to_uint32(right) & 31) as u32)
7469            }
7470            BinaryOp::ShiftRight => {
7471                Value::int32((to_int32(left) >> (to_uint32(right) & 31)) as u32)
7472            }
7473            BinaryOp::UnsignedShiftRight => {
7474                number_value((to_uint32(left) >> (to_uint32(right) & 31)) as f64)
7475            }
7476            _ => unreachable!("numeric binary operator partition"),
7477        };
7478        Ok(value)
7479    }
7480
7481    fn coercion_is_primitive(&self, value: Value) -> Result<bool, EvalFailure> {
7482        let Some(index) = self.runtime_slot(value).map_err(EvalFailure::Runtime)? else {
7483            return Ok(true);
7484        };
7485        Ok(matches!(
7486            self.heap[index],
7487            HeapEntry::String(_)
7488                | HeapEntry::BigInt(_)
7489                | HeapEntry::Symbol { .. }
7490                | HeapEntry::PrivateName { .. }
7491        ))
7492    }
7493
7494    fn to_primitive_default(&mut self, value: Value) -> Result<Value, EvalFailure> {
7495        let prefer_string = self
7496            .runtime_slot(value)
7497            .map_err(EvalFailure::Runtime)?
7498            .is_some_and(|index| matches!(self.heap[index], HeapEntry::Date { .. }));
7499        self.to_primitive_observable(value, prefer_string)
7500    }
7501
7502    pub(crate) fn to_primitive_observable(
7503        &mut self,
7504        value: Value,
7505        prefer_string: bool,
7506    ) -> Result<Value, EvalFailure> {
7507        if self.coercion_is_primitive(value)? {
7508            return Ok(value);
7509        }
7510        let methods = if prefer_string {
7511            ["toString", "valueOf"]
7512        } else {
7513            ["valueOf", "toString"]
7514        };
7515        for name in methods {
7516            let method = self.get_named_property(value, name)?;
7517            if !self.is_callable(method)? {
7518                continue;
7519            }
7520            let primitive = self.call_value(method, value, &[])?;
7521            if self.coercion_is_primitive(primitive)? {
7522                return Ok(primitive);
7523            }
7524        }
7525        Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7526            operation: "cannot convert object to primitive",
7527        }))
7528    }
7529
7530    pub(crate) fn to_string_observable(&mut self, value: Value) -> Result<EcmaString, EvalFailure> {
7531        let primitive = self.to_primitive_observable(value, true)?;
7532        self.to_string(primitive)
7533    }
7534
7535    pub(crate) fn to_number_observable(&mut self, value: Value) -> Result<Value, EvalFailure> {
7536        let primitive = self.to_primitive_observable(value, false)?;
7537        self.to_number(primitive)
7538    }
7539
7540    fn to_number(&self, value: Value) -> Result<Value, EvalFailure> {
7541        match value.decode() {
7542            Some(Decoded::Number(_)) | Some(Decoded::Int32(_)) => self.to_primitive(value),
7543            Some(Decoded::Undefined) => Ok(Value::number(f64::NAN)),
7544            Some(Decoded::Null) => Ok(Value::int32(0)),
7545            Some(Decoded::Boolean(value)) => Ok(Value::int32(u32::from(value))),
7546            Some(Decoded::Hole) | Some(Decoded::Uninitialized) => Ok(Value::number(f64::NAN)),
7547            Some(Decoded::HeapRef(_)) => {
7548                match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7549                    Some(index) => match &self.heap[index] {
7550                        HeapEntry::String(text) => Ok(number_value(parse_number(text))),
7551                        HeapEntry::BigInt(_) => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7552                            operation: "convert bigint to number",
7553                        })),
7554                        HeapEntry::Array { elements, .. } if elements.is_empty() => {
7555                            Ok(Value::int32(0))
7556                        }
7557                        HeapEntry::Array { elements, .. } if elements.len() == 1 => {
7558                            self.to_number(elements[0])
7559                        }
7560                        HeapEntry::Symbol { .. } | HeapEntry::PrivateName { .. } => {
7561                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7562                                operation: "convert symbol to number",
7563                            }))
7564                        }
7565                        HeapEntry::Object { .. }
7566                        | HeapEntry::Generator { .. }
7567                        | HeapEntry::Script { .. }
7568                        | HeapEntry::Array { .. }
7569                        | HeapEntry::Function { .. }
7570                        | HeapEntry::ModuleNamespace { .. }
7571                        | HeapEntry::ExternalModuleNamespace { .. }
7572                        | HeapEntry::HashState { .. }
7573                        | HeapEntry::NativeFunction { .. }
7574                        | HeapEntry::RegExp { .. }
7575                        | HeapEntry::Date { .. }
7576                        | HeapEntry::BuiltinIterator { .. }
7577                        | HeapEntry::Collection { .. }
7578                        | HeapEntry::Promise { .. }
7579                        | HeapEntry::PromiseResolver { .. }
7580                        | HeapEntry::PromiseFinally { .. }
7581                        | HeapEntry::PromiseAll { .. }
7582                        | HeapEntry::AsyncActivation { .. }
7583                        | HeapEntry::PromiseAllElement { .. }
7584                        | HeapEntry::ProcessEnv { .. }
7585                        | HeapEntry::Iterator { .. }
7586                        | HeapEntry::Timeout { .. } => Ok(Value::number(f64::NAN)),
7587                    },
7588                    None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7589                        operation: "coerce host object to number",
7590                    })),
7591                }
7592            }
7593            None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7594                value,
7595            })),
7596        }
7597    }
7598
7599    fn truthy(&self, value: Value) -> bool {
7600        match value.decode() {
7601            Some(Decoded::Number(number)) => number != 0.0 && !number.is_nan(),
7602            Some(Decoded::Int32(value)) => value != 0,
7603            Some(Decoded::Undefined | Decoded::Null | Decoded::Hole | Decoded::Uninitialized)
7604            | None => false,
7605            Some(Decoded::Boolean(value)) => value,
7606            Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7607                Ok(Some(index)) => match &self.heap[index] {
7608                    HeapEntry::String(text) => !text.is_empty(),
7609                    HeapEntry::BigInt(text) => text != "0",
7610                    HeapEntry::Object { .. }
7611                    | HeapEntry::Generator { .. }
7612                    | HeapEntry::Script { .. }
7613                    | HeapEntry::Array { .. }
7614                    | HeapEntry::Function { .. }
7615                    | HeapEntry::ModuleNamespace { .. }
7616                    | HeapEntry::ExternalModuleNamespace { .. }
7617                    | HeapEntry::HashState { .. }
7618                    | HeapEntry::NativeFunction { .. }
7619                    | HeapEntry::Symbol { .. }
7620                    | HeapEntry::PrivateName { .. }
7621                    | HeapEntry::RegExp { .. }
7622                    | HeapEntry::Date { .. }
7623                    | HeapEntry::BuiltinIterator { .. }
7624                    | HeapEntry::Collection { .. }
7625                    | HeapEntry::Promise { .. }
7626                    | HeapEntry::PromiseResolver { .. }
7627                    | HeapEntry::PromiseFinally { .. }
7628                    | HeapEntry::PromiseAll { .. }
7629                    | HeapEntry::AsyncActivation { .. }
7630                    | HeapEntry::PromiseAllElement { .. }
7631                    | HeapEntry::ProcessEnv { .. }
7632                    | HeapEntry::Iterator { .. }
7633                    | HeapEntry::Timeout { .. } => true,
7634                },
7635                Ok(None) => true,
7636                Err(_) => false,
7637            },
7638        }
7639    }
7640
7641    fn type_of(&self, value: Value) -> &'static str {
7642        match value.decode() {
7643            Some(Decoded::Undefined | Decoded::Hole | Decoded::Uninitialized) | None => "undefined",
7644            Some(Decoded::Number(_) | Decoded::Int32(_)) => "number",
7645            Some(Decoded::Null) => "object",
7646            Some(Decoded::Boolean(_)) => "boolean",
7647            Some(Decoded::HeapRef(_)) => match self.runtime_slot(value) {
7648                Ok(Some(index)) => match &self.heap[index] {
7649                    HeapEntry::String(_) => "string",
7650                    HeapEntry::BigInt(_) => "bigint",
7651                    HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. } => "function",
7652                    HeapEntry::Symbol { .. } => "symbol",
7653                    HeapEntry::PrivateName { .. } => "object",
7654                    HeapEntry::Object { .. }
7655                    | HeapEntry::Generator { .. }
7656                    | HeapEntry::Script { .. }
7657                    | HeapEntry::Array { .. }
7658                    | HeapEntry::ModuleNamespace { .. }
7659                    | HeapEntry::ExternalModuleNamespace { .. }
7660                    | HeapEntry::HashState { .. }
7661                    | HeapEntry::RegExp { .. }
7662                    | HeapEntry::Date { .. }
7663                    | HeapEntry::BuiltinIterator { .. }
7664                    | HeapEntry::Collection { .. }
7665                    | HeapEntry::Promise { .. }
7666                    | HeapEntry::PromiseResolver { .. }
7667                    | HeapEntry::PromiseFinally { .. }
7668                    | HeapEntry::PromiseAll { .. }
7669                    | HeapEntry::AsyncActivation { .. }
7670                    | HeapEntry::PromiseAllElement { .. }
7671                    | HeapEntry::ProcessEnv { .. }
7672                    | HeapEntry::Iterator { .. }
7673                    | HeapEntry::Timeout { .. } => "object",
7674                },
7675                _ => "object",
7676            },
7677        }
7678    }
7679
7680    fn strict_equal(&self, left: Value, right: Value) -> bool {
7681        match (left.decode(), right.decode()) {
7682            (Some(Decoded::Number(a)), Some(Decoded::Number(b))) => a == b,
7683            (Some(Decoded::Number(a)), Some(Decoded::Int32(b)))
7684            | (Some(Decoded::Int32(b)), Some(Decoded::Number(a))) => a == f64::from(b as i32),
7685            (Some(Decoded::Int32(a)), Some(Decoded::Int32(b))) => a == b,
7686            (Some(Decoded::HeapRef(_)), Some(Decoded::HeapRef(_))) => {
7687                match (self.runtime_slot(left), self.runtime_slot(right)) {
7688                    (Ok(Some(a)), Ok(Some(b))) => match (&self.heap[a], &self.heap[b]) {
7689                        (HeapEntry::String(a), HeapEntry::String(b)) => a == b,
7690                        (HeapEntry::BigInt(a), HeapEntry::BigInt(b)) => a == b,
7691                        _ => left == right,
7692                    },
7693                    _ => left == right,
7694                }
7695            }
7696            _ => left == right,
7697        }
7698    }
7699
7700    fn abstract_equal(&self, left: Value, right: Value) -> Result<bool, EvalFailure> {
7701        if self.strict_equal(left, right) {
7702            return Ok(true);
7703        }
7704        if matches!(
7705            (left.decode(), right.decode()),
7706            (Some(Decoded::Null), Some(Decoded::Undefined))
7707                | (Some(Decoded::Undefined), Some(Decoded::Null))
7708        ) {
7709            return Ok(true);
7710        }
7711        let left_number = self.to_number(left);
7712        let right_number = self.to_number(right);
7713        match (left_number, right_number) {
7714            (Ok(left), Ok(right)) => Ok(numeric_f64(left).unwrap() == numeric_f64(right).unwrap()),
7715            _ => Ok(false),
7716        }
7717    }
7718
7719    fn relational_compare(
7720        &self,
7721        left: Value,
7722        right: Value,
7723    ) -> Result<Option<Ordering>, EvalFailure> {
7724        if let (Some(left), Some(right)) = (self.string_text(left), self.string_text(right)) {
7725            return Ok(Some(left.cmp(right)));
7726        }
7727        if let (Some(left), Some(right)) = (self.bigint_text(left), self.bigint_text(right)) {
7728            return Ok(Some(bigint_i128(left)?.cmp(&bigint_i128(right)?)));
7729        }
7730        let left = numeric_f64(self.to_number(left)?).unwrap();
7731        let right = numeric_f64(self.to_number(right)?).unwrap();
7732        Ok(left.partial_cmp(&right))
7733    }
7734
7735    /// `value instanceof constructor`: walks `value`'s prototype chain for the
7736    /// constructor's own `prototype` object, matching by heap identity.
7737    fn instance_of(&mut self, value: Value, constructor: Value) -> Result<bool, EvalFailure> {
7738        let constructor = self
7739            .bound_target(constructor)
7740            .map_err(EvalFailure::Runtime)?;
7741        match self
7742            .runtime_slot(constructor)
7743            .map_err(EvalFailure::Runtime)?
7744        {
7745            Some(index) => {
7746                if !matches!(
7747                    self.heap[index],
7748                    HeapEntry::Function { .. } | HeapEntry::NativeFunction { .. }
7749                ) {
7750                    return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7751                        operation: "instanceof",
7752                    }));
7753                }
7754                let target = match self.own_get_ascii(index, "prototype") {
7755                    Some(Found::Value(value)) if self.is_object(value) => value,
7756                    _ => {
7757                        return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7758                            operation: "instanceof prototype is not an object",
7759                        }));
7760                    }
7761                };
7762                let target_slot = self.runtime_slot(target).map_err(EvalFailure::Runtime)?;
7763                let mut node = match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7764                    Some(node) => node,
7765                    None => return Ok(false),
7766                };
7767                let mut guard = 0;
7768                loop {
7769                    if Some(node) == target_slot {
7770                        return Ok(true);
7771                    }
7772                    match self.prototype_index(node)? {
7773                        Some(next) => {
7774                            node = next;
7775                            guard += 1;
7776                            if guard > self.heap.len() + 1 {
7777                                return Ok(false);
7778                            }
7779                        }
7780                        None => return Ok(false),
7781                    }
7782                }
7783            }
7784            None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7785                operation: "instanceof",
7786            })),
7787        }
7788    }
7789
7790    fn value_to_string(&self, value: Value, depth: usize) -> Result<EcmaString, EvalFailure> {
7791        if depth >= 32 {
7792            return Ok(EcmaString::default());
7793        }
7794        let ascii = |text: String| EcmaString::from_utf8(&text);
7795        match value.decode() {
7796            Some(Decoded::Number(number)) => Ok(ascii(Self::ordinary_number_to_string(number))),
7797            Some(Decoded::Int32(raw)) => Ok(ascii((raw as i32).to_string())),
7798            Some(Decoded::Undefined | Decoded::Uninitialized) => {
7799                Ok(EcmaString::from_utf8("undefined"))
7800            }
7801            Some(Decoded::Null) => Ok(EcmaString::from_utf8("null")),
7802            Some(Decoded::Boolean(value)) => {
7803                Ok(EcmaString::from_utf8(if value { "true" } else { "false" }))
7804            }
7805            Some(Decoded::Hole) => Ok(EcmaString::default()),
7806            Some(Decoded::HeapRef(_)) => {
7807                match self.runtime_slot(value).map_err(EvalFailure::Runtime)? {
7808                    Some(index) => match &self.heap[index] {
7809                        HeapEntry::String(text) => Ok(text.clone()),
7810                        HeapEntry::BigInt(text) => Ok(EcmaString::from_utf8(text)),
7811                        HeapEntry::Object { .. }
7812                        | HeapEntry::Generator { .. }
7813                        | HeapEntry::Script { .. }
7814                        | HeapEntry::Date { .. }
7815                        | HeapEntry::BuiltinIterator { .. }
7816                        | HeapEntry::Collection { .. }
7817                        | HeapEntry::Promise { .. }
7818                        | HeapEntry::PromiseResolver { .. }
7819                        | HeapEntry::PromiseFinally { .. }
7820                        | HeapEntry::PromiseAll { .. }
7821                        | HeapEntry::AsyncActivation { .. }
7822                        | HeapEntry::PromiseAllElement { .. }
7823                        | HeapEntry::ModuleNamespace { .. }
7824                        | HeapEntry::ExternalModuleNamespace { .. }
7825                        | HeapEntry::ProcessEnv { .. }
7826                        | HeapEntry::Iterator { .. }
7827                        | HeapEntry::Timeout { .. }
7828                        | HeapEntry::HashState { .. } => {
7829                            Ok(EcmaString::from_utf8("[object Object]"))
7830                        }
7831                        HeapEntry::RegExp { pattern, flags, .. } => {
7832                            let mut builder = EcmaStringBuilder::with_capacity(
7833                                pattern
7834                                    .len_units()
7835                                    .saturating_add(flags.len_units())
7836                                    .saturating_add(2),
7837                            );
7838                            builder.push_unit(u16::from(b'/'));
7839                            for &unit in pattern.as_units() {
7840                                builder.push_unit(unit);
7841                            }
7842                            builder.push_unit(u16::from(b'/'));
7843                            for &unit in flags.as_units() {
7844                                builder.push_unit(unit);
7845                            }
7846                            Ok(builder.finish())
7847                        }
7848                        HeapEntry::Symbol { .. } => {
7849                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7850                                operation: "convert symbol to string",
7851                            }))
7852                        }
7853                        HeapEntry::PrivateName { .. } => {
7854                            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7855                                operation: "convert private name to string",
7856                            }))
7857                        }
7858                        HeapEntry::Function {
7859                            module, function, ..
7860                        } => {
7861                            let flags = self.module_code(*module).functions()
7862                                [function.get() as usize]
7863                                .flags();
7864                            Ok(EcmaString::from_utf8(
7865                                match (flags.is_async, flags.is_generator) {
7866                                    (true, true) => "async function* () { [bytecode] }",
7867                                    (true, false) => "async function () { [bytecode] }",
7868                                    (false, true) => "function* () { [bytecode] }",
7869                                    (false, false) => "function () { [bytecode] }",
7870                                },
7871                            ))
7872                        }
7873                        HeapEntry::NativeFunction { .. } => {
7874                            Ok(EcmaString::from_utf8("function () { [native code] }"))
7875                        }
7876                        HeapEntry::Array { elements, .. } => {
7877                            let mut text = EcmaStringBuilder::new();
7878                            for (index, element) in elements.iter().copied().enumerate() {
7879                                if index != 0 {
7880                                    text.push_unit(u16::from(b','));
7881                                }
7882                                if element != Value::HOLE
7883                                    && element != Value::NULL
7884                                    && element != Value::UNDEFINED
7885                                {
7886                                    for &unit in
7887                                        self.value_to_string(element, depth + 1)?.as_units()
7888                                    {
7889                                        text.push_unit(unit);
7890                                    }
7891                                }
7892                            }
7893                            Ok(text.finish())
7894                        }
7895                    },
7896                    None => Err(EvalFailure::Throw(ThrowOrigin::TypeError {
7897                        operation: "coerce host object to string",
7898                    })),
7899                }
7900            }
7901            None => Err(EvalFailure::Runtime(RuntimeErrorKind::InvalidValue {
7902                value,
7903            })),
7904        }
7905    }
7906
7907    fn string_text(&self, value: Value) -> Option<&EcmaString> {
7908        let index = self.runtime_slot(value).ok()??;
7909        match &self.heap[index] {
7910            HeapEntry::String(text) => Some(text),
7911            _ => None,
7912        }
7913    }
7914
7915    fn bigint_text(&self, value: Value) -> Option<&str> {
7916        let index = self.runtime_slot(value).ok()??;
7917        match &self.heap[index] {
7918            HeapEntry::BigInt(text) => Some(text),
7919            _ => None,
7920        }
7921    }
7922
7923    fn is_object(&self, value: Value) -> bool {
7924        match self.runtime_slot(value) {
7925            Ok(Some(index)) => !matches!(
7926                self.heap[index],
7927                HeapEntry::String(_)
7928                    | HeapEntry::BigInt(_)
7929                    | HeapEntry::PromiseResolver { .. }
7930                    | HeapEntry::PromiseFinally { .. }
7931                    | HeapEntry::PromiseAll { .. }
7932                    | HeapEntry::AsyncActivation { .. }
7933                    | HeapEntry::PromiseAllElement { .. }
7934            ),
7935            Ok(None) => matches!(value.decode(), Some(Decoded::HeapRef(_))),
7936            Err(_) => false,
7937        }
7938    }
7939}
7940
7941fn ordered_property_keys(properties: &PropertyMap) -> Vec<PropertyKey> {
7942    let mut indices = Vec::new();
7943    let mut strings = Vec::new();
7944    let mut symbols = Vec::new();
7945    for (key, _) in properties.iter() {
7946        match key {
7947            PropertyKey::Named(name) => match array_index(name) {
7948                Some(index) => indices.push((index, key.clone())),
7949                None => strings.push(key.clone()),
7950            },
7951            PropertyKey::Symbol(_) => symbols.push(key.clone()),
7952            PropertyKey::Private(_) => {}
7953        }
7954    }
7955    indices.sort_unstable_by_key(|(index, _)| *index);
7956    indices
7957        .into_iter()
7958        .map(|(_, key)| key)
7959        .chain(strings)
7960        .chain(symbols)
7961        .collect()
7962}
7963
7964fn property_lookup(properties: &PropertyMap, key: &PropertyKey) -> Option<Found> {
7965    match properties.get(key) {
7966        Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
7967        Some(Property::Accessor { getter, .. }) => Some(match getter {
7968            Some(getter) => Found::Getter(*getter),
7969            None => Found::NoGetter,
7970        }),
7971        None => None,
7972    }
7973}
7974
7975fn property_lookup_ascii(properties: &PropertyMap, name: &str) -> Option<Found> {
7976    match properties.get_ascii(name) {
7977        Some(Property::Data { value, .. }) => Some(Found::Value(*value)),
7978        Some(Property::Accessor { getter, .. }) => Some(match getter {
7979            Some(getter) => Found::Getter(*getter),
7980            None => Found::NoGetter,
7981        }),
7982        None => None,
7983    }
7984}
7985
7986fn innermost_handler(function: &Function, pc: usize) -> Option<bamts_bytecode::ExceptionHandler> {
7987    function
7988        .handlers()
7989        .iter()
7990        .copied()
7991        .filter(|handler| handler.start.get() as usize <= pc && pc < handler.end.get() as usize)
7992        .max_by(|left, right| {
7993            left.start
7994                .get()
7995                .cmp(&right.start.get())
7996                .then_with(|| right.end.get().cmp(&left.end.get()))
7997        })
7998}
7999
8000fn numeric_f64(value: Value) -> Option<f64> {
8001    match value.decode()? {
8002        Decoded::Number(number) => Some(number),
8003        Decoded::Int32(raw) => Some(f64::from(raw as i32)),
8004        _ => None,
8005    }
8006}
8007
8008fn number_value(number: f64) -> Value {
8009    if number.is_finite()
8010        && number.fract() == 0.0
8011        && number >= f64::from(i32::MIN)
8012        && number <= f64::from(i32::MAX)
8013    {
8014        Value::int32(number as i32 as u32)
8015    } else {
8016        Value::number(number)
8017    }
8018}
8019
8020fn parse_number(text: &EcmaString) -> f64 {
8021    let Ok(text) = text.to_utf8_strict() else {
8022        return f64::NAN;
8023    };
8024    parse_number_utf8(&text)
8025}
8026
8027fn parse_number_utf8(text: &str) -> f64 {
8028    let trimmed = text.trim();
8029    if trimmed.is_empty() {
8030        0.0
8031    } else {
8032        trimmed.parse::<f64>().unwrap_or(f64::NAN)
8033    }
8034}
8035
8036fn format_number(number: f64) -> String {
8037    if number.is_nan() {
8038        return "NaN".to_owned();
8039    }
8040    if number == f64::INFINITY {
8041        return "Infinity".to_owned();
8042    }
8043    if number == f64::NEG_INFINITY {
8044        return "-Infinity".to_owned();
8045    }
8046    if number == 0.0 {
8047        return "0".to_owned();
8048    }
8049
8050    let negative = number.is_sign_negative();
8051    let raw = number.abs().to_string();
8052    let (mantissa, explicit_exponent) = match raw.split_once(['e', 'E']) {
8053        Some((mantissa, exponent)) => (
8054            mantissa,
8055            exponent
8056                .parse::<i32>()
8057                .expect("Rust formats finite f64 exponents as i32"),
8058        ),
8059        None => (raw.as_str(), 0),
8060    };
8061    let decimal = mantissa.find('.').unwrap_or(mantissa.len());
8062    let untrimmed: String = mantissa.chars().filter(|ch| *ch != '.').collect();
8063    let first = untrimmed
8064        .find(|ch| ch != '0')
8065        .expect("a nonzero number has a nonzero decimal digit");
8066    let digits = untrimmed[first..].trim_end_matches('0');
8067    let exponent = explicit_exponent + decimal as i32 - first as i32 - 1;
8068
8069    let mut result = String::new();
8070    if negative {
8071        result.push('-');
8072    }
8073    if !(-6..21).contains(&exponent) {
8074        result.push(digits.as_bytes()[0] as char);
8075        if digits.len() > 1 {
8076            result.push('.');
8077            result.push_str(&digits[1..]);
8078        }
8079        result.push('e');
8080        if exponent >= 0 {
8081            result.push('+');
8082        }
8083        result.push_str(&exponent.to_string());
8084    } else if exponent >= 0 {
8085        let integer_digits = exponent as usize + 1;
8086        if digits.len() <= integer_digits {
8087            result.push_str(digits);
8088            result.extend(std::iter::repeat_n('0', integer_digits - digits.len()));
8089        } else {
8090            result.push_str(&digits[..integer_digits]);
8091            result.push('.');
8092            result.push_str(&digits[integer_digits..]);
8093        }
8094    } else {
8095        result.push_str("0.");
8096        result.extend(std::iter::repeat_n('0', (-exponent - 1) as usize));
8097        result.push_str(digits);
8098    }
8099    result
8100}
8101
8102fn to_uint32(number: f64) -> u32 {
8103    if !number.is_finite() || number == 0.0 {
8104        0
8105    } else {
8106        number.trunc().rem_euclid(4_294_967_296.0) as u32
8107    }
8108}
8109
8110fn to_int32(number: f64) -> i32 {
8111    to_uint32(number) as i32
8112}
8113
8114fn array_index_ascii(key: &str) -> Option<u32> {
8115    if !key.is_ascii() || key.is_empty() || (key.len() > 1 && key.as_bytes()[0] == b'0') {
8116        return None;
8117    }
8118    let mut index = 0_u32;
8119    for byte in key.bytes() {
8120        if !byte.is_ascii_digit() {
8121            return None;
8122        }
8123        index = index.checked_mul(10)?.checked_add(u32::from(byte - b'0'))?;
8124    }
8125    (index != u32::MAX).then_some(index)
8126}
8127
8128fn array_index(key: &EcmaString) -> Option<u32> {
8129    let units = key.as_units();
8130    if units.is_empty() || (units.len() > 1 && units[0] == u16::from(b'0')) {
8131        return None;
8132    }
8133    let mut index = 0_u32;
8134    for &unit in units {
8135        if !(u16::from(b'0')..=u16::from(b'9')).contains(&unit) {
8136            return None;
8137        }
8138        index = index
8139            .checked_mul(10)?
8140            .checked_add(u32::from(unit - u16::from(b'0')))?;
8141    }
8142    (index != u32::MAX).then_some(index)
8143}
8144
8145fn exact_array_length(value: Value) -> Option<usize> {
8146    let number = numeric_f64(value)?;
8147    if number.is_finite() && number >= 0.0 && number.fract() == 0.0 && number <= u32::MAX as f64 {
8148        Some(number as usize)
8149    } else {
8150        None
8151    }
8152}
8153
8154pub(crate) fn apply_array_length(
8155    elements: &mut Vec<Value>,
8156    properties: &mut PropertyMap,
8157    length: usize,
8158    operation: &'static str,
8159) -> Result<(), EvalFailure> {
8160    if length >= elements.len() {
8161        elements.resize(length, Value::HOLE);
8162        return Ok(());
8163    }
8164    let blocked = properties
8165        .iter()
8166        .filter_map(|(key, property)| {
8167            (!property.configurable())
8168                .then(|| key.as_string().and_then(array_index))
8169                .flatten()
8170        })
8171        .map(|offset| offset as usize)
8172        .filter(|offset| *offset >= length)
8173        .max();
8174    let effective_length = blocked.map_or(length, |offset| offset + 1);
8175    properties.0.retain(|(key, _)| {
8176        key.as_string()
8177            .and_then(array_index)
8178            .is_none_or(|offset| (offset as usize) < effective_length)
8179    });
8180    elements.resize(effective_length, Value::HOLE);
8181    if blocked.is_some() {
8182        return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8183    }
8184    Ok(())
8185}
8186
8187pub(crate) fn array_set_length(
8188    elements: &mut Vec<Value>,
8189    properties: &mut PropertyMap,
8190    length_writable: bool,
8191    value: Value,
8192    operation: &'static str,
8193) -> Result<(), EvalFailure> {
8194    let length = exact_array_length(value)
8195        .ok_or(EvalFailure::Throw(ThrowOrigin::RangeError { operation }))?;
8196    if !length_writable {
8197        return Err(EvalFailure::Throw(ThrowOrigin::TypeError { operation }));
8198    }
8199    apply_array_length(elements, properties, length, operation)
8200}
8201
8202fn bigint_i128(text: &str) -> Result<i128, EvalFailure> {
8203    text.parse::<i128>().map_err(|_| {
8204        EvalFailure::Throw(ThrowOrigin::RangeError {
8205            operation: "bigint magnitude exceeds runtime width",
8206        })
8207    })
8208}
8209
8210fn bigint_binary(op: BinaryOp, left: &str, right: &str) -> Result<String, EvalFailure> {
8211    let left = bigint_i128(left)?;
8212    let right = bigint_i128(right)?;
8213    let overflow =
8214        |operation: &'static str| EvalFailure::Throw(ThrowOrigin::RangeError { operation });
8215    let result = match op {
8216        BinaryOp::Subtract => left
8217            .checked_sub(right)
8218            .ok_or_else(|| overflow("bigint subtract overflow"))?,
8219        BinaryOp::Multiply => left
8220            .checked_mul(right)
8221            .ok_or_else(|| overflow("bigint multiply overflow"))?,
8222        BinaryOp::Divide => {
8223            if right == 0 {
8224                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8225                    operation: "bigint division by zero",
8226                }));
8227            }
8228            left.checked_div(right)
8229                .ok_or_else(|| overflow("bigint divide overflow"))?
8230        }
8231        BinaryOp::Remainder => {
8232            if right == 0 {
8233                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8234                    operation: "bigint remainder by zero",
8235                }));
8236            }
8237            left.checked_rem(right)
8238                .ok_or_else(|| overflow("bigint remainder overflow"))?
8239        }
8240        BinaryOp::Exponent => {
8241            if right < 0 {
8242                return Err(EvalFailure::Throw(ThrowOrigin::RangeError {
8243                    operation: "bigint negative exponent",
8244                }));
8245            }
8246            let exponent =
8247                u32::try_from(right).map_err(|_| overflow("bigint exponent overflow"))?;
8248            left.checked_pow(exponent)
8249                .ok_or_else(|| overflow("bigint exponent overflow"))?
8250        }
8251        BinaryOp::BitAnd => left & right,
8252        BinaryOp::BitOr => left | right,
8253        BinaryOp::BitXor => left ^ right,
8254        BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
8255            let left_shift = (op == BinaryOp::ShiftLeft) == (right >= 0);
8256            let amount =
8257                u32::try_from(right.unsigned_abs()).map_err(|_| overflow("bigint shift width"))?;
8258            let shifted = if left_shift {
8259                left.checked_shl(amount)
8260            } else {
8261                left.checked_shr(amount)
8262            };
8263            shifted.ok_or_else(|| overflow("bigint shift overflow"))?
8264        }
8265        BinaryOp::UnsignedShiftRight => {
8266            return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
8267                operation: "unsigned shift on bigint",
8268            }));
8269        }
8270        _ => unreachable!("bigint arithmetic partition"),
8271    };
8272    Ok(result.to_string())
8273}
8274
8275pub(crate) fn unary_from_selector(op: u32) -> Option<UnaryOp> {
8276    match op {
8277        0 => Some(UnaryOp::Void),
8278        1 => Some(UnaryOp::TypeOf),
8279        2 => Some(UnaryOp::Plus),
8280        3 => Some(UnaryOp::Negate),
8281        4 => Some(UnaryOp::BitwiseNot),
8282        5 => Some(UnaryOp::LogicalNot),
8283        _ => None,
8284    }
8285}
8286
8287pub(crate) fn binary_from_selector(op: u32) -> Option<BinaryOp> {
8288    match op {
8289        0 => Some(BinaryOp::Add),
8290        1 => Some(BinaryOp::Subtract),
8291        2 => Some(BinaryOp::Multiply),
8292        3 => Some(BinaryOp::Divide),
8293        4 => Some(BinaryOp::Remainder),
8294        5 => Some(BinaryOp::Exponent),
8295        6 => Some(BinaryOp::BitAnd),
8296        7 => Some(BinaryOp::BitOr),
8297        8 => Some(BinaryOp::BitXor),
8298        9 => Some(BinaryOp::ShiftLeft),
8299        10 => Some(BinaryOp::ShiftRight),
8300        11 => Some(BinaryOp::UnsignedShiftRight),
8301        12 => Some(BinaryOp::Equal),
8302        13 => Some(BinaryOp::NotEqual),
8303        14 => Some(BinaryOp::StrictEqual),
8304        15 => Some(BinaryOp::StrictNotEqual),
8305        16 => Some(BinaryOp::LessThan),
8306        17 => Some(BinaryOp::LessThanOrEqual),
8307        18 => Some(BinaryOp::GreaterThan),
8308        19 => Some(BinaryOp::GreaterThanOrEqual),
8309        20 => Some(BinaryOp::InstanceOf),
8310        21 => Some(BinaryOp::In),
8311        _ => None,
8312    }
8313}
8314
8315pub(crate) fn iterator_kind_from_selector(kind: u32) -> Option<IteratorKind> {
8316    match kind {
8317        0 => Some(IteratorKind::Sync),
8318        1 => Some(IteratorKind::Async),
8319        2 => Some(IteratorKind::Keys),
8320        _ => None,
8321    }
8322}
8323
8324pub(crate) fn accessor_from_selector(kind: u32) -> Option<AccessorKind> {
8325    match kind {
8326        0 => Some(AccessorKind::Getter),
8327        1 => Some(AccessorKind::Setter),
8328        _ => None,
8329    }
8330}
8331
8332#[cfg(test)]
8333mod tests {
8334    use std::sync::Arc;
8335
8336    use super::*;
8337    use crate::intrinsics::BuiltinOutcome;
8338    use bamts_bytecode::{
8339        Binding, Edge, EdgeKind, ExceptionHandler, Export, ExportSource, FunctionFlags, NumberBits,
8340        ProgramModule, Register,
8341    };
8342
8343    fn reg(raw: u32) -> Register {
8344        Register::new(raw)
8345    }
8346    fn pc(raw: u32) -> Pc {
8347        Pc::new(raw)
8348    }
8349    fn cid(raw: u32) -> ConstantId {
8350        ConstantId::new(raw)
8351    }
8352
8353    /// A function with no captures.
8354    fn function(
8355        parameters: u32,
8356        registers: u32,
8357        code: Vec<Instruction>,
8358        handlers: Vec<ExceptionHandler>,
8359    ) -> Function {
8360        Function::new(
8361            None,
8362            0,
8363            parameters,
8364            registers,
8365            FunctionFlags::default(),
8366            code,
8367            handlers,
8368        )
8369    }
8370
8371    fn generator_function(
8372        parameters: u32,
8373        registers: u32,
8374        code: Vec<Instruction>,
8375        handlers: Vec<ExceptionHandler>,
8376    ) -> Function {
8377        Function::new(
8378            None,
8379            0,
8380            parameters,
8381            registers,
8382            FunctionFlags {
8383                is_async: false,
8384                is_generator: true,
8385            },
8386            code,
8387            handlers,
8388        )
8389    }
8390
8391    fn async_function(
8392        parameters: u32,
8393        registers: u32,
8394        code: Vec<Instruction>,
8395        handlers: Vec<ExceptionHandler>,
8396    ) -> Function {
8397        Function::new(
8398            None,
8399            0,
8400            parameters,
8401            registers,
8402            FunctionFlags {
8403                is_async: true,
8404                is_generator: false,
8405            },
8406            code,
8407            handlers,
8408        )
8409    }
8410
8411    /// A function with `captures` leading capture registers.
8412    fn closure_function(
8413        captures: u32,
8414        parameters: u32,
8415        registers: u32,
8416        code: Vec<Instruction>,
8417    ) -> Function {
8418        Function::new(
8419            None,
8420            captures,
8421            parameters,
8422            registers,
8423            FunctionFlags::default(),
8424            code,
8425            Vec::new(),
8426        )
8427    }
8428
8429    fn verified(mut constants: Vec<Constant>, functions: Vec<Function>) -> Program<Verified> {
8430        let name = ConstantId::new(constants.len() as u32);
8431        constants.push(Constant::String(EcmaString::from_utf8("<test>")));
8432        let code = Module::new(constants, functions, FunctionId::new(0))
8433            .verify()
8434            .expect("valid test bytecode");
8435        Program::link(
8436            vec![ProgramModule {
8437                name,
8438                code,
8439                edges: Vec::new(),
8440                bindings: Vec::new(),
8441                exports: Vec::new(),
8442            }],
8443            ModuleId::new(0),
8444        )
8445        .expect("valid one-module test program")
8446    }
8447    fn program_module(
8448        name: &str,
8449        mut constants: Vec<Constant>,
8450        functions: Vec<Function>,
8451        edges: Vec<Edge>,
8452        bindings: Vec<Binding>,
8453        exports: Vec<Export>,
8454    ) -> ProgramModule<Verified> {
8455        constants.insert(0, Constant::String(EcmaString::from_utf8(name)));
8456        let code = Module::new(constants, functions, FunctionId::new(0))
8457            .verify()
8458            .expect("valid test bytecode");
8459        ProgramModule {
8460            name: ConstantId::new(0),
8461            code,
8462            edges,
8463            bindings,
8464            exports,
8465        }
8466    }
8467
8468    fn linked(modules: Vec<ProgramModule<Verified>>, entry: u32) -> Program<Verified> {
8469        Program::link(modules, ModuleId::new(entry)).expect("valid linked test program")
8470    }
8471
8472    fn namespace_descriptor_entry() -> Function {
8473        function(
8474            0,
8475            7,
8476            vec![
8477                Instruction::LoadGlobal {
8478                    dst: reg(0),
8479                    name: cid(1),
8480                },
8481                Instruction::LoadGlobal {
8482                    dst: reg(1),
8483                    name: cid(3),
8484                },
8485                Instruction::LoadConst {
8486                    dst: reg(2),
8487                    constant: cid(4),
8488                },
8489                Instruction::GetProperty {
8490                    dst: reg(3),
8491                    object: reg(1),
8492                    key: reg(2),
8493                },
8494                Instruction::CreateArray { dst: reg(4) },
8495                Instruction::ArrayPush {
8496                    array: reg(4),
8497                    value: reg(0),
8498                },
8499                Instruction::LoadConst {
8500                    dst: reg(5),
8501                    constant: cid(5),
8502                },
8503                Instruction::ArrayPush {
8504                    array: reg(4),
8505                    value: reg(5),
8506                },
8507                Instruction::Call {
8508                    dst: reg(6),
8509                    callee: reg(3),
8510                    this_value: reg(4),
8511                    arguments: reg(4),
8512                },
8513                Instruction::Return { value: reg(6) },
8514            ],
8515            Vec::new(),
8516        )
8517    }
8518
8519    #[derive(Default)]
8520    struct TestHost;
8521    impl Host for TestHost {}
8522
8523    #[test]
8524    fn async_await_setup_failure_releases_suspended_registers() {
8525        let program = verified(
8526            vec![Constant::Undefined],
8527            vec![
8528                function(0, 1, vec![Instruction::Halt], Vec::new()),
8529                async_function(
8530                    0,
8531                    2,
8532                    vec![
8533                        Instruction::LoadConst {
8534                            dst: reg(0),
8535                            constant: cid(0),
8536                        },
8537                        Instruction::Suspend {
8538                            dst: reg(1),
8539                            src: reg(0),
8540                            resume: pc(2),
8541                        },
8542                        Instruction::Return { value: reg(1) },
8543                    ],
8544                    Vec::new(),
8545                ),
8546            ],
8547        );
8548        let mut host = TestHost;
8549        let limits = Limits {
8550            max_microtasks: 0,
8551            ..Limits::default()
8552        };
8553        let mut machine = Machine::new(&program, &mut host, limits);
8554        machine.frames.clear();
8555        machine.live_registers = 0;
8556        let callable = generator_callable(&mut machine, 1);
8557
8558        assert!(matches!(
8559            machine.call_value(callable, Value::UNDEFINED, &[]),
8560            Err(EvalFailure::Runtime(
8561                RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 0 }
8562            ))
8563        ));
8564        assert_eq!(machine.live_registers, 0);
8565    }
8566
8567    fn run_ok(program: &Program<Verified>) -> Execution {
8568        let mut host = TestHost;
8569        Machine::new(program, &mut host, Limits::default())
8570            .run()
8571            .unwrap()
8572    }
8573
8574    fn generator_callable<H: Host>(machine: &mut Machine<'_, H>, function: u32) -> Value {
8575        machine
8576            .allocate(HeapEntry::Function {
8577                module: ModuleId::new(0),
8578                function: FunctionId::new(function),
8579                captures: Vec::new(),
8580                properties: PropertyMap::default(),
8581                prototype: Some(machine.intrinsics.function_prototype),
8582                extensible: true,
8583            })
8584            .unwrap()
8585    }
8586
8587    fn generator_next<H: Host>(
8588        machine: &mut Machine<'_, H>,
8589        generator: Value,
8590        resume_value: Value,
8591    ) -> Result<(Value, bool), EvalFailure> {
8592        let next = machine.get_named_property(generator, "next")?;
8593        let result = machine.call_value(next, generator, &[resume_value])?;
8594        let done = machine.get_named_property(result, "done")?;
8595        let value = machine.get_named_property(result, "value")?;
8596        Ok((value, machine.truthy(done)))
8597    }
8598
8599    #[test]
8600    fn sync_generator_is_lazy_resumes_registers_and_stays_completed() {
8601        let program = verified(
8602            vec![Constant::Int32(10)],
8603            vec![
8604                function(0, 1, vec![Instruction::Halt], Vec::new()),
8605                generator_function(
8606                    0,
8607                    3,
8608                    vec![
8609                        Instruction::LoadConst {
8610                            dst: reg(0),
8611                            constant: cid(0),
8612                        },
8613                        Instruction::Suspend {
8614                            dst: reg(1),
8615                            src: reg(0),
8616                            resume: pc(2),
8617                        },
8618                        Instruction::Binary {
8619                            dst: reg(2),
8620                            op: BinaryOp::Add,
8621                            left: reg(0),
8622                            right: reg(1),
8623                        },
8624                        Instruction::Return { value: reg(2) },
8625                    ],
8626                    Vec::new(),
8627                ),
8628            ],
8629        );
8630        let mut host = TestHost;
8631        let mut machine = Machine::new(&program, &mut host, Limits::default());
8632        machine.frames.clear();
8633        machine.live_registers = 0;
8634        let callable = generator_callable(&mut machine, 1);
8635        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8636        assert_eq!(machine.live_registers, 0, "calling must not start the body");
8637        machine
8638            .set_data_property(generator, "visible", Value::int32(1))
8639            .unwrap();
8640        assert_eq!(
8641            machine.get_named_property(generator, "visible").unwrap(),
8642            Value::int32(1),
8643        );
8644        assert_eq!(
8645            machine.own_property_keys(generator).unwrap(),
8646            vec![PropertyKey::Named(EcmaString::from_utf8("visible"))],
8647        );
8648        assert!(
8649            machine
8650                .inherits_from_prototype(
8651                    generator,
8652                    machine.intrinsics.builtins.generator_prototype(),
8653                )
8654                .unwrap()
8655        );
8656
8657        assert_eq!(
8658            generator_next(&mut machine, generator, Value::int32(99)).unwrap(),
8659            (Value::int32(10), false),
8660        );
8661        assert_eq!(machine.live_registers, 3);
8662        assert_eq!(
8663            generator_next(&mut machine, generator, Value::int32(5)).unwrap(),
8664            (Value::int32(15), true),
8665        );
8666        assert_eq!(machine.live_registers, 0);
8667        assert_eq!(
8668            generator_next(&mut machine, generator, Value::int32(8)).unwrap(),
8669            (Value::UNDEFINED, true),
8670        );
8671    }
8672
8673    #[test]
8674    fn sync_generator_reentrant_next_is_a_type_error() {
8675        let program = verified(
8676            Vec::new(),
8677            vec![
8678                function(0, 1, vec![Instruction::Halt], Vec::new()),
8679                generator_function(0, 1, vec![Instruction::Halt], Vec::new()),
8680            ],
8681        );
8682        let mut host = TestHost;
8683        let mut machine = Machine::new(&program, &mut host, Limits::default());
8684        machine.frames.clear();
8685        machine.live_registers = 0;
8686        let callable = generator_callable(&mut machine, 1);
8687        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8688        let _ = machine.take_generator_state(generator).unwrap();
8689
8690        assert!(matches!(
8691            generator_next(&mut machine, generator, Value::UNDEFINED),
8692            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
8693        ));
8694    }
8695
8696    #[test]
8697    fn sync_generator_uncaught_throw_preserves_origin_and_completes() {
8698        let program = verified(
8699            vec![Constant::Int32(7)],
8700            vec![
8701                function(0, 1, vec![Instruction::Halt], Vec::new()),
8702                generator_function(
8703                    0,
8704                    1,
8705                    vec![
8706                        Instruction::LoadConst {
8707                            dst: reg(0),
8708                            constant: cid(0),
8709                        },
8710                        Instruction::Throw { value: reg(0) },
8711                    ],
8712                    Vec::new(),
8713                ),
8714            ],
8715        );
8716        let mut host = TestHost;
8717        let mut machine = Machine::new(&program, &mut host, Limits::default());
8718        machine.frames.clear();
8719        machine.live_registers = 0;
8720        let callable = generator_callable(&mut machine, 1);
8721        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8722
8723        assert!(matches!(
8724            generator_next(&mut machine, generator, Value::UNDEFINED),
8725            Err(EvalFailure::ThrowValueOrigin {
8726                value,
8727                origin: ThrowOrigin::Bytecode,
8728            }) if value == Value::int32(7)
8729        ));
8730        assert_eq!(
8731            generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8732            (Value::UNDEFINED, true),
8733        );
8734        assert_eq!(machine.live_registers, 0);
8735    }
8736
8737    #[test]
8738    fn outer_compiled_handler_catches_generator_throw_value() {
8739        let program = verified(
8740            vec![
8741                Constant::Int32(7),
8742                Constant::Undefined,
8743                Constant::String(EcmaString::from_utf8("next")),
8744            ],
8745            vec![
8746                function(
8747                    0,
8748                    8,
8749                    vec![
8750                        Instruction::CreateArray { dst: reg(0) },
8751                        Instruction::CreateClosure {
8752                            dst: reg(1),
8753                            function: FunctionId::new(1),
8754                            captures: reg(0),
8755                        },
8756                        Instruction::CreateArray { dst: reg(2) },
8757                        Instruction::LoadConst {
8758                            dst: reg(3),
8759                            constant: cid(1),
8760                        },
8761                        Instruction::Call {
8762                            dst: reg(4),
8763                            callee: reg(1),
8764                            this_value: reg(3),
8765                            arguments: reg(2),
8766                        },
8767                        Instruction::LoadConst {
8768                            dst: reg(5),
8769                            constant: cid(2),
8770                        },
8771                        Instruction::GetProperty {
8772                            dst: reg(6),
8773                            object: reg(4),
8774                            key: reg(5),
8775                        },
8776                        Instruction::Call {
8777                            dst: reg(7),
8778                            callee: reg(6),
8779                            this_value: reg(4),
8780                            arguments: reg(2),
8781                        },
8782                        Instruction::Return { value: reg(3) },
8783                        Instruction::Return { value: reg(7) },
8784                    ],
8785                    vec![ExceptionHandler {
8786                        start: pc(7),
8787                        end: pc(8),
8788                        handler: pc(9),
8789                        catch_register: reg(7),
8790                    }],
8791                ),
8792                generator_function(
8793                    0,
8794                    1,
8795                    vec![
8796                        Instruction::LoadConst {
8797                            dst: reg(0),
8798                            constant: cid(0),
8799                        },
8800                        Instruction::Throw { value: reg(0) },
8801                    ],
8802                    Vec::new(),
8803                ),
8804            ],
8805        );
8806
8807        assert_eq!(run_ok(&program).value, Value::int32(7));
8808    }
8809
8810    #[test]
8811    fn sync_generator_catches_body_throw_before_suspending() {
8812        let program = verified(
8813            vec![Constant::Int32(7)],
8814            vec![
8815                function(0, 1, vec![Instruction::Halt], Vec::new()),
8816                generator_function(
8817                    0,
8818                    3,
8819                    vec![
8820                        Instruction::LoadConst {
8821                            dst: reg(0),
8822                            constant: cid(0),
8823                        },
8824                        Instruction::Throw { value: reg(0) },
8825                        Instruction::Suspend {
8826                            dst: reg(2),
8827                            src: reg(1),
8828                            resume: pc(3),
8829                        },
8830                        Instruction::Return { value: reg(2) },
8831                    ],
8832                    vec![ExceptionHandler {
8833                        start: pc(1),
8834                        end: pc(2),
8835                        handler: pc(2),
8836                        catch_register: reg(1),
8837                    }],
8838                ),
8839            ],
8840        );
8841        let mut host = TestHost;
8842        let mut machine = Machine::new(&program, &mut host, Limits::default());
8843        machine.frames.clear();
8844        machine.live_registers = 0;
8845        let callable = generator_callable(&mut machine, 1);
8846        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8847
8848        assert_eq!(
8849            generator_next(&mut machine, generator, Value::UNDEFINED).unwrap(),
8850            (Value::int32(7), false),
8851        );
8852        assert_eq!(
8853            generator_next(&mut machine, generator, Value::int32(9)).unwrap(),
8854            (Value::int32(9), true),
8855        );
8856    }
8857
8858    #[test]
8859    fn suspended_generator_registers_remain_charged() {
8860        let program = verified(
8861            vec![Constant::Int32(1)],
8862            vec![
8863                function(0, 1, vec![Instruction::Halt], Vec::new()),
8864                generator_function(
8865                    0,
8866                    3,
8867                    vec![
8868                        Instruction::LoadConst {
8869                            dst: reg(0),
8870                            constant: cid(0),
8871                        },
8872                        Instruction::Suspend {
8873                            dst: reg(1),
8874                            src: reg(0),
8875                            resume: pc(2),
8876                        },
8877                        Instruction::Return { value: reg(1) },
8878                    ],
8879                    Vec::new(),
8880                ),
8881            ],
8882        );
8883        let mut host = TestHost;
8884        let mut machine = Machine::new(
8885            &program,
8886            &mut host,
8887            Limits {
8888                max_total_registers: 3,
8889                ..Limits::default()
8890            },
8891        );
8892        machine.frames.clear();
8893        machine.live_registers = 0;
8894        let callable = generator_callable(&mut machine, 1);
8895        let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8896        let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8897        assert_eq!(
8898            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8899            (Value::int32(1), false),
8900        );
8901        assert!(matches!(
8902            generator_next(&mut machine, second, Value::UNDEFINED),
8903            Err(EvalFailure::Runtime(
8904                RuntimeErrorKind::RegisterLimitExceeded { .. }
8905            ))
8906        ));
8907        assert_eq!(machine.live_registers, 3);
8908        assert_eq!(
8909            generator_next(&mut machine, first, Value::int32(4)).unwrap(),
8910            (Value::int32(4), true),
8911        );
8912        assert_eq!(machine.live_registers, 0);
8913    }
8914
8915    #[test]
8916    fn resumed_generator_call_depth_failure_releases_registers() {
8917        let program = verified(
8918            vec![Constant::Int32(1)],
8919            vec![
8920                function(0, 1, vec![Instruction::Halt], Vec::new()),
8921                generator_function(
8922                    0,
8923                    2,
8924                    vec![
8925                        Instruction::LoadConst {
8926                            dst: reg(0),
8927                            constant: cid(0),
8928                        },
8929                        Instruction::Suspend {
8930                            dst: reg(1),
8931                            src: reg(0),
8932                            resume: pc(2),
8933                        },
8934                        Instruction::Return { value: reg(1) },
8935                    ],
8936                    Vec::new(),
8937                ),
8938            ],
8939        );
8940        let mut host = TestHost;
8941        let mut machine = Machine::new(
8942            &program,
8943            &mut host,
8944            Limits {
8945                max_total_registers: 2,
8946                ..Limits::default()
8947            },
8948        );
8949        machine.frames.clear();
8950        machine.live_registers = 0;
8951
8952        let callable = generator_callable(&mut machine, 1);
8953        let first = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8954
8955        // Start and suspend the first generator, charging its two registers.
8956        assert_eq!(
8957            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8958            (Value::int32(1), false),
8959        );
8960        assert_eq!(machine.live_registers, 2);
8961
8962        // Fill the compiled call depth to the exact limit so the next resume
8963        // fails in push_resumed_generator_frame before it can take ownership.
8964        machine.frames.push(Frame {
8965            module: ModuleId::new(0),
8966            function: 0,
8967            pc: 0,
8968            registers: Vec::new(),
8969            return_to: None,
8970            this_value: Value::UNDEFINED,
8971            new_target: Value::UNDEFINED,
8972            args: Vec::new(),
8973            arguments_object: None,
8974        });
8975        machine.limits.max_call_depth = machine.frames.len();
8976
8977        assert!(matches!(
8978            generator_next(&mut machine, first, Value::int32(7)),
8979            Err(EvalFailure::Runtime(
8980                RuntimeErrorKind::CallDepthExceeded { .. }
8981            ))
8982        ));
8983        assert_eq!(machine.live_registers, 0);
8984
8985        // The generator is now sticky Completed.
8986        assert_eq!(
8987            generator_next(&mut machine, first, Value::UNDEFINED).unwrap(),
8988            (Value::UNDEFINED, true),
8989        );
8990
8991        // Remove the artificial depth and make room for another activation.
8992        machine.frames.pop();
8993        machine.limits.max_call_depth = Limits::default().max_call_depth;
8994
8995        // A second generator can suspend again only if the first's charge was released.
8996        let second = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
8997        assert_eq!(
8998            generator_next(&mut machine, second, Value::UNDEFINED).unwrap(),
8999            (Value::int32(1), false),
9000        );
9001        assert_eq!(machine.live_registers, 2);
9002        assert_eq!(
9003            generator_next(&mut machine, second, Value::int32(9)).unwrap(),
9004            (Value::int32(9), true),
9005        );
9006        assert_eq!(machine.live_registers, 0);
9007    }
9008    #[test]
9009    fn array_extend_consumes_generator_through_sync_iterator_protocol() {
9010        let program = verified(
9011            vec![Constant::Int32(1), Constant::Int32(2)],
9012            vec![
9013                function(0, 1, vec![Instruction::Halt], Vec::new()),
9014                generator_function(
9015                    0,
9016                    3,
9017                    vec![
9018                        Instruction::LoadConst {
9019                            dst: reg(0),
9020                            constant: cid(0),
9021                        },
9022                        Instruction::Suspend {
9023                            dst: reg(2),
9024                            src: reg(0),
9025                            resume: pc(2),
9026                        },
9027                        Instruction::LoadConst {
9028                            dst: reg(1),
9029                            constant: cid(1),
9030                        },
9031                        Instruction::Suspend {
9032                            dst: reg(2),
9033                            src: reg(1),
9034                            resume: pc(4),
9035                        },
9036                        Instruction::Return { value: reg(2) },
9037                    ],
9038                    Vec::new(),
9039                ),
9040            ],
9041        );
9042        let mut host = TestHost;
9043        let mut machine = Machine::new(&program, &mut host, Limits::default());
9044        machine.frames.clear();
9045        machine.live_registers = 0;
9046        let callable = generator_callable(&mut machine, 1);
9047        let generator = machine.call_value(callable, Value::UNDEFINED, &[]).unwrap();
9048        let array = machine
9049            .allocate(HeapEntry::Array {
9050                elements: Vec::new(),
9051                properties: PropertyMap::default(),
9052                prototype: Some(machine.intrinsics.array_prototype),
9053                extensible: true,
9054                length_writable: true,
9055            })
9056            .unwrap();
9057
9058        machine.array_extend(array, generator).unwrap();
9059        assert_eq!(
9060            machine.array_elements(array).unwrap(),
9061            Some(vec![Value::int32(1), Value::int32(2)]),
9062        );
9063        assert_eq!(machine.live_registers, 0);
9064    }
9065
9066    #[test]
9067    fn runtime_callback_without_interpreter_caller_propagates_throw() {
9068        let program = verified(
9069            Vec::new(),
9070            vec![
9071                function(0, 1, vec![Instruction::Halt], Vec::new()),
9072                function(1, 1, vec![Instruction::Throw { value: reg(0) }], Vec::new()),
9073            ],
9074        );
9075        let mut host = TestHost;
9076        let mut machine = Machine::new(&program, &mut host, Limits::default());
9077        machine.frames.clear();
9078        machine.live_registers = 0;
9079        let callee = machine
9080            .allocate(HeapEntry::Function {
9081                module: ModuleId::new(0),
9082                function: FunctionId::new(1),
9083                captures: Vec::new(),
9084                properties: PropertyMap::default(),
9085                prototype: Some(machine.intrinsics.function_prototype),
9086                extensible: true,
9087            })
9088            .unwrap();
9089        let thrown = Value::int32(7);
9090
9091        assert!(matches!(
9092            machine.call_value(callee, Value::UNDEFINED, &[thrown]),
9093            Err(EvalFailure::ThrowValue(value)) if value == thrown
9094        ));
9095    }
9096
9097    #[test]
9098    fn runtime_callback_failure_releases_root_frame() {
9099        let program = verified(
9100            Vec::new(),
9101            vec![
9102                function(0, 1, vec![Instruction::Halt], Vec::new()),
9103                function(
9104                    1,
9105                    1,
9106                    vec![Instruction::Return { value: reg(0) }],
9107                    Vec::new(),
9108                ),
9109            ],
9110        );
9111        let mut host = TestHost;
9112        let mut machine = Machine::new(&program, &mut host, Limits::default());
9113        machine.frames.clear();
9114        machine.live_registers = 0;
9115        let callee = machine
9116            .allocate(HeapEntry::Function {
9117                module: ModuleId::new(0),
9118                function: FunctionId::new(1),
9119                captures: Vec::new(),
9120                properties: PropertyMap::default(),
9121                prototype: Some(machine.intrinsics.function_prototype),
9122                extensible: true,
9123            })
9124            .unwrap();
9125        machine.fuel = 0;
9126
9127        assert!(matches!(
9128            machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9129            Err(EvalFailure::Runtime(RuntimeErrorKind::FuelExhausted { .. }))
9130        ));
9131        assert!(machine.frames.is_empty());
9132        assert_eq!(machine.live_registers, 0);
9133
9134        machine.fuel = 1;
9135        assert!(matches!(
9136            machine.call_value(callee, Value::UNDEFINED, &[Value::int32(7)]),
9137            Ok(value) if value == Value::int32(7)
9138        ));
9139    }
9140
9141    #[test]
9142    fn object_values_have_stable_distinct_heap_identity() {
9143        let module = verified(
9144            vec![],
9145            vec![function(
9146                0,
9147                5,
9148                vec![
9149                    Instruction::CreateObject { dst: reg(0) },
9150                    Instruction::CreateObject { dst: reg(1) },
9151                    Instruction::Binary {
9152                        dst: reg(2),
9153                        op: BinaryOp::StrictEqual,
9154                        left: reg(0),
9155                        right: reg(1),
9156                    },
9157                    Instruction::Move {
9158                        dst: reg(3),
9159                        src: reg(0),
9160                    },
9161                    Instruction::Binary {
9162                        dst: reg(4),
9163                        op: BinaryOp::StrictEqual,
9164                        left: reg(0),
9165                        right: reg(3),
9166                    },
9167                    Instruction::Return { value: reg(4) },
9168                ],
9169                vec![],
9170            )],
9171        );
9172        let execution = run_ok(&module);
9173        assert_eq!(execution.entry_registers[2], Value::FALSE);
9174        assert_eq!(execution.value, Value::TRUE);
9175    }
9176
9177    #[test]
9178    fn addition_coerces_objects_left_to_right_and_interpolates_errors() {
9179        let module = verified(
9180            vec![
9181                Constant::String(EcmaString::from_utf8("L")),
9182                Constant::String(EcmaString::from_utf8("additionOrder")),
9183                Constant::String(EcmaString::from_utf8("message")),
9184            ],
9185            vec![
9186                function(0, 1, vec![Instruction::Halt], Vec::new()),
9187                function(
9188                    0,
9189                    1,
9190                    vec![
9191                        Instruction::LoadConst {
9192                            dst: reg(0),
9193                            constant: cid(0),
9194                        },
9195                        Instruction::StoreGlobal {
9196                            name: cid(1),
9197                            value: reg(0),
9198                        },
9199                        Instruction::Return { value: reg(0) },
9200                    ],
9201                    Vec::new(),
9202                ),
9203                function(
9204                    0,
9205                    1,
9206                    vec![
9207                        Instruction::LoadGlobal {
9208                            dst: reg(0),
9209                            name: cid(1),
9210                        },
9211                        Instruction::Return { value: reg(0) },
9212                    ],
9213                    Vec::new(),
9214                ),
9215            ],
9216        );
9217        let mut host = TestHost;
9218        let mut machine = Machine::new(&module, &mut host, Limits::default());
9219        machine.frames.clear();
9220        machine.live_registers = 0;
9221        let left = machine
9222            .allocate(HeapEntry::Object {
9223                properties: PropertyMap::default(),
9224                prototype: Some(machine.intrinsics.object_prototype),
9225                extensible: true,
9226                boxed_primitive: None,
9227            })
9228            .unwrap();
9229        let right = machine
9230            .allocate(HeapEntry::Object {
9231                properties: PropertyMap::default(),
9232                prototype: Some(machine.intrinsics.object_prototype),
9233                extensible: true,
9234                boxed_primitive: None,
9235            })
9236            .unwrap();
9237        let left_value_of = machine
9238            .allocate(HeapEntry::Function {
9239                module: ModuleId::new(0),
9240                function: FunctionId::new(1),
9241                captures: Vec::new(),
9242                properties: PropertyMap::default(),
9243                prototype: Some(machine.intrinsics.function_prototype),
9244                extensible: true,
9245            })
9246            .unwrap();
9247        let right_value_of = machine
9248            .allocate(HeapEntry::Function {
9249                module: ModuleId::new(0),
9250                function: FunctionId::new(2),
9251                captures: Vec::new(),
9252                properties: PropertyMap::default(),
9253                prototype: Some(machine.intrinsics.function_prototype),
9254                extensible: true,
9255            })
9256            .unwrap();
9257        machine
9258            .set_data_property(left, "valueOf", left_value_of)
9259            .unwrap();
9260        machine
9261            .set_data_property(right, "valueOf", right_value_of)
9262            .unwrap();
9263        let coerced = machine.add(left, right).unwrap();
9264        assert!(
9265            machine
9266                .string_value(coerced)
9267                .is_some_and(|text| text.eq_ascii("LL"))
9268        );
9269
9270        let error_constructor = machine.intrinsics.global("Error").unwrap();
9271        let message = machine
9272            .allocate(HeapEntry::String(EcmaString::from_utf8("message")))
9273            .unwrap();
9274        let error = machine
9275            .call_value(error_constructor, Value::UNDEFINED, &[message])
9276            .unwrap();
9277        let empty = machine
9278            .allocate(HeapEntry::String(EcmaString::default()))
9279            .unwrap();
9280        let interpolated = machine.add(empty, error).unwrap();
9281        assert!(
9282            machine
9283                .string_value(interpolated)
9284                .is_some_and(|text| text.eq_ascii("Error: message"))
9285        );
9286
9287        let date_constructor = machine.intrinsics.global("Date").unwrap();
9288        let date_prototype = machine
9289            .get_named_property(date_constructor, "prototype")
9290            .unwrap();
9291        let date = machine
9292            .allocate(HeapEntry::Date {
9293                time: 0.0,
9294                properties: PropertyMap::default(),
9295                prototype: Some(date_prototype),
9296                extensible: true,
9297            })
9298            .unwrap();
9299        machine
9300            .set_data_property(date, "toString", left_value_of)
9301            .unwrap();
9302        let date_text = machine.add(date, empty).unwrap();
9303        assert!(
9304            machine
9305                .string_value(date_text)
9306                .is_some_and(|text| text.eq_ascii("L"))
9307        );
9308    }
9309
9310    #[test]
9311    fn computed_member_access_uses_dynamic_register_key() {
9312        // key = "a" + "b"; obj[key] = 7; return obj[key].
9313        let module = verified(
9314            vec![
9315                Constant::String(EcmaString::from_utf8("a")),
9316                Constant::String(EcmaString::from_utf8("b")),
9317                Constant::Int32(7),
9318            ],
9319            vec![function(
9320                0,
9321                6,
9322                vec![
9323                    Instruction::LoadConst {
9324                        dst: reg(1),
9325                        constant: cid(0),
9326                    },
9327                    Instruction::LoadConst {
9328                        dst: reg(2),
9329                        constant: cid(1),
9330                    },
9331                    Instruction::Binary {
9332                        dst: reg(3),
9333                        op: BinaryOp::Add,
9334                        left: reg(1),
9335                        right: reg(2),
9336                    },
9337                    Instruction::CreateObject { dst: reg(0) },
9338                    Instruction::LoadConst {
9339                        dst: reg(4),
9340                        constant: cid(2),
9341                    },
9342                    Instruction::SetProperty {
9343                        object: reg(0),
9344                        key: reg(3),
9345                        value: reg(4),
9346                    },
9347                    Instruction::GetProperty {
9348                        dst: reg(5),
9349                        object: reg(0),
9350                        key: reg(3),
9351                    },
9352                    Instruction::Return { value: reg(5) },
9353                ],
9354                vec![],
9355            )],
9356        );
9357        assert_eq!(run_ok(&module).value, Value::int32(7));
9358    }
9359
9360    #[test]
9361    fn property_delete_and_array_holes_are_real_mutations() {
9362        let module = verified(
9363            vec![
9364                Constant::String(EcmaString::from_utf8("0")),
9365                Constant::Int32(5),
9366            ],
9367            vec![function(
9368                0,
9369                5,
9370                vec![
9371                    Instruction::CreateArray { dst: reg(0) },
9372                    Instruction::LoadConst {
9373                        dst: reg(1),
9374                        constant: cid(0),
9375                    },
9376                    Instruction::LoadConst {
9377                        dst: reg(4),
9378                        constant: cid(1),
9379                    },
9380                    Instruction::SetProperty {
9381                        object: reg(0),
9382                        key: reg(1),
9383                        value: reg(4),
9384                    },
9385                    Instruction::GetProperty {
9386                        dst: reg(2),
9387                        object: reg(0),
9388                        key: reg(1),
9389                    },
9390                    Instruction::DeleteProperty {
9391                        dst: reg(3),
9392                        object: reg(0),
9393                        key: reg(1),
9394                    },
9395                    Instruction::GetProperty {
9396                        dst: reg(4),
9397                        object: reg(0),
9398                        key: reg(1),
9399                    },
9400                    Instruction::Return { value: reg(3) },
9401                ],
9402                vec![],
9403            )],
9404        );
9405        let execution = run_ok(&module);
9406        assert_eq!(execution.entry_registers[2], Value::int32(5));
9407        assert_eq!(execution.entry_registers[4], Value::UNDEFINED);
9408        assert_eq!(execution.value, Value::TRUE);
9409    }
9410
9411    #[test]
9412    fn closure_captures_seed_leading_registers_before_parameters() {
9413        // captures = [42]; fn1(7) => capture(r0) + param(r1) = 49.
9414        let entry = function(
9415            0,
9416            3,
9417            vec![
9418                Instruction::CreateArray { dst: reg(0) },
9419                Instruction::LoadConst {
9420                    dst: reg(1),
9421                    constant: cid(0),
9422                },
9423                Instruction::ArrayPush {
9424                    array: reg(0),
9425                    value: reg(1),
9426                },
9427                Instruction::CreateClosure {
9428                    dst: reg(2),
9429                    function: FunctionId::new(1),
9430                    captures: reg(0),
9431                },
9432                // arguments array [7]
9433                Instruction::CreateArray { dst: reg(0) },
9434                Instruction::LoadConst {
9435                    dst: reg(1),
9436                    constant: cid(1),
9437                },
9438                Instruction::ArrayPush {
9439                    array: reg(0),
9440                    value: reg(1),
9441                },
9442                Instruction::LoadConst {
9443                    dst: reg(1),
9444                    constant: cid(2),
9445                },
9446                Instruction::Call {
9447                    dst: reg(1),
9448                    callee: reg(2),
9449                    this_value: reg(1),
9450                    arguments: reg(0),
9451                },
9452                Instruction::Return { value: reg(1) },
9453            ],
9454            vec![],
9455        );
9456        // capture_count = 1, parameter_count = 1: r0 = capture, r1 = param.
9457        let callee = closure_function(
9458            1,
9459            1,
9460            3,
9461            vec![
9462                Instruction::Binary {
9463                    dst: reg(2),
9464                    op: BinaryOp::Add,
9465                    left: reg(0),
9466                    right: reg(1),
9467                },
9468                Instruction::Return { value: reg(2) },
9469            ],
9470        );
9471        let module = verified(
9472            vec![Constant::Int32(42), Constant::Int32(7), Constant::Undefined],
9473            vec![entry, callee],
9474        );
9475        assert_eq!(run_ok(&module).value, Value::int32(49));
9476    }
9477
9478    #[test]
9479    fn calls_scale_past_fixed_window_via_arguments_array() {
9480        // Build a 500-element arguments array and call a callee returning
9481        // arguments.length — impossible under a 127 fixed window.
9482        let mut code = vec![Instruction::CreateArray { dst: reg(0) }];
9483        code.push(Instruction::LoadConst {
9484            dst: reg(1),
9485            constant: cid(0),
9486        });
9487        for _ in 0..500 {
9488            code.push(Instruction::ArrayPush {
9489                array: reg(0),
9490                value: reg(1),
9491            });
9492        }
9493        code.push(Instruction::CreateClosure {
9494            dst: reg(2),
9495            function: FunctionId::new(1),
9496            captures: reg(3),
9497        });
9498        // captures array for a zero-capture function
9499        // (reg(3) must be an empty array)
9500        // Insert its creation before CreateClosure:
9501        let mut prelude = vec![Instruction::CreateArray { dst: reg(3) }];
9502        prelude.append(&mut code);
9503        let mut code = prelude;
9504        code.push(Instruction::LoadConst {
9505            dst: reg(1),
9506            constant: cid(1),
9507        });
9508        code.push(Instruction::Call {
9509            dst: reg(1),
9510            callee: reg(2),
9511            this_value: reg(1),
9512            arguments: reg(0),
9513        });
9514        code.push(Instruction::Return { value: reg(1) });
9515
9516        let entry = function(0, 4, code, vec![]);
9517        let callee = function(
9518            0,
9519            2,
9520            vec![
9521                Instruction::LoadArguments { dst: reg(0) },
9522                Instruction::LoadConst {
9523                    dst: reg(1),
9524                    constant: cid(2),
9525                },
9526                Instruction::GetProperty {
9527                    dst: reg(0),
9528                    object: reg(0),
9529                    key: reg(1),
9530                },
9531                Instruction::Return { value: reg(0) },
9532            ],
9533            vec![],
9534        );
9535        let module = verified(
9536            vec![
9537                Constant::Int32(1),
9538                Constant::Undefined,
9539                Constant::String(EcmaString::from_utf8("length")),
9540            ],
9541            vec![entry, callee],
9542        );
9543        assert_eq!(run_ok(&module).value, Value::int32(500));
9544    }
9545
9546    #[test]
9547    fn array_extend_spreads_iterable_elements() {
9548        // dst = []; dst.push(1); dst.extend([2,3]); return dst.length == 3.
9549        let entry = function(
9550            0,
9551            4,
9552            vec![
9553                Instruction::CreateArray { dst: reg(0) },
9554                Instruction::LoadConst {
9555                    dst: reg(1),
9556                    constant: cid(0),
9557                },
9558                Instruction::ArrayPush {
9559                    array: reg(0),
9560                    value: reg(1),
9561                },
9562                // source [2,3]
9563                Instruction::CreateArray { dst: reg(2) },
9564                Instruction::LoadConst {
9565                    dst: reg(1),
9566                    constant: cid(1),
9567                },
9568                Instruction::ArrayPush {
9569                    array: reg(2),
9570                    value: reg(1),
9571                },
9572                Instruction::LoadConst {
9573                    dst: reg(1),
9574                    constant: cid(2),
9575                },
9576                Instruction::ArrayPush {
9577                    array: reg(2),
9578                    value: reg(1),
9579                },
9580                Instruction::ArrayExtend {
9581                    array: reg(0),
9582                    iterable: reg(2),
9583                },
9584                Instruction::LoadConst {
9585                    dst: reg(3),
9586                    constant: cid(3),
9587                },
9588                Instruction::GetProperty {
9589                    dst: reg(0),
9590                    object: reg(0),
9591                    key: reg(3),
9592                },
9593                Instruction::Return { value: reg(0) },
9594            ],
9595            vec![],
9596        );
9597        let module = verified(
9598            vec![
9599                Constant::Int32(1),
9600                Constant::Int32(2),
9601                Constant::Int32(3),
9602                Constant::String(EcmaString::from_utf8("length")),
9603            ],
9604            vec![entry],
9605        );
9606        assert_eq!(run_ok(&module).value, Value::int32(3));
9607    }
9608
9609    #[test]
9610    fn array_extend_uses_sync_protocol_for_set_and_rejects_plain_object() {
9611        let module = verified(
9612            Vec::new(),
9613            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9614        );
9615        let mut host = TestHost;
9616        let mut machine = Machine::new(&module, &mut host, Limits::default());
9617        let set_constructor = machine.intrinsics.global("Set").unwrap();
9618        let set_prototype = machine
9619            .get_named_property(set_constructor, "prototype")
9620            .unwrap();
9621        let set = machine
9622            .allocate(HeapEntry::Collection {
9623                entries: vec![CollectionEntry {
9624                    order: 0,
9625                    key: Value::int32(7),
9626                    value: Value::int32(7),
9627                }],
9628                next_order: 1,
9629                properties: PropertyMap::default(),
9630                prototype: Some(set_prototype),
9631                extensible: true,
9632            })
9633            .unwrap();
9634        let target = machine
9635            .allocate(HeapEntry::Array {
9636                elements: Vec::new(),
9637                properties: PropertyMap::default(),
9638                prototype: Some(machine.intrinsics.array_prototype),
9639                extensible: true,
9640                length_writable: true,
9641            })
9642            .unwrap();
9643
9644        machine.array_extend(target, set).unwrap();
9645        assert_eq!(
9646            machine.array_elements(target).unwrap(),
9647            Some(vec![Value::int32(7)])
9648        );
9649
9650        let plain_object = machine
9651            .allocate(HeapEntry::Object {
9652                properties: PropertyMap::default(),
9653                prototype: Some(machine.intrinsics.object_prototype),
9654                boxed_primitive: None,
9655                extensible: true,
9656            })
9657            .unwrap();
9658        assert!(matches!(
9659            machine.array_extend(target, plain_object),
9660            Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9661                operation: "value is not iterable"
9662            }))
9663        ));
9664    }
9665
9666    #[test]
9667    fn sync_iterator_uses_symbol_method_and_caches_next() {
9668        fn iterator_identity<H: Host>(
9669            _machine: &mut Machine<'_, H>,
9670            this: Value,
9671            _args: &[Value],
9672            _constructing: bool,
9673        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9674            Ok(intrinsics::BuiltinOutcome::Value(this))
9675        }
9676
9677        fn next_getter<H: Host>(
9678            machine: &mut Machine<'_, H>,
9679            this: Value,
9680            _args: &[Value],
9681            _constructing: bool,
9682        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9683            let reads = machine.get_named_property(this, "nextReads")?;
9684            let reads = if reads == Value::int32(0) { 1 } else { 2 };
9685            machine.set_data_property(this, "nextReads", Value::int32(reads))?;
9686            Ok(intrinsics::BuiltinOutcome::Value(
9687                machine.get_named_property(this, "nextFunction")?,
9688            ))
9689        }
9690
9691        fn next_result<H: Host>(
9692            machine: &mut Machine<'_, H>,
9693            this: Value,
9694            _args: &[Value],
9695            _constructing: bool,
9696        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9697            Ok(intrinsics::BuiltinOutcome::Value(
9698                machine.get_named_property(this, "result")?,
9699            ))
9700        }
9701
9702        fn done_getter<H: Host>(
9703            machine: &mut Machine<'_, H>,
9704            this: Value,
9705            _args: &[Value],
9706            _constructing: bool,
9707        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9708            machine.set_data_property(this, "order", Value::int32(1))?;
9709            Ok(intrinsics::BuiltinOutcome::Value(Value::FALSE))
9710        }
9711
9712        fn value_getter<H: Host>(
9713            machine: &mut Machine<'_, H>,
9714            this: Value,
9715            _args: &[Value],
9716            _constructing: bool,
9717        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
9718            if machine.get_named_property(this, "order")? != Value::int32(1) {
9719                return Err(EvalFailure::Throw(ThrowOrigin::TypeError {
9720                    operation: "iterator value read before done",
9721                }));
9722            }
9723            Ok(intrinsics::BuiltinOutcome::Value(Value::int32(42)))
9724        }
9725
9726        let module = verified(
9727            Vec::new(),
9728            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9729        );
9730        let mut host = TestHost;
9731        let mut machine = Machine::new(&module, &mut host, Limits::default());
9732        let mut install = |name, handler| {
9733            let id = machine
9734                .intrinsics
9735                .builtins
9736                .register(intrinsics::BuiltinDef {
9737                    name,
9738                    length: 0,
9739                    handler,
9740                });
9741            intrinsics::native_function(&mut machine.heap, id, name, 0)
9742        };
9743        let iterator_identity = install(
9744            "[Symbol.iterator]",
9745            iterator_identity::<TestHost> as intrinsics::BuiltinHandler<TestHost>,
9746        );
9747        let next_getter = install("get next", next_getter::<TestHost>);
9748        let next_result = install("next", next_result::<TestHost>);
9749        let done_getter = install("get done", done_getter::<TestHost>);
9750        let value_getter = install("get value", value_getter::<TestHost>);
9751        let object_prototype = machine.intrinsics.object_prototype;
9752        let result = machine
9753            .allocate(HeapEntry::Object {
9754                properties: {
9755                    let mut properties = PropertyMap::default();
9756                    for (key, property) in [
9757                        (
9758                            PropertyKey::Named(EcmaString::from_utf8("order")),
9759                            Property::Data {
9760                                value: Value::int32(0),
9761                                writable: true,
9762                                enumerable: true,
9763                                configurable: true,
9764                            },
9765                        ),
9766                        (
9767                            PropertyKey::Named(EcmaString::from_utf8("done")),
9768                            Property::Accessor {
9769                                getter: Some(done_getter),
9770                                setter: None,
9771                                enumerable: true,
9772                                configurable: true,
9773                            },
9774                        ),
9775                        (
9776                            PropertyKey::Named(EcmaString::from_utf8("value")),
9777                            Property::Accessor {
9778                                getter: Some(value_getter),
9779                                setter: None,
9780                                enumerable: true,
9781                                configurable: true,
9782                            },
9783                        ),
9784                    ] {
9785                        properties.insert(key, property);
9786                    }
9787                    properties
9788                },
9789                prototype: Some(object_prototype),
9790                boxed_primitive: None,
9791                extensible: true,
9792            })
9793            .unwrap();
9794        let iterator_symbol = machine.intrinsics.builtins.symbol_iterator();
9795        let iterator_key = machine.to_property_key(iterator_symbol).unwrap();
9796        let source = machine
9797            .allocate(HeapEntry::Object {
9798                properties: {
9799                    let mut properties = PropertyMap::default();
9800                    for (key, property) in [
9801                        (
9802                            iterator_key,
9803                            Property::Data {
9804                                value: iterator_identity,
9805                                writable: true,
9806                                enumerable: false,
9807                                configurable: true,
9808                            },
9809                        ),
9810                        (
9811                            PropertyKey::Named(EcmaString::from_utf8("next")),
9812                            Property::Accessor {
9813                                getter: Some(next_getter),
9814                                setter: None,
9815                                enumerable: false,
9816                                configurable: true,
9817                            },
9818                        ),
9819                        (
9820                            PropertyKey::Named(EcmaString::from_utf8("nextReads")),
9821                            Property::Data {
9822                                value: Value::int32(0),
9823                                writable: true,
9824                                enumerable: true,
9825                                configurable: true,
9826                            },
9827                        ),
9828                        (
9829                            PropertyKey::Named(EcmaString::from_utf8("nextFunction")),
9830                            Property::Data {
9831                                value: next_result,
9832                                writable: true,
9833                                enumerable: true,
9834                                configurable: true,
9835                            },
9836                        ),
9837                        (
9838                            PropertyKey::Named(EcmaString::from_utf8("result")),
9839                            Property::Data {
9840                                value: result,
9841                                writable: true,
9842                                enumerable: true,
9843                                configurable: true,
9844                            },
9845                        ),
9846                    ] {
9847                        properties.insert(key, property);
9848                    }
9849                    properties
9850                },
9851                prototype: Some(object_prototype),
9852                boxed_primitive: None,
9853                extensible: true,
9854            })
9855            .unwrap();
9856
9857        let iterator = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9858        assert_eq!(
9859            machine.iterator_next(iterator).unwrap(),
9860            (false, Value::int32(42))
9861        );
9862        assert_eq!(
9863            machine.iterator_next(iterator).unwrap(),
9864            (false, Value::int32(42))
9865        );
9866        assert_eq!(
9867            machine.get_named_property(source, "nextReads").unwrap(),
9868            Value::int32(1)
9869        );
9870
9871        let mut completed_properties = PropertyMap::default();
9872        completed_properties.insert(
9873            PropertyKey::Named(EcmaString::from_utf8("done")),
9874            Property::Data {
9875                value: Value::TRUE,
9876                writable: true,
9877                enumerable: true,
9878                configurable: true,
9879            },
9880        );
9881        completed_properties.insert(
9882            PropertyKey::Named(EcmaString::from_utf8("value")),
9883            Property::Accessor {
9884                getter: Some(value_getter),
9885                setter: None,
9886                enumerable: true,
9887                configurable: true,
9888            },
9889        );
9890        let completed = machine
9891            .allocate(HeapEntry::Object {
9892                properties: completed_properties,
9893                prototype: Some(object_prototype),
9894                boxed_primitive: None,
9895                extensible: true,
9896            })
9897            .unwrap();
9898        machine
9899            .set_data_property(source, "result", completed)
9900            .unwrap();
9901        assert_eq!(
9902            machine.iterator_next(iterator).unwrap(),
9903            (true, Value::UNDEFINED)
9904        );
9905
9906        machine
9907            .delete_property(source, &PropertyKey::Named(EcmaString::from_utf8("next")))
9908            .unwrap();
9909        machine
9910            .set_data_property(source, "next", Value::int32(1))
9911            .unwrap();
9912        let invalid_next = machine.create_iterator(source, IteratorKind::Sync).unwrap();
9913        assert!(matches!(
9914            machine.iterator_next(invalid_next),
9915            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
9916        ));
9917    }
9918
9919    #[test]
9920    fn object_spread_copies_own_properties() {
9921        // src = {}; src.x = 9; target = {}; { ...src }; return target.x.
9922        let key = |c: u32| Instruction::LoadConst {
9923            dst: reg(3),
9924            constant: cid(c),
9925        };
9926        let module = verified(
9927            vec![
9928                Constant::String(EcmaString::from_utf8("x")),
9929                Constant::Int32(9),
9930            ],
9931            vec![function(
9932                0,
9933                4,
9934                vec![
9935                    Instruction::CreateObject { dst: reg(0) },
9936                    key(0),
9937                    Instruction::LoadConst {
9938                        dst: reg(2),
9939                        constant: cid(1),
9940                    },
9941                    Instruction::SetProperty {
9942                        object: reg(0),
9943                        key: reg(3),
9944                        value: reg(2),
9945                    },
9946                    Instruction::CreateObject { dst: reg(1) },
9947                    Instruction::ObjectSpread {
9948                        target: reg(1),
9949                        source: reg(0),
9950                    },
9951                    key(0),
9952                    Instruction::GetProperty {
9953                        dst: reg(2),
9954                        object: reg(1),
9955                        key: reg(3),
9956                    },
9957                    Instruction::Return { value: reg(2) },
9958                ],
9959                vec![],
9960            )],
9961        );
9962        assert_eq!(run_ok(&module).value, Value::int32(9));
9963    }
9964
9965    #[test]
9966    fn object_spread_copies_enumerable_symbol_properties() {
9967        let module = verified(
9968            Vec::new(),
9969            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
9970        );
9971        let mut host = TestHost;
9972        let mut machine = Machine::new(&module, &mut host, Limits::default());
9973        let prototype = machine.intrinsics.object_prototype;
9974        let object = |machine: &mut Machine<'_, TestHost>| {
9975            machine
9976                .allocate(HeapEntry::Object {
9977                    properties: PropertyMap::default(),
9978                    prototype: Some(prototype),
9979                    boxed_primitive: None,
9980                    extensible: true,
9981                })
9982                .unwrap()
9983        };
9984        let source = object(&mut machine);
9985        let target = object(&mut machine);
9986        let symbol = machine
9987            .allocate(HeapEntry::Symbol {
9988                description: EcmaString::from_utf8("key"),
9989            })
9990            .unwrap();
9991        let key = machine.to_property_key(symbol).unwrap();
9992        machine
9993            .set_data_property_key(source, key.clone(), Value::int32(42))
9994            .unwrap();
9995
9996        machine.object_spread(target, source).unwrap();
9997
9998        assert_eq!(
9999            machine.get_property_key(target, &key).unwrap(),
10000            Value::int32(42)
10001        );
10002    }
10003
10004    #[test]
10005    fn object_spread_rechecks_descriptors_after_getters() {
10006        fn delete_next<H: Host>(
10007            machine: &mut Machine<'_, H>,
10008            this: Value,
10009            _args: &[Value],
10010            _constructing: bool,
10011        ) -> Result<intrinsics::BuiltinOutcome, EvalFailure> {
10012            machine.delete_property(this, &PropertyKey::Named(EcmaString::from_utf8("next")))?;
10013            Ok(intrinsics::BuiltinOutcome::Value(Value::int32(1)))
10014        }
10015
10016        let module = verified(
10017            Vec::new(),
10018            vec![function(0, 0, vec![Instruction::Halt], Vec::new())],
10019        );
10020        let mut host = TestHost;
10021        let mut machine = Machine::new(&module, &mut host, Limits::default());
10022        let getter_id = machine
10023            .intrinsics
10024            .builtins
10025            .register(intrinsics::BuiltinDef {
10026                name: "delete next",
10027                length: 0,
10028                handler: delete_next::<TestHost>,
10029            });
10030        let getter = intrinsics::native_function(&mut machine.heap, getter_id, "delete next", 0);
10031        let first = PropertyKey::Named(EcmaString::from_utf8("first"));
10032        let next = PropertyKey::Named(EcmaString::from_utf8("next"));
10033        let mut source_properties = PropertyMap::default();
10034        source_properties.insert(
10035            first.clone(),
10036            Property::Accessor {
10037                getter: Some(getter),
10038                setter: None,
10039                enumerable: true,
10040                configurable: true,
10041            },
10042        );
10043        source_properties.insert(
10044            next.clone(),
10045            Property::Data {
10046                value: Value::int32(2),
10047                writable: true,
10048                enumerable: true,
10049                configurable: true,
10050            },
10051        );
10052        let prototype = machine.intrinsics.object_prototype;
10053        let source = machine
10054            .allocate(HeapEntry::Object {
10055                properties: source_properties,
10056                prototype: Some(prototype),
10057                boxed_primitive: None,
10058                extensible: true,
10059            })
10060            .unwrap();
10061        let target = machine
10062            .allocate(HeapEntry::Object {
10063                properties: PropertyMap::default(),
10064                prototype: Some(prototype),
10065                boxed_primitive: None,
10066                extensible: true,
10067            })
10068            .unwrap();
10069
10070        machine.object_spread(target, source).unwrap();
10071
10072        assert_eq!(
10073            machine.get_property_key(target, &first).unwrap(),
10074            Value::int32(1)
10075        );
10076        assert!(!machine.has_own_property_key(target, &next).unwrap());
10077    }
10078
10079    #[test]
10080    fn private_names_have_distinct_identity_and_are_gettable() {
10081        // Two private names with the same description are distinct keys.
10082        let module = verified(
10083            vec![
10084                Constant::String(EcmaString::from_utf8("x")),
10085                Constant::Int32(1),
10086                Constant::Int32(2),
10087            ],
10088            vec![function(
10089                0,
10090                6,
10091                vec![
10092                    Instruction::CreateObject { dst: reg(0) },
10093                    Instruction::CreatePrivateName {
10094                        dst: reg(1),
10095                        description: cid(0),
10096                    },
10097                    Instruction::CreatePrivateName {
10098                        dst: reg(2),
10099                        description: cid(0),
10100                    },
10101                    Instruction::LoadConst {
10102                        dst: reg(3),
10103                        constant: cid(1),
10104                    },
10105                    Instruction::SetProperty {
10106                        object: reg(0),
10107                        key: reg(1),
10108                        value: reg(3),
10109                    },
10110                    Instruction::LoadConst {
10111                        dst: reg(3),
10112                        constant: cid(2),
10113                    },
10114                    Instruction::SetProperty {
10115                        object: reg(0),
10116                        key: reg(2),
10117                        value: reg(3),
10118                    },
10119                    // r4 = obj[#1] (1), r5 = obj[#2] (2)
10120                    Instruction::GetProperty {
10121                        dst: reg(4),
10122                        object: reg(0),
10123                        key: reg(1),
10124                    },
10125                    Instruction::GetProperty {
10126                        dst: reg(5),
10127                        object: reg(0),
10128                        key: reg(2),
10129                    },
10130                    // distinctness: #1 !== #2
10131                    Instruction::Binary {
10132                        dst: reg(3),
10133                        op: BinaryOp::StrictEqual,
10134                        left: reg(1),
10135                        right: reg(2),
10136                    },
10137                    Instruction::Return { value: reg(4) },
10138                ],
10139                vec![],
10140            )],
10141        );
10142        let execution = run_ok(&module);
10143        assert_eq!(execution.value, Value::int32(1));
10144        assert_eq!(execution.entry_registers[5], Value::int32(2));
10145        assert_eq!(execution.entry_registers[3], Value::FALSE);
10146    }
10147
10148    #[test]
10149    fn accessor_getter_is_invoked_on_property_read() {
10150        // Define a getter returning 99, then read the property.
10151        let entry = function(
10152            0,
10153            4,
10154            vec![
10155                Instruction::CreateObject { dst: reg(0) },
10156                Instruction::CreateArray { dst: reg(3) },
10157                Instruction::CreateClosure {
10158                    dst: reg(1),
10159                    function: FunctionId::new(1),
10160                    captures: reg(3),
10161                },
10162                Instruction::LoadConst {
10163                    dst: reg(2),
10164                    constant: cid(0),
10165                },
10166                Instruction::DefineAccessor {
10167                    object: reg(0),
10168                    key: reg(2),
10169                    accessor: reg(1),
10170                    kind: AccessorKind::Getter,
10171                },
10172                Instruction::GetProperty {
10173                    dst: reg(1),
10174                    object: reg(0),
10175                    key: reg(2),
10176                },
10177                Instruction::Return { value: reg(1) },
10178            ],
10179            vec![],
10180        );
10181        let getter = function(
10182            0,
10183            1,
10184            vec![
10185                Instruction::LoadConst {
10186                    dst: reg(0),
10187                    constant: cid(1),
10188                },
10189                Instruction::Return { value: reg(0) },
10190            ],
10191            vec![],
10192        );
10193        let module = verified(
10194            vec![
10195                Constant::String(EcmaString::from_utf8("g")),
10196                Constant::Int32(99),
10197            ],
10198            vec![entry, getter],
10199        );
10200        assert_eq!(run_ok(&module).value, Value::int32(99));
10201    }
10202
10203    #[test]
10204    fn prototype_chain_lookup_and_instanceof() {
10205        // proto = {}; proto.m = 5; ctor.prototype = proto; obj = new ctor();
10206        // return (obj.m == 5) && (obj instanceof ctor).
10207        let entry = function(
10208            0,
10209            6,
10210            vec![
10211                // proto object with m = 5
10212                Instruction::CreateObject { dst: reg(0) },
10213                Instruction::LoadConst {
10214                    dst: reg(1),
10215                    constant: cid(0),
10216                },
10217                Instruction::LoadConst {
10218                    dst: reg(2),
10219                    constant: cid(1),
10220                },
10221                Instruction::SetProperty {
10222                    object: reg(0),
10223                    key: reg(1),
10224                    value: reg(2),
10225                },
10226                // ctor closure
10227                Instruction::CreateArray { dst: reg(4) },
10228                Instruction::CreateClosure {
10229                    dst: reg(3),
10230                    function: FunctionId::new(1),
10231                    captures: reg(4),
10232                },
10233                // ctor.prototype = proto
10234                Instruction::LoadConst {
10235                    dst: reg(1),
10236                    constant: cid(2),
10237                },
10238                Instruction::SetProperty {
10239                    object: reg(3),
10240                    key: reg(1),
10241                    value: reg(0),
10242                },
10243                // obj = new ctor()  (empty args)
10244                Instruction::CreateArray { dst: reg(4) },
10245                Instruction::Construct {
10246                    dst: reg(0),
10247                    callee: reg(3),
10248                    arguments: reg(4),
10249                },
10250                // obj.m via prototype chain
10251                Instruction::LoadConst {
10252                    dst: reg(1),
10253                    constant: cid(0),
10254                },
10255                Instruction::GetProperty {
10256                    dst: reg(2),
10257                    object: reg(0),
10258                    key: reg(1),
10259                },
10260                // obj instanceof ctor
10261                Instruction::Binary {
10262                    dst: reg(5),
10263                    op: BinaryOp::InstanceOf,
10264                    left: reg(0),
10265                    right: reg(3),
10266                },
10267                Instruction::Return { value: reg(2) },
10268            ],
10269            vec![],
10270        );
10271        let ctor = function(0, 1, vec![Instruction::Halt], vec![]);
10272        let module = verified(
10273            vec![
10274                Constant::String(EcmaString::from_utf8("m")),
10275                Constant::Int32(5),
10276                Constant::String(EcmaString::from_utf8("prototype")),
10277            ],
10278            vec![entry, ctor],
10279        );
10280        let execution = run_ok(&module);
10281        assert_eq!(execution.value, Value::int32(5));
10282        assert_eq!(execution.entry_registers[5], Value::TRUE);
10283    }
10284
10285    #[test]
10286    fn sync_iterator_walks_array_elements() {
10287        // Sum [10,20] via GetIterator/IteratorNext loop.
10288        let entry = function(
10289            0,
10290            6,
10291            vec![
10292                Instruction::CreateArray { dst: reg(0) },
10293                Instruction::LoadConst {
10294                    dst: reg(1),
10295                    constant: cid(0),
10296                },
10297                Instruction::ArrayPush {
10298                    array: reg(0),
10299                    value: reg(1),
10300                },
10301                Instruction::LoadConst {
10302                    dst: reg(1),
10303                    constant: cid(1),
10304                },
10305                Instruction::ArrayPush {
10306                    array: reg(0),
10307                    value: reg(1),
10308                },
10309                // acc = 0
10310                Instruction::LoadConst {
10311                    dst: reg(2),
10312                    constant: cid(2),
10313                },
10314                Instruction::GetIterator {
10315                    dst: reg(3),
10316                    src: reg(0),
10317                    kind: IteratorKind::Sync,
10318                },
10319                // loop head @7: next
10320                Instruction::IteratorNext {
10321                    done: reg(4),
10322                    value: reg(5),
10323                    iterator: reg(3),
10324                },
10325                Instruction::JumpIfTrue {
10326                    condition: reg(4),
10327                    target: pc(11),
10328                },
10329                Instruction::Binary {
10330                    dst: reg(2),
10331                    op: BinaryOp::Add,
10332                    left: reg(2),
10333                    right: reg(5),
10334                },
10335                Instruction::Jump { target: pc(7) },
10336                // @11 done
10337                Instruction::Return { value: reg(2) },
10338            ],
10339            vec![],
10340        );
10341        let module = verified(
10342            vec![Constant::Int32(10), Constant::Int32(20), Constant::Int32(0)],
10343            vec![entry],
10344        );
10345        assert_eq!(run_ok(&module).value, Value::int32(30));
10346    }
10347
10348    #[test]
10349    fn keys_iterator_enumerates_own_object_keys() {
10350        // obj = {a:1}; for-in yields "a".
10351        let entry = function(
10352            0,
10353            6,
10354            vec![
10355                Instruction::CreateObject { dst: reg(0) },
10356                Instruction::LoadConst {
10357                    dst: reg(1),
10358                    constant: cid(0),
10359                },
10360                Instruction::LoadConst {
10361                    dst: reg(2),
10362                    constant: cid(1),
10363                },
10364                Instruction::SetProperty {
10365                    object: reg(0),
10366                    key: reg(1),
10367                    value: reg(2),
10368                },
10369                Instruction::GetIterator {
10370                    dst: reg(3),
10371                    src: reg(0),
10372                    kind: IteratorKind::Keys,
10373                },
10374                Instruction::IteratorNext {
10375                    done: reg(4),
10376                    value: reg(5),
10377                    iterator: reg(3),
10378                },
10379                Instruction::Return { value: reg(5) },
10380            ],
10381            vec![],
10382        );
10383        let module = verified(
10384            vec![
10385                Constant::String(EcmaString::from_utf8("a")),
10386                Constant::Int32(1),
10387            ],
10388            vec![entry],
10389        );
10390        let execution = run_ok(&module);
10391        // The produced key must equal a fresh "a" string.
10392        let key = execution.value;
10393        // Compare via a second machine's constant is awkward; instead assert it
10394        // is a heap string by checking done flag was false.
10395        assert_eq!(execution.entry_registers[4], Value::FALSE);
10396        assert_ne!(key, Value::UNDEFINED);
10397    }
10398
10399    #[test]
10400    fn async_iterator_steps_like_sync() {
10401        let entry = function(
10402            0,
10403            5,
10404            vec![
10405                Instruction::CreateArray { dst: reg(0) },
10406                Instruction::LoadConst {
10407                    dst: reg(1),
10408                    constant: cid(0),
10409                },
10410                Instruction::ArrayPush {
10411                    array: reg(0),
10412                    value: reg(1),
10413                },
10414                Instruction::GetIterator {
10415                    dst: reg(2),
10416                    src: reg(0),
10417                    kind: IteratorKind::Async,
10418                },
10419                Instruction::IteratorNext {
10420                    done: reg(3),
10421                    value: reg(4),
10422                    iterator: reg(2),
10423                },
10424                Instruction::Return { value: reg(4) },
10425            ],
10426            vec![],
10427        );
10428        let module = verified(vec![Constant::Int32(8)], vec![entry]);
10429        let execution = run_ok(&module);
10430        assert_eq!(execution.value, Value::int32(8));
10431        assert_eq!(execution.entry_registers[3], Value::FALSE);
10432    }
10433
10434    #[test]
10435    fn globals_store_load_and_typeof_undeclared() {
10436        // StoreGlobal x=5; TypeOfGlobal y (undeclared) -> "undefined";
10437        // TypeOfGlobal x -> "number"; return LoadGlobal x.
10438        let entry = function(
10439            0,
10440            3,
10441            vec![
10442                Instruction::LoadConst {
10443                    dst: reg(0),
10444                    constant: cid(2),
10445                },
10446                Instruction::StoreGlobal {
10447                    name: cid(0),
10448                    value: reg(0),
10449                },
10450                Instruction::TypeOfGlobal {
10451                    dst: reg(1),
10452                    name: cid(1),
10453                },
10454                Instruction::TypeOfGlobal {
10455                    dst: reg(2),
10456                    name: cid(0),
10457                },
10458                Instruction::LoadGlobal {
10459                    dst: reg(0),
10460                    name: cid(0),
10461                },
10462                Instruction::Return { value: reg(0) },
10463            ],
10464            vec![],
10465        );
10466        let module = verified(
10467            vec![
10468                Constant::String(EcmaString::from_utf8("x")),
10469                Constant::String(EcmaString::from_utf8("y")),
10470                Constant::Int32(5),
10471            ],
10472            vec![entry],
10473        );
10474        assert_eq!(run_ok(&module).value, Value::int32(5));
10475    }
10476
10477    #[test]
10478    fn create_cell_throws_reference_error_before_initialization() {
10479        let module = verified(
10480            vec![Constant::Int32(0)],
10481            vec![function(
10482                0,
10483                3,
10484                vec![
10485                    Instruction::CreateCell { dst: reg(0) },
10486                    Instruction::LoadConst {
10487                        dst: reg(1),
10488                        constant: cid(0),
10489                    },
10490                    Instruction::GetProperty {
10491                        dst: reg(2),
10492                        object: reg(0),
10493                        key: reg(1),
10494                    },
10495                    Instruction::Return { value: reg(2) },
10496                ],
10497                vec![],
10498            )],
10499        );
10500        let mut host = TestHost;
10501        let error = Machine::new(&module, &mut host, Limits::default())
10502            .run()
10503            .expect_err("uninitialized cell read throws");
10504        assert!(matches!(
10505            error.kind,
10506            RuntimeErrorKind::UncaughtThrow {
10507                origin: ThrowOrigin::ReferenceError { .. },
10508                ..
10509            }
10510        ));
10511    }
10512
10513    #[test]
10514    fn create_cell_can_be_initialized_to_undefined() {
10515        let module = verified(
10516            vec![Constant::Int32(0), Constant::Undefined],
10517            vec![function(
10518                0,
10519                4,
10520                vec![
10521                    Instruction::CreateCell { dst: reg(0) },
10522                    Instruction::LoadConst {
10523                        dst: reg(1),
10524                        constant: cid(0),
10525                    },
10526                    Instruction::LoadConst {
10527                        dst: reg(2),
10528                        constant: cid(1),
10529                    },
10530                    Instruction::SetProperty {
10531                        object: reg(0),
10532                        key: reg(1),
10533                        value: reg(2),
10534                    },
10535                    Instruction::GetProperty {
10536                        dst: reg(3),
10537                        object: reg(0),
10538                        key: reg(1),
10539                    },
10540                    Instruction::Return { value: reg(3) },
10541                ],
10542                vec![],
10543            )],
10544        );
10545        let mut host = TestHost;
10546        let execution = Machine::new(&module, &mut host, Limits::default())
10547            .run()
10548            .expect("explicit undefined initializes the cell");
10549        assert_eq!(execution.value, Value::UNDEFINED);
10550    }
10551
10552    #[test]
10553    fn load_undeclared_global_throws_reference_error() {
10554        let module = verified(
10555            vec![Constant::String(EcmaString::from_utf8("missing"))],
10556            vec![function(
10557                0,
10558                2,
10559                vec![
10560                    Instruction::LoadGlobal {
10561                        dst: reg(0),
10562                        name: cid(0),
10563                    },
10564                    Instruction::Halt,
10565                    Instruction::Return { value: reg(1) },
10566                ],
10567                vec![ExceptionHandler {
10568                    start: pc(0),
10569                    end: pc(1),
10570                    handler: pc(2),
10571                    catch_register: reg(1),
10572                }],
10573            )],
10574        );
10575        let mut host = TestHost;
10576        // No handler at top level path would raise; here it is caught, and the
10577        // caught value is undefined (the ReferenceError marker value).
10578        let execution = Machine::new(&module, &mut host, Limits::default())
10579            .run()
10580            .unwrap();
10581        assert_eq!(execution.value, Value::UNDEFINED);
10582    }
10583
10584    #[test]
10585    fn uncaught_reference_error_reports_origin() {
10586        let module = verified(
10587            vec![Constant::String(EcmaString::from_utf8("missing"))],
10588            vec![function(
10589                0,
10590                1,
10591                vec![
10592                    Instruction::LoadGlobal {
10593                        dst: reg(0),
10594                        name: cid(0),
10595                    },
10596                    Instruction::Return { value: reg(0) },
10597                ],
10598                vec![],
10599            )],
10600        );
10601        let mut host = TestHost;
10602        let error = Machine::new(&module, &mut host, Limits::default())
10603            .run()
10604            .unwrap_err();
10605        assert_eq!(error.pc, pc(0));
10606        assert!(matches!(
10607            error.kind,
10608            RuntimeErrorKind::UncaughtThrow {
10609                origin: ThrowOrigin::ReferenceError { .. },
10610                ..
10611            }
10612        ));
10613    }
10614
10615    fn assert_uri_error(global: &str, argument: EcmaString) {
10616        let module = verified(
10617            vec![
10618                Constant::String(EcmaString::from_utf8(global)),
10619                Constant::String(argument),
10620                Constant::Undefined,
10621            ],
10622            vec![function(
10623                0,
10624                5,
10625                vec![
10626                    Instruction::LoadGlobal {
10627                        dst: reg(0),
10628                        name: cid(0),
10629                    },
10630                    Instruction::LoadConst {
10631                        dst: reg(1),
10632                        constant: cid(1),
10633                    },
10634                    Instruction::LoadConst {
10635                        dst: reg(2),
10636                        constant: cid(2),
10637                    },
10638                    Instruction::CreateArray { dst: reg(3) },
10639                    Instruction::ArrayPush {
10640                        array: reg(3),
10641                        value: reg(1),
10642                    },
10643                    Instruction::Call {
10644                        dst: reg(4),
10645                        callee: reg(0),
10646                        this_value: reg(2),
10647                        arguments: reg(3),
10648                    },
10649                    Instruction::Return { value: reg(4) },
10650                ],
10651                Vec::new(),
10652            )],
10653        );
10654        let mut host = TestHost;
10655        let error = Machine::new(&module, &mut host, Limits::default())
10656            .run()
10657            .unwrap_err();
10658        assert_eq!(error.pc, pc(5));
10659        assert!(matches!(
10660            error.kind,
10661            RuntimeErrorKind::UncaughtThrow {
10662                origin: ThrowOrigin::UriError {
10663                    operation: "URI malformed"
10664                },
10665                ..
10666            }
10667        ));
10668    }
10669
10670    #[test]
10671    fn uri_builtins_report_uri_error() {
10672        for (global, argument) in [
10673            ("encodeURIComponent", EcmaString::from_units(&[0xd800])),
10674            ("decodeURIComponent", EcmaString::from_utf8("%")),
10675            ("decodeURIComponent", EcmaString::from_utf8("%GG")),
10676            ("decodeURIComponent", EcmaString::from_utf8("%FF")),
10677            ("decodeURIComponent", EcmaString::from_utf8("%80")),
10678            ("decodeURIComponent", EcmaString::from_utf8("%C0%80")),
10679            ("decodeURIComponent", EcmaString::from_utf8("%E2%82")),
10680            ("decodeURIComponent", EcmaString::from_utf8("%ED%A0%80")),
10681            ("decodeURIComponent", EcmaString::from_utf8("%F4%90%80%80")),
10682            (
10683                "decodeURIComponent",
10684                EcmaString::from_utf8("%F8%80%80%80%80"),
10685            ),
10686        ] {
10687            assert_uri_error(global, argument);
10688        }
10689    }
10690
10691    fn assert_uri_decode(argument: EcmaString, expected: EcmaString) {
10692        let module = verified(
10693            vec![
10694                Constant::String(EcmaString::from_utf8("decodeURIComponent")),
10695                Constant::String(argument),
10696                Constant::Undefined,
10697                Constant::String(expected),
10698            ],
10699            vec![function(
10700                0,
10701                7,
10702                vec![
10703                    Instruction::LoadGlobal {
10704                        dst: reg(0),
10705                        name: cid(0),
10706                    },
10707                    Instruction::LoadConst {
10708                        dst: reg(1),
10709                        constant: cid(1),
10710                    },
10711                    Instruction::LoadConst {
10712                        dst: reg(2),
10713                        constant: cid(2),
10714                    },
10715                    Instruction::CreateArray { dst: reg(3) },
10716                    Instruction::ArrayPush {
10717                        array: reg(3),
10718                        value: reg(1),
10719                    },
10720                    Instruction::Call {
10721                        dst: reg(4),
10722                        callee: reg(0),
10723                        this_value: reg(2),
10724                        arguments: reg(3),
10725                    },
10726                    Instruction::LoadConst {
10727                        dst: reg(5),
10728                        constant: cid(3),
10729                    },
10730                    Instruction::Binary {
10731                        dst: reg(6),
10732                        op: BinaryOp::StrictEqual,
10733                        left: reg(4),
10734                        right: reg(5),
10735                    },
10736                    Instruction::Return { value: reg(6) },
10737                ],
10738                Vec::new(),
10739            )],
10740        );
10741        let mut host = TestHost;
10742        let execution = Machine::new(&module, &mut host, Limits::default())
10743            .run()
10744            .unwrap();
10745        assert_eq!(execution.value, Value::TRUE);
10746    }
10747
10748    #[test]
10749    fn decode_uri_component_preserves_units_and_decodes_utf8() {
10750        let exact = EcmaString::from_units(&[0xd800, 0x61, 0xdfff]);
10751        for (argument, expected) in [
10752            (exact.clone(), exact),
10753            (EcmaString::from_utf8("%2F"), EcmaString::from_utf8("/")),
10754            (
10755                EcmaString::from_utf8("%F0%9F%98%80"),
10756                EcmaString::from_utf8("😀"),
10757            ),
10758            (
10759                EcmaString::from_utf8("%E4%B8%ADA"),
10760                EcmaString::from_utf8("中A"),
10761            ),
10762            (EcmaString::from_utf8("%00"), EcmaString::from_units(&[0])),
10763        ] {
10764            assert_uri_decode(argument, expected);
10765        }
10766    }
10767
10768    #[test]
10769    fn regexp_is_object_with_source_and_flags() {
10770        // typeof re === "object" is not directly returnable; return re.source.
10771        let module = verified(
10772            vec![
10773                Constant::String(EcmaString::from_utf8("ab")),
10774                Constant::String(EcmaString::from_utf8("gi")),
10775                Constant::String(EcmaString::from_utf8("source")),
10776                Constant::String(EcmaString::from_utf8("global")),
10777            ],
10778            vec![function(
10779                0,
10780                4,
10781                vec![
10782                    Instruction::CreateRegExp {
10783                        dst: reg(0),
10784                        pattern: cid(0),
10785                        flags: cid(1),
10786                    },
10787                    Instruction::LoadConst {
10788                        dst: reg(1),
10789                        constant: cid(3),
10790                    },
10791                    Instruction::GetProperty {
10792                        dst: reg(2),
10793                        object: reg(0),
10794                        key: reg(1),
10795                    },
10796                    Instruction::Unary {
10797                        dst: reg(3),
10798                        op: UnaryOp::TypeOf,
10799                        operand: reg(0),
10800                    },
10801                    Instruction::Return { value: reg(2) },
10802                ],
10803                vec![],
10804            )],
10805        );
10806        let execution = run_ok(&module);
10807        // re.global -> true
10808        assert_eq!(execution.value, Value::TRUE);
10809    }
10810
10811    #[test]
10812    fn this_and_new_target_are_frame_owned() {
10813        // Call passes this; new.target is undefined in a plain call.
10814        let entry = function(
10815            0,
10816            4,
10817            vec![
10818                Instruction::CreateObject { dst: reg(0) },
10819                Instruction::CreateArray { dst: reg(3) },
10820                Instruction::CreateClosure {
10821                    dst: reg(1),
10822                    function: FunctionId::new(1),
10823                    captures: reg(3),
10824                },
10825                Instruction::CreateArray { dst: reg(2) },
10826                Instruction::Call {
10827                    dst: reg(0),
10828                    callee: reg(1),
10829                    this_value: reg(0),
10830                    arguments: reg(2),
10831                },
10832                Instruction::Return { value: reg(0) },
10833            ],
10834            vec![],
10835        );
10836        // returns (this === passed) is hard cross-frame; instead return typeof
10837        // new.target which is "undefined" for a plain call.
10838        let callee = function(
10839            0,
10840            2,
10841            vec![
10842                Instruction::LoadNewTarget { dst: reg(0) },
10843                Instruction::Unary {
10844                    dst: reg(1),
10845                    op: UnaryOp::TypeOf,
10846                    operand: reg(0),
10847                },
10848                Instruction::Return { value: reg(1) },
10849            ],
10850            vec![],
10851        );
10852        let module = verified(vec![], vec![entry, callee]);
10853        let execution = run_ok(&module);
10854        // typeof undefined is a heap "undefined" string; strict-compare against
10855        // typeof of a known-undefined value is awkward, so assert non-undefined
10856        // heap string was produced and the call completed.
10857        assert_ne!(execution.value, Value::UNDEFINED);
10858    }
10859
10860    #[test]
10861    fn new_target_is_constructor_during_construct() {
10862        // In a constructor, new.target === callee; verify via instanceof-style
10863        // check: store new.target on this, then read back after construct.
10864        let entry = function(
10865            0,
10866            4,
10867            vec![
10868                Instruction::CreateArray { dst: reg(3) },
10869                Instruction::CreateClosure {
10870                    dst: reg(0),
10871                    function: FunctionId::new(1),
10872                    captures: reg(3),
10873                },
10874                // ctor.prototype = {}
10875                Instruction::CreateObject { dst: reg(1) },
10876                Instruction::LoadConst {
10877                    dst: reg(2),
10878                    constant: cid(0),
10879                },
10880                Instruction::SetProperty {
10881                    object: reg(0),
10882                    key: reg(2),
10883                    value: reg(1),
10884                },
10885                Instruction::CreateArray { dst: reg(3) },
10886                Instruction::Construct {
10887                    dst: reg(1),
10888                    callee: reg(0),
10889                    arguments: reg(3),
10890                },
10891                // read back this.nt === ctor
10892                Instruction::LoadConst {
10893                    dst: reg(2),
10894                    constant: cid(1),
10895                },
10896                Instruction::GetProperty {
10897                    dst: reg(3),
10898                    object: reg(1),
10899                    key: reg(2),
10900                },
10901                Instruction::Binary {
10902                    dst: reg(3),
10903                    op: BinaryOp::StrictEqual,
10904                    left: reg(3),
10905                    right: reg(0),
10906                },
10907                Instruction::Return { value: reg(3) },
10908            ],
10909            vec![],
10910        );
10911        let ctor = function(
10912            0,
10913            3,
10914            vec![
10915                Instruction::LoadNewTarget { dst: reg(0) },
10916                Instruction::LoadThis { dst: reg(1) },
10917                Instruction::LoadConst {
10918                    dst: reg(2),
10919                    constant: cid(1),
10920                },
10921                Instruction::SetProperty {
10922                    object: reg(1),
10923                    key: reg(2),
10924                    value: reg(0),
10925                },
10926                Instruction::Halt,
10927            ],
10928            vec![],
10929        );
10930        let module = verified(
10931            vec![
10932                Constant::String(EcmaString::from_utf8("prototype")),
10933                Constant::String(EcmaString::from_utf8("nt")),
10934            ],
10935            vec![entry, ctor],
10936        );
10937        assert_eq!(run_ok(&module).value, Value::TRUE);
10938    }
10939
10940    #[test]
10941    fn arguments_object_reflects_passed_values() {
10942        // callee returns arguments[0].
10943        let entry = function(
10944            0,
10945            4,
10946            vec![
10947                Instruction::CreateArray { dst: reg(3) },
10948                Instruction::CreateClosure {
10949                    dst: reg(0),
10950                    function: FunctionId::new(1),
10951                    captures: reg(3),
10952                },
10953                // args = [42]
10954                Instruction::CreateArray { dst: reg(2) },
10955                Instruction::LoadConst {
10956                    dst: reg(1),
10957                    constant: cid(0),
10958                },
10959                Instruction::ArrayPush {
10960                    array: reg(2),
10961                    value: reg(1),
10962                },
10963                Instruction::Call {
10964                    dst: reg(0),
10965                    callee: reg(0),
10966                    this_value: reg(1),
10967                    arguments: reg(2),
10968                },
10969                Instruction::Return { value: reg(0) },
10970            ],
10971            vec![],
10972        );
10973        let callee = function(
10974            0,
10975            2,
10976            vec![
10977                Instruction::LoadArguments { dst: reg(0) },
10978                Instruction::LoadConst {
10979                    dst: reg(1),
10980                    constant: cid(1),
10981                },
10982                Instruction::GetProperty {
10983                    dst: reg(0),
10984                    object: reg(0),
10985                    key: reg(1),
10986                },
10987                Instruction::Return { value: reg(0) },
10988            ],
10989            vec![],
10990        );
10991        let module = verified(
10992            vec![
10993                Constant::Int32(42),
10994                Constant::String(EcmaString::from_utf8("0")),
10995            ],
10996            vec![entry, callee],
10997        );
10998        assert_eq!(run_ok(&module).value, Value::int32(42));
10999    }
11000
11001    #[test]
11002    fn catch_register_receives_exact_thrown_value() {
11003        let module = verified(
11004            vec![Constant::Int32(9)],
11005            vec![function(
11006                0,
11007                2,
11008                vec![
11009                    Instruction::LoadConst {
11010                        dst: reg(0),
11011                        constant: cid(0),
11012                    },
11013                    Instruction::Throw { value: reg(0) },
11014                    Instruction::Return { value: reg(1) },
11015                ],
11016                vec![ExceptionHandler {
11017                    start: pc(1),
11018                    end: pc(2),
11019                    handler: pc(2),
11020                    catch_register: reg(1),
11021                }],
11022            )],
11023        );
11024        assert_eq!(run_ok(&module).value, Value::int32(9));
11025    }
11026
11027    #[test]
11028    fn native_callback_throw_is_caught_at_outer_call_site() {
11029        let entry = function(
11030            0,
11031            9,
11032            vec![
11033                Instruction::CreateArray { dst: reg(0) },
11034                Instruction::LoadConst {
11035                    dst: reg(1),
11036                    constant: cid(0),
11037                },
11038                Instruction::ArrayPush {
11039                    array: reg(0),
11040                    value: reg(1),
11041                },
11042                Instruction::CreateArray { dst: reg(2) },
11043                Instruction::CreateClosure {
11044                    dst: reg(3),
11045                    function: FunctionId::new(1),
11046                    captures: reg(2),
11047                },
11048                Instruction::LoadConst {
11049                    dst: reg(4),
11050                    constant: cid(1),
11051                },
11052                Instruction::GetProperty {
11053                    dst: reg(5),
11054                    object: reg(0),
11055                    key: reg(4),
11056                },
11057                Instruction::CreateArray { dst: reg(6) },
11058                Instruction::ArrayPush {
11059                    array: reg(6),
11060                    value: reg(3),
11061                },
11062                Instruction::Call {
11063                    dst: reg(7),
11064                    callee: reg(5),
11065                    this_value: reg(0),
11066                    arguments: reg(6),
11067                },
11068                Instruction::Halt,
11069                Instruction::Return { value: reg(8) },
11070            ],
11071            vec![ExceptionHandler {
11072                start: pc(9),
11073                end: pc(10),
11074                handler: pc(11),
11075                catch_register: reg(8),
11076            }],
11077        );
11078        let callback = closure_function(
11079            0,
11080            0,
11081            1,
11082            vec![
11083                Instruction::LoadConst {
11084                    dst: reg(0),
11085                    constant: cid(0),
11086                },
11087                Instruction::Throw { value: reg(0) },
11088            ],
11089        );
11090        let module = verified(
11091            vec![
11092                Constant::Int32(7),
11093                Constant::String(EcmaString::from_utf8("map")),
11094            ],
11095            vec![entry, callback],
11096        );
11097
11098        assert_eq!(run_ok(&module).value, Value::int32(7));
11099    }
11100
11101    #[test]
11102    fn native_callback_throw_uncaught_at_outer_call_site() {
11103        let entry = function(
11104            0,
11105            9,
11106            vec![
11107                Instruction::CreateArray { dst: reg(0) },
11108                Instruction::LoadConst {
11109                    dst: reg(1),
11110                    constant: cid(0),
11111                },
11112                Instruction::ArrayPush {
11113                    array: reg(0),
11114                    value: reg(1),
11115                },
11116                Instruction::CreateArray { dst: reg(2) },
11117                Instruction::CreateClosure {
11118                    dst: reg(3),
11119                    function: FunctionId::new(1),
11120                    captures: reg(2),
11121                },
11122                Instruction::LoadConst {
11123                    dst: reg(4),
11124                    constant: cid(1),
11125                },
11126                Instruction::GetProperty {
11127                    dst: reg(5),
11128                    object: reg(0),
11129                    key: reg(4),
11130                },
11131                Instruction::CreateArray { dst: reg(6) },
11132                Instruction::ArrayPush {
11133                    array: reg(6),
11134                    value: reg(3),
11135                },
11136                Instruction::Call {
11137                    dst: reg(7),
11138                    callee: reg(5),
11139                    this_value: reg(0),
11140                    arguments: reg(6),
11141                },
11142                Instruction::Halt,
11143            ],
11144            Vec::new(),
11145        );
11146        let callback = closure_function(
11147            0,
11148            0,
11149            1,
11150            vec![
11151                Instruction::LoadConst {
11152                    dst: reg(0),
11153                    constant: cid(0),
11154                },
11155                Instruction::Throw { value: reg(0) },
11156            ],
11157        );
11158        let simple = closure_function(
11159            0,
11160            0,
11161            1,
11162            vec![
11163                Instruction::LoadConst {
11164                    dst: reg(0),
11165                    constant: cid(2),
11166                },
11167                Instruction::Return { value: reg(0) },
11168            ],
11169        );
11170        let module = verified(
11171            vec![
11172                Constant::Int32(7),
11173                Constant::String(EcmaString::from_utf8("map")),
11174                Constant::Int32(42),
11175            ],
11176            vec![entry, callback, simple],
11177        );
11178
11179        let mut host = TestHost;
11180        let mut machine = Machine::new(&module, &mut host, Limits::default());
11181        let error = machine.run_loop(0).unwrap_err();
11182        assert_eq!(
11183            error.kind,
11184            RuntimeErrorKind::UncaughtThrow {
11185                value: Value::int32(7),
11186                origin: ThrowOrigin::Bytecode,
11187            }
11188        );
11189        assert!(machine.callback_boundaries.is_empty());
11190        assert!(machine.frames.is_empty());
11191        assert_eq!(machine.live_registers, 0);
11192
11193        let callee = machine
11194            .allocate(HeapEntry::Function {
11195                module: ModuleId::new(0),
11196                function: FunctionId::new(2),
11197                captures: Vec::new(),
11198                properties: PropertyMap::default(),
11199                prototype: Some(machine.intrinsics.function_prototype),
11200                extensible: true,
11201            })
11202            .unwrap();
11203        assert_eq!(
11204            machine.call_value(callee, Value::UNDEFINED, &[]).unwrap(),
11205            Value::int32(42)
11206        );
11207    }
11208
11209    #[test]
11210    fn callee_throw_unwinds_to_call_site_handler() {
11211        let entry = function(
11212            0,
11213            4,
11214            vec![
11215                Instruction::CreateArray { dst: reg(3) },
11216                Instruction::CreateClosure {
11217                    dst: reg(0),
11218                    function: FunctionId::new(1),
11219                    captures: reg(3),
11220                },
11221                Instruction::CreateArray { dst: reg(1) },
11222                Instruction::Call {
11223                    dst: reg(2),
11224                    callee: reg(0),
11225                    this_value: reg(1),
11226                    arguments: reg(1),
11227                },
11228                Instruction::Halt,
11229                Instruction::Return { value: reg(3) },
11230            ],
11231            vec![ExceptionHandler {
11232                start: pc(3),
11233                end: pc(4),
11234                handler: pc(5),
11235                catch_register: reg(3),
11236            }],
11237        );
11238        let callee = function(
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            vec![],
11249        );
11250        let module = verified(vec![Constant::Int32(7)], vec![entry, callee]);
11251        assert_eq!(run_ok(&module).value, Value::int32(7));
11252    }
11253
11254    #[test]
11255    fn heap_and_register_limits_fail_before_unbounded_growth() {
11256        let module = verified(
11257            vec![],
11258            vec![function(
11259                0,
11260                2,
11261                vec![
11262                    Instruction::CreateObject { dst: reg(0) },
11263                    Instruction::CreateObject { dst: reg(1) },
11264                    Instruction::Halt,
11265                ],
11266                vec![],
11267            )],
11268        );
11269        let mut host = TestHost;
11270        let error = Machine::new(
11271            &module,
11272            &mut host,
11273            Limits {
11274                max_heap_slots: 1,
11275                ..Limits::default()
11276            },
11277        )
11278        .run()
11279        .unwrap_err();
11280        assert_eq!(error.pc, pc(1));
11281        assert_eq!(
11282            error.kind,
11283            RuntimeErrorKind::HeapSlotLimitExceeded { limit: 1 }
11284        );
11285
11286        let mut host = TestHost;
11287        let error = Machine::new(
11288            &module,
11289            &mut host,
11290            Limits {
11291                max_total_registers: 1,
11292                ..Limits::default()
11293            },
11294        )
11295        .run()
11296        .unwrap_err();
11297        assert_eq!(
11298            error.kind,
11299            RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
11300        );
11301    }
11302
11303    #[test]
11304    fn argument_array_length_limit_is_enforced() {
11305        let entry = function(
11306            0,
11307            4,
11308            vec![
11309                Instruction::CreateArray { dst: reg(3) },
11310                Instruction::CreateClosure {
11311                    dst: reg(0),
11312                    function: FunctionId::new(1),
11313                    captures: reg(3),
11314                },
11315                Instruction::CreateArray { dst: reg(2) },
11316                Instruction::LoadConst {
11317                    dst: reg(1),
11318                    constant: cid(0),
11319                },
11320                Instruction::ArrayPush {
11321                    array: reg(2),
11322                    value: reg(1),
11323                },
11324                Instruction::Call {
11325                    dst: reg(0),
11326                    callee: reg(0),
11327                    this_value: reg(1),
11328                    arguments: reg(2),
11329                },
11330                Instruction::Halt,
11331            ],
11332            vec![],
11333        );
11334        let callee = function(1, 1, vec![Instruction::Return { value: reg(0) }], vec![]);
11335        let module = verified(vec![Constant::Int32(1)], vec![entry, callee]);
11336        let mut host = TestHost;
11337        let error = Machine::new(
11338            &module,
11339            &mut host,
11340            Limits {
11341                max_argument_count: 0,
11342                ..Limits::default()
11343            },
11344        )
11345        .run()
11346        .unwrap_err();
11347        assert_eq!(
11348            error.kind,
11349            RuntimeErrorKind::ArgumentLimitExceeded {
11350                limit: 0,
11351                requested: 1
11352            }
11353        );
11354    }
11355
11356    #[test]
11357    fn u32_registers_and_instruction_pcs_do_not_truncate_at_127() {
11358        let mut code = vec![Instruction::LoadConst {
11359            dst: reg(0),
11360            constant: cid(0),
11361        }];
11362        for register in 1..=199 {
11363            code.push(Instruction::Move {
11364                dst: reg(register),
11365                src: reg(register - 1),
11366            });
11367        }
11368        code.push(Instruction::Return { value: reg(199) });
11369        let module = verified(
11370            vec![Constant::Number(NumberBits::from_f64(3.5))],
11371            vec![function(0, 200, code, vec![])],
11372        );
11373        let execution = run_ok(&module);
11374        assert_eq!(execution.value, Value::number(3.5));
11375        assert_eq!(execution.entry_registers[199], Value::number(3.5));
11376    }
11377
11378    #[test]
11379    fn construct_returned_object_overrides_default_instance() {
11380        // A constructor returning its own object overrides the default instance.
11381        let entry = function(
11382            0,
11383            3,
11384            vec![
11385                Instruction::CreateArray { dst: reg(2) },
11386                Instruction::CreateClosure {
11387                    dst: reg(0),
11388                    function: FunctionId::new(1),
11389                    captures: reg(2),
11390                },
11391                Instruction::CreateArray { dst: reg(2) },
11392                Instruction::Construct {
11393                    dst: reg(1),
11394                    callee: reg(0),
11395                    arguments: reg(2),
11396                },
11397                // returned object has marker property set to 5
11398                Instruction::LoadConst {
11399                    dst: reg(0),
11400                    constant: cid(0),
11401                },
11402                Instruction::GetProperty {
11403                    dst: reg(2),
11404                    object: reg(1),
11405                    key: reg(0),
11406                },
11407                Instruction::Return { value: reg(2) },
11408            ],
11409            vec![],
11410        );
11411        let returns_object = function(
11412            0,
11413            3,
11414            vec![
11415                Instruction::CreateObject { dst: reg(0) },
11416                Instruction::LoadConst {
11417                    dst: reg(1),
11418                    constant: cid(0),
11419                },
11420                Instruction::LoadConst {
11421                    dst: reg(2),
11422                    constant: cid(1),
11423                },
11424                Instruction::SetProperty {
11425                    object: reg(0),
11426                    key: reg(1),
11427                    value: reg(2),
11428                },
11429                Instruction::Return { value: reg(0) },
11430            ],
11431            vec![],
11432        );
11433        let module = verified(
11434            vec![
11435                Constant::String(EcmaString::from_utf8("marker")),
11436                Constant::Int32(5),
11437            ],
11438            vec![entry, returns_object],
11439        );
11440        assert_eq!(run_ok(&module).value, Value::int32(5));
11441    }
11442
11443    #[test]
11444    fn ecmascript_number_formatting_is_shortest_round_trip() {
11445        let cases = [
11446            (0.1 + 0.2, "0.30000000000000004"),
11447            (1e21, "1e+21"),
11448            (-0.0, "0"),
11449            (1.0 / 3.0, "0.3333333333333333"),
11450            (1e-6, "0.000001"),
11451            (1e-7, "1e-7"),
11452        ];
11453        for (number, expected) in cases {
11454            assert_eq!(
11455                Machine::<TestHost>::ordinary_number_to_string(number),
11456                expected
11457            );
11458        }
11459    }
11460
11461    #[test]
11462    fn own_keys_put_indices_before_insertion_ordered_strings() {
11463        let module = verified(
11464            Vec::new(),
11465            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11466        );
11467        let mut host = TestHost;
11468        let mut machine = Machine::new(&module, &mut host, Limits::default());
11469        let object = machine
11470            .allocate(HeapEntry::Object {
11471                properties: PropertyMap::default(),
11472                prototype: Some(machine.intrinsics.object_prototype),
11473                boxed_primitive: None,
11474                extensible: true,
11475            })
11476            .unwrap();
11477        let index = machine.runtime_slot(object).unwrap().unwrap();
11478        for (key, value) in [("b", 1), ("2", 2), ("a", 3), ("1", 4)] {
11479            machine
11480                .set_own_data(
11481                    index,
11482                    PropertyKey::Named(EcmaString::from_utf8(key)),
11483                    Value::int32(value),
11484                )
11485                .unwrap();
11486        }
11487        assert_eq!(
11488            machine.enumerable_keys(object).unwrap(),
11489            ["1", "2", "b", "a"].map(EcmaString::from_utf8)
11490        );
11491    }
11492
11493    #[test]
11494    fn object_prototype_to_string_uses_realm_tags() {
11495        let module = verified(
11496            Vec::new(),
11497            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11498        );
11499        let mut host = TestHost;
11500        let mut machine = Machine::new(&module, &mut host, Limits::default());
11501        let array = machine
11502            .allocate(HeapEntry::Array {
11503                elements: Vec::new(),
11504                properties: PropertyMap::default(),
11505                prototype: Some(machine.intrinsics.array_prototype),
11506                extensible: true,
11507                length_writable: true,
11508            })
11509            .unwrap();
11510        let object = machine
11511            .allocate(HeapEntry::Object {
11512                properties: PropertyMap::default(),
11513                prototype: Some(machine.intrinsics.object_prototype),
11514                boxed_primitive: None,
11515                extensible: true,
11516            })
11517            .unwrap();
11518        let function = machine.intrinsics.global("Object").unwrap();
11519        let to_string = machine.intrinsics.object_to_string();
11520        for (value, expected) in [
11521            (Value::UNDEFINED, "[object Undefined]"),
11522            (Value::NULL, "[object Null]"),
11523            (Value::TRUE, "[object Boolean]"),
11524            (array, "[object Array]"),
11525            (object, "[object Object]"),
11526            (function, "[object Function]"),
11527        ] {
11528            let tag = machine.call_value(to_string, value, &[]).unwrap();
11529            assert!(
11530                machine
11531                    .string_text(tag)
11532                    .is_some_and(|text| text.eq_ascii(expected))
11533            );
11534        }
11535    }
11536
11537    #[derive(Default)]
11538    struct CapabilityHost {
11539        stdout: Vec<u8>,
11540        stderr: Vec<u8>,
11541        env: BTreeMap<String, String>,
11542    }
11543
11544    impl Host for CapabilityHost {
11545        fn write_stdout(&mut self, bytes: &[u8]) {
11546            self.stdout.extend_from_slice(bytes);
11547        }
11548
11549        fn write_stderr(&mut self, bytes: &[u8]) {
11550            self.stderr.extend_from_slice(bytes);
11551        }
11552
11553        fn env(&self, name: &str) -> Option<&str> {
11554            self.env.get(name).map(String::as_str)
11555        }
11556
11557        fn set_env(&mut self, name: &str, value: &str) {
11558            self.env.insert(name.to_owned(), value.to_owned());
11559        }
11560
11561        fn delete_env(&mut self, name: &str) -> bool {
11562            self.env.remove(name).is_some()
11563        }
11564    }
11565
11566    #[test]
11567    fn console_formats_node_value_shapes_byte_exactly() {
11568        let module = verified(
11569            Vec::new(),
11570            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11571        );
11572        let mut host = CapabilityHost::default();
11573        {
11574            let mut machine = Machine::new(&module, &mut host, Limits::default());
11575            let console = machine.intrinsics.global("console").unwrap();
11576            let log = machine.get_named_property(console, "log").unwrap();
11577            let string = machine
11578                .allocate(HeapEntry::String(EcmaString::from_utf8("hello")))
11579                .unwrap();
11580            let array_string = machine
11581                .allocate(HeapEntry::String(EcmaString::from_utf8("x")))
11582                .unwrap();
11583            let array = machine
11584                .allocate(HeapEntry::Array {
11585                    elements: vec![Value::int32(1), array_string],
11586                    properties: PropertyMap::default(),
11587                    prototype: Some(machine.intrinsics.array_prototype),
11588                    extensible: true,
11589                    length_writable: true,
11590                })
11591                .unwrap();
11592            let mut inner_properties = PropertyMap::default();
11593            inner_properties.insert(
11594                PropertyKey::Named(EcmaString::from_utf8("answer")),
11595                Property::Data {
11596                    value: Value::int32(42),
11597                    writable: true,
11598                    enumerable: true,
11599                    configurable: true,
11600                },
11601            );
11602            let inner = machine
11603                .allocate(HeapEntry::Object {
11604                    properties: inner_properties,
11605                    prototype: Some(machine.intrinsics.object_prototype),
11606                    boxed_primitive: None,
11607                    extensible: true,
11608                })
11609                .unwrap();
11610            let mut outer_properties = PropertyMap::default();
11611            outer_properties.insert(
11612                PropertyKey::Named(EcmaString::from_utf8("nested")),
11613                Property::Data {
11614                    value: inner,
11615                    writable: true,
11616                    enumerable: true,
11617                    configurable: true,
11618                },
11619            );
11620            let outer = machine
11621                .allocate(HeapEntry::Object {
11622                    properties: outer_properties,
11623                    prototype: Some(machine.intrinsics.object_prototype),
11624                    boxed_primitive: None,
11625                    extensible: true,
11626                })
11627                .unwrap();
11628            let symbol = machine
11629                .allocate(HeapEntry::Symbol {
11630                    description: EcmaString::from_utf8("token"),
11631                })
11632                .unwrap();
11633            for value in [
11634                string,
11635                Value::int32(42),
11636                array,
11637                outer,
11638                Value::UNDEFINED,
11639                Value::NULL,
11640                symbol,
11641            ] {
11642                machine.call_value(log, console, &[value]).unwrap();
11643            }
11644        }
11645        assert_eq!(
11646            host.stdout,
11647            b"hello\n42\n[ 1, 'x' ]\n{ nested: { answer: 42 } }\nundefined\nnull\nSymbol(token)\n"
11648        );
11649        assert!(host.stderr.is_empty());
11650    }
11651
11652    #[test]
11653    fn console_and_process_properties_are_reassignable_and_env_is_live() {
11654        let module = verified(
11655            Vec::new(),
11656            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
11657        );
11658        let mut host = CapabilityHost::default();
11659        {
11660            let mut machine = Machine::new(&module, &mut host, Limits::default());
11661            let console = machine.intrinsics.global("console").unwrap();
11662            let warn = machine.get_named_property(console, "warn").unwrap();
11663            machine
11664                .set_data_property(console, "warn", Value::int32(91))
11665                .unwrap();
11666            assert_eq!(
11667                machine.get_named_property(console, "warn").unwrap(),
11668                Value::int32(91)
11669            );
11670            machine.set_data_property(console, "warn", warn).unwrap();
11671
11672            let process = machine.intrinsics.global("process").unwrap();
11673            let env = machine.get_named_property(process, "env").unwrap();
11674            machine
11675                .set_data_property(env, "BAMTS_MODE", Value::int32(7))
11676                .unwrap();
11677            let value = machine.get_named_property(env, "BAMTS_MODE").unwrap();
11678            assert!(
11679                machine
11680                    .string_text(value)
11681                    .is_some_and(|text| text.eq_ascii("7"))
11682            );
11683            assert!(
11684                machine
11685                    .delete_property(
11686                        env,
11687                        &PropertyKey::Named(EcmaString::from_utf8("BAMTS_MODE"))
11688                    )
11689                    .unwrap()
11690            );
11691            assert_eq!(
11692                machine.get_named_property(env, "BAMTS_MODE").unwrap(),
11693                Value::UNDEFINED
11694            );
11695        }
11696        assert_eq!(host.env("BAMTS_MODE"), None);
11697    }
11698
11699    #[test]
11700    fn independent_modules_keep_same_name_globals_isolated() {
11701        let dependency = |name: &str, value: i32| {
11702            program_module(
11703                name,
11704                vec![
11705                    Constant::String(EcmaString::from_utf8("x")),
11706                    Constant::Int32(value),
11707                ],
11708                vec![function(
11709                    0,
11710                    1,
11711                    vec![
11712                        Instruction::LoadConst {
11713                            dst: reg(0),
11714                            constant: cid(2),
11715                        },
11716                        Instruction::StoreGlobal {
11717                            name: cid(1),
11718                            value: reg(0),
11719                        },
11720                        Instruction::Return { value: reg(0) },
11721                    ],
11722                    Vec::new(),
11723                )],
11724                Vec::new(),
11725                vec![Binding {
11726                    name: cid(1),
11727                    kind: BindingKind::Hoisted,
11728                }],
11729                vec![Export {
11730                    name: cid(1),
11731                    source: ExportSource::Local(BindingId::new(0)),
11732                }],
11733            )
11734        };
11735        let root = program_module(
11736            "root",
11737            vec![
11738                Constant::String(EcmaString::from_utf8("left")),
11739                Constant::String(EcmaString::from_utf8("right")),
11740                Constant::String(EcmaString::from_utf8("x")),
11741            ],
11742            vec![function(
11743                0,
11744                5,
11745                vec![
11746                    Instruction::LoadGlobal {
11747                        dst: reg(0),
11748                        name: cid(1),
11749                    },
11750                    Instruction::LoadGlobal {
11751                        dst: reg(1),
11752                        name: cid(2),
11753                    },
11754                    Instruction::LoadConst {
11755                        dst: reg(2),
11756                        constant: cid(3),
11757                    },
11758                    Instruction::GetProperty {
11759                        dst: reg(3),
11760                        object: reg(0),
11761                        key: reg(2),
11762                    },
11763                    Instruction::GetProperty {
11764                        dst: reg(4),
11765                        object: reg(1),
11766                        key: reg(2),
11767                    },
11768                    Instruction::Binary {
11769                        dst: reg(0),
11770                        op: BinaryOp::Add,
11771                        left: reg(3),
11772                        right: reg(4),
11773                    },
11774                    Instruction::Return { value: reg(0) },
11775                ],
11776                Vec::new(),
11777            )],
11778            vec![
11779                Edge {
11780                    specifier: cid(1),
11781                    target: EdgeTarget::Local(ModuleId::new(0)),
11782                    kind: EdgeKind::Static,
11783                },
11784                Edge {
11785                    specifier: cid(2),
11786                    target: EdgeTarget::Local(ModuleId::new(1)),
11787                    kind: EdgeKind::Static,
11788                },
11789            ],
11790            vec![
11791                Binding {
11792                    name: cid(1),
11793                    kind: BindingKind::Namespace {
11794                        edge: EdgeId::new(0),
11795                    },
11796                },
11797                Binding {
11798                    name: cid(2),
11799                    kind: BindingKind::Namespace {
11800                        edge: EdgeId::new(1),
11801                    },
11802                },
11803            ],
11804            Vec::new(),
11805        );
11806        let program = linked(vec![dependency("left", 1), dependency("right", 2), root], 2);
11807        assert_eq!(run_ok(&program).value, Value::int32(3));
11808    }
11809
11810    #[test]
11811    fn imported_binding_observes_post_link_mutation_live() {
11812        let dependency = program_module(
11813            "dependency",
11814            vec![
11815                Constant::String(EcmaString::from_utf8("x")),
11816                Constant::Int32(1),
11817                Constant::Int32(2),
11818                Constant::String(EcmaString::from_utf8("set")),
11819            ],
11820            vec![
11821                function(
11822                    0,
11823                    3,
11824                    vec![
11825                        Instruction::LoadConst {
11826                            dst: reg(0),
11827                            constant: cid(2),
11828                        },
11829                        Instruction::StoreGlobal {
11830                            name: cid(1),
11831                            value: reg(0),
11832                        },
11833                        Instruction::CreateArray { dst: reg(1) },
11834                        Instruction::CreateClosure {
11835                            dst: reg(2),
11836                            function: FunctionId::new(1),
11837                            captures: reg(1),
11838                        },
11839                        Instruction::StoreGlobal {
11840                            name: cid(4),
11841                            value: reg(2),
11842                        },
11843                        Instruction::Return { value: reg(0) },
11844                    ],
11845                    Vec::new(),
11846                ),
11847                function(
11848                    0,
11849                    1,
11850                    vec![
11851                        Instruction::LoadConst {
11852                            dst: reg(0),
11853                            constant: cid(3),
11854                        },
11855                        Instruction::StoreGlobal {
11856                            name: cid(1),
11857                            value: reg(0),
11858                        },
11859                        Instruction::Return { value: reg(0) },
11860                    ],
11861                    Vec::new(),
11862                ),
11863            ],
11864            Vec::new(),
11865            vec![
11866                Binding {
11867                    name: cid(1),
11868                    kind: BindingKind::Hoisted,
11869                },
11870                Binding {
11871                    name: cid(4),
11872                    kind: BindingKind::Hoisted,
11873                },
11874            ],
11875            vec![
11876                Export {
11877                    name: cid(1),
11878                    source: ExportSource::Local(BindingId::new(0)),
11879                },
11880                Export {
11881                    name: cid(4),
11882                    source: ExportSource::Local(BindingId::new(1)),
11883                },
11884            ],
11885        );
11886        let root = program_module(
11887            "root",
11888            vec![
11889                Constant::String(EcmaString::from_utf8("x")),
11890                Constant::String(EcmaString::from_utf8("set")),
11891                Constant::String(EcmaString::from_utf8("dep")),
11892            ],
11893            vec![function(
11894                0,
11895                3,
11896                vec![
11897                    Instruction::LoadGlobal {
11898                        dst: reg(0),
11899                        name: cid(2),
11900                    },
11901                    Instruction::CreateArray { dst: reg(1) },
11902                    Instruction::Call {
11903                        dst: reg(2),
11904                        callee: reg(0),
11905                        this_value: reg(1),
11906                        arguments: reg(1),
11907                    },
11908                    Instruction::LoadGlobal {
11909                        dst: reg(0),
11910                        name: cid(1),
11911                    },
11912                    Instruction::Return { value: reg(0) },
11913                ],
11914                Vec::new(),
11915            )],
11916            vec![Edge {
11917                specifier: cid(3),
11918                target: EdgeTarget::Local(ModuleId::new(0)),
11919                kind: EdgeKind::Static,
11920            }],
11921            vec![
11922                Binding {
11923                    name: cid(1),
11924                    kind: BindingKind::Imported {
11925                        edge: EdgeId::new(0),
11926                        name: cid(1),
11927                    },
11928                },
11929                Binding {
11930                    name: cid(2),
11931                    kind: BindingKind::Imported {
11932                        edge: EdgeId::new(0),
11933                        name: cid(2),
11934                    },
11935                },
11936            ],
11937            Vec::new(),
11938        );
11939        assert_eq!(
11940            run_ok(&linked(vec![dependency, root], 1)).value,
11941            Value::int32(2)
11942        );
11943    }
11944
11945    #[test]
11946    fn closure_globals_resolve_in_the_defining_module() {
11947        let dependency = program_module(
11948            "dependency",
11949            vec![
11950                Constant::String(EcmaString::from_utf8("x")),
11951                Constant::Int32(10),
11952                Constant::String(EcmaString::from_utf8("read")),
11953            ],
11954            vec![
11955                function(
11956                    0,
11957                    3,
11958                    vec![
11959                        Instruction::LoadConst {
11960                            dst: reg(0),
11961                            constant: cid(2),
11962                        },
11963                        Instruction::StoreGlobal {
11964                            name: cid(1),
11965                            value: reg(0),
11966                        },
11967                        Instruction::CreateArray { dst: reg(1) },
11968                        Instruction::CreateClosure {
11969                            dst: reg(2),
11970                            function: FunctionId::new(1),
11971                            captures: reg(1),
11972                        },
11973                        Instruction::StoreGlobal {
11974                            name: cid(3),
11975                            value: reg(2),
11976                        },
11977                        Instruction::Return { value: reg(0) },
11978                    ],
11979                    Vec::new(),
11980                ),
11981                function(
11982                    0,
11983                    1,
11984                    vec![
11985                        Instruction::LoadGlobal {
11986                            dst: reg(0),
11987                            name: cid(1),
11988                        },
11989                        Instruction::Return { value: reg(0) },
11990                    ],
11991                    Vec::new(),
11992                ),
11993            ],
11994            Vec::new(),
11995            vec![
11996                Binding {
11997                    name: cid(1),
11998                    kind: BindingKind::Hoisted,
11999                },
12000                Binding {
12001                    name: cid(3),
12002                    kind: BindingKind::Hoisted,
12003                },
12004            ],
12005            vec![Export {
12006                name: cid(3),
12007                source: ExportSource::Local(BindingId::new(1)),
12008            }],
12009        );
12010        let root = program_module(
12011            "root",
12012            vec![
12013                Constant::String(EcmaString::from_utf8("x")),
12014                Constant::Int32(20),
12015                Constant::String(EcmaString::from_utf8("read")),
12016                Constant::String(EcmaString::from_utf8("dep")),
12017            ],
12018            vec![function(
12019                0,
12020                4,
12021                vec![
12022                    Instruction::LoadConst {
12023                        dst: reg(0),
12024                        constant: cid(2),
12025                    },
12026                    Instruction::StoreGlobal {
12027                        name: cid(1),
12028                        value: reg(0),
12029                    },
12030                    Instruction::LoadGlobal {
12031                        dst: reg(1),
12032                        name: cid(3),
12033                    },
12034                    Instruction::CreateArray { dst: reg(2) },
12035                    Instruction::Call {
12036                        dst: reg(3),
12037                        callee: reg(1),
12038                        this_value: reg(2),
12039                        arguments: reg(2),
12040                    },
12041                    Instruction::Return { value: reg(3) },
12042                ],
12043                Vec::new(),
12044            )],
12045            vec![Edge {
12046                specifier: cid(4),
12047                target: EdgeTarget::Local(ModuleId::new(0)),
12048                kind: EdgeKind::Static,
12049            }],
12050            vec![
12051                Binding {
12052                    name: cid(1),
12053                    kind: BindingKind::Hoisted,
12054                },
12055                Binding {
12056                    name: cid(3),
12057                    kind: BindingKind::Imported {
12058                        edge: EdgeId::new(0),
12059                        name: cid(3),
12060                    },
12061                },
12062            ],
12063            Vec::new(),
12064        );
12065        assert_eq!(
12066            run_ok(&linked(vec![dependency, root], 1)).value,
12067            Value::int32(10)
12068        );
12069    }
12070
12071    #[test]
12072    fn cycle_traps_a_lexical_read_before_initialization() {
12073        let first = program_module(
12074            "first",
12075            vec![
12076                Constant::String(EcmaString::from_utf8("a")),
12077                Constant::Int32(1),
12078                Constant::String(EcmaString::from_utf8("second")),
12079            ],
12080            vec![function(
12081                0,
12082                1,
12083                vec![
12084                    Instruction::LoadConst {
12085                        dst: reg(0),
12086                        constant: cid(2),
12087                    },
12088                    Instruction::StoreGlobal {
12089                        name: cid(1),
12090                        value: reg(0),
12091                    },
12092                    Instruction::Return { value: reg(0) },
12093                ],
12094                Vec::new(),
12095            )],
12096            vec![Edge {
12097                specifier: cid(3),
12098                target: EdgeTarget::Local(ModuleId::new(1)),
12099                kind: EdgeKind::Static,
12100            }],
12101            vec![Binding {
12102                name: cid(1),
12103                kind: BindingKind::Lexical,
12104            }],
12105            vec![Export {
12106                name: cid(1),
12107                source: ExportSource::Local(BindingId::new(0)),
12108            }],
12109        );
12110        let second = program_module(
12111            "second",
12112            vec![
12113                Constant::String(EcmaString::from_utf8("a")),
12114                Constant::String(EcmaString::from_utf8("first")),
12115            ],
12116            vec![function(
12117                0,
12118                1,
12119                vec![
12120                    Instruction::LoadGlobal {
12121                        dst: reg(0),
12122                        name: cid(1),
12123                    },
12124                    Instruction::Return { value: reg(0) },
12125                ],
12126                Vec::new(),
12127            )],
12128            vec![Edge {
12129                specifier: cid(2),
12130                target: EdgeTarget::Local(ModuleId::new(0)),
12131                kind: EdgeKind::Static,
12132            }],
12133            vec![Binding {
12134                name: cid(1),
12135                kind: BindingKind::Imported {
12136                    edge: EdgeId::new(0),
12137                    name: cid(1),
12138                },
12139            }],
12140            Vec::new(),
12141        );
12142        let program = linked(vec![first, second], 0);
12143        let mut host = TestHost;
12144        let error = Machine::new(&program, &mut host, Limits::default())
12145            .run()
12146            .unwrap_err();
12147        assert!(matches!(
12148            error.kind,
12149            RuntimeErrorKind::TemporalDeadZone { module, binding }
12150                if module == ModuleId::new(1) && binding == BindingId::new(0)
12151        ));
12152    }
12153
12154    #[test]
12155    fn cycle_reentry_with_a_hoisted_binding_completes() {
12156        let first = program_module(
12157            "first",
12158            vec![
12159                Constant::String(EcmaString::from_utf8("a")),
12160                Constant::Int32(1),
12161                Constant::String(EcmaString::from_utf8("second")),
12162            ],
12163            vec![function(
12164                0,
12165                1,
12166                vec![
12167                    Instruction::LoadConst {
12168                        dst: reg(0),
12169                        constant: cid(2),
12170                    },
12171                    Instruction::StoreGlobal {
12172                        name: cid(1),
12173                        value: reg(0),
12174                    },
12175                    Instruction::Return { value: reg(0) },
12176                ],
12177                Vec::new(),
12178            )],
12179            vec![Edge {
12180                specifier: cid(3),
12181                target: EdgeTarget::Local(ModuleId::new(1)),
12182                kind: EdgeKind::Static,
12183            }],
12184            vec![Binding {
12185                name: cid(1),
12186                kind: BindingKind::Hoisted,
12187            }],
12188            vec![Export {
12189                name: cid(1),
12190                source: ExportSource::Local(BindingId::new(0)),
12191            }],
12192        );
12193        let second = program_module(
12194            "second",
12195            vec![
12196                Constant::String(EcmaString::from_utf8("a")),
12197                Constant::String(EcmaString::from_utf8("first")),
12198            ],
12199            vec![function(
12200                0,
12201                1,
12202                vec![
12203                    Instruction::LoadGlobal {
12204                        dst: reg(0),
12205                        name: cid(1),
12206                    },
12207                    Instruction::Return { value: reg(0) },
12208                ],
12209                Vec::new(),
12210            )],
12211            vec![Edge {
12212                specifier: cid(2),
12213                target: EdgeTarget::Local(ModuleId::new(0)),
12214                kind: EdgeKind::Static,
12215            }],
12216            vec![Binding {
12217                name: cid(1),
12218                kind: BindingKind::Imported {
12219                    edge: EdgeId::new(0),
12220                    name: cid(1),
12221                },
12222            }],
12223            Vec::new(),
12224        );
12225        assert_eq!(
12226            run_ok(&linked(vec![first, second], 0)).value,
12227            Value::int32(1)
12228        );
12229    }
12230
12231    #[test]
12232    fn namespace_identity_reads_live_cells_and_enumerates_sorted_keys() {
12233        let dependency = program_module(
12234            "dependency",
12235            vec![
12236                Constant::String(EcmaString::from_utf8("z")),
12237                Constant::String(EcmaString::from_utf8("a")),
12238                Constant::String(EcmaString::from_utf8("mutate")),
12239                Constant::Int32(1),
12240                Constant::Int32(2),
12241                Constant::Int32(3),
12242            ],
12243            vec![
12244                function(
12245                    0,
12246                    4,
12247                    vec![
12248                        Instruction::LoadConst {
12249                            dst: reg(0),
12250                            constant: cid(4),
12251                        },
12252                        Instruction::StoreGlobal {
12253                            name: cid(1),
12254                            value: reg(0),
12255                        },
12256                        Instruction::LoadConst {
12257                            dst: reg(0),
12258                            constant: cid(5),
12259                        },
12260                        Instruction::StoreGlobal {
12261                            name: cid(2),
12262                            value: reg(0),
12263                        },
12264                        Instruction::CreateArray { dst: reg(1) },
12265                        Instruction::CreateClosure {
12266                            dst: reg(2),
12267                            function: FunctionId::new(1),
12268                            captures: reg(1),
12269                        },
12270                        Instruction::StoreGlobal {
12271                            name: cid(3),
12272                            value: reg(2),
12273                        },
12274                        Instruction::Return { value: reg(0) },
12275                    ],
12276                    Vec::new(),
12277                ),
12278                function(
12279                    0,
12280                    1,
12281                    vec![
12282                        Instruction::LoadConst {
12283                            dst: reg(0),
12284                            constant: cid(6),
12285                        },
12286                        Instruction::StoreGlobal {
12287                            name: cid(1),
12288                            value: reg(0),
12289                        },
12290                        Instruction::Return { value: reg(0) },
12291                    ],
12292                    Vec::new(),
12293                ),
12294            ],
12295            Vec::new(),
12296            vec![
12297                Binding {
12298                    name: cid(1),
12299                    kind: BindingKind::Hoisted,
12300                },
12301                Binding {
12302                    name: cid(2),
12303                    kind: BindingKind::Hoisted,
12304                },
12305                Binding {
12306                    name: cid(3),
12307                    kind: BindingKind::Hoisted,
12308                },
12309            ],
12310            vec![
12311                Export {
12312                    name: cid(1),
12313                    source: ExportSource::Local(BindingId::new(0)),
12314                },
12315                Export {
12316                    name: cid(2),
12317                    source: ExportSource::Local(BindingId::new(1)),
12318                },
12319                Export {
12320                    name: cid(3),
12321                    source: ExportSource::Local(BindingId::new(2)),
12322                },
12323            ],
12324        );
12325        let root = program_module(
12326            "root",
12327            vec![
12328                Constant::String(EcmaString::from_utf8("ns1")),
12329                Constant::String(EcmaString::from_utf8("ns2")),
12330                Constant::String(EcmaString::from_utf8("mutate")),
12331                Constant::String(EcmaString::from_utf8("z")),
12332                Constant::String(EcmaString::from_utf8("a")),
12333                Constant::String(EcmaString::from_utf8("dep")),
12334                Constant::String(EcmaString::from_utf8("Object")),
12335                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
12336                Constant::String(EcmaString::from_utf8("value")),
12337                Constant::String(EcmaString::from_utf8("writable")),
12338                Constant::String(EcmaString::from_utf8("enumerable")),
12339                Constant::String(EcmaString::from_utf8("configurable")),
12340                Constant::String(EcmaString::from_utf8("missing")),
12341            ],
12342            vec![function(
12343                0,
12344                31,
12345                vec![
12346                    Instruction::LoadGlobal {
12347                        dst: reg(0),
12348                        name: cid(1),
12349                    },
12350                    Instruction::LoadGlobal {
12351                        dst: reg(1),
12352                        name: cid(2),
12353                    },
12354                    Instruction::Binary {
12355                        dst: reg(2),
12356                        op: BinaryOp::StrictEqual,
12357                        left: reg(0),
12358                        right: reg(1),
12359                    },
12360                    Instruction::LoadGlobal {
12361                        dst: reg(3),
12362                        name: cid(3),
12363                    },
12364                    Instruction::CreateArray { dst: reg(4) },
12365                    Instruction::Call {
12366                        dst: reg(5),
12367                        callee: reg(3),
12368                        this_value: reg(4),
12369                        arguments: reg(4),
12370                    },
12371                    Instruction::LoadConst {
12372                        dst: reg(6),
12373                        constant: cid(4),
12374                    },
12375                    Instruction::GetProperty {
12376                        dst: reg(7),
12377                        object: reg(0),
12378                        key: reg(6),
12379                    },
12380                    Instruction::GetIterator {
12381                        dst: reg(8),
12382                        src: reg(0),
12383                        kind: IteratorKind::Keys,
12384                    },
12385                    Instruction::IteratorNext {
12386                        done: reg(9),
12387                        value: reg(10),
12388                        iterator: reg(8),
12389                    },
12390                    Instruction::LoadConst {
12391                        dst: reg(11),
12392                        constant: cid(5),
12393                    },
12394                    Instruction::Binary {
12395                        dst: reg(12),
12396                        op: BinaryOp::StrictEqual,
12397                        left: reg(10),
12398                        right: reg(11),
12399                    },
12400                    Instruction::IteratorNext {
12401                        done: reg(9),
12402                        value: reg(10),
12403                        iterator: reg(8),
12404                    },
12405                    Instruction::LoadConst {
12406                        dst: reg(13),
12407                        constant: cid(3),
12408                    },
12409                    Instruction::Binary {
12410                        dst: reg(5),
12411                        op: BinaryOp::StrictEqual,
12412                        left: reg(10),
12413                        right: reg(13),
12414                    },
12415                    Instruction::IteratorNext {
12416                        done: reg(9),
12417                        value: reg(10),
12418                        iterator: reg(8),
12419                    },
12420                    Instruction::Binary {
12421                        dst: reg(14),
12422                        op: BinaryOp::StrictEqual,
12423                        left: reg(10),
12424                        right: reg(6),
12425                    },
12426                    Instruction::LoadGlobal {
12427                        dst: reg(15),
12428                        name: cid(7),
12429                    },
12430                    Instruction::LoadConst {
12431                        dst: reg(16),
12432                        constant: cid(8),
12433                    },
12434                    Instruction::GetProperty {
12435                        dst: reg(17),
12436                        object: reg(15),
12437                        key: reg(16),
12438                    },
12439                    Instruction::CreateArray { dst: reg(18) },
12440                    Instruction::ArrayPush {
12441                        array: reg(18),
12442                        value: reg(0),
12443                    },
12444                    Instruction::ArrayPush {
12445                        array: reg(18),
12446                        value: reg(6),
12447                    },
12448                    Instruction::Call {
12449                        dst: reg(19),
12450                        callee: reg(17),
12451                        this_value: reg(18),
12452                        arguments: reg(18),
12453                    },
12454                    Instruction::LoadConst {
12455                        dst: reg(20),
12456                        constant: cid(9),
12457                    },
12458                    Instruction::GetProperty {
12459                        dst: reg(21),
12460                        object: reg(19),
12461                        key: reg(20),
12462                    },
12463                    Instruction::LoadConst {
12464                        dst: reg(22),
12465                        constant: cid(10),
12466                    },
12467                    Instruction::GetProperty {
12468                        dst: reg(23),
12469                        object: reg(19),
12470                        key: reg(22),
12471                    },
12472                    Instruction::LoadConst {
12473                        dst: reg(24),
12474                        constant: cid(11),
12475                    },
12476                    Instruction::GetProperty {
12477                        dst: reg(25),
12478                        object: reg(19),
12479                        key: reg(24),
12480                    },
12481                    Instruction::LoadConst {
12482                        dst: reg(26),
12483                        constant: cid(12),
12484                    },
12485                    Instruction::GetProperty {
12486                        dst: reg(27),
12487                        object: reg(19),
12488                        key: reg(26),
12489                    },
12490                    Instruction::CreateArray { dst: reg(28) },
12491                    Instruction::LoadConst {
12492                        dst: reg(29),
12493                        constant: cid(13),
12494                    },
12495                    Instruction::ArrayPush {
12496                        array: reg(28),
12497                        value: reg(0),
12498                    },
12499                    Instruction::ArrayPush {
12500                        array: reg(28),
12501                        value: reg(29),
12502                    },
12503                    Instruction::Call {
12504                        dst: reg(30),
12505                        callee: reg(17),
12506                        this_value: reg(28),
12507                        arguments: reg(28),
12508                    },
12509                    Instruction::Return { value: reg(21) },
12510                ],
12511                Vec::new(),
12512            )],
12513            vec![Edge {
12514                specifier: cid(6),
12515                target: EdgeTarget::Local(ModuleId::new(0)),
12516                kind: EdgeKind::Static,
12517            }],
12518            vec![
12519                Binding {
12520                    name: cid(1),
12521                    kind: BindingKind::Namespace {
12522                        edge: EdgeId::new(0),
12523                    },
12524                },
12525                Binding {
12526                    name: cid(2),
12527                    kind: BindingKind::Namespace {
12528                        edge: EdgeId::new(0),
12529                    },
12530                },
12531                Binding {
12532                    name: cid(3),
12533                    kind: BindingKind::Imported {
12534                        edge: EdgeId::new(0),
12535                        name: cid(3),
12536                    },
12537                },
12538            ],
12539            Vec::new(),
12540        );
12541        let execution = run_ok(&linked(vec![dependency, root], 1));
12542        assert_eq!(execution.value, Value::int32(3));
12543        assert_eq!(execution.entry_registers[2], Value::TRUE);
12544        assert_eq!(execution.entry_registers[5], Value::TRUE);
12545        assert_eq!(execution.entry_registers[12], Value::TRUE);
12546        assert_eq!(execution.entry_registers[14], Value::TRUE);
12547        assert_eq!(execution.entry_registers[23], Value::TRUE);
12548        assert_eq!(execution.entry_registers[25], Value::TRUE);
12549        assert_eq!(execution.entry_registers[27], Value::FALSE);
12550        assert_eq!(execution.entry_registers[30], Value::UNDEFINED);
12551    }
12552
12553    #[test]
12554    fn side_effect_module_runs_once_with_single_or_duplicate_static_edges() {
12555        for duplicate in [false, true] {
12556            let dependency = program_module(
12557                "dependency",
12558                vec![
12559                    Constant::String(EcmaString::from_utf8("count")),
12560                    Constant::Int32(0),
12561                    Constant::Int32(1),
12562                ],
12563                vec![function(
12564                    0,
12565                    2,
12566                    vec![
12567                        Instruction::LoadGlobal {
12568                            dst: reg(0),
12569                            name: cid(1),
12570                        },
12571                        Instruction::JumpIfFalse {
12572                            condition: reg(0),
12573                            target: pc(3),
12574                        },
12575                        Instruction::Jump { target: pc(5) },
12576                        Instruction::LoadConst {
12577                            dst: reg(0),
12578                            constant: cid(2),
12579                        },
12580                        Instruction::StoreGlobal {
12581                            name: cid(1),
12582                            value: reg(0),
12583                        },
12584                        Instruction::LoadConst {
12585                            dst: reg(1),
12586                            constant: cid(3),
12587                        },
12588                        Instruction::Binary {
12589                            dst: reg(0),
12590                            op: BinaryOp::Add,
12591                            left: reg(0),
12592                            right: reg(1),
12593                        },
12594                        Instruction::StoreGlobal {
12595                            name: cid(1),
12596                            value: reg(0),
12597                        },
12598                        Instruction::Return { value: reg(0) },
12599                    ],
12600                    Vec::new(),
12601                )],
12602                Vec::new(),
12603                vec![Binding {
12604                    name: cid(1),
12605                    kind: BindingKind::Hoisted,
12606                }],
12607                vec![Export {
12608                    name: cid(1),
12609                    source: ExportSource::Local(BindingId::new(0)),
12610                }],
12611            );
12612            let mut edges = vec![Edge {
12613                specifier: cid(2),
12614                target: EdgeTarget::Local(ModuleId::new(0)),
12615                kind: EdgeKind::Static,
12616            }];
12617            if duplicate {
12618                edges.push(Edge {
12619                    specifier: cid(3),
12620                    target: EdgeTarget::Local(ModuleId::new(0)),
12621                    kind: EdgeKind::Static,
12622                });
12623            }
12624            let root = program_module(
12625                "root",
12626                vec![
12627                    Constant::String(EcmaString::from_utf8("count")),
12628                    Constant::String(EcmaString::from_utf8("dep-one")),
12629                    Constant::String(EcmaString::from_utf8("dep-two")),
12630                ],
12631                vec![function(
12632                    0,
12633                    1,
12634                    vec![
12635                        Instruction::LoadGlobal {
12636                            dst: reg(0),
12637                            name: cid(1),
12638                        },
12639                        Instruction::Return { value: reg(0) },
12640                    ],
12641                    Vec::new(),
12642                )],
12643                edges,
12644                vec![Binding {
12645                    name: cid(1),
12646                    kind: BindingKind::Imported {
12647                        edge: EdgeId::new(0),
12648                        name: cid(1),
12649                    },
12650                }],
12651                Vec::new(),
12652            );
12653            assert_eq!(
12654                run_ok(&linked(vec![dependency, root], 1)).value,
12655                Value::int32(1)
12656            );
12657        }
12658    }
12659
12660    #[test]
12661    fn failed_module_rethrows_the_identical_stored_value() {
12662        let module = program_module(
12663            "throws",
12664            Vec::new(),
12665            vec![function(
12666                0,
12667                1,
12668                vec![
12669                    Instruction::CreateObject { dst: reg(0) },
12670                    Instruction::Throw { value: reg(0) },
12671                ],
12672                Vec::new(),
12673            )],
12674            Vec::new(),
12675            Vec::new(),
12676            Vec::new(),
12677        );
12678        let program = linked(vec![module], 0);
12679        let mut host = TestHost;
12680        let mut machine = Machine::new(&program, &mut host, Limits::default());
12681        machine.frames.clear();
12682        machine.live_registers = 0;
12683        machine.instantiate_modules().unwrap();
12684        let first = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12685        let second = machine.evaluate_module(ModuleId::new(0)).unwrap_err();
12686        let RuntimeErrorKind::UncaughtThrow { value: first, .. } = first.kind else {
12687            panic!("module must fail by throwing");
12688        };
12689        let RuntimeErrorKind::UncaughtThrow { value: second, .. } = second.kind else {
12690            panic!("stored failure must remain a throw");
12691        };
12692        assert_eq!(first, second);
12693        assert!(first.as_heap_ref().is_some());
12694    }
12695
12696    #[test]
12697    fn external_static_edge_is_a_typed_runtime_error() {
12698        let module = program_module(
12699            "root",
12700            vec![Constant::String(EcmaString::from_utf8("external"))],
12701            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12702            vec![Edge {
12703                specifier: cid(1),
12704                target: EdgeTarget::External,
12705                kind: EdgeKind::Static,
12706            }],
12707            Vec::new(),
12708            Vec::new(),
12709        );
12710        let program = linked(vec![module], 0);
12711        let mut host = TestHost;
12712        let error = Machine::new(&program, &mut host, Limits::default())
12713            .run()
12714            .unwrap_err();
12715        assert!(matches!(
12716            error.kind,
12717            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
12718                if module == ModuleId::new(0) && edge == EdgeId::new(0)
12719        ));
12720    }
12721
12722    #[test]
12723    fn external_module_and_export_names_preserve_unicode() {
12724        for (specifier, export) in [("módulo", "value"), ("external", "café")] {
12725            let module = program_module(
12726                "root",
12727                vec![
12728                    Constant::String(EcmaString::from_utf8(export)),
12729                    Constant::String(EcmaString::from_utf8(specifier)),
12730                ],
12731                vec![function(
12732                    0,
12733                    1,
12734                    vec![
12735                        Instruction::LoadGlobal {
12736                            dst: reg(0),
12737                            name: cid(1),
12738                        },
12739                        Instruction::Return { value: reg(0) },
12740                    ],
12741                    Vec::new(),
12742                )],
12743                vec![Edge {
12744                    specifier: cid(2),
12745                    target: EdgeTarget::External,
12746                    kind: EdgeKind::Static,
12747                }],
12748                vec![Binding {
12749                    name: cid(1),
12750                    kind: BindingKind::Imported {
12751                        edge: EdgeId::new(0),
12752                        name: cid(1),
12753                    },
12754                }],
12755                Vec::new(),
12756            );
12757            let program = linked(vec![module], 0);
12758            let mut host = TestHost;
12759            let mut machine = Machine::new(&program, &mut host, Limits::default());
12760            machine.registry.external.insert(
12761                EcmaString::from_utf8(specifier),
12762                ExternalModuleInstance {
12763                    namespace: Value::UNDEFINED,
12764                    exports: BTreeMap::from([(
12765                        EcmaString::from_utf8(export),
12766                        ExternalExport {
12767                            value: Value::int32(7),
12768                            cell: None,
12769                        },
12770                    )]),
12771                    internals: BTreeMap::new(),
12772                },
12773            );
12774
12775            assert_eq!(machine.run().unwrap().value, Value::int32(7));
12776        }
12777    }
12778
12779    #[test]
12780    fn dynamic_import_preserves_cycles_identity_and_single_evaluation() {
12781        let root = program_module(
12782            "root",
12783            vec![
12784                Constant::String(EcmaString::from_utf8("./dependency")),
12785                Constant::String(EcmaString::from_utf8("count")),
12786                Constant::Int32(0),
12787                Constant::String(EcmaString::from_utf8("value")),
12788            ],
12789            vec![function(
12790                0,
12791                7,
12792                vec![
12793                    Instruction::LoadConst {
12794                        dst: reg(0),
12795                        constant: cid(3),
12796                    },
12797                    Instruction::StoreGlobal {
12798                        name: cid(2),
12799                        value: reg(0),
12800                    },
12801                    Instruction::Import {
12802                        dst: reg(1),
12803                        specifier: cid(1),
12804                    },
12805                    Instruction::Import {
12806                        dst: reg(2),
12807                        specifier: cid(1),
12808                    },
12809                    Instruction::Binary {
12810                        dst: reg(3),
12811                        op: BinaryOp::StrictEqual,
12812                        left: reg(1),
12813                        right: reg(2),
12814                    },
12815                    Instruction::LoadConst {
12816                        dst: reg(4),
12817                        constant: cid(4),
12818                    },
12819                    Instruction::GetProperty {
12820                        dst: reg(5),
12821                        object: reg(2),
12822                        key: reg(4),
12823                    },
12824                    Instruction::LoadGlobal {
12825                        dst: reg(6),
12826                        name: cid(2),
12827                    },
12828                    Instruction::Return { value: reg(5) },
12829                ],
12830                Vec::new(),
12831            )],
12832            vec![Edge {
12833                specifier: cid(1),
12834                target: EdgeTarget::Local(ModuleId::new(1)),
12835                kind: EdgeKind::Dynamic,
12836            }],
12837            Vec::new(),
12838            Vec::new(),
12839        );
12840        let dependency = program_module(
12841            "dependency",
12842            vec![
12843                Constant::String(EcmaString::from_utf8("./root")),
12844                Constant::String(EcmaString::from_utf8("count")),
12845                Constant::Int32(1),
12846                Constant::Int32(7),
12847                Constant::String(EcmaString::from_utf8("value")),
12848            ],
12849            vec![function(
12850                0,
12851                3,
12852                vec![
12853                    Instruction::LoadGlobal {
12854                        dst: reg(0),
12855                        name: cid(2),
12856                    },
12857                    Instruction::LoadConst {
12858                        dst: reg(1),
12859                        constant: cid(3),
12860                    },
12861                    Instruction::Binary {
12862                        dst: reg(2),
12863                        op: BinaryOp::Add,
12864                        left: reg(0),
12865                        right: reg(1),
12866                    },
12867                    Instruction::StoreGlobal {
12868                        name: cid(2),
12869                        value: reg(2),
12870                    },
12871                    Instruction::LoadConst {
12872                        dst: reg(0),
12873                        constant: cid(4),
12874                    },
12875                    Instruction::StoreGlobal {
12876                        name: cid(5),
12877                        value: reg(0),
12878                    },
12879                    Instruction::Return { value: reg(0) },
12880                ],
12881                Vec::new(),
12882            )],
12883            vec![Edge {
12884                specifier: cid(1),
12885                target: EdgeTarget::Local(ModuleId::new(0)),
12886                kind: EdgeKind::Static,
12887            }],
12888            vec![Binding {
12889                name: cid(5),
12890                kind: BindingKind::Hoisted,
12891            }],
12892            vec![Export {
12893                name: cid(5),
12894                source: ExportSource::Local(BindingId::new(0)),
12895            }],
12896        );
12897
12898        let execution = run_ok(&linked(vec![root, dependency], 0));
12899        assert_eq!(execution.value, Value::int32(7));
12900        assert_eq!(execution.entry_registers[1], execution.entry_registers[2]);
12901        assert_eq!(execution.entry_registers[3], Value::TRUE);
12902        assert_eq!(execution.entry_registers[6], Value::int32(1));
12903    }
12904
12905    #[test]
12906    fn dynamic_import_counts_live_registers_and_retries_engine_failures() {
12907        let target = program_module(
12908            "target",
12909            Vec::new(),
12910            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
12911            Vec::new(),
12912            Vec::new(),
12913            Vec::new(),
12914        );
12915        let root = program_module(
12916            "root",
12917            vec![Constant::String(EcmaString::from_utf8("./target"))],
12918            vec![function(
12919                0,
12920                1,
12921                vec![
12922                    Instruction::Import {
12923                        dst: reg(0),
12924                        specifier: cid(1),
12925                    },
12926                    Instruction::Return { value: reg(0) },
12927                ],
12928                Vec::new(),
12929            )],
12930            vec![Edge {
12931                specifier: cid(1),
12932                target: EdgeTarget::Local(ModuleId::new(1)),
12933                kind: EdgeKind::Dynamic,
12934            }],
12935            Vec::new(),
12936            Vec::new(),
12937        );
12938        let program = linked(vec![root, target], 0);
12939        let mut host = TestHost;
12940        let mut machine = Machine::new(
12941            &program,
12942            &mut host,
12943            Limits {
12944                max_total_registers: 1,
12945                ..Limits::default()
12946            },
12947        );
12948        machine.frames.clear();
12949        machine.live_registers = 0;
12950        machine.instantiate_modules().unwrap();
12951
12952        let error = machine.evaluate_import(ModuleId::new(0)).unwrap_err();
12953        assert!(matches!(
12954            error.kind,
12955            RuntimeErrorKind::RegisterLimitExceeded { limit: 1 }
12956        ));
12957        assert_eq!(machine.frames.len(), 0);
12958        assert_eq!(machine.live_registers, 0);
12959
12960        machine.limits.max_total_registers = 2;
12961        machine.evaluate_import(ModuleId::new(0)).unwrap();
12962    }
12963
12964    #[test]
12965    fn dynamic_import_rethrows_one_stored_failure_at_each_import_site() {
12966        let root = program_module(
12967            "root",
12968            vec![
12969                Constant::String(EcmaString::from_utf8("./target")),
12970                Constant::String(EcmaString::from_utf8("count")),
12971                Constant::Int32(0),
12972            ],
12973            vec![function(
12974                0,
12975                4,
12976                vec![
12977                    Instruction::LoadConst {
12978                        dst: reg(0),
12979                        constant: cid(3),
12980                    },
12981                    Instruction::StoreGlobal {
12982                        name: cid(2),
12983                        value: reg(0),
12984                    },
12985                    Instruction::Import {
12986                        dst: reg(0),
12987                        specifier: cid(1),
12988                    },
12989                    Instruction::Halt,
12990                    Instruction::Import {
12991                        dst: reg(0),
12992                        specifier: cid(1),
12993                    },
12994                    Instruction::Halt,
12995                    Instruction::LoadGlobal {
12996                        dst: reg(3),
12997                        name: cid(2),
12998                    },
12999                    Instruction::Return { value: reg(2) },
13000                ],
13001                vec![
13002                    ExceptionHandler {
13003                        start: pc(2),
13004                        end: pc(3),
13005                        handler: pc(4),
13006                        catch_register: reg(1),
13007                    },
13008                    ExceptionHandler {
13009                        start: pc(4),
13010                        end: pc(5),
13011                        handler: pc(6),
13012                        catch_register: reg(2),
13013                    },
13014                ],
13015            )],
13016            vec![Edge {
13017                specifier: cid(1),
13018                target: EdgeTarget::Local(ModuleId::new(1)),
13019                kind: EdgeKind::Dynamic,
13020            }],
13021            Vec::new(),
13022            Vec::new(),
13023        );
13024        let target = program_module(
13025            "target",
13026            vec![
13027                Constant::String(EcmaString::from_utf8("count")),
13028                Constant::Int32(1),
13029                Constant::Int32(9),
13030            ],
13031            vec![function(
13032                0,
13033                3,
13034                vec![
13035                    Instruction::LoadGlobal {
13036                        dst: reg(0),
13037                        name: cid(1),
13038                    },
13039                    Instruction::LoadConst {
13040                        dst: reg(1),
13041                        constant: cid(2),
13042                    },
13043                    Instruction::Binary {
13044                        dst: reg(2),
13045                        op: BinaryOp::Add,
13046                        left: reg(0),
13047                        right: reg(1),
13048                    },
13049                    Instruction::StoreGlobal {
13050                        name: cid(1),
13051                        value: reg(2),
13052                    },
13053                    Instruction::LoadConst {
13054                        dst: reg(0),
13055                        constant: cid(3),
13056                    },
13057                    Instruction::Throw { value: reg(0) },
13058                ],
13059                Vec::new(),
13060            )],
13061            Vec::new(),
13062            Vec::new(),
13063            Vec::new(),
13064        );
13065
13066        let execution = run_ok(&linked(vec![root, target], 0));
13067        assert_eq!(execution.value, Value::int32(9));
13068        assert_eq!(execution.entry_registers[1], Value::int32(9));
13069        assert_eq!(execution.entry_registers[2], Value::int32(9));
13070        assert_eq!(execution.entry_registers[3], Value::int32(1));
13071    }
13072
13073    #[test]
13074    fn dynamic_import_returns_the_registered_external_namespace() {
13075        let module = program_module(
13076            "root",
13077            vec![Constant::String(EcmaString::from_utf8("external"))],
13078            vec![function(
13079                0,
13080                3,
13081                vec![
13082                    Instruction::Import {
13083                        dst: reg(0),
13084                        specifier: cid(1),
13085                    },
13086                    Instruction::Import {
13087                        dst: reg(1),
13088                        specifier: cid(1),
13089                    },
13090                    Instruction::Binary {
13091                        dst: reg(2),
13092                        op: BinaryOp::StrictEqual,
13093                        left: reg(0),
13094                        right: reg(1),
13095                    },
13096                    Instruction::Return { value: reg(2) },
13097                ],
13098                Vec::new(),
13099            )],
13100            vec![Edge {
13101                specifier: cid(1),
13102                target: EdgeTarget::External,
13103                kind: EdgeKind::Dynamic,
13104            }],
13105            Vec::new(),
13106            Vec::new(),
13107        );
13108        let program = linked(vec![module], 0);
13109        let mut host = TestHost;
13110        let mut machine = Machine::new(&program, &mut host, Limits::default());
13111        let namespace = machine
13112            .allocate(HeapEntry::Object {
13113                properties: PropertyMap::default(),
13114                prototype: Some(machine.intrinsics.object_prototype),
13115                boxed_primitive: None,
13116                extensible: true,
13117            })
13118            .unwrap();
13119        machine.registry.external.insert(
13120            EcmaString::from_utf8("external"),
13121            ExternalModuleInstance {
13122                namespace,
13123                exports: BTreeMap::new(),
13124                internals: BTreeMap::new(),
13125            },
13126        );
13127
13128        let execution = machine.run().unwrap();
13129        assert_eq!(execution.value, Value::TRUE);
13130        assert_eq!(execution.entry_registers[0], namespace);
13131        assert_eq!(execution.entry_registers[1], namespace);
13132    }
13133
13134    #[test]
13135    fn dynamic_import_resolution_is_requester_scoped() {
13136        let requester = |name, target| {
13137            program_module(
13138                name,
13139                vec![Constant::String(EcmaString::from_utf8("./target"))],
13140                vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13141                vec![Edge {
13142                    specifier: cid(1),
13143                    target: EdgeTarget::Local(ModuleId::new(target)),
13144                    kind: EdgeKind::Dynamic,
13145                }],
13146                Vec::new(),
13147                Vec::new(),
13148            )
13149        };
13150        let target = |name| {
13151            program_module(
13152                name,
13153                Vec::new(),
13154                vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13155                Vec::new(),
13156                Vec::new(),
13157                Vec::new(),
13158            )
13159        };
13160        let program = linked(
13161            vec![
13162                requester("first", 2),
13163                requester("second", 3),
13164                target("first-target"),
13165                target("second-target"),
13166            ],
13167            0,
13168        );
13169        let mut host = TestHost;
13170        let machine = Machine::new(&program, &mut host, Limits::default());
13171
13172        assert_eq!(
13173            machine.resolve_import(ModuleId::new(0), cid(1)),
13174            Ok(ImportTarget::Local(ModuleId::new(2)))
13175        );
13176        assert_eq!(
13177            machine.resolve_import(ModuleId::new(1), cid(1)),
13178            Ok(ImportTarget::Local(ModuleId::new(3)))
13179        );
13180    }
13181
13182    #[test]
13183    fn dynamic_import_of_a_missing_external_is_a_runtime_error() {
13184        let module = program_module(
13185            "root",
13186            vec![Constant::String(EcmaString::from_utf8("dynamic"))],
13187            vec![function(
13188                0,
13189                1,
13190                vec![
13191                    Instruction::Import {
13192                        dst: reg(0),
13193                        specifier: cid(1),
13194                    },
13195                    Instruction::Return { value: reg(0) },
13196                ],
13197                Vec::new(),
13198            )],
13199            vec![Edge {
13200                specifier: cid(1),
13201                target: EdgeTarget::External,
13202                kind: EdgeKind::Dynamic,
13203            }],
13204            Vec::new(),
13205            Vec::new(),
13206        );
13207        let program = linked(vec![module], 0);
13208        let mut host = TestHost;
13209        let error = Machine::new(&program, &mut host, Limits::default())
13210            .run()
13211            .unwrap_err();
13212        assert!(matches!(
13213            error.kind,
13214            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13215                if module == ModuleId::new(0) && edge == EdgeId::new(0)
13216        ));
13217    }
13218
13219    #[test]
13220    fn unbound_global_names_fall_back_to_the_realm_global_map() {
13221        let program = verified(
13222            vec![
13223                Constant::String(EcmaString::from_utf8("realmOnly")),
13224                Constant::Int32(7),
13225            ],
13226            vec![function(
13227                0,
13228                1,
13229                vec![
13230                    Instruction::LoadConst {
13231                        dst: reg(0),
13232                        constant: cid(1),
13233                    },
13234                    Instruction::StoreGlobal {
13235                        name: cid(0),
13236                        value: reg(0),
13237                    },
13238                    Instruction::LoadGlobal {
13239                        dst: reg(0),
13240                        name: cid(0),
13241                    },
13242                    Instruction::Return { value: reg(0) },
13243                ],
13244                Vec::new(),
13245            )],
13246        );
13247        assert_eq!(run_ok(&program).value, Value::int32(7));
13248    }
13249
13250    #[test]
13251    fn module_cell_limit_is_enforced_before_evaluation() {
13252        let module = program_module(
13253            "root",
13254            vec![Constant::String(EcmaString::from_utf8("x"))],
13255            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13256            Vec::new(),
13257            vec![Binding {
13258                name: cid(1),
13259                kind: BindingKind::Hoisted,
13260            }],
13261            Vec::new(),
13262        );
13263        let program = linked(vec![module], 0);
13264        let mut host = TestHost;
13265        let error = Machine::new(
13266            &program,
13267            &mut host,
13268            Limits {
13269                max_module_cells: 0,
13270                ..Limits::default()
13271            },
13272        )
13273        .run()
13274        .unwrap_err();
13275        assert!(matches!(
13276            error.kind,
13277            RuntimeErrorKind::ModuleCellLimitExceeded { limit: 0 }
13278        ));
13279    }
13280    #[test]
13281    fn imported_binding_store_throws_without_mutating_the_exporter() {
13282        let dependency = program_module(
13283            "dependency",
13284            vec![
13285                Constant::String(EcmaString::from_utf8("x")),
13286                Constant::Int32(1),
13287            ],
13288            vec![function(
13289                0,
13290                1,
13291                vec![
13292                    Instruction::LoadConst {
13293                        dst: reg(0),
13294                        constant: cid(2),
13295                    },
13296                    Instruction::StoreGlobal {
13297                        name: cid(1),
13298                        value: reg(0),
13299                    },
13300                    Instruction::Return { value: reg(0) },
13301                ],
13302                Vec::new(),
13303            )],
13304            Vec::new(),
13305            vec![Binding {
13306                name: cid(1),
13307                kind: BindingKind::Hoisted,
13308            }],
13309            vec![Export {
13310                name: cid(1),
13311                source: ExportSource::Local(BindingId::new(0)),
13312            }],
13313        );
13314        let root = program_module(
13315            "root",
13316            vec![
13317                Constant::String(EcmaString::from_utf8("x")),
13318                Constant::Int32(2),
13319                Constant::String(EcmaString::from_utf8("dep")),
13320            ],
13321            vec![function(
13322                0,
13323                1,
13324                vec![
13325                    Instruction::LoadConst {
13326                        dst: reg(0),
13327                        constant: cid(2),
13328                    },
13329                    Instruction::StoreGlobal {
13330                        name: cid(1),
13331                        value: reg(0),
13332                    },
13333                    Instruction::Return { value: reg(0) },
13334                ],
13335                Vec::new(),
13336            )],
13337            vec![Edge {
13338                specifier: cid(3),
13339                target: EdgeTarget::Local(ModuleId::new(0)),
13340                kind: EdgeKind::Static,
13341            }],
13342            vec![Binding {
13343                name: cid(1),
13344                kind: BindingKind::Imported {
13345                    edge: EdgeId::new(0),
13346                    name: cid(1),
13347                },
13348            }],
13349            Vec::new(),
13350        );
13351        let program = linked(vec![dependency, root], 1);
13352        let mut host = TestHost;
13353        let mut machine = Machine::new(&program, &mut host, Limits::default());
13354        machine.frames.clear();
13355        machine.live_registers = 0;
13356        machine.instantiate_modules().unwrap();
13357        assert!(machine.evaluate_module(ModuleId::new(1)).is_err());
13358        let exporter = machine.registry.modules[0].binding_cells[0].unwrap();
13359        assert_eq!(machine.registry.cells[exporter.0].value, Value::int32(1));
13360    }
13361
13362    #[test]
13363    fn namespace_descriptor_propagates_temporal_dead_zone() {
13364        let root = program_module(
13365            "root",
13366            vec![
13367                Constant::String(EcmaString::from_utf8("x")),
13368                Constant::Int32(1),
13369                Constant::String(EcmaString::from_utf8("dependency")),
13370            ],
13371            vec![function(
13372                0,
13373                1,
13374                vec![
13375                    Instruction::LoadConst {
13376                        dst: reg(0),
13377                        constant: cid(2),
13378                    },
13379                    Instruction::StoreGlobal {
13380                        name: cid(1),
13381                        value: reg(0),
13382                    },
13383                    Instruction::Return { value: reg(0) },
13384                ],
13385                Vec::new(),
13386            )],
13387            vec![Edge {
13388                specifier: cid(3),
13389                target: EdgeTarget::Local(ModuleId::new(1)),
13390                kind: EdgeKind::Static,
13391            }],
13392            vec![Binding {
13393                name: cid(1),
13394                kind: BindingKind::Lexical,
13395            }],
13396            vec![Export {
13397                name: cid(1),
13398                source: ExportSource::Local(BindingId::new(0)),
13399            }],
13400        );
13401        let dependency = program_module(
13402            "dependency",
13403            vec![
13404                Constant::String(EcmaString::from_utf8("ns")),
13405                Constant::String(EcmaString::from_utf8("root")),
13406                Constant::String(EcmaString::from_utf8("Object")),
13407                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13408                Constant::String(EcmaString::from_utf8("x")),
13409            ],
13410            vec![namespace_descriptor_entry()],
13411            vec![Edge {
13412                specifier: cid(2),
13413                target: EdgeTarget::Local(ModuleId::new(0)),
13414                kind: EdgeKind::Static,
13415            }],
13416            vec![Binding {
13417                name: cid(1),
13418                kind: BindingKind::Namespace {
13419                    edge: EdgeId::new(0),
13420                },
13421            }],
13422            Vec::new(),
13423        );
13424        let program = linked(vec![root, dependency], 0);
13425        let mut host = TestHost;
13426        let error = Machine::new(&program, &mut host, Limits::default())
13427            .run()
13428            .expect_err("descriptor reads uninitialized namespace export");
13429        assert!(matches!(
13430            error.kind,
13431            RuntimeErrorKind::TemporalDeadZone { module, binding }
13432                if module == ModuleId::new(0) && binding == BindingId::new(0)
13433        ));
13434    }
13435
13436    #[test]
13437    fn namespace_descriptor_propagates_external_linkage_error() {
13438        let exported = program_module(
13439            "exported",
13440            vec![
13441                Constant::String(EcmaString::from_utf8("x")),
13442                Constant::String(EcmaString::from_utf8("external")),
13443            ],
13444            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13445            vec![Edge {
13446                specifier: cid(2),
13447                target: EdgeTarget::External,
13448                kind: EdgeKind::Dynamic,
13449            }],
13450            Vec::new(),
13451            vec![Export {
13452                name: cid(1),
13453                source: ExportSource::Indirect {
13454                    edge: EdgeId::new(0),
13455                    name: cid(1),
13456                },
13457            }],
13458        );
13459        let importer = program_module(
13460            "importer",
13461            vec![
13462                Constant::String(EcmaString::from_utf8("ns")),
13463                Constant::String(EcmaString::from_utf8("exported")),
13464                Constant::String(EcmaString::from_utf8("Object")),
13465                Constant::String(EcmaString::from_utf8("getOwnPropertyDescriptor")),
13466                Constant::String(EcmaString::from_utf8("x")),
13467            ],
13468            vec![namespace_descriptor_entry()],
13469            vec![Edge {
13470                specifier: cid(2),
13471                target: EdgeTarget::Local(ModuleId::new(0)),
13472                kind: EdgeKind::Static,
13473            }],
13474            vec![Binding {
13475                name: cid(1),
13476                kind: BindingKind::Namespace {
13477                    edge: EdgeId::new(0),
13478                },
13479            }],
13480            Vec::new(),
13481        );
13482        let program = linked(vec![exported, importer], 1);
13483        let mut host = TestHost;
13484        let error = Machine::new(&program, &mut host, Limits::default())
13485            .run()
13486            .expect_err("descriptor resolves external namespace export");
13487        assert!(matches!(
13488            error.kind,
13489            RuntimeErrorKind::ExternalModuleUnavailable { module, edge }
13490                if module == ModuleId::new(0) && edge == EdgeId::new(0)
13491        ));
13492    }
13493
13494    #[test]
13495    fn installed_script_uses_machine_wide_id_and_keeps_its_code() {
13496        let root = verified(
13497            Vec::new(),
13498            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13499        );
13500        let script = Arc::new(verified(
13501            vec![Constant::Int32(42)],
13502            vec![function(
13503                0,
13504                1,
13505                vec![
13506                    Instruction::LoadConst {
13507                        dst: reg(0),
13508                        constant: cid(0),
13509                    },
13510                    Instruction::Return { value: reg(0) },
13511                ],
13512                Vec::new(),
13513            )],
13514        ));
13515        let mut host = TestHost;
13516        let mut machine = Machine::new(&root, &mut host, Limits::default());
13517        machine.instantiate_modules().unwrap();
13518        let module = machine.install_script_reserving(script, 0, 0).unwrap();
13519
13520        assert_eq!(module, ModuleId::new(root.modules().len() as u32));
13521        assert!(machine.program().module(module).is_none());
13522        assert_eq!(
13523            machine.module_code(module).constants()[0],
13524            Constant::Int32(42)
13525        );
13526
13527        let closure = machine
13528            .allocate(HeapEntry::Function {
13529                module,
13530                function: FunctionId::new(0),
13531                captures: Vec::new(),
13532                properties: PropertyMap::default(),
13533                prototype: Some(machine.intrinsics.function_prototype),
13534                extensible: true,
13535            })
13536            .unwrap();
13537        assert!(matches!(
13538            machine.call_value(closure, Value::UNDEFINED, &[]),
13539            Ok(value) if value == Value::int32(42)
13540        ));
13541    }
13542
13543    #[test]
13544    fn installed_script_rejects_non_classic_programs_and_enforces_limit() {
13545        let root = verified(
13546            Vec::new(),
13547            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13548        );
13549        let two_modules = Arc::new(linked(
13550            vec![
13551                program_module(
13552                    "first",
13553                    Vec::new(),
13554                    vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13555                    Vec::new(),
13556                    Vec::new(),
13557                    Vec::new(),
13558                ),
13559                program_module(
13560                    "second",
13561                    Vec::new(),
13562                    vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13563                    Vec::new(),
13564                    Vec::new(),
13565                    Vec::new(),
13566                ),
13567            ],
13568            0,
13569        ));
13570        let script = Arc::new(verified(
13571            Vec::new(),
13572            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13573        ));
13574        let mut host = TestHost;
13575        let mut machine = Machine::new(
13576            &root,
13577            &mut host,
13578            Limits {
13579                max_dynamic_modules: 1,
13580                ..Limits::default()
13581            },
13582        );
13583        machine.instantiate_modules().unwrap();
13584
13585        assert!(matches!(
13586            machine.install_script_reserving(two_modules, 0, 0),
13587            Err(RuntimeErrorKind::InvalidDynamicScript { .. })
13588        ));
13589        machine
13590            .install_script_reserving(script.clone(), 0, 0)
13591            .unwrap();
13592        assert!(matches!(
13593            machine.install_script_reserving(script, 0, 0),
13594            Err(RuntimeErrorKind::DynamicModuleLimitExceeded { limit: 1 })
13595        ));
13596    }
13597
13598    #[test]
13599    fn script_heap_cost_counts_scalar_constant_slots() {
13600        let entry = || vec![function(0, 1, vec![Instruction::Halt], Vec::new())];
13601        let empty = verified(Vec::new(), entry());
13602        let constants = vec![Constant::Int32(0); 128];
13603        let scalars = verified(constants.clone(), entry());
13604
13605        let added = Machine::<TestHost>::script_heap_cost(&scalars)
13606            - Machine::<TestHost>::script_heap_cost(&empty);
13607
13608        assert!(added >= constants.len() * std::mem::size_of::<Constant>());
13609    }
13610
13611    #[test]
13612    fn script_heap_cost_includes_verification_storage() {
13613        let small = verified(
13614            Vec::new(),
13615            vec![function(0, 1, vec![Instruction::Halt], Vec::new())],
13616        );
13617        let large = verified(
13618            Vec::new(),
13619            vec![function(0, 130, vec![Instruction::Halt], Vec::new())],
13620        );
13621        let small_verification = small.modules()[0].code.verification_bytes();
13622        let large_verification = large.modules()[0].code.verification_bytes();
13623
13624        assert_eq!(
13625            Machine::<TestHost>::script_heap_cost(&large)
13626                - Machine::<TestHost>::script_heap_cost(&small),
13627            large_verification - small_verification
13628        );
13629    }
13630    #[test]
13631    fn promise_resolver_settles_once_and_reactions_wait_for_drain() {
13632        let program = verified(
13633            vec![
13634                Constant::String(EcmaString::from_utf8("resolve")),
13635                Constant::String(EcmaString::from_utf8("reject")),
13636                Constant::String(EcmaString::from_utf8("observed")),
13637            ],
13638            vec![
13639                function(0, 1, vec![Instruction::Halt], Vec::new()),
13640                function(
13641                    2,
13642                    2,
13643                    vec![
13644                        Instruction::StoreGlobal {
13645                            name: cid(0),
13646                            value: reg(0),
13647                        },
13648                        Instruction::StoreGlobal {
13649                            name: cid(1),
13650                            value: reg(1),
13651                        },
13652                        Instruction::Return { value: reg(0) },
13653                    ],
13654                    Vec::new(),
13655                ),
13656                function(
13657                    1,
13658                    1,
13659                    vec![
13660                        Instruction::StoreGlobal {
13661                            name: cid(2),
13662                            value: reg(0),
13663                        },
13664                        Instruction::Return { value: reg(0) },
13665                    ],
13666                    Vec::new(),
13667                ),
13668            ],
13669        );
13670        let mut host = TestHost;
13671        let mut machine = Machine::new(&program, &mut host, Limits::default());
13672        machine.frames.clear();
13673        machine.live_registers = 0;
13674        let executor = machine
13675            .allocate(HeapEntry::Function {
13676                module: ModuleId::new(0),
13677                function: FunctionId::new(1),
13678                captures: Vec::new(),
13679                properties: PropertyMap::default(),
13680                prototype: Some(machine.intrinsics.function_prototype),
13681                extensible: true,
13682            })
13683            .unwrap();
13684        let observer = machine
13685            .allocate(HeapEntry::Function {
13686                module: ModuleId::new(0),
13687                function: FunctionId::new(2),
13688                captures: Vec::new(),
13689                properties: PropertyMap::default(),
13690                prototype: Some(machine.intrinsics.function_prototype),
13691                extensible: true,
13692            })
13693            .unwrap();
13694        let constructor = machine.intrinsics.global("Promise").unwrap();
13695        let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13696        let HeapEntry::NativeFunction {
13697            callable: NativeCallable::Builtin(constructor_id),
13698            ..
13699        } = machine.heap[constructor_index]
13700        else {
13701            panic!("Promise must be a native constructor");
13702        };
13703        let BuiltinOutcome::Value(promise) = machine
13704            .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13705            .unwrap()
13706        else {
13707            panic!("Promise construction returns a Promise");
13708        };
13709        let then = machine.get_named_property(promise, "then").unwrap();
13710        machine
13711            .call_value(then, promise, &[observer])
13712            .expect("then returns a derived Promise");
13713        let resolve = machine
13714            .globals
13715            .get(&EcmaString::from_utf8("resolve"))
13716            .copied()
13717            .unwrap();
13718        let reject = machine
13719            .globals
13720            .get(&EcmaString::from_utf8("reject"))
13721            .copied()
13722            .unwrap();
13723        assert_eq!(
13724            machine
13725                .call_value(resolve, Value::UNDEFINED, &[Value::int32(1)])
13726                .unwrap(),
13727            Value::UNDEFINED
13728        );
13729        assert_eq!(
13730            machine
13731                .call_value(reject, Value::UNDEFINED, &[Value::int32(2)])
13732                .unwrap(),
13733            Value::UNDEFINED
13734        );
13735        assert!(
13736            !machine
13737                .globals
13738                .contains_key(&EcmaString::from_utf8("observed"))
13739        );
13740
13741        let drain = machine.drain_microtasks().unwrap();
13742        assert_eq!(drain.executed, 1);
13743        assert!(drain.uncaught.is_empty());
13744        assert_eq!(
13745            machine
13746                .globals
13747                .get(&EcmaString::from_utf8("observed"))
13748                .copied(),
13749            Some(Value::int32(1))
13750        );
13751    }
13752
13753    #[test]
13754    fn promise_resolution_adopts_thenables_with_a_fresh_resolver() {
13755        let program = verified(
13756            vec![
13757                Constant::String(EcmaString::from_utf8("resolve")),
13758                Constant::String(EcmaString::from_utf8("reject")),
13759                Constant::String(EcmaString::from_utf8("observed")),
13760                Constant::Int32(7),
13761                Constant::Int32(8),
13762                Constant::Int32(9),
13763                Constant::Undefined,
13764            ],
13765            vec![
13766                function(0, 1, vec![Instruction::Halt], Vec::new()),
13767                function(
13768                    2,
13769                    2,
13770                    vec![
13771                        Instruction::StoreGlobal {
13772                            name: cid(0),
13773                            value: reg(0),
13774                        },
13775                        Instruction::StoreGlobal {
13776                            name: cid(1),
13777                            value: reg(1),
13778                        },
13779                        Instruction::Return { value: reg(0) },
13780                    ],
13781                    Vec::new(),
13782                ),
13783                function(
13784                    1,
13785                    1,
13786                    vec![
13787                        Instruction::StoreGlobal {
13788                            name: cid(2),
13789                            value: reg(0),
13790                        },
13791                        Instruction::Return { value: reg(0) },
13792                    ],
13793                    Vec::new(),
13794                ),
13795                function(
13796                    2,
13797                    6,
13798                    vec![
13799                        Instruction::LoadConst {
13800                            dst: reg(2),
13801                            constant: cid(3),
13802                        },
13803                        Instruction::CreateArray { dst: reg(3) },
13804                        Instruction::ArrayPush {
13805                            array: reg(3),
13806                            value: reg(2),
13807                        },
13808                        Instruction::LoadConst {
13809                            dst: reg(4),
13810                            constant: cid(6),
13811                        },
13812                        Instruction::Call {
13813                            dst: reg(5),
13814                            callee: reg(0),
13815                            this_value: reg(4),
13816                            arguments: reg(3),
13817                        },
13818                        Instruction::LoadConst {
13819                            dst: reg(2),
13820                            constant: cid(4),
13821                        },
13822                        Instruction::CreateArray { dst: reg(3) },
13823                        Instruction::ArrayPush {
13824                            array: reg(3),
13825                            value: reg(2),
13826                        },
13827                        Instruction::Call {
13828                            dst: reg(5),
13829                            callee: reg(1),
13830                            this_value: reg(4),
13831                            arguments: reg(3),
13832                        },
13833                        Instruction::LoadConst {
13834                            dst: reg(2),
13835                            constant: cid(5),
13836                        },
13837                        Instruction::Throw { value: reg(2) },
13838                    ],
13839                    Vec::new(),
13840                ),
13841            ],
13842        );
13843        let mut host = TestHost;
13844        let mut machine = Machine::new(&program, &mut host, Limits::default());
13845        machine.frames.clear();
13846        machine.live_registers = 0;
13847        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
13848            machine
13849                .allocate(HeapEntry::Function {
13850                    module: ModuleId::new(0),
13851                    function: FunctionId::new(function),
13852                    captures: Vec::new(),
13853                    properties: PropertyMap::default(),
13854                    prototype: Some(machine.intrinsics.function_prototype),
13855                    extensible: true,
13856                })
13857                .unwrap()
13858        };
13859        let executor = runtime_function(&mut machine, 1);
13860        let observer = runtime_function(&mut machine, 2);
13861        let then_callback = runtime_function(&mut machine, 3);
13862        let thenable = machine
13863            .allocate(HeapEntry::Object {
13864                properties: PropertyMap::default(),
13865                prototype: Some(machine.intrinsics.object_prototype),
13866                boxed_primitive: None,
13867                extensible: true,
13868            })
13869            .unwrap();
13870        machine
13871            .set_data_property(thenable, "then", then_callback)
13872            .unwrap();
13873
13874        let constructor = machine.intrinsics.global("Promise").unwrap();
13875        let constructor_index = machine.runtime_slot(constructor).unwrap().unwrap();
13876        let HeapEntry::NativeFunction {
13877            callable: NativeCallable::Builtin(constructor_id),
13878            ..
13879        } = machine.heap[constructor_index]
13880        else {
13881            panic!("Promise must be a native constructor");
13882        };
13883        let BuiltinOutcome::Value(promise) = machine
13884            .call_builtin(constructor_id, Value::UNDEFINED, &[executor], true)
13885            .unwrap()
13886        else {
13887            panic!("Promise construction returns a Promise");
13888        };
13889        let resolve = machine
13890            .globals
13891            .get(&EcmaString::from_utf8("resolve"))
13892            .copied()
13893            .unwrap();
13894        let reject = machine
13895            .globals
13896            .get(&EcmaString::from_utf8("reject"))
13897            .copied()
13898            .unwrap();
13899        machine
13900            .call_value(resolve, Value::UNDEFINED, &[thenable])
13901            .unwrap();
13902        let then = machine.get_named_property(promise, "then").unwrap();
13903        machine.call_value(then, promise, &[observer]).unwrap();
13904        machine
13905            .call_value(reject, Value::UNDEFINED, &[Value::int32(9)])
13906            .unwrap();
13907        assert!(
13908            !machine
13909                .globals
13910                .contains_key(&EcmaString::from_utf8("observed"))
13911        );
13912
13913        let drain = machine.drain_microtasks().unwrap();
13914        assert_eq!(drain.executed, 2);
13915        assert!(drain.uncaught.is_empty());
13916        assert_eq!(
13917            machine
13918                .globals
13919                .get(&EcmaString::from_utf8("observed"))
13920                .copied(),
13921            Some(Value::int32(7))
13922        );
13923    }
13924
13925    #[test]
13926    fn queue_microtask_drains_fifo_including_jobs_added_during_drain() {
13927        let program = verified(
13928            vec![
13929                Constant::String(EcmaString::from_utf8("order")),
13930                Constant::String(EcmaString::from_utf8("queueMicrotask")),
13931                Constant::String(EcmaString::from_utf8("third")),
13932                Constant::Int32(1),
13933                Constant::Int32(2),
13934                Constant::Int32(3),
13935                Constant::Undefined,
13936            ],
13937            vec![
13938                function(0, 1, vec![Instruction::Halt], Vec::new()),
13939                function(
13940                    0,
13941                    7,
13942                    vec![
13943                        Instruction::LoadGlobal {
13944                            dst: reg(0),
13945                            name: cid(0),
13946                        },
13947                        Instruction::LoadConst {
13948                            dst: reg(1),
13949                            constant: cid(3),
13950                        },
13951                        Instruction::ArrayPush {
13952                            array: reg(0),
13953                            value: reg(1),
13954                        },
13955                        Instruction::LoadGlobal {
13956                            dst: reg(2),
13957                            name: cid(1),
13958                        },
13959                        Instruction::LoadGlobal {
13960                            dst: reg(3),
13961                            name: cid(2),
13962                        },
13963                        Instruction::CreateArray { dst: reg(4) },
13964                        Instruction::ArrayPush {
13965                            array: reg(4),
13966                            value: reg(3),
13967                        },
13968                        Instruction::LoadConst {
13969                            dst: reg(5),
13970                            constant: cid(6),
13971                        },
13972                        Instruction::Call {
13973                            dst: reg(6),
13974                            callee: reg(2),
13975                            this_value: reg(5),
13976                            arguments: reg(4),
13977                        },
13978                        Instruction::Return { value: reg(1) },
13979                    ],
13980                    Vec::new(),
13981                ),
13982                function(
13983                    0,
13984                    2,
13985                    vec![
13986                        Instruction::LoadGlobal {
13987                            dst: reg(0),
13988                            name: cid(0),
13989                        },
13990                        Instruction::LoadConst {
13991                            dst: reg(1),
13992                            constant: cid(4),
13993                        },
13994                        Instruction::ArrayPush {
13995                            array: reg(0),
13996                            value: reg(1),
13997                        },
13998                        Instruction::Return { value: reg(1) },
13999                    ],
14000                    Vec::new(),
14001                ),
14002                function(
14003                    0,
14004                    2,
14005                    vec![
14006                        Instruction::LoadGlobal {
14007                            dst: reg(0),
14008                            name: cid(0),
14009                        },
14010                        Instruction::LoadConst {
14011                            dst: reg(1),
14012                            constant: cid(5),
14013                        },
14014                        Instruction::ArrayPush {
14015                            array: reg(0),
14016                            value: reg(1),
14017                        },
14018                        Instruction::Return { value: reg(1) },
14019                    ],
14020                    Vec::new(),
14021                ),
14022            ],
14023        );
14024        let mut host = TestHost;
14025        let mut machine = Machine::new(&program, &mut host, Limits::default());
14026        machine.frames.clear();
14027        machine.live_registers = 0;
14028        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14029            machine
14030                .allocate(HeapEntry::Function {
14031                    module: ModuleId::new(0),
14032                    function: FunctionId::new(function),
14033                    captures: Vec::new(),
14034                    properties: PropertyMap::default(),
14035                    prototype: Some(machine.intrinsics.function_prototype),
14036                    extensible: true,
14037                })
14038                .unwrap()
14039        };
14040        let first = runtime_function(&mut machine, 1);
14041        let second = runtime_function(&mut machine, 2);
14042        let third = runtime_function(&mut machine, 3);
14043        let order = machine
14044            .allocate(HeapEntry::Array {
14045                elements: Vec::new(),
14046                properties: PropertyMap::default(),
14047                prototype: Some(machine.intrinsics.array_prototype),
14048                extensible: true,
14049                length_writable: true,
14050            })
14051            .unwrap();
14052        machine
14053            .globals
14054            .insert(EcmaString::from_utf8("order"), order);
14055        machine
14056            .globals
14057            .insert(EcmaString::from_utf8("third"), third);
14058        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14059        machine
14060            .call_value(queue, Value::UNDEFINED, &[first])
14061            .unwrap();
14062        machine
14063            .call_value(queue, Value::UNDEFINED, &[second])
14064            .unwrap();
14065
14066        let drain = machine.drain_microtasks().unwrap();
14067        assert_eq!(drain.executed, 3);
14068        assert!(drain.uncaught.is_empty());
14069        let index = machine.runtime_slot(order).unwrap().unwrap();
14070        let HeapEntry::Array { elements, .. } = &machine.heap[index] else {
14071            panic!("order remains an array");
14072        };
14073        assert_eq!(
14074            elements,
14075            &[Value::int32(1), Value::int32(2), Value::int32(3)]
14076        );
14077    }
14078
14079    #[test]
14080    fn queue_microtask_reports_callback_throws_and_continues() {
14081        let program = verified(
14082            vec![
14083                Constant::Int32(7),
14084                Constant::Int32(1),
14085                Constant::String(EcmaString::from_utf8("observed")),
14086            ],
14087            vec![
14088                function(0, 1, vec![Instruction::Halt], Vec::new()),
14089                function(
14090                    0,
14091                    1,
14092                    vec![
14093                        Instruction::LoadConst {
14094                            dst: reg(0),
14095                            constant: cid(0),
14096                        },
14097                        Instruction::Throw { value: reg(0) },
14098                    ],
14099                    Vec::new(),
14100                ),
14101                function(
14102                    0,
14103                    1,
14104                    vec![
14105                        Instruction::LoadConst {
14106                            dst: reg(0),
14107                            constant: cid(1),
14108                        },
14109                        Instruction::StoreGlobal {
14110                            name: cid(2),
14111                            value: reg(0),
14112                        },
14113                        Instruction::Return { value: reg(0) },
14114                    ],
14115                    Vec::new(),
14116                ),
14117            ],
14118        );
14119        let mut host = TestHost;
14120        let mut machine = Machine::new(&program, &mut host, Limits::default());
14121        machine.frames.clear();
14122        machine.live_registers = 0;
14123        let runtime_function = |machine: &mut Machine<'_, TestHost>, function| {
14124            machine
14125                .allocate(HeapEntry::Function {
14126                    module: ModuleId::new(0),
14127                    function: FunctionId::new(function),
14128                    captures: Vec::new(),
14129                    properties: PropertyMap::default(),
14130                    prototype: Some(machine.intrinsics.function_prototype),
14131                    extensible: true,
14132                })
14133                .unwrap()
14134        };
14135        let throwing = runtime_function(&mut machine, 1);
14136        let observer = runtime_function(&mut machine, 2);
14137        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14138        machine
14139            .call_value(queue, Value::UNDEFINED, &[throwing])
14140            .unwrap();
14141        machine
14142            .call_value(queue, Value::UNDEFINED, &[observer])
14143            .unwrap();
14144
14145        let drain = machine.drain_microtasks().unwrap();
14146        assert_eq!(drain.executed, 2);
14147        assert_eq!(
14148            drain.uncaught,
14149            vec![CallbackException {
14150                value: Value::int32(7),
14151                origin: ThrowOrigin::Bytecode,
14152            }]
14153        );
14154        assert_eq!(
14155            machine
14156                .globals
14157                .get(&EcmaString::from_utf8("observed"))
14158                .copied(),
14159            Some(Value::int32(1))
14160        );
14161    }
14162
14163    #[test]
14164    fn microtask_boundaries_preserve_the_queued_head() {
14165        let program = verified(
14166            vec![Constant::Undefined],
14167            vec![
14168                function(0, 1, vec![Instruction::Halt], Vec::new()),
14169                function(
14170                    0,
14171                    1,
14172                    vec![
14173                        Instruction::LoadConst {
14174                            dst: reg(0),
14175                            constant: cid(0),
14176                        },
14177                        Instruction::Return { value: reg(0) },
14178                    ],
14179                    Vec::new(),
14180                ),
14181            ],
14182        );
14183        let mut host = TestHost;
14184        let mut machine = Machine::new(
14185            &program,
14186            &mut host,
14187            Limits {
14188                max_microtasks: 1,
14189                ..Limits::default()
14190            },
14191        );
14192        machine.frames.clear();
14193        machine.live_registers = 0;
14194        let callback = machine
14195            .allocate(HeapEntry::Function {
14196                module: ModuleId::new(0),
14197                function: FunctionId::new(1),
14198                captures: Vec::new(),
14199                properties: PropertyMap::default(),
14200                prototype: Some(machine.intrinsics.function_prototype),
14201                extensible: true,
14202            })
14203            .unwrap();
14204        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14205        assert!(matches!(
14206            machine.call_value(queue, Value::UNDEFINED, &[Value::int32(1)]),
14207            Err(EvalFailure::Throw(ThrowOrigin::TypeError { .. }))
14208        ));
14209        machine
14210            .call_value(queue, Value::UNDEFINED, &[callback])
14211            .unwrap();
14212        assert!(matches!(
14213            machine.call_value(queue, Value::UNDEFINED, &[callback]),
14214            Err(EvalFailure::Runtime(
14215                RuntimeErrorKind::MicrotaskQueueLimitExceeded { limit: 1 }
14216            ))
14217        ));
14218
14219        let fuel = machine.fuel;
14220        machine.microtask_drain_active = true;
14221        let reentry = machine.drain_microtasks().unwrap_err();
14222        assert!(matches!(
14223            reentry.kind,
14224            RuntimeErrorKind::MicrotaskDrainReentry
14225        ));
14226        assert_eq!(machine.fuel, fuel);
14227        assert_eq!(machine.microtasks.len(), 1);
14228        machine.microtask_drain_active = false;
14229
14230        machine.fuel = 0;
14231        let exhausted = machine.drain_microtasks().unwrap_err();
14232        assert!(matches!(
14233            exhausted.kind,
14234            RuntimeErrorKind::FuelExhausted { .. }
14235        ));
14236        assert!(!machine.microtask_drain_active);
14237        assert_eq!(machine.microtasks.len(), 1);
14238
14239        machine.fuel = 100;
14240        let drain = machine.drain_microtasks().unwrap();
14241        assert_eq!(drain.executed, 1);
14242        assert!(machine.microtasks.is_empty());
14243    }
14244
14245    // ---- timers -----------------------------------------------------------
14246
14247    #[derive(Default)]
14248    struct ManualTimerState {
14249        live: std::collections::BTreeMap<u64, u64>,
14250        reports: std::collections::VecDeque<TimerWakeup>,
14251        scheduled: Vec<(u64, u32)>,
14252        cancelled: Vec<u64>,
14253        fail_schedule: bool,
14254        fail_poll: bool,
14255    }
14256
14257    #[derive(Clone, Default)]
14258    struct ManualTimerProvider {
14259        state: std::rc::Rc<std::cell::RefCell<ManualTimerState>>,
14260    }
14261
14262    impl TimerProvider for ManualTimerProvider {
14263        fn schedule(&mut self, id: u64, delay_ms: u32) -> Result<u64, TimerError> {
14264            let mut state = self.state.borrow_mut();
14265            state.scheduled.push((id, delay_ms));
14266            if state.fail_schedule {
14267                return Err(TimerError::new("manual schedule failure"));
14268            }
14269            let deadline = u64::from(delay_ms);
14270            state.live.insert(id, deadline);
14271            Ok(deadline)
14272        }
14273
14274        fn cancel(&mut self, id: u64) -> Result<bool, TimerError> {
14275            let mut state = self.state.borrow_mut();
14276            state.cancelled.push(id);
14277            Ok(state.live.remove(&id).is_some())
14278        }
14279
14280        fn poll_expired(&mut self, output: &mut Vec<TimerWakeup>) -> Result<(), TimerError> {
14281            let mut state = self.state.borrow_mut();
14282            if state.fail_poll {
14283                return Err(TimerError::new("manual poll failure"));
14284            }
14285            output.extend(state.reports.drain(..));
14286            Ok(())
14287        }
14288
14289        fn wait_expired(&mut self) -> Result<Option<TimerWakeup>, TimerError> {
14290            Ok(self.state.borrow_mut().reports.pop_front())
14291        }
14292
14293        fn has_pending(&self) -> bool {
14294            !self.state.borrow().live.is_empty()
14295        }
14296    }
14297
14298    #[derive(Default)]
14299    struct TimerTestHost {
14300        provider: ManualTimerProvider,
14301    }
14302
14303    impl Host for TimerTestHost {
14304        fn timers(&mut self) -> Option<&mut (dyn TimerProvider + 'static)> {
14305            Some(&mut self.provider)
14306        }
14307    }
14308
14309    fn timer_program() -> Program<Verified> {
14310        verified(
14311            vec![
14312                Constant::String(EcmaString::from_utf8("a")),
14313                Constant::String(EcmaString::from_utf8("b")),
14314                Constant::String(EcmaString::from_utf8("this_seen")),
14315                Constant::String(EcmaString::from_utf8("arg_seen")),
14316                Constant::Int32(1),
14317                Constant::Int32(7),
14318            ],
14319            vec![
14320                function(0, 1, vec![Instruction::Halt], Vec::new()),
14321                function(
14322                    0,
14323                    1,
14324                    vec![
14325                        Instruction::LoadConst {
14326                            dst: reg(0),
14327                            constant: cid(4),
14328                        },
14329                        Instruction::StoreGlobal {
14330                            name: cid(0),
14331                            value: reg(0),
14332                        },
14333                        Instruction::Return { value: reg(0) },
14334                    ],
14335                    Vec::new(),
14336                ),
14337                function(
14338                    0,
14339                    1,
14340                    vec![
14341                        Instruction::LoadConst {
14342                            dst: reg(0),
14343                            constant: cid(4),
14344                        },
14345                        Instruction::StoreGlobal {
14346                            name: cid(1),
14347                            value: reg(0),
14348                        },
14349                        Instruction::Return { value: reg(0) },
14350                    ],
14351                    Vec::new(),
14352                ),
14353                function(
14354                    1,
14355                    2,
14356                    vec![
14357                        Instruction::LoadThis { dst: reg(1) },
14358                        Instruction::StoreGlobal {
14359                            name: cid(2),
14360                            value: reg(1),
14361                        },
14362                        Instruction::StoreGlobal {
14363                            name: cid(3),
14364                            value: reg(0),
14365                        },
14366                        Instruction::Return { value: reg(0) },
14367                    ],
14368                    Vec::new(),
14369                ),
14370                function(
14371                    0,
14372                    1,
14373                    vec![
14374                        Instruction::LoadConst {
14375                            dst: reg(0),
14376                            constant: cid(5),
14377                        },
14378                        Instruction::Throw { value: reg(0) },
14379                    ],
14380                    Vec::new(),
14381                ),
14382            ],
14383        )
14384    }
14385
14386    fn timer_fn(machine: &mut Machine<'_, TimerTestHost>, index: u32) -> Value {
14387        machine
14388            .allocate(HeapEntry::Function {
14389                module: ModuleId::new(0),
14390                function: FunctionId::new(index),
14391                captures: Vec::new(),
14392                properties: PropertyMap::default(),
14393                prototype: Some(machine.intrinsics.function_prototype),
14394                extensible: true,
14395            })
14396            .unwrap()
14397    }
14398
14399    fn read_global(machine: &Machine<'_, TimerTestHost>, name: &str) -> Option<Value> {
14400        machine.globals.get(&EcmaString::from_utf8(name)).copied()
14401    }
14402
14403    fn set_timeout_global(machine: &Machine<'_, TimerTestHost>) -> Value {
14404        machine
14405            .intrinsics
14406            .global("setTimeout")
14407            .expect("setTimeout is installed")
14408    }
14409
14410    fn schedule_nested_timer(
14411        machine: &mut Machine<'_, TimerTestHost>,
14412        _this: Value,
14413        _args: &[Value],
14414        _constructing: bool,
14415    ) -> Result<BuiltinOutcome, EvalFailure> {
14416        let callback = machine
14417            .globals
14418            .get(&EcmaString::from_utf8("nestedCallback"))
14419            .copied()
14420            .expect("test installs nested callback");
14421        let set_timeout = set_timeout_global(machine);
14422        machine.call_value(set_timeout, Value::UNDEFINED, &[callback, Value::int32(1)])?;
14423        Ok(BuiltinOutcome::Value(Value::UNDEFINED))
14424    }
14425
14426    fn timer_native(
14427        machine: &mut Machine<'_, TimerTestHost>,
14428        name: &'static str,
14429        handler: crate::intrinsics::BuiltinHandler<TimerTestHost>,
14430    ) -> Value {
14431        let id = machine
14432            .intrinsics
14433            .builtins
14434            .register(crate::intrinsics::BuiltinDef {
14435                name,
14436                length: 0,
14437                handler,
14438            });
14439        crate::intrinsics::native_function(&mut machine.heap, id, name, 0)
14440    }
14441
14442    #[test]
14443    fn timers_are_absent_without_the_capability() {
14444        let program = timer_program();
14445        let mut host = TestHost;
14446        let mut machine = Machine::new(&program, &mut host, Limits::default());
14447        machine.frames.clear();
14448        machine.live_registers = 0;
14449        assert!(machine.intrinsics.global("setTimeout").is_none());
14450        assert!(machine.intrinsics.global("clearTimeout").is_none());
14451        assert!(!machine.has_pending_timers());
14452        assert_eq!(
14453            machine.run_one_expired_timer().unwrap(),
14454            TimerRun::default()
14455        );
14456        assert!(!machine.wait_for_timer_expiry().unwrap());
14457    }
14458
14459    #[test]
14460    fn set_timeout_rejects_a_non_callable_callback_before_coercion() {
14461        let program = timer_program();
14462        let mut host = TimerTestHost::default();
14463        let shared = host.provider.state.clone();
14464        let mut machine = Machine::new(&program, &mut host, Limits::default());
14465        machine.frames.clear();
14466        machine.live_registers = 0;
14467        let set_timeout = set_timeout_global(&machine);
14468        let failure = machine
14469            .call_value(
14470                set_timeout,
14471                Value::UNDEFINED,
14472                &[Value::int32(3), Value::int32(5)],
14473            )
14474            .unwrap_err();
14475        assert!(matches!(
14476            failure,
14477            EvalFailure::Throw(ThrowOrigin::TypeError { .. })
14478        ));
14479        // Nothing was armed, so no delay coercion or provider call happened.
14480        assert!(shared.borrow().scheduled.is_empty());
14481        assert!(!machine.has_pending_timers());
14482    }
14483
14484    #[test]
14485    fn set_timeout_clamps_and_truncates_like_node() {
14486        let program = timer_program();
14487        let mut host = TimerTestHost::default();
14488        let shared = host.provider.state.clone();
14489        let mut machine = Machine::new(&program, &mut host, Limits::default());
14490        machine.frames.clear();
14491        machine.live_registers = 0;
14492        let set_timeout = set_timeout_global(&machine);
14493        let callback = timer_fn(&mut machine, 1);
14494        for delay in [
14495            Value::int32(0),
14496            Value::number(-5.0),
14497            Value::number(f64::NAN),
14498            Value::number(2_147_483_648.0),
14499            Value::int32(2_147_483_647),
14500            Value::number(3.9),
14501        ] {
14502            machine
14503                .call_value(set_timeout, Value::UNDEFINED, &[callback, delay])
14504                .unwrap();
14505        }
14506        let delays: Vec<u32> = shared.borrow().scheduled.iter().map(|(_, d)| *d).collect();
14507        assert_eq!(delays, vec![1, 1, 1, 1, 2_147_483_647, 3]);
14508        // Ids are minted monotonically from 1 and never reused.
14509        let ids: Vec<u64> = shared
14510            .borrow()
14511            .scheduled
14512            .iter()
14513            .map(|(id, _)| *id)
14514            .collect();
14515        assert_eq!(ids, vec![1, 2, 3, 4, 5, 6]);
14516    }
14517
14518    #[test]
14519    fn same_deadline_timers_run_in_registration_order_despite_reverse_reports() {
14520        let program = timer_program();
14521        let mut host = TimerTestHost::default();
14522        let shared = host.provider.state.clone();
14523        let mut machine = Machine::new(&program, &mut host, Limits::default());
14524        machine.frames.clear();
14525        machine.live_registers = 0;
14526        let set_timeout = set_timeout_global(&machine);
14527        let a = timer_fn(&mut machine, 1);
14528        let b = timer_fn(&mut machine, 2);
14529        machine
14530            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14531            .unwrap();
14532        machine
14533            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(5)])
14534            .unwrap();
14535        // Host reports the later registration first and in split batches.
14536        shared.borrow_mut().reports.push_back(TimerWakeup {
14537            id: 2,
14538            deadline_ms: 5,
14539        });
14540        let first = machine.run_one_expired_timer().unwrap();
14541        assert_eq!(first.executed, 1);
14542        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14543        assert_eq!(read_global(&machine, "b"), None);
14544        let second = machine.run_one_expired_timer().unwrap();
14545        assert_eq!(second.executed, 1);
14546        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14547        assert!(!machine.has_pending_timers());
14548    }
14549
14550    #[test]
14551    fn a_shorter_deadline_beats_an_older_sequence() {
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 a = timer_fn(&mut machine, 1);
14560        let b = timer_fn(&mut machine, 2);
14561        machine
14562            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(5)])
14563            .unwrap();
14564        machine
14565            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14566            .unwrap();
14567        shared.borrow_mut().reports.push_back(TimerWakeup {
14568            id: 1,
14569            deadline_ms: 5,
14570        });
14571        machine.run_one_expired_timer().unwrap();
14572        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14573        assert_eq!(read_global(&machine, "a"), None);
14574    }
14575
14576    #[test]
14577    fn clear_timeout_prevents_a_ready_timer_and_ignores_stale_ids() {
14578        let program = timer_program();
14579        let mut host = TimerTestHost::default();
14580        let shared = host.provider.state.clone();
14581        let mut machine = Machine::new(&program, &mut host, Limits::default());
14582        machine.frames.clear();
14583        machine.live_registers = 0;
14584        let set_timeout = set_timeout_global(&machine);
14585        let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14586        let a = timer_fn(&mut machine, 1);
14587        let b = timer_fn(&mut machine, 2);
14588        let handle_a = machine
14589            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14590            .unwrap();
14591        machine
14592            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(3)])
14593            .unwrap();
14594        // Clear the first timer even though the host already reported it.
14595        shared.borrow_mut().reports.push_back(TimerWakeup {
14596            id: 1,
14597            deadline_ms: 3,
14598        });
14599        machine
14600            .call_value(clear_timeout, Value::UNDEFINED, &[handle_a])
14601            .unwrap();
14602        assert!(shared.borrow().cancelled.contains(&1));
14603        // A stale positive-integer id must not cancel the surviving timer.
14604        machine
14605            .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14606            .unwrap();
14607        shared.borrow_mut().reports.push_back(TimerWakeup {
14608            id: 2,
14609            deadline_ms: 3,
14610        });
14611        let run = machine.run_one_expired_timer().unwrap();
14612        assert_eq!(run.executed, 1);
14613        assert_eq!(read_global(&machine, "a"), None);
14614        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14615    }
14616
14617    #[test]
14618    fn clear_timeout_accepts_a_direct_positive_integer_id() {
14619        let program = timer_program();
14620        let mut host = TimerTestHost::default();
14621        let shared = host.provider.state.clone();
14622        let mut machine = Machine::new(&program, &mut host, Limits::default());
14623        machine.frames.clear();
14624        machine.live_registers = 0;
14625        let set_timeout = set_timeout_global(&machine);
14626        let clear_timeout = machine.intrinsics.global("clearTimeout").unwrap();
14627        let a = timer_fn(&mut machine, 1);
14628        machine
14629            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14630            .unwrap();
14631        machine
14632            .call_value(clear_timeout, Value::UNDEFINED, &[Value::int32(1)])
14633            .unwrap();
14634        assert!(!machine.has_pending_timers());
14635        shared.borrow_mut().reports.push_back(TimerWakeup {
14636            id: 1,
14637            deadline_ms: 3,
14638        });
14639        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 0);
14640
14641        machine.next_timer_id = Some(u64::MAX);
14642        let handle = machine
14643            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(3)])
14644            .unwrap();
14645        machine
14646            .call_value(
14647                clear_timeout,
14648                Value::UNDEFINED,
14649                &[Value::number(u64::MAX as f64)],
14650            )
14651            .unwrap();
14652        assert!(machine.has_pending_timers());
14653        machine
14654            .call_value(clear_timeout, Value::UNDEFINED, &[handle])
14655            .unwrap();
14656        assert!(!machine.has_pending_timers());
14657        // A no-op clear of an unrelated value never coerces or errors.
14658        machine
14659            .call_value(clear_timeout, Value::UNDEFINED, &[Value::UNDEFINED])
14660            .unwrap();
14661    }
14662
14663    #[test]
14664    fn timer_callback_receives_trailing_args_and_the_handle_as_this() {
14665        let program = timer_program();
14666        let mut host = TimerTestHost::default();
14667        let shared = host.provider.state.clone();
14668        let mut machine = Machine::new(&program, &mut host, Limits::default());
14669        machine.frames.clear();
14670        machine.live_registers = 0;
14671        let set_timeout = set_timeout_global(&machine);
14672        let callback = timer_fn(&mut machine, 3);
14673        let handle = machine
14674            .call_value(
14675                set_timeout,
14676                Value::UNDEFINED,
14677                &[callback, Value::int32(1), Value::int32(42)],
14678            )
14679            .unwrap();
14680        shared.borrow_mut().reports.push_back(TimerWakeup {
14681            id: 1,
14682            deadline_ms: 1,
14683        });
14684        machine.run_one_expired_timer().unwrap();
14685        assert_eq!(read_global(&machine, "this_seen"), Some(handle));
14686        assert_eq!(read_global(&machine, "arg_seen"), Some(Value::int32(42)));
14687    }
14688
14689    #[test]
14690    fn a_callback_created_timer_waits_for_a_later_checkpoint() {
14691        let program = timer_program();
14692        let mut host = TimerTestHost::default();
14693        let shared = host.provider.state.clone();
14694        let mut machine = Machine::new(&program, &mut host, Limits::default());
14695        machine.frames.clear();
14696        machine.live_registers = 0;
14697        let set_timeout = set_timeout_global(&machine);
14698        let nested = timer_fn(&mut machine, 2);
14699        machine
14700            .globals
14701            .insert(EcmaString::from_utf8("nestedCallback"), nested);
14702        let creator = timer_native(&mut machine, "schedule nested", schedule_nested_timer);
14703        machine
14704            .call_value(set_timeout, Value::UNDEFINED, &[creator, Value::int32(1)])
14705            .unwrap();
14706        shared.borrow_mut().reports.push_back(TimerWakeup {
14707            id: 1,
14708            deadline_ms: 1,
14709        });
14710        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14711        assert_eq!(read_global(&machine, "b"), None);
14712        assert!(machine.has_pending_timers());
14713        // Even if the provider can report it immediately, it runs only in a
14714        // later explicit timer checkpoint.
14715        shared.borrow_mut().reports.push_back(TimerWakeup {
14716            id: 2,
14717            deadline_ms: 1,
14718        });
14719        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14720        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14721    }
14722
14723    #[test]
14724    fn timer_callback_throw_is_reported_and_a_runtime_failure_propagates() {
14725        let program = timer_program();
14726        let mut host = TimerTestHost::default();
14727        let shared = host.provider.state.clone();
14728        let mut machine = Machine::new(&program, &mut host, Limits::default());
14729        machine.frames.clear();
14730        machine.live_registers = 0;
14731        let set_timeout = set_timeout_global(&machine);
14732        let thrower = timer_fn(&mut machine, 4);
14733        machine
14734            .call_value(set_timeout, Value::UNDEFINED, &[thrower, Value::int32(1)])
14735            .unwrap();
14736        shared.borrow_mut().reports.push_back(TimerWakeup {
14737            id: 1,
14738            deadline_ms: 1,
14739        });
14740        let run = machine.run_one_expired_timer().unwrap();
14741        assert_eq!(run.executed, 1);
14742        assert_eq!(
14743            run.uncaught,
14744            vec![CallbackException {
14745                value: Value::int32(7),
14746                origin: ThrowOrigin::Bytecode
14747            }]
14748        );
14749
14750        // A runtime failure inside the callback stops the checkpoint.
14751        let another = timer_fn(&mut machine, 1);
14752        machine
14753            .call_value(set_timeout, Value::UNDEFINED, &[another, Value::int32(1)])
14754            .unwrap();
14755        shared.borrow_mut().reports.push_back(TimerWakeup {
14756            id: 2,
14757            deadline_ms: 1,
14758        });
14759        machine.fuel = 1;
14760        let error = machine.run_one_expired_timer().unwrap_err();
14761        assert!(matches!(error.kind, RuntimeErrorKind::FuelExhausted { .. }));
14762    }
14763
14764    #[test]
14765    fn a_timer_checkpoint_never_drains_microtasks() {
14766        let program = timer_program();
14767        let mut host = TimerTestHost::default();
14768        let shared = host.provider.state.clone();
14769        let mut machine = Machine::new(&program, &mut host, Limits::default());
14770        machine.frames.clear();
14771        machine.live_registers = 0;
14772        let set_timeout = set_timeout_global(&machine);
14773        let queue = machine.intrinsics.global("queueMicrotask").unwrap();
14774        let a = timer_fn(&mut machine, 1);
14775        let b = timer_fn(&mut machine, 2);
14776        machine
14777            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14778            .unwrap();
14779        machine.call_value(queue, Value::UNDEFINED, &[b]).unwrap();
14780        shared.borrow_mut().reports.push_back(TimerWakeup {
14781            id: 1,
14782            deadline_ms: 1,
14783        });
14784        let run = machine.run_one_expired_timer().unwrap();
14785        assert_eq!(run.executed, 1);
14786        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14787        assert_eq!(read_global(&machine, "b"), None);
14788        assert_eq!(machine.microtasks.len(), 1);
14789        machine.drain_microtasks().unwrap();
14790        assert_eq!(read_global(&machine, "b"), Some(Value::int32(1)));
14791    }
14792
14793    #[test]
14794    fn timer_reentry_capacity_and_fuel_preserve_state() {
14795        let program = timer_program();
14796        let mut host = TimerTestHost::default();
14797        let shared = host.provider.state.clone();
14798        let mut machine = Machine::new(
14799            &program,
14800            &mut host,
14801            Limits {
14802                max_timers: 1,
14803                ..Limits::default()
14804            },
14805        );
14806        machine.frames.clear();
14807        machine.live_registers = 0;
14808        let set_timeout = set_timeout_global(&machine);
14809        let a = timer_fn(&mut machine, 1);
14810        let b = timer_fn(&mut machine, 2);
14811        machine
14812            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14813            .unwrap();
14814        // Capacity is enforced before any provider or table mutation.
14815        let capacity = machine
14816            .call_value(set_timeout, Value::UNDEFINED, &[b, Value::int32(1)])
14817            .unwrap_err();
14818        assert!(matches!(
14819            capacity,
14820            EvalFailure::Runtime(RuntimeErrorKind::TimerCapacityExceeded { limit: 1 })
14821        ));
14822        assert_eq!(shared.borrow().scheduled.len(), 1);
14823
14824        // Reentry fails without consuming fuel or touching the ready timer.
14825        shared.borrow_mut().reports.push_back(TimerWakeup {
14826            id: 1,
14827            deadline_ms: 1,
14828        });
14829        machine.timer_checkpoint_active = true;
14830        let fuel = machine.fuel;
14831        let reentry = machine.run_one_expired_timer().unwrap_err();
14832        assert!(matches!(
14833            reentry.kind,
14834            RuntimeErrorKind::TimerCheckpointReentry
14835        ));
14836        assert_eq!(machine.fuel, fuel);
14837        machine.timer_checkpoint_active = false;
14838
14839        // Fuel is charged before the live record is removed.
14840        machine.fuel = 0;
14841        let exhausted = machine.run_one_expired_timer().unwrap_err();
14842        assert!(matches!(
14843            exhausted.kind,
14844            RuntimeErrorKind::FuelExhausted { .. }
14845        ));
14846        assert!(machine.has_pending_timers());
14847        machine.fuel = 100;
14848        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14849        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14850    }
14851
14852    #[test]
14853    fn a_failed_schedule_never_reuses_its_timer_id() {
14854        let program = timer_program();
14855        let mut host = TimerTestHost::default();
14856        let shared = host.provider.state.clone();
14857        let mut machine = Machine::new(&program, &mut host, Limits::default());
14858        machine.frames.clear();
14859        machine.live_registers = 0;
14860        let set_timeout = set_timeout_global(&machine);
14861        let a = timer_fn(&mut machine, 1);
14862        shared.borrow_mut().fail_schedule = true;
14863        let failure = machine
14864            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14865            .unwrap_err();
14866        assert!(matches!(
14867            failure,
14868            EvalFailure::Runtime(RuntimeErrorKind::TimerProviderFailure { .. })
14869        ));
14870        shared.borrow_mut().fail_schedule = false;
14871        machine
14872            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14873            .unwrap();
14874        let ids: Vec<u64> = shared
14875            .borrow()
14876            .scheduled
14877            .iter()
14878            .map(|(id, _)| *id)
14879            .collect();
14880        assert_eq!(ids, vec![1, 2]);
14881    }
14882
14883    #[test]
14884    fn wait_for_timer_expiry_promotes_a_reported_timer() {
14885        let program = timer_program();
14886        let mut host = TimerTestHost::default();
14887        let shared = host.provider.state.clone();
14888        let mut machine = Machine::new(&program, &mut host, Limits::default());
14889        machine.frames.clear();
14890        machine.live_registers = 0;
14891        assert!(!machine.wait_for_timer_expiry().unwrap());
14892        let set_timeout = set_timeout_global(&machine);
14893        let a = timer_fn(&mut machine, 1);
14894        machine
14895            .call_value(set_timeout, Value::UNDEFINED, &[a, Value::int32(1)])
14896            .unwrap();
14897        shared.borrow_mut().reports.push_back(TimerWakeup {
14898            id: 1,
14899            deadline_ms: 1,
14900        });
14901        assert!(machine.wait_for_timer_expiry().unwrap());
14902        assert_eq!(machine.run_one_expired_timer().unwrap().executed, 1);
14903        assert_eq!(read_global(&machine, "a"), Some(Value::int32(1)));
14904    }
14905}