Skip to main content

zapcode_core/vm/
mod.rs

1use std::collections::{HashMap, HashSet};
2use std::sync::Arc;
3
4use indexmap::IndexMap;
5
6use crate::compiler::instruction::{Constant, Instruction};
7use crate::compiler::CompiledProgram;
8use crate::error::{Result, ZapcodeError};
9use crate::sandbox::{ResourceLimits, ResourceTracker};
10use crate::snapshot::ZapcodeSnapshot;
11use crate::trace::{ExecutionTrace, SpanBuilder, TraceStatus};
12use crate::value::{Closure, FunctionId, GeneratorObject, SuspendedFrame, Value};
13
14mod builtins;
15
16/// The result of VM execution.
17#[derive(Debug)]
18pub enum VmState {
19    Complete(Value),
20    Suspended {
21        function_name: String,
22        args: Vec<Value>,
23        snapshot: ZapcodeSnapshot,
24    },
25}
26
27/// Tracks where a method receiver originated so that mutations to `this`
28/// inside the method can be written back to the source variable.
29#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
30pub(crate) enum ReceiverSource {
31    /// The receiver was loaded from a global variable with the given name.
32    Global(String),
33    /// The receiver was loaded from a local variable at the given slot index
34    /// in the frame at the given depth (index into `self.frames`).
35    Local { frame_index: usize, slot: usize },
36}
37
38/// A call frame in the VM stack.
39#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
40pub(crate) struct CallFrame {
41    pub(crate) func_index: Option<usize>,
42    pub(crate) ip: usize,
43    pub(crate) locals: Vec<Value>,
44    pub(crate) stack_base: usize,
45    /// The `this` value for method/constructor calls.
46    pub(crate) this_value: Option<Value>,
47    /// Where the method receiver came from, so we can write back mutations.
48    pub(crate) receiver_source: Option<ReceiverSource>,
49}
50
51/// A continuation for array callback methods that may suspend (e.g., `.map()` with async callbacks).
52/// Instead of running callbacks in a Rust for-loop (which can't be suspended), the continuation
53/// tracks progress so the main `execute()` loop can drive iteration one callback at a time.
54#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
55pub(crate) enum Continuation {
56    /// Collecting `.map()` results element-by-element.
57    ArrayMap {
58        callback: Value,
59        source: Vec<Value>,
60        results: Vec<Value>,
61        next_index: usize,
62        /// Frame depth of the caller — the continuation fires when
63        /// we return to this depth AND the callback's frame has been popped.
64        caller_frame_depth: usize,
65        /// The frame index of the currently-executing callback. Only when
66        /// this specific frame is popped does the continuation advance.
67        callback_frame_index: usize,
68    },
69    /// Collecting `.forEach()` calls element-by-element.
70    ArrayForEach {
71        callback: Value,
72        source: Vec<Value>,
73        next_index: usize,
74        caller_frame_depth: usize,
75        callback_frame_index: usize,
76    },
77}
78
79/// The Zapcode VM.
80pub struct Vm {
81    pub(crate) program: CompiledProgram,
82    pub(crate) stack: Vec<Value>,
83    pub(crate) frames: Vec<CallFrame>,
84    pub(crate) globals: HashMap<String, Value>,
85    pub(crate) stdout: String,
86    pub(crate) limits: ResourceLimits,
87    pub(crate) tracker: ResourceTracker,
88    pub(crate) external_functions: HashSet<String>,
89    pub(crate) try_stack: Vec<TryInfo>,
90    /// Active continuations for array callback methods that may suspend.
91    pub(crate) continuations: Vec<Continuation>,
92    /// The last object a property was accessed on — used for method dispatch.
93    last_receiver: Option<Value>,
94    /// Where the last receiver came from — used to write back `this` mutations.
95    last_receiver_source: Option<ReceiverSource>,
96    /// The name of the last global loaded — used to identify known globals.
97    last_global_name: Option<String>,
98    /// Tracks the source of the most recent Load instruction for receiver tracking.
99    last_load_source: Option<ReceiverSource>,
100    /// Counter for assigning unique generator IDs.
101    next_generator_id: u64,
102}
103
104#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
105pub(crate) struct TryInfo {
106    pub(crate) catch_ip: usize,
107    pub(crate) frame_depth: usize,
108    pub(crate) stack_depth: usize,
109}
110
111impl Vm {
112    fn new(
113        program: CompiledProgram,
114        limits: ResourceLimits,
115        external_functions: HashSet<String>,
116    ) -> Self {
117        let mut globals = HashMap::new();
118
119        // Register built-in globals
120        builtins::register_globals(&mut globals);
121
122        Self {
123            program,
124            stack: Vec::new(),
125            frames: Vec::new(),
126            globals,
127            stdout: String::new(),
128            limits,
129            tracker: ResourceTracker::default(),
130            external_functions,
131            try_stack: Vec::new(),
132            continuations: Vec::new(),
133            last_receiver: None,
134            last_receiver_source: None,
135            last_global_name: None,
136            last_load_source: None,
137            next_generator_id: 0,
138        }
139    }
140
141    /// Names of all builtin globals registered by `register_globals`.
142    pub(crate) const BUILTIN_GLOBAL_NAMES: &'static [&'static str] =
143        &["console", "JSON", "Object", "Array", "Math", "Promise"];
144
145    /// Restore a VM from snapshot state and continue execution.
146    /// Builtins are re-registered after restoring user globals.
147    /// The return_value is pushed onto the stack (result of the external call).
148    #[allow(clippy::too_many_arguments)]
149    pub(crate) fn from_snapshot(
150        program: CompiledProgram,
151        stack: Vec<Value>,
152        frames: Vec<CallFrame>,
153        user_globals: HashMap<String, Value>,
154        try_stack: Vec<TryInfo>,
155        continuations: Vec<Continuation>,
156        stdout: String,
157        limits: ResourceLimits,
158        external_functions: HashSet<String>,
159    ) -> Self {
160        let mut globals = HashMap::new();
161        // Re-register builtins first
162        builtins::register_globals(&mut globals);
163        // Then overlay user globals (user globals take precedence if names collide)
164        for (k, v) in user_globals {
165            globals.insert(k, v);
166        }
167
168        Self {
169            program,
170            stack,
171            frames,
172            globals,
173            stdout,
174            limits,
175            tracker: ResourceTracker::default(),
176            external_functions,
177            try_stack,
178            continuations,
179            last_receiver: None,
180            last_receiver_source: None,
181            last_global_name: None,
182            last_load_source: None,
183            next_generator_id: 0,
184        }
185    }
186
187    /// Resume execution after a snapshot restore. The return value from
188    /// the external function should already be pushed onto the stack.
189    pub(crate) fn resume_execution(&mut self) -> Result<VmState> {
190        self.tracker.start();
191        self.execute()
192    }
193
194    fn push(&mut self, value: Value) -> Result<()> {
195        self.tracker.track_allocation(&self.limits)?;
196        self.stack.push(value);
197        Ok(())
198    }
199
200    fn pop(&mut self) -> Result<Value> {
201        self.stack
202            .pop()
203            .ok_or_else(|| ZapcodeError::RuntimeError("stack underflow".to_string()))
204    }
205
206    fn peek(&self) -> Result<&Value> {
207        self.stack
208            .last()
209            .ok_or_else(|| ZapcodeError::RuntimeError("stack underflow".to_string()))
210    }
211
212    fn current_frame(&self) -> &CallFrame {
213        // Frames are always non-empty during execution (run() pushes the initial frame).
214        // This is an internal invariant, not reachable by guest code.
215        self.frames.last().expect("internal error: no active frame")
216    }
217
218    fn current_frame_mut(&mut self) -> &mut CallFrame {
219        self.frames
220            .last_mut()
221            .expect("internal error: no active frame")
222    }
223
224    #[allow(dead_code)]
225    fn instructions(&self) -> &[Instruction] {
226        match self.current_frame().func_index {
227            Some(idx) => &self.program.functions[idx].instructions,
228            None => &self.program.instructions,
229        }
230    }
231
232    /// Build the locals vec by binding `args` to the function's declared `params`.
233    /// Handles positional, rest, and default-value patterns.
234    fn bind_params(params: &[ParamPattern], args: &[Value], local_count: usize) -> Vec<Value> {
235        let mut locals = Vec::with_capacity(local_count);
236        for (i, param) in params.iter().enumerate() {
237            match param {
238                ParamPattern::Ident(_) => {
239                    locals.push(args.get(i).cloned().unwrap_or(Value::Undefined));
240                }
241                ParamPattern::Rest(_) => {
242                    let rest: Vec<Value> = args[i..].to_vec();
243                    locals.push(Value::Array(rest));
244                }
245                ParamPattern::DefaultValue { .. } => {
246                    let val = args.get(i).cloned().unwrap_or(Value::Undefined);
247                    // Keep Undefined so the compiler-emitted default init can fire
248                    locals.push(val);
249                }
250                _ => {
251                    locals.push(args.get(i).cloned().unwrap_or(Value::Undefined));
252                }
253            }
254        }
255        locals
256    }
257
258    /// Common setup for calling a closure: inject captures, bind params, push frame.
259    fn push_call_frame(
260        &mut self,
261        closure: &Closure,
262        args: &[Value],
263        this_value: Option<Value>,
264    ) -> Result<()> {
265        self.tracker.push_frame();
266        self.tracker.check_stack(&self.limits)?;
267
268        // Inject captured variables as globals
269        for (name, val) in &closure.captured {
270            if !self.globals.contains_key(name) {
271                self.globals.insert(name.clone(), val.clone());
272            }
273        }
274
275        let func = &self.program.functions[closure.func_id.0];
276        let locals = Self::bind_params(&func.params, args, func.local_count);
277
278        // If this is a method call (has this_value from a receiver), transfer
279        // the receiver source so we can write back mutations on return.
280        let receiver_source = if this_value.is_some() {
281            self.last_receiver_source.take()
282        } else {
283            self.last_receiver_source = None;
284            None
285        };
286
287        self.frames.push(CallFrame {
288            func_index: Some(closure.func_id.0),
289            ip: 0,
290            locals,
291            stack_base: self.stack.len(),
292            this_value,
293            receiver_source,
294        });
295        Ok(())
296    }
297
298    fn run(&mut self) -> Result<VmState> {
299        self.tracker.start();
300
301        // Set up top-level frame
302        self.frames.push(CallFrame {
303            func_index: None,
304            ip: 0,
305            locals: Vec::new(),
306            stack_base: 0,
307            this_value: None,
308            receiver_source: None,
309        });
310
311        self.execute()
312    }
313
314    fn execute(&mut self) -> Result<VmState> {
315        loop {
316            // Resource checks
317            self.tracker.check_time(&self.limits)?;
318
319            let frame = self.frames.last().unwrap();
320            let instructions = match frame.func_index {
321                Some(idx) => &self.program.functions[idx].instructions,
322                None => &self.program.instructions,
323            };
324
325            if frame.ip >= instructions.len() {
326                // End of function/program
327                if self.frames.len() <= 1 {
328                    // Top-level: return last value on stack or undefined
329                    let result = if self.stack.is_empty() {
330                        Value::Undefined
331                    } else {
332                        self.stack.pop().unwrap_or(Value::Undefined)
333                    };
334                    return Ok(VmState::Complete(result));
335                } else {
336                    // Return from function
337                    let frame = self.frames.pop().unwrap();
338                    self.tracker.pop_frame();
339                    // If this was a constructor, return `this`
340                    if let Some(this_val) = frame.this_value {
341                        self.stack.truncate(frame.stack_base);
342                        self.push(this_val)?;
343                    } else {
344                        self.push(Value::Undefined)?;
345                    }
346                    // Check if a continuation callback just completed
347                    self.process_continuation()?;
348                    continue;
349                }
350            }
351
352            let instr = instructions[frame.ip].clone();
353            let result = self.dispatch(instr);
354
355            match result {
356                Ok(Some(state)) => return Ok(state),
357                Ok(None) => {
358                    // After dispatch, check if a continuation callback returned
359                    // (via Return instruction or ip overflow)
360                    if self.process_continuation()? {
361                        continue;
362                    }
363                }
364                Err(err) => {
365                    // Try to catch the error
366                    if let Some(try_info) = self.try_stack.pop() {
367                        // Unwind to catch block
368                        while self.frames.len() > try_info.frame_depth {
369                            self.frames.pop();
370                            self.tracker.pop_frame();
371                        }
372                        self.stack.truncate(try_info.stack_depth);
373
374                        // Push error value
375                        let error_val = Value::String(Arc::from(err.to_string()));
376                        self.push(error_val)?;
377
378                        // Jump to catch
379                        self.current_frame_mut().ip = try_info.catch_ip;
380                    } else {
381                        return Err(err);
382                    }
383                }
384            }
385        }
386    }
387
388    /// Process the top continuation if the current frame depth indicates a callback
389    /// has returned. Returns `true` if a continuation was processed (caller should
390    /// `continue` the execute loop).
391    fn process_continuation(&mut self) -> Result<bool> {
392        let cont = match self.continuations.last() {
393            Some(c) => c,
394            None => return Ok(false),
395        };
396
397        // Check if the callback's specific frame has been popped — only then
398        // has the callback returned. This avoids false triggers when inner
399        // helper functions return to the same depth.
400        let (callback_frame_index, caller_frame_depth) = match cont {
401            Continuation::ArrayMap {
402                callback_frame_index,
403                caller_frame_depth,
404                ..
405            } => (*callback_frame_index, *caller_frame_depth),
406            Continuation::ArrayForEach {
407                callback_frame_index,
408                caller_frame_depth,
409                ..
410            } => (*callback_frame_index, *caller_frame_depth),
411        };
412
413        // The callback frame is still active — not done yet
414        if self.frames.len() > callback_frame_index {
415            return Ok(false);
416        }
417
418        // Guard against stale continuations on stack unwinds — we must be
419        // back at the original caller's frame depth.
420        if self.frames.len() != caller_frame_depth {
421            return Ok(false);
422        }
423
424        // The callback just returned — collect its result from the stack.
425        // The compiler always emits PushUndefined+Return for implicit returns,
426        // so an empty stack here indicates a VM bug.
427        let callback_result = self.pop()?;
428
429        // Unwrap internal promise values: async callbacks return
430        // {__promise__: true, status: "resolved", value: X} or {status: "rejected", ...}.
431        // Only unwrap objects with the __promise__ marker to avoid mangling user objects.
432        let callback_result = if let Value::Object(ref map) = callback_result {
433            if !matches!(map.get("__promise__"), Some(Value::Bool(true))) {
434                // Not an internal promise — leave untouched
435                callback_result
436            } else {
437                match map.get("status") {
438                    Some(Value::String(s)) if s.as_ref() == "resolved" => {
439                        map.get("value").cloned().unwrap_or(Value::Undefined)
440                    }
441                    Some(Value::String(s)) if s.as_ref() == "rejected" => {
442                        let reason = map.get("reason").cloned().unwrap_or(Value::Undefined);
443                        // Clean up the continuation before returning error
444                        self.continuations.pop();
445                        return Err(ZapcodeError::RuntimeError(format!(
446                            "Unhandled promise rejection: {}",
447                            reason.to_js_string()
448                        )));
449                    }
450                    _ => callback_result,
451                }
452            }
453        } else {
454            callback_result
455        };
456
457        // Pop the continuation, take ownership to avoid cloning results
458        let cont = self.continuations.pop().unwrap();
459
460        match cont {
461            Continuation::ArrayMap {
462                callback,
463                source,
464                mut results,
465                next_index,
466                caller_frame_depth,
467                ..
468            } => {
469                results.push(callback_result);
470                let next = next_index + 1;
471
472                if next < source.len() {
473                    // Set up next callback call
474                    let item = source[next].clone();
475                    let closure = match &callback {
476                        Value::Function(c) => c.clone(),
477                        _ => unreachable!("callback validated at start"),
478                    };
479                    self.push_call_frame(&closure, &[item, Value::Int(next as i64)], None)?;
480                    let new_frame_index = self.frames.len() - 1;
481                    // Push updated continuation back
482                    self.continuations.push(Continuation::ArrayMap {
483                        callback,
484                        source,
485                        results,
486                        next_index: next,
487                        caller_frame_depth,
488                        callback_frame_index: new_frame_index,
489                    });
490                    Ok(true)
491                } else {
492                    // All done — push final array, no clone needed
493                    self.push(Value::Array(results))?;
494                    Ok(true)
495                }
496            }
497            Continuation::ArrayForEach {
498                callback,
499                source,
500                next_index,
501                caller_frame_depth,
502                ..
503            } => {
504                let next = next_index + 1;
505
506                if next < source.len() {
507                    let item = source[next].clone();
508                    let closure = match &callback {
509                        Value::Function(c) => c.clone(),
510                        _ => unreachable!("callback validated at start"),
511                    };
512                    self.push_call_frame(&closure, &[item, Value::Int(next as i64)], None)?;
513                    let new_frame_index = self.frames.len() - 1;
514                    self.continuations.push(Continuation::ArrayForEach {
515                        callback,
516                        source,
517                        next_index: next,
518                        caller_frame_depth,
519                        callback_frame_index: new_frame_index,
520                    });
521                    Ok(true)
522                } else {
523                    self.push(Value::Undefined)?;
524                    Ok(true)
525                }
526            }
527        }
528    }
529
530    /// Call a function value with the given arguments and run it to completion.
531    /// Returns the function's return value.
532    fn call_function_internal(&mut self, callee: &Value, args: Vec<Value>) -> Result<Value> {
533        let closure = match callee {
534            Value::Function(c) => c.clone(),
535            other => {
536                return Err(ZapcodeError::TypeError(format!(
537                    "{} is not a function",
538                    other.to_js_string()
539                )));
540            }
541        };
542
543        let target_frame_depth = self.frames.len();
544        self.push_call_frame(&closure, &args, None)?;
545
546        // Run until the new frame returns
547        loop {
548            self.tracker.check_time(&self.limits)?;
549
550            let frame = self.frames.last().unwrap();
551            let instructions = match frame.func_index {
552                Some(idx) => &self.program.functions[idx].instructions,
553                None => &self.program.instructions,
554            };
555
556            if frame.ip >= instructions.len() {
557                // End of function without explicit return
558                if self.frames.len() > target_frame_depth + 1 {
559                    // Inner function ended, pop and continue
560                    self.frames.pop();
561                    self.tracker.pop_frame();
562                    self.push(Value::Undefined)?;
563                    continue;
564                } else {
565                    // Our target function ended
566                    self.frames.pop();
567                    self.tracker.pop_frame();
568                    return Ok(Value::Undefined);
569                }
570            }
571
572            let instr = instructions[frame.ip].clone();
573            let result = self.dispatch(instr);
574
575            match result {
576                Ok(Some(VmState::Complete(val))) => {
577                    // A return happened that completed the top-level program.
578                    // This shouldn't happen inside a callback but handle gracefully.
579                    return Ok(val);
580                }
581                Ok(Some(VmState::Suspended { .. })) => {
582                    return Err(ZapcodeError::RuntimeError(
583                        "cannot suspend inside array callback".to_string(),
584                    ));
585                }
586                Ok(None) => {
587                    // Check if the frame was popped by a Return instruction
588                    if self.frames.len() == target_frame_depth {
589                        // The function returned; return value is on the stack
590                        return Ok(self.pop().unwrap_or(Value::Undefined));
591                    }
592                }
593                Err(err) => {
594                    // Try to catch the error within the callback
595                    if let Some(try_info) = self.try_stack.pop() {
596                        while self.frames.len() > try_info.frame_depth {
597                            self.frames.pop();
598                            self.tracker.pop_frame();
599                        }
600                        self.stack.truncate(try_info.stack_depth);
601                        let error_val = Value::String(Arc::from(err.to_string()));
602                        self.push(error_val)?;
603                        self.current_frame_mut().ip = try_info.catch_ip;
604                    } else {
605                        return Err(err);
606                    }
607                }
608            }
609        }
610    }
611
612    /// Call a callback for each array element. Passes (item, index) — the full
613    /// array reference (3rd JS argument) is only built lazily if the callback
614    /// actually uses 3+ params, avoiding O(n²) cloning.
615    fn call_element_callback(
616        &mut self,
617        callback: &Value,
618        item: &Value,
619        index: usize,
620    ) -> Result<Value> {
621        self.call_function_internal(callback, vec![item.clone(), Value::Int(index as i64)])
622    }
623
624    /// Check if a callback value is an async function that might suspend.
625    fn is_async_callback(&self, callback: &Value) -> bool {
626        if let Value::Function(closure) = callback {
627            if let Some(func) = self.program.functions.get(closure.func_id.0) {
628                return func.is_async;
629            }
630        }
631        false
632    }
633
634    /// Start a continuation-based `.map()` call: push the continuation and set up
635    /// the first callback invocation. Returns `None` to signal that the main
636    /// `execute()` loop should drive the iteration.
637    fn start_continuation_map(
638        &mut self,
639        callback: Value,
640        arr: Vec<Value>,
641    ) -> Result<Option<Value>> {
642        if arr.is_empty() {
643            return Ok(Some(Value::Array(Vec::new())));
644        }
645
646        // Validate callback type BEFORE pushing continuation
647        let closure = match &callback {
648            Value::Function(c) => c.clone(),
649            _ => {
650                return Err(ZapcodeError::TypeError(
651                    "map callback is not a function".to_string(),
652                ))
653            }
654        };
655
656        let caller_frame_depth = self.frames.len();
657        let first_item = arr[0].clone();
658
659        self.push_call_frame(&closure, &[first_item, Value::Int(0)], None)?;
660        let callback_frame_index = self.frames.len() - 1;
661
662        self.continuations.push(Continuation::ArrayMap {
663            callback,
664            source: arr,
665            results: Vec::new(),
666            next_index: 0,
667            caller_frame_depth,
668            callback_frame_index,
669        });
670
671        Ok(None) // Signal: continuation in progress
672    }
673
674    /// Start a continuation-based `.forEach()` call.
675    fn start_continuation_foreach(
676        &mut self,
677        callback: Value,
678        arr: Vec<Value>,
679    ) -> Result<Option<Value>> {
680        if arr.is_empty() {
681            return Ok(Some(Value::Undefined));
682        }
683
684        // Validate callback type BEFORE pushing continuation
685        let closure = match &callback {
686            Value::Function(c) => c.clone(),
687            _ => {
688                return Err(ZapcodeError::TypeError(
689                    "forEach callback is not a function".to_string(),
690                ))
691            }
692        };
693
694        let caller_frame_depth = self.frames.len();
695        let first_item = arr[0].clone();
696
697        self.push_call_frame(&closure, &[first_item, Value::Int(0)], None)?;
698        let callback_frame_index = self.frames.len() - 1;
699
700        self.continuations.push(Continuation::ArrayForEach {
701            callback,
702            source: arr,
703            next_index: 0,
704            caller_frame_depth,
705            callback_frame_index,
706        });
707
708        Ok(None)
709    }
710
711    /// Execute an array callback method (map, filter, reduce, forEach, etc.)
712    /// Returns `Ok(Some(value))` if the method completed synchronously, or
713    /// `Ok(None)` if a continuation was started (async callback).
714    fn execute_array_callback_method(
715        &mut self,
716        arr: Vec<Value>,
717        method: &str,
718        all_args: Vec<Value>,
719    ) -> Result<Option<Value>> {
720        let callback = all_args.first().cloned().unwrap_or(Value::Undefined);
721
722        match method {
723            "map" => {
724                // Use continuation-based execution for async callbacks
725                if self.is_async_callback(&callback) {
726                    return self.start_continuation_map(callback, arr);
727                }
728                let mut result = Vec::with_capacity(arr.len());
729                for (i, item) in arr.iter().enumerate() {
730                    result.push(self.call_element_callback(&callback, item, i)?);
731                }
732                Ok(Some(Value::Array(result)))
733            }
734            "filter" | "find" | "findIndex" | "every" | "some" | "reduce" | "sort" | "flatMap" => {
735                // Async callbacks are not supported for these methods
736                if self.is_async_callback(&callback) {
737                    return Err(ZapcodeError::RuntimeError(format!(
738                        ".{}() does not support async callbacks — use .map() or a for-of loop instead",
739                        method
740                    )));
741                }
742                match method {
743                    "filter" => {
744                        let mut result = Vec::new();
745                        for (i, item) in arr.iter().enumerate() {
746                            if self.call_element_callback(&callback, item, i)?.is_truthy() {
747                                result.push(item.clone());
748                            }
749                        }
750                        Ok(Some(Value::Array(result)))
751                    }
752                    "find" => {
753                        for (i, item) in arr.iter().enumerate() {
754                            if self.call_element_callback(&callback, item, i)?.is_truthy() {
755                                return Ok(Some(item.clone()));
756                            }
757                        }
758                        Ok(Some(Value::Undefined))
759                    }
760                    "findIndex" => {
761                        for (i, item) in arr.iter().enumerate() {
762                            if self.call_element_callback(&callback, item, i)?.is_truthy() {
763                                return Ok(Some(Value::Int(i as i64)));
764                            }
765                        }
766                        Ok(Some(Value::Int(-1)))
767                    }
768                    "every" => {
769                        for (i, item) in arr.iter().enumerate() {
770                            if !self.call_element_callback(&callback, item, i)?.is_truthy() {
771                                return Ok(Some(Value::Bool(false)));
772                            }
773                        }
774                        Ok(Some(Value::Bool(true)))
775                    }
776                    "some" => {
777                        for (i, item) in arr.iter().enumerate() {
778                            if self.call_element_callback(&callback, item, i)?.is_truthy() {
779                                return Ok(Some(Value::Bool(true)));
780                            }
781                        }
782                        Ok(Some(Value::Bool(false)))
783                    }
784                    "reduce" => {
785                        let mut acc = match all_args.get(1).cloned() {
786                            Some(init) => Some(init),
787                            None if !arr.is_empty() => Some(arr[0].clone()),
788                            None => {
789                                return Err(ZapcodeError::TypeError(
790                                    "Reduce of empty array with no initial value".to_string(),
791                                ));
792                            }
793                        };
794                        let start = if all_args.get(1).is_some() { 0 } else { 1 };
795                        for (i, item) in arr.iter().enumerate().skip(start) {
796                            acc = Some(self.call_function_internal(
797                                &callback,
798                                vec![acc.unwrap(), item.clone(), Value::Int(i as i64)],
799                            )?);
800                        }
801                        Ok(Some(acc.unwrap_or(Value::Undefined)))
802                    }
803                    "sort" => {
804                        let mut result = arr;
805                        if matches!(callback, Value::Function(_)) {
806                            let len = result.len();
807                            for i in 1..len {
808                                let mut j = i;
809                                while j > 0 {
810                                    let cmp = self
811                                        .call_function_internal(
812                                            &callback,
813                                            vec![result[j - 1].clone(), result[j].clone()],
814                                        )?
815                                        .to_number();
816                                    if cmp > 0.0 {
817                                        result.swap(j - 1, j);
818                                        j -= 1;
819                                    } else {
820                                        break;
821                                    }
822                                }
823                            }
824                        } else {
825                            result.sort_by_key(|a| a.to_js_string());
826                        }
827                        Ok(Some(Value::Array(result)))
828                    }
829                    "flatMap" => {
830                        let mut result = Vec::new();
831                        for (i, item) in arr.iter().enumerate() {
832                            match self.call_element_callback(&callback, item, i)? {
833                                Value::Array(inner) => result.extend(inner),
834                                other => result.push(other),
835                            }
836                        }
837                        Ok(Some(Value::Array(result)))
838                    }
839                    _ => unreachable!(),
840                }
841            }
842            "forEach" => {
843                // Use continuation-based execution for async callbacks
844                if self.is_async_callback(&callback) {
845                    return self.start_continuation_foreach(callback, arr);
846                }
847                for (i, item) in arr.iter().enumerate() {
848                    self.call_element_callback(&callback, item, i)?;
849                }
850                Ok(Some(Value::Undefined))
851            }
852            _ => Err(ZapcodeError::TypeError(format!(
853                "Unknown array callback method: {}",
854                method
855            ))),
856        }
857    }
858
859    /// Execute .then(), .catch(), or .finally() on a resolved/rejected promise.
860    /// Synchronously invokes the callback and returns a new promise wrapping the result.
861    fn execute_promise_method(
862        &mut self,
863        promise: Value,
864        method: &str,
865        args: Vec<Value>,
866    ) -> Result<Option<Value>> {
867        let (status, value, reason) = if let Value::Object(ref map) = promise {
868            let status = match map.get("status") {
869                Some(Value::String(s)) => s.to_string(),
870                _ => "pending".to_string(),
871            };
872            let value = map.get("value").cloned().unwrap_or(Value::Undefined);
873            let reason = map.get("reason").cloned().unwrap_or(Value::Undefined);
874            (status, value, reason)
875        } else {
876            return Ok(None);
877        };
878
879        let on_fulfilled = args.first().cloned().unwrap_or(Value::Undefined);
880        let on_rejected = args.get(1).cloned().unwrap_or(Value::Undefined);
881
882        match method {
883            "then" => {
884                if status == "resolved" {
885                    if matches!(on_fulfilled, Value::Function(_)) {
886                        let result = self.call_function_internal(&on_fulfilled, vec![value])?;
887                        Ok(Some(builtins::make_resolved_promise(result)))
888                    } else {
889                        // No callback — pass through the promise
890                        Ok(Some(promise))
891                    }
892                } else if status == "rejected" {
893                    if matches!(on_rejected, Value::Function(_)) {
894                        let result = self.call_function_internal(&on_rejected, vec![reason])?;
895                        Ok(Some(builtins::make_resolved_promise(result)))
896                    } else {
897                        // No onRejected — pass through the rejection
898                        Ok(Some(promise))
899                    }
900                } else {
901                    Ok(Some(promise))
902                }
903            }
904            "catch" => {
905                if status == "rejected" {
906                    let handler = args.first().cloned().unwrap_or(Value::Undefined);
907                    if matches!(handler, Value::Function(_)) {
908                        let result = self.call_function_internal(&handler, vec![reason])?;
909                        Ok(Some(builtins::make_resolved_promise(result)))
910                    } else {
911                        Ok(Some(promise))
912                    }
913                } else {
914                    // Resolved — pass through
915                    Ok(Some(promise))
916                }
917            }
918            "finally" => {
919                let handler = args.first().cloned().unwrap_or(Value::Undefined);
920                if matches!(handler, Value::Function(_)) {
921                    // finally callback receives no arguments
922                    self.call_function_internal(&handler, vec![])?;
923                }
924                // finally always passes through the original promise
925                Ok(Some(promise))
926            }
927            _ => Ok(None),
928        }
929    }
930
931    fn alloc_generator_id(&mut self) -> u64 {
932        let id = self.next_generator_id;
933        self.next_generator_id += 1;
934        id
935    }
936
937    fn generator_next(&mut self, mut gen_obj: GeneratorObject, arg: Value) -> Result<Value> {
938        if gen_obj.done {
939            return Ok(self.make_iterator_result(Value::Undefined, true));
940        }
941        for (name, val) in &gen_obj.captured {
942            if !self.globals.contains_key(name) {
943                self.globals.insert(name.clone(), val.clone());
944            }
945        }
946        let func_idx = gen_obj.func_id.0;
947        match gen_obj.suspended.take() {
948            None => {
949                let func = &self.program.functions[func_idx];
950                self.tracker.push_frame();
951                let mut locals = Vec::with_capacity(func.local_count);
952                for param in func.params.iter() {
953                    match param {
954                        ParamPattern::Ident(name) => {
955                            let val = gen_obj
956                                .captured
957                                .iter()
958                                .find(|(n, _)| n == name)
959                                .map(|(_, v)| v.clone())
960                                .unwrap_or(Value::Undefined);
961                            locals.push(val);
962                        }
963                        ParamPattern::Rest(name) => {
964                            let val = gen_obj
965                                .captured
966                                .iter()
967                                .find(|(n, _)| n == name)
968                                .map(|(_, v)| v.clone())
969                                .unwrap_or(Value::Array(Vec::new()));
970                            locals.push(val);
971                        }
972                        _ => {
973                            locals.push(Value::Undefined);
974                        }
975                    }
976                }
977                let stack_base = self.stack.len();
978                self.frames.push(CallFrame {
979                    func_index: Some(func_idx),
980                    ip: 0,
981                    locals,
982                    stack_base,
983                    this_value: None,
984                    receiver_source: None,
985                });
986                self.run_generator_until_yield_or_return(gen_obj)
987            }
988            Some(suspended) => {
989                self.tracker.push_frame();
990                let stack_base = self.stack.len();
991                for val in &suspended.stack {
992                    self.push(val.clone())?;
993                }
994                self.push(arg)?;
995                self.frames.push(CallFrame {
996                    func_index: Some(func_idx),
997                    ip: suspended.ip,
998                    locals: suspended.locals,
999                    stack_base,
1000                    this_value: None,
1001                    receiver_source: None,
1002                });
1003                self.run_generator_until_yield_or_return(gen_obj)
1004            }
1005        }
1006    }
1007
1008    /// Store generator state back into the globals registry.
1009    /// For done generators, the key is removed to prevent unbounded growth.
1010    fn store_generator(&mut self, gen_obj: GeneratorObject) {
1011        let gen_key = format!("__gen_{}", gen_obj.id);
1012        if gen_obj.done {
1013            self.globals.remove(&gen_key);
1014        } else {
1015            self.globals.insert(gen_key, Value::Generator(gen_obj));
1016        }
1017    }
1018
1019    /// Mark a generator as done, store it, and return the final iterator result.
1020    fn finish_generator(&mut self, mut gen_obj: GeneratorObject, value: Value) -> Value {
1021        gen_obj.done = true;
1022        gen_obj.suspended = None;
1023        self.store_generator(gen_obj);
1024        self.make_iterator_result(value, true)
1025    }
1026
1027    fn run_generator_until_yield_or_return(
1028        &mut self,
1029        mut gen_obj: GeneratorObject,
1030    ) -> Result<Value> {
1031        let target_frame_depth = self.frames.len() - 1;
1032        loop {
1033            self.tracker.check_time(&self.limits)?;
1034            let frame = self.frames.last().unwrap();
1035            let instructions = match frame.func_index {
1036                Some(idx) => &self.program.functions[idx].instructions,
1037                None => &self.program.instructions,
1038            };
1039            if frame.ip >= instructions.len() {
1040                if self.frames.len() > target_frame_depth + 1 {
1041                    let frame = self.frames.pop().unwrap();
1042                    self.tracker.pop_frame();
1043                    if let Some(this_val) = frame.this_value {
1044                        self.stack.truncate(frame.stack_base);
1045                        self.push(this_val)?;
1046                    } else {
1047                        self.push(Value::Undefined)?;
1048                    }
1049                    continue;
1050                }
1051                let frame = self.frames.pop().unwrap();
1052                self.tracker.pop_frame();
1053                self.stack.truncate(frame.stack_base);
1054                let result = self.finish_generator(gen_obj, Value::Undefined);
1055                return Ok(result);
1056            }
1057            let instr = instructions[frame.ip].clone();
1058            if matches!(instr, Instruction::Yield) {
1059                self.current_frame_mut().ip += 1;
1060                let yielded_value = self.pop()?;
1061                let frame = self.frames.pop().unwrap();
1062                self.tracker.pop_frame();
1063                let frame_stack: Vec<Value> = self.stack.drain(frame.stack_base..).collect();
1064                gen_obj.suspended = Some(SuspendedFrame {
1065                    ip: frame.ip,
1066                    locals: frame.locals,
1067                    stack: frame_stack,
1068                });
1069                gen_obj.done = false;
1070                self.store_generator(gen_obj);
1071                return Ok(self.make_iterator_result(yielded_value, false));
1072            }
1073            if matches!(instr, Instruction::Return) {
1074                self.current_frame_mut().ip += 1;
1075                let return_val = self.pop().unwrap_or(Value::Undefined);
1076                if self.frames.len() > target_frame_depth + 1 {
1077                    let frame = self.frames.pop().unwrap();
1078                    self.tracker.pop_frame();
1079                    self.stack.truncate(frame.stack_base);
1080                    self.push(return_val)?;
1081                    continue;
1082                }
1083                let frame = self.frames.pop().unwrap();
1084                self.tracker.pop_frame();
1085                self.stack.truncate(frame.stack_base);
1086                let result = self.finish_generator(gen_obj, return_val);
1087                return Ok(result);
1088            }
1089            let result = self.dispatch(instr);
1090            match result {
1091                Ok(Some(VmState::Complete(val))) => return Ok(val),
1092                Ok(Some(VmState::Suspended { .. })) => {
1093                    return Err(ZapcodeError::RuntimeError(
1094                        "cannot suspend inside a generator".to_string(),
1095                    ));
1096                }
1097                Ok(None) => {
1098                    if self.frames.len() == target_frame_depth {
1099                        let return_val = self.pop().unwrap_or(Value::Undefined);
1100                        let result = self.finish_generator(gen_obj, return_val);
1101                        return Ok(result);
1102                    }
1103                }
1104                Err(err) => {
1105                    if let Some(try_info) = self.try_stack.pop() {
1106                        while self.frames.len() > try_info.frame_depth {
1107                            self.frames.pop();
1108                            self.tracker.pop_frame();
1109                        }
1110                        self.stack.truncate(try_info.stack_depth);
1111                        let error_val = Value::String(Arc::from(err.to_string()));
1112                        self.push(error_val)?;
1113                        self.current_frame_mut().ip = try_info.catch_ip;
1114                    } else {
1115                        return Err(err);
1116                    }
1117                }
1118            }
1119        }
1120    }
1121
1122    fn make_iterator_result(&self, value: Value, done: bool) -> Value {
1123        let mut obj = IndexMap::new();
1124        obj.insert(Arc::from("value"), value);
1125        obj.insert(Arc::from("done"), Value::Bool(done));
1126        Value::Object(obj)
1127    }
1128
1129    fn dispatch(&mut self, instr: Instruction) -> Result<Option<VmState>> {
1130        self.current_frame_mut().ip += 1;
1131
1132        match instr {
1133            Instruction::Push(constant) => {
1134                let value = match constant {
1135                    Constant::Undefined => Value::Undefined,
1136                    Constant::Null => Value::Null,
1137                    Constant::Bool(b) => Value::Bool(b),
1138                    Constant::Int(n) => Value::Int(n),
1139                    Constant::Float(n) => Value::Float(n),
1140                    Constant::String(s) => Value::String(Arc::from(s.as_str())),
1141                };
1142                self.push(value)?;
1143            }
1144            Instruction::Pop => {
1145                self.pop()?;
1146            }
1147            Instruction::Dup => {
1148                let val = self.peek()?.clone();
1149                self.push(val)?;
1150            }
1151            Instruction::LoadLocal(idx) => {
1152                let frame_index = self.frames.len() - 1;
1153                let frame = self.current_frame();
1154                let val = frame.locals.get(idx).cloned().unwrap_or(Value::Undefined);
1155                self.last_load_source = Some(ReceiverSource::Local {
1156                    frame_index,
1157                    slot: idx,
1158                });
1159                self.push(val)?;
1160            }
1161            Instruction::StoreLocal(idx) => {
1162                let val = self.pop()?;
1163                let frame = self.current_frame_mut();
1164                while frame.locals.len() <= idx {
1165                    frame.locals.push(Value::Undefined);
1166                }
1167                frame.locals[idx] = val;
1168            }
1169            Instruction::LoadGlobal(name) => {
1170                let val = self.globals.get(&name).cloned().unwrap_or(Value::Undefined);
1171                self.last_global_name = Some(name.clone());
1172                // Only track receiver source for user-defined globals — builtins
1173                // (console, Math, JSON, etc.) contain non-serializable BuiltinMethod
1174                // values that would break snapshot serialization if written back.
1175                if Self::BUILTIN_GLOBAL_NAMES.contains(&name.as_str()) {
1176                    self.last_load_source = None;
1177                } else {
1178                    self.last_load_source = Some(ReceiverSource::Global(name));
1179                }
1180                self.push(val)?;
1181            }
1182            Instruction::StoreGlobal(name) => {
1183                let val = self.pop()?;
1184                self.globals.insert(name, val);
1185            }
1186            Instruction::DeclareLocal(_) => {
1187                let frame = self.current_frame_mut();
1188                frame.locals.push(Value::Undefined);
1189            }
1190
1191            // Arithmetic
1192            Instruction::Add => {
1193                let right = self.pop()?;
1194                let left = self.pop()?;
1195                let result = match (&left, &right) {
1196                    (Value::Int(a), Value::Int(b)) => match a.checked_add(*b) {
1197                        Some(r) => Value::Int(r),
1198                        None => Value::Float(*a as f64 + *b as f64),
1199                    },
1200                    (Value::Float(a), Value::Float(b)) => Value::Float(a + b),
1201                    (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 + b),
1202                    (Value::Float(a), Value::Int(b)) => Value::Float(a + *b as f64),
1203                    (Value::String(a), _) => {
1204                        let rhs = right.to_js_string();
1205                        let new_len = a.len().saturating_add(rhs.len());
1206                        if new_len > 10_000_000 {
1207                            return Err(ZapcodeError::AllocationLimitExceeded);
1208                        }
1209                        let mut s = a.to_string();
1210                        s.push_str(&rhs);
1211                        Value::String(Arc::from(s.as_str()))
1212                    }
1213                    (_, Value::String(b)) => {
1214                        let lhs = left.to_js_string();
1215                        let new_len = lhs.len().saturating_add(b.len());
1216                        if new_len > 10_000_000 {
1217                            return Err(ZapcodeError::AllocationLimitExceeded);
1218                        }
1219                        let mut s = lhs;
1220                        s.push_str(b);
1221                        Value::String(Arc::from(s.as_str()))
1222                    }
1223                    _ => Value::Float(left.to_number() + right.to_number()),
1224                };
1225                self.push(result)?;
1226            }
1227            Instruction::Sub => {
1228                let right = self.pop()?;
1229                let left = self.pop()?;
1230                let result = match (&left, &right) {
1231                    (Value::Int(a), Value::Int(b)) => match a.checked_sub(*b) {
1232                        Some(r) => Value::Int(r),
1233                        None => Value::Float(*a as f64 - *b as f64),
1234                    },
1235                    _ => Value::Float(left.to_number() - right.to_number()),
1236                };
1237                self.push(result)?;
1238            }
1239            Instruction::Mul => {
1240                let right = self.pop()?;
1241                let left = self.pop()?;
1242                let result = match (&left, &right) {
1243                    (Value::Int(a), Value::Int(b)) => match a.checked_mul(*b) {
1244                        Some(r) => Value::Int(r),
1245                        None => Value::Float(*a as f64 * *b as f64),
1246                    },
1247                    _ => Value::Float(left.to_number() * right.to_number()),
1248                };
1249                self.push(result)?;
1250            }
1251            Instruction::Div => {
1252                let right = self.pop()?;
1253                let left = self.pop()?;
1254                let result = Value::Float(left.to_number() / right.to_number());
1255                self.push(result)?;
1256            }
1257            Instruction::Rem => {
1258                let right = self.pop()?;
1259                let left = self.pop()?;
1260                let result = match (&left, &right) {
1261                    (Value::Int(a), Value::Int(b)) if *b != 0 => Value::Int(a % b),
1262                    _ => Value::Float(left.to_number() % right.to_number()),
1263                };
1264                self.push(result)?;
1265            }
1266            Instruction::Pow => {
1267                let right = self.pop()?;
1268                let left = self.pop()?;
1269                let result = Value::Float(left.to_number().powf(right.to_number()));
1270                self.push(result)?;
1271            }
1272            Instruction::Neg => {
1273                let val = self.pop()?;
1274                let result = match val {
1275                    Value::Int(n) => Value::Int(-n),
1276                    _ => Value::Float(-val.to_number()),
1277                };
1278                self.push(result)?;
1279            }
1280            Instruction::BitNot => {
1281                let val = self.pop()?;
1282                let n = val.to_number() as i32;
1283                self.push(Value::Int(!n as i64))?;
1284            }
1285            Instruction::BitAnd => {
1286                let right = self.pop()?;
1287                let left = self.pop()?;
1288                let result = (left.to_number() as i32) & (right.to_number() as i32);
1289                self.push(Value::Int(result as i64))?;
1290            }
1291            Instruction::BitOr => {
1292                let right = self.pop()?;
1293                let left = self.pop()?;
1294                let result = (left.to_number() as i32) | (right.to_number() as i32);
1295                self.push(Value::Int(result as i64))?;
1296            }
1297            Instruction::BitXor => {
1298                let right = self.pop()?;
1299                let left = self.pop()?;
1300                let result = (left.to_number() as i32) ^ (right.to_number() as i32);
1301                self.push(Value::Int(result as i64))?;
1302            }
1303            Instruction::Shl => {
1304                let right = self.pop()?;
1305                let left = self.pop()?;
1306                let shift = (right.to_number() as u32) & 0x1f;
1307                let result = (left.to_number() as i32) << shift;
1308                self.push(Value::Int(result as i64))?;
1309            }
1310            Instruction::Shr => {
1311                let right = self.pop()?;
1312                let left = self.pop()?;
1313                let shift = (right.to_number() as u32) & 0x1f;
1314                let result = (left.to_number() as i32) >> shift;
1315                self.push(Value::Int(result as i64))?;
1316            }
1317            Instruction::Ushr => {
1318                let right = self.pop()?;
1319                let left = self.pop()?;
1320                let shift = (right.to_number() as u32) & 0x1f;
1321                let result = (left.to_number() as u32) >> shift;
1322                self.push(Value::Int(result as i64))?;
1323            }
1324
1325            // Comparison
1326            Instruction::Eq | Instruction::StrictEq => {
1327                let right = self.pop()?;
1328                let left = self.pop()?;
1329                self.push(Value::Bool(left.strict_eq(&right)))?;
1330            }
1331            Instruction::Neq | Instruction::StrictNeq => {
1332                let right = self.pop()?;
1333                let left = self.pop()?;
1334                self.push(Value::Bool(!left.strict_eq(&right)))?;
1335            }
1336            Instruction::Lt => {
1337                let right = self.pop()?;
1338                let left = self.pop()?;
1339                self.push(Value::Bool(left.to_number() < right.to_number()))?;
1340            }
1341            Instruction::Lte => {
1342                let right = self.pop()?;
1343                let left = self.pop()?;
1344                self.push(Value::Bool(left.to_number() <= right.to_number()))?;
1345            }
1346            Instruction::Gt => {
1347                let right = self.pop()?;
1348                let left = self.pop()?;
1349                self.push(Value::Bool(left.to_number() > right.to_number()))?;
1350            }
1351            Instruction::Gte => {
1352                let right = self.pop()?;
1353                let left = self.pop()?;
1354                self.push(Value::Bool(left.to_number() >= right.to_number()))?;
1355            }
1356
1357            // Logical
1358            Instruction::Not => {
1359                let val = self.pop()?;
1360                self.push(Value::Bool(!val.is_truthy()))?;
1361            }
1362
1363            // Objects & Arrays
1364            Instruction::CreateArray(count) => {
1365                self.tracker.track_allocation(&self.limits)?;
1366                let mut arr = Vec::with_capacity(count);
1367                for _ in 0..count {
1368                    arr.push(self.pop()?);
1369                }
1370                arr.reverse();
1371                self.push(Value::Array(arr))?;
1372            }
1373            Instruction::CreateObject(count) => {
1374                self.tracker.track_allocation(&self.limits)?;
1375                let mut obj = IndexMap::new();
1376                // Pop key-value pairs (or spread values)
1377                let mut entries = Vec::new();
1378                for _ in 0..count {
1379                    let val = self.pop()?;
1380                    let key = self.pop()?;
1381                    entries.push((key, val));
1382                }
1383                entries.reverse();
1384                for (key, val) in entries {
1385                    match key {
1386                        Value::String(k) => {
1387                            obj.insert(k, val);
1388                        }
1389                        _ => {
1390                            let k: Arc<str> = Arc::from(key.to_js_string().as_str());
1391                            obj.insert(k, val);
1392                        }
1393                    }
1394                }
1395                self.push(Value::Object(obj))?;
1396            }
1397            Instruction::GetProperty(name) => {
1398                let obj = self.pop()?;
1399                let result = self.get_property(&obj, &name)?;
1400                // Store receiver for method calls
1401                if matches!(result, Value::BuiltinMethod { .. } | Value::Function(_)) {
1402                    self.last_receiver = Some(obj);
1403                    self.last_receiver_source = self.last_load_source.take();
1404                } else {
1405                    self.last_receiver_source = None;
1406                }
1407                self.push(result)?;
1408            }
1409            Instruction::SetProperty(name) => {
1410                // Stack: [value_to_store, object] with object on top
1411                // (compile_store pushes object after the value)
1412                let obj_val = self.pop()?;
1413                let value = self.pop()?;
1414                match obj_val {
1415                    Value::Object(mut obj) => {
1416                        obj.insert(Arc::from(name.as_str()), value);
1417                        // Push modified object back so compile_store can store it
1418                        self.push(Value::Object(obj))?;
1419                    }
1420                    _ => {
1421                        return Err(ZapcodeError::TypeError(format!(
1422                            "cannot set property '{}' on {}",
1423                            name,
1424                            obj_val.type_name()
1425                        )));
1426                    }
1427                }
1428            }
1429            Instruction::GetIndex => {
1430                let index = self.pop()?;
1431                let obj = self.pop()?;
1432                let result = match (&obj, &index) {
1433                    (Value::Array(arr), Value::Int(i)) => {
1434                        arr.get(*i as usize).cloned().unwrap_or(Value::Undefined)
1435                    }
1436                    (Value::Array(arr), Value::Float(f)) => {
1437                        arr.get(*f as usize).cloned().unwrap_or(Value::Undefined)
1438                    }
1439                    (Value::Object(map), Value::String(key)) => {
1440                        map.get(key.as_ref()).cloned().unwrap_or(Value::Undefined)
1441                    }
1442                    (Value::Object(map), _) => {
1443                        let key: Arc<str> = Arc::from(index.to_js_string().as_str());
1444                        map.get(key.as_ref()).cloned().unwrap_or(Value::Undefined)
1445                    }
1446                    (Value::String(s), Value::Int(i)) => s
1447                        .chars()
1448                        .nth(*i as usize)
1449                        .map(|c| Value::String(Arc::from(c.to_string().as_str())))
1450                        .unwrap_or(Value::Undefined),
1451                    _ => Value::Undefined,
1452                };
1453                self.push(result)?;
1454            }
1455            Instruction::SetIndex => {
1456                let index = self.pop()?;
1457                let mut obj = self.pop()?;
1458                let value = self.pop()?;
1459                match &mut obj {
1460                    Value::Array(arr) => {
1461                        let idx = match &index {
1462                            Value::Int(i) if *i >= 0 => *i as usize,
1463                            Value::Float(f) if *f >= 0.0 && *f == (*f as usize as f64) => {
1464                                *f as usize
1465                            }
1466                            _ => {
1467                                // Negative or non-numeric index: treat as no-op (like JS)
1468                                self.push(obj)?;
1469                                return Ok(None);
1470                            }
1471                        };
1472                        // Cap maximum sparse array growth to prevent memory exhaustion
1473                        if idx > arr.len() + 1024 {
1474                            return Err(ZapcodeError::RuntimeError(format!(
1475                                "array index {} too far beyond length {}",
1476                                idx,
1477                                arr.len()
1478                            )));
1479                        }
1480                        while arr.len() <= idx {
1481                            arr.push(Value::Undefined);
1482                        }
1483                        arr[idx] = value;
1484                    }
1485                    Value::Object(map) => {
1486                        let key: Arc<str> = Arc::from(index.to_js_string().as_str());
1487                        map.insert(key, value);
1488                    }
1489                    _ => {}
1490                }
1491                // Push modified object back so compile_store can store it to the variable
1492                self.push(obj)?;
1493            }
1494            Instruction::Spread => {
1495                // Handled contextually in CreateArray/CreateObject
1496            }
1497            Instruction::In => {
1498                let right = self.pop()?;
1499                let left = self.pop()?;
1500                let result = match &right {
1501                    Value::Object(map) => {
1502                        let key = left.to_js_string();
1503                        map.contains_key(key.as_str())
1504                    }
1505                    Value::Array(arr) => {
1506                        if let Value::Int(i) = left {
1507                            (i as usize) < arr.len()
1508                        } else {
1509                            false
1510                        }
1511                    }
1512                    _ => false,
1513                };
1514                self.push(Value::Bool(result))?;
1515            }
1516            Instruction::InstanceOf => {
1517                let right = self.pop()?;
1518                let left = self.pop()?;
1519                // Check if left's __class__ matches right's __class_name__
1520                let result = match (&left, &right) {
1521                    (Value::Object(instance), Value::Object(class_obj)) => {
1522                        if let (Some(inst_class), Some(class_name)) =
1523                            (instance.get("__class__"), class_obj.get("__class_name__"))
1524                        {
1525                            inst_class == class_name
1526                        } else {
1527                            false
1528                        }
1529                    }
1530                    _ => false,
1531                };
1532                self.push(Value::Bool(result))?;
1533            }
1534
1535            // Functions
1536            Instruction::CreateClosure(func_idx) => {
1537                // Capture current scope for closure
1538                let mut captured = Vec::new();
1539                // Capture all locals from all active frames using local_names
1540                for frame in &self.frames {
1541                    let local_names = if let Some(fidx) = frame.func_index {
1542                        &self.program.functions[fidx].local_names
1543                    } else {
1544                        &self.program.local_names
1545                    };
1546                    for (i, val) in frame.locals.iter().enumerate() {
1547                        if let Some(name) = local_names.get(i) {
1548                            captured.push((name.clone(), val.clone()));
1549                        }
1550                    }
1551                }
1552                // Also capture all globals that are user-defined (not builtins)
1553                let builtins = ["console", "Math", "JSON", "Object", "Array"];
1554                for (name, val) in &self.globals {
1555                    if !builtins.contains(&name.as_str()) {
1556                        captured.push((name.clone(), val.clone()));
1557                    }
1558                }
1559                let closure = Closure {
1560                    func_id: FunctionId(func_idx),
1561                    captured,
1562                };
1563                self.push(Value::Function(closure))?;
1564            }
1565            Instruction::Call(arg_count) => {
1566                let mut args = Vec::with_capacity(arg_count);
1567                for _ in 0..arg_count {
1568                    args.push(self.pop()?);
1569                }
1570                args.reverse();
1571
1572                let callee = self.pop()?;
1573                match callee {
1574                    Value::Function(closure) => {
1575                        let func_idx = closure.func_id.0;
1576                        let is_generator = self.program.functions[func_idx].is_generator;
1577
1578                        // Generator function: create a Generator object instead of running
1579                        if is_generator {
1580                            let params = self.program.functions[func_idx].params.clone();
1581                            let gen_id = self.alloc_generator_id();
1582                            // Capture args as named params so generator_next can restore them
1583                            let mut captured = closure.captured.clone();
1584                            for (i, param) in params.iter().enumerate() {
1585                                match param {
1586                                    ParamPattern::Ident(name) => {
1587                                        captured.push((
1588                                            name.clone(),
1589                                            args.get(i).cloned().unwrap_or(Value::Undefined),
1590                                        ));
1591                                    }
1592                                    ParamPattern::Rest(name) => {
1593                                        let rest: Vec<Value> = args[i..].to_vec();
1594                                        captured.push((name.clone(), Value::Array(rest)));
1595                                    }
1596                                    _ => {}
1597                                }
1598                            }
1599                            let gen_obj = GeneratorObject {
1600                                id: gen_id,
1601                                func_id: closure.func_id,
1602                                captured,
1603                                suspended: None,
1604                                done: false,
1605                            };
1606                            // Store in globals registry so we can look it up by ID later
1607                            self.globals.insert(
1608                                format!("__gen_{}", gen_id),
1609                                Value::Generator(gen_obj.clone()),
1610                            );
1611                            self.push(Value::Generator(gen_obj))?;
1612                            self.last_receiver = None;
1613                            self.last_receiver_source = None;
1614                        } else {
1615                            let this_value = self.last_receiver.take();
1616                            self.push_call_frame(&closure, &args, this_value)?;
1617                        }
1618                    }
1619                    Value::BuiltinMethod {
1620                        object_name,
1621                        method_name,
1622                    } => {
1623                        let receiver = self.last_receiver.take();
1624                        let result = match object_name.as_ref() {
1625                            "__array__" => {
1626                                if let Some(Value::Array(arr)) = &receiver {
1627                                    // Check if this is a callback method first
1628                                    match method_name.as_ref() {
1629                                        "map" | "filter" | "forEach" | "find" | "findIndex"
1630                                        | "every" | "some" | "reduce" | "sort" | "flatMap" => {
1631                                            match self.execute_array_callback_method(
1632                                                arr.clone(),
1633                                                &method_name,
1634                                                args,
1635                                            )? {
1636                                                Some(val) => Some(val),
1637                                                None => {
1638                                                    // Continuation started — the main execute()
1639                                                    // loop will drive the callbacks. Don't push
1640                                                    // a result; just return Ok(None).
1641                                                    return Ok(None);
1642                                                }
1643                                            }
1644                                        }
1645                                        _ => builtins::call_builtin(
1646                                            &Value::Array(arr.clone()),
1647                                            &method_name,
1648                                            &args,
1649                                            &mut self.stdout,
1650                                        )?,
1651                                    }
1652                                } else {
1653                                    None
1654                                }
1655                            }
1656                            "__string__" => {
1657                                if let Some(Value::String(s)) = &receiver {
1658                                    builtins::call_builtin(
1659                                        &Value::String(s.clone()),
1660                                        &method_name,
1661                                        &args,
1662                                        &mut self.stdout,
1663                                    )?
1664                                } else {
1665                                    None
1666                                }
1667                            }
1668                            "__generator__" => {
1669                                if let Some(Value::Generator(gen_obj)) = receiver {
1670                                    match method_name.as_ref() {
1671                                        "next" => {
1672                                            let arg =
1673                                                args.into_iter().next().unwrap_or(Value::Undefined);
1674                                            // Get the latest generator state from registry.
1675                                            // If the key is missing, the generator has finished
1676                                            // and was cleaned up — return done immediately.
1677                                            let gen_key = format!("__gen_{}", gen_obj.id);
1678                                            if let Some(Value::Generator(g)) =
1679                                                self.globals.remove(&gen_key)
1680                                            {
1681                                                let result = self.generator_next(g, arg)?;
1682                                                Some(result)
1683                                            } else {
1684                                                Some(
1685                                                    self.make_iterator_result(
1686                                                        Value::Undefined,
1687                                                        true,
1688                                                    ),
1689                                                )
1690                                            }
1691                                        }
1692                                        "return" => {
1693                                            let val =
1694                                                args.into_iter().next().unwrap_or(Value::Undefined);
1695                                            let gen_key = format!("__gen_{}", gen_obj.id);
1696                                            if let Some(Value::Generator(g)) =
1697                                                self.globals.remove(&gen_key)
1698                                            {
1699                                                let result = self.finish_generator(g, val);
1700                                                Some(result)
1701                                            } else {
1702                                                Some(self.make_iterator_result(val, true))
1703                                            }
1704                                        }
1705                                        _ => None,
1706                                    }
1707                                } else {
1708                                    None
1709                                }
1710                            }
1711                            "__promise__" => {
1712                                if let Some(promise) = receiver {
1713                                    self.execute_promise_method(promise, &method_name, args)?
1714                                } else {
1715                                    None
1716                                }
1717                            }
1718                            global_name => builtins::call_global_method(
1719                                global_name,
1720                                &method_name,
1721                                &args,
1722                                &mut self.stdout,
1723                            )?,
1724                        };
1725                        match result {
1726                            Some(val) => self.push(val)?,
1727                            None => {
1728                                return Err(ZapcodeError::TypeError(format!(
1729                                    "{}.{} is not a function",
1730                                    object_name, method_name
1731                                )));
1732                            }
1733                        }
1734                    }
1735                    _ => {
1736                        return Err(ZapcodeError::TypeError(format!(
1737                            "{} is not a function",
1738                            callee.to_js_string()
1739                        )));
1740                    }
1741                }
1742            }
1743            Instruction::Return => {
1744                let return_val = self.pop().unwrap_or(Value::Undefined);
1745
1746                if self.frames.len() <= 1 {
1747                    return Ok(Some(VmState::Complete(return_val)));
1748                }
1749
1750                let frame = self.frames.pop().unwrap();
1751                self.tracker.pop_frame();
1752
1753                // If this was a constructor frame (has this_value), return the
1754                // updated `this` instead of the explicit return value (unless
1755                // the constructor explicitly returns an object).
1756                let actual_return = if let Some(ref this_val) = frame.this_value {
1757                    // Also propagate this back to parent frame (for super() calls)
1758                    if let Some(parent) = self.frames.last_mut() {
1759                        if parent.this_value.is_some() {
1760                            parent.this_value = Some(this_val.clone());
1761                        }
1762                    }
1763                    // Write back the mutated `this` to the original variable
1764                    // that the method receiver came from. This ensures that
1765                    // value-type semantics work correctly for method calls
1766                    // that mutate `this` properties (e.g., this.count += 1).
1767                    if let Some(ref source) = frame.receiver_source {
1768                        match source {
1769                            ReceiverSource::Global(name) => {
1770                                self.globals.insert(name.clone(), this_val.clone());
1771                            }
1772                            ReceiverSource::Local { frame_index, slot } => {
1773                                if let Some(target_frame) = self.frames.get_mut(*frame_index) {
1774                                    while target_frame.locals.len() <= *slot {
1775                                        target_frame.locals.push(Value::Undefined);
1776                                    }
1777                                    target_frame.locals[*slot] = this_val.clone();
1778                                }
1779                            }
1780                        }
1781                    }
1782                    if matches!(return_val, Value::Undefined) {
1783                        this_val.clone()
1784                    } else {
1785                        return_val
1786                    }
1787                } else {
1788                    return_val
1789                };
1790
1791                self.stack.truncate(frame.stack_base);
1792                self.push(actual_return)?;
1793            }
1794            Instruction::CallExternal(name, arg_count) => {
1795                if !self.external_functions.contains(&name) {
1796                    return Err(ZapcodeError::UnknownExternalFunction(name));
1797                }
1798                let mut args = Vec::with_capacity(arg_count);
1799                for _ in 0..arg_count {
1800                    args.push(self.pop()?);
1801                }
1802                args.reverse();
1803                // Suspend execution
1804                let snapshot = ZapcodeSnapshot::capture(self)?;
1805                return Ok(Some(VmState::Suspended {
1806                    function_name: name,
1807                    args,
1808                    snapshot,
1809                }));
1810            }
1811
1812            // Control flow
1813            Instruction::Jump(target) => {
1814                self.current_frame_mut().ip = target;
1815            }
1816            Instruction::JumpIfFalse(target) => {
1817                let val = self.pop()?;
1818                if !val.is_truthy() {
1819                    self.current_frame_mut().ip = target;
1820                }
1821            }
1822            Instruction::JumpIfTrue(target) => {
1823                let val = self.pop()?;
1824                if val.is_truthy() {
1825                    self.current_frame_mut().ip = target;
1826                }
1827            }
1828            Instruction::JumpIfNullish(target) => {
1829                let val = self.peek()?;
1830                if matches!(val, Value::Null | Value::Undefined) {
1831                    self.current_frame_mut().ip = target;
1832                }
1833            }
1834
1835            // Loops
1836            Instruction::SetupLoop => {}
1837            Instruction::Break | Instruction::Continue => {
1838                // These should have been compiled to jumps
1839            }
1840
1841            // Iterators
1842            Instruction::GetIterator => {
1843                let val = self.pop()?;
1844                match val {
1845                    Value::Array(arr) => {
1846                        // Push an iterator object: [array, index]
1847                        let iter_obj = Value::Array(vec![Value::Array(arr), Value::Int(0)]);
1848                        self.push(iter_obj)?;
1849                    }
1850                    Value::String(s) => {
1851                        let chars: Vec<Value> = s
1852                            .chars()
1853                            .map(|c| Value::String(Arc::from(c.to_string().as_str())))
1854                            .collect();
1855                        let iter_obj = Value::Array(vec![Value::Array(chars), Value::Int(0)]);
1856                        self.push(iter_obj)?;
1857                    }
1858                    Value::Generator(gen_obj) => {
1859                        let iter_obj = Value::Array(vec![
1860                            Value::String(Arc::from("__gen__")),
1861                            Value::Int(gen_obj.id as i64),
1862                            Value::Bool(false),
1863                        ]);
1864                        self.push(iter_obj)?;
1865                    }
1866                    _ => {
1867                        return Err(ZapcodeError::TypeError(format!(
1868                            "{} is not iterable",
1869                            val.type_name()
1870                        )));
1871                    }
1872                }
1873            }
1874            Instruction::IteratorNext => {
1875                let iter = self.pop()?;
1876                // Check for generator iterator (3-element sentinel)
1877                if let Value::Array(ref items) = iter {
1878                    if items.len() == 3 {
1879                        if let Value::String(ref s) = items[0] {
1880                            if s.as_ref() == "__gen__" {
1881                                let gen_id = match &items[1] {
1882                                    Value::Int(id) => *id as u64,
1883                                    _ => {
1884                                        return Err(ZapcodeError::RuntimeError(
1885                                            "bad gen iter".into(),
1886                                        ))
1887                                    }
1888                                };
1889                                let gen_key = format!("__gen_{}", gen_id);
1890                                let gen_obj = if let Some(Value::Generator(g)) =
1891                                    self.globals.remove(&gen_key)
1892                                {
1893                                    g
1894                                } else {
1895                                    self.push(Value::Array(vec![
1896                                        Value::String(Arc::from("__gen__")),
1897                                        Value::Int(gen_id as i64),
1898                                        Value::Bool(true),
1899                                    ]))?;
1900                                    self.push(Value::Undefined)?;
1901                                    return Ok(None);
1902                                };
1903                                let result = self.generator_next(gen_obj, Value::Undefined)?;
1904                                if let Value::Object(ref obj) = result {
1905                                    let done = obj
1906                                        .get("done")
1907                                        .is_some_and(|v| matches!(v, Value::Bool(true)));
1908                                    let value =
1909                                        obj.get("value").cloned().unwrap_or(Value::Undefined);
1910                                    self.push(Value::Array(vec![
1911                                        Value::String(Arc::from("__gen__")),
1912                                        Value::Int(gen_id as i64),
1913                                        Value::Bool(done),
1914                                    ]))?;
1915                                    self.push(value)?;
1916                                } else {
1917                                    self.push(iter)?;
1918                                    self.push(Value::Undefined)?;
1919                                }
1920                                return Ok(None);
1921                            }
1922                        }
1923                    }
1924                }
1925                match iter {
1926                    Value::Array(ref items) if items.len() == 2 => {
1927                        let arr = match &items[0] {
1928                            Value::Array(a) => a,
1929                            _ => return Err(ZapcodeError::RuntimeError("invalid iterator".into())),
1930                        };
1931                        let idx = match &items[1] {
1932                            Value::Int(i) => *i as usize,
1933                            _ => return Err(ZapcodeError::RuntimeError("invalid iterator".into())),
1934                        };
1935                        if idx < arr.len() {
1936                            let value = arr[idx].clone();
1937                            // Update iterator
1938                            let new_iter =
1939                                Value::Array(vec![items[0].clone(), Value::Int((idx + 1) as i64)]);
1940                            // Push updated iterator back, then the value
1941                            self.push(new_iter)?;
1942                            self.push(value)?;
1943                        } else {
1944                            // Done
1945                            self.push(iter)?;
1946                            self.push(Value::Undefined)?;
1947                        }
1948                    }
1949                    _ => {
1950                        return Err(ZapcodeError::RuntimeError("invalid iterator state".into()));
1951                    }
1952                }
1953            }
1954            Instruction::IteratorDone => {
1955                let value = self.pop()?;
1956                let iter = self.peek()?;
1957                // Check for generator iterator first
1958                if let Value::Array(items) = iter {
1959                    if items.len() == 3 {
1960                        if let Value::String(ref s) = items[0] {
1961                            if s.as_ref() == "__gen__" {
1962                                let done = matches!(&items[2], Value::Bool(true));
1963                                if !done {
1964                                    self.push(value)?;
1965                                }
1966                                self.push(Value::Bool(done))?;
1967                                return Ok(None);
1968                            }
1969                        }
1970                    }
1971                }
1972                let iter = self.peek()?;
1973                match iter {
1974                    Value::Array(items) if items.len() == 2 => {
1975                        let arr = match &items[0] {
1976                            Value::Array(a) => a,
1977                            _ => {
1978                                self.push(value)?;
1979                                self.push(Value::Bool(true))?;
1980                                return Ok(None);
1981                            }
1982                        };
1983                        let idx = match &items[1] {
1984                            Value::Int(i) => *i as usize,
1985                            _ => {
1986                                self.push(value)?;
1987                                self.push(Value::Bool(true))?;
1988                                return Ok(None);
1989                            }
1990                        };
1991                        let done = idx > arr.len();
1992                        if !done {
1993                            // Push value back for the binding
1994                            self.push(value)?;
1995                        }
1996                        self.push(Value::Bool(done))?;
1997                    }
1998                    _ => {
1999                        self.push(value)?;
2000                        self.push(Value::Bool(true))?;
2001                    }
2002                }
2003            }
2004
2005            // Error handling
2006            Instruction::SetupTry(catch_ip, _) => {
2007                self.try_stack.push(TryInfo {
2008                    catch_ip,
2009                    frame_depth: self.frames.len(),
2010                    stack_depth: self.stack.len(),
2011                });
2012            }
2013            Instruction::Throw => {
2014                let val = self.pop()?;
2015                let msg = val.to_js_string();
2016                return Err(ZapcodeError::RuntimeError(msg));
2017            }
2018            Instruction::EndTry => {
2019                self.try_stack.pop();
2020            }
2021
2022            // Typeof
2023            Instruction::TypeOf => {
2024                let val = self.pop()?;
2025                let type_str = val.type_name();
2026                self.push(Value::String(Arc::from(type_str)))?;
2027            }
2028
2029            // Void
2030            Instruction::Void => {
2031                self.pop()?;
2032                self.push(Value::Undefined)?;
2033            }
2034
2035            // Update
2036            Instruction::Increment => {
2037                let val = self.pop()?;
2038                let result = match val {
2039                    Value::Int(n) => Value::Int(n + 1),
2040                    _ => Value::Float(val.to_number() + 1.0),
2041                };
2042                self.push(result)?;
2043            }
2044            Instruction::Decrement => {
2045                let val = self.pop()?;
2046                let result = match val {
2047                    Value::Int(n) => Value::Int(n - 1),
2048                    _ => Value::Float(val.to_number() - 1.0),
2049                };
2050                self.push(result)?;
2051            }
2052
2053            // Template literals
2054            Instruction::ConcatStrings(count) => {
2055                let mut parts = Vec::with_capacity(count);
2056                for _ in 0..count {
2057                    parts.push(self.pop()?);
2058                }
2059                parts.reverse();
2060                let result: String = parts.iter().map(|v| v.to_js_string()).collect();
2061                self.push(Value::String(Arc::from(result.as_str())))?;
2062            }
2063
2064            // Destructuring
2065            Instruction::DestructureObject(keys) => {
2066                let obj = self.pop()?;
2067                for key in keys {
2068                    let val = self.get_property(&obj, &key)?;
2069                    self.push(val)?;
2070                }
2071            }
2072            Instruction::DestructureArray(count) => {
2073                let arr = self.pop()?;
2074                match arr {
2075                    Value::Array(items) => {
2076                        for i in 0..count {
2077                            self.push(items.get(i).cloned().unwrap_or(Value::Undefined))?;
2078                        }
2079                    }
2080                    _ => {
2081                        for _ in 0..count {
2082                            self.push(Value::Undefined)?;
2083                        }
2084                    }
2085                }
2086            }
2087
2088            Instruction::Nop => {}
2089
2090            // Generators
2091            Instruction::CreateGenerator(_func_idx) => {
2092                // Generator creation is handled at Call time via is_generator check.
2093            }
2094            Instruction::Yield => {
2095                // Yield is handled in run_generator_until_yield_or_return.
2096                // Reaching here means yield outside a generator function.
2097                return Err(ZapcodeError::RuntimeError(
2098                    "yield can only be used inside a generator function".to_string(),
2099                ));
2100            }
2101
2102            Instruction::Await => {
2103                // Check if the value on the stack is a Promise object.
2104                // If resolved, unwrap its value. If rejected, throw its reason.
2105                // If it's a regular (non-promise) value, leave it as-is.
2106                let val = self.pop()?;
2107                if builtins::is_promise(&val) {
2108                    if let Value::Object(map) = &val {
2109                        let status = map.get("status").cloned().unwrap_or(Value::Undefined);
2110                        match status {
2111                            Value::String(s) if s.as_ref() == "resolved" => {
2112                                let inner = map.get("value").cloned().unwrap_or(Value::Undefined);
2113                                self.push(inner)?;
2114                            }
2115                            Value::String(s) if s.as_ref() == "rejected" => {
2116                                let reason = map.get("reason").cloned().unwrap_or(Value::Undefined);
2117                                return Err(ZapcodeError::RuntimeError(format!(
2118                                    "Unhandled promise rejection: {}",
2119                                    reason.to_js_string()
2120                                )));
2121                            }
2122                            _ => {
2123                                // Unknown status — pass through
2124                                self.push(val)?;
2125                            }
2126                        }
2127                    } else {
2128                        self.push(val)?;
2129                    }
2130                } else {
2131                    // Not a promise — pass through (await on non-promise returns the value)
2132                    self.push(val)?;
2133                }
2134            }
2135
2136            // Classes
2137            Instruction::CreateClass {
2138                name,
2139                n_methods,
2140                n_statics,
2141                has_super,
2142            } => {
2143                // Stack layout (top to bottom):
2144                // constructor closure (or undefined)
2145                // n_methods * (closure, method_name_string) pairs
2146                // n_statics * (closure, method_name_string) pairs
2147                // [optional super class if has_super]
2148
2149                let constructor = self.pop()?;
2150
2151                // Pop instance methods
2152                let mut prototype = IndexMap::new();
2153                for _ in 0..n_methods {
2154                    let method_closure = self.pop()?;
2155                    let method_name = self.pop()?;
2156                    if let Value::String(mn) = method_name {
2157                        prototype.insert(mn, method_closure);
2158                    }
2159                }
2160
2161                // Pop static methods
2162                let mut statics = IndexMap::new();
2163                for _ in 0..n_statics {
2164                    let method_closure = self.pop()?;
2165                    let method_name = self.pop()?;
2166                    if let Value::String(mn) = method_name {
2167                        statics.insert(mn, method_closure);
2168                    }
2169                }
2170
2171                // Pop super class if present
2172                let super_class = if has_super { Some(self.pop()?) } else { None };
2173
2174                // If super class, copy its prototype methods to ours (inheritance)
2175                if let Some(Value::Object(ref sc)) = super_class {
2176                    if let Some(Value::Object(super_proto)) = sc.get("__prototype__").cloned() {
2177                        // Super prototype methods go first, then our own (which override)
2178                        let mut merged = super_proto;
2179                        for (k, v) in prototype {
2180                            merged.insert(k, v);
2181                        }
2182                        prototype = merged;
2183                    }
2184                }
2185
2186                // Build the class object
2187                let mut class_obj = IndexMap::new();
2188                class_obj.insert(
2189                    Arc::from("__class_name__"),
2190                    Value::String(Arc::from(name.as_str())),
2191                );
2192                class_obj.insert(Arc::from("__constructor__"), constructor);
2193                class_obj.insert(Arc::from("__prototype__"), Value::Object(prototype));
2194
2195                // Store super class reference for super() calls
2196                if let Some(sc) = super_class {
2197                    class_obj.insert(Arc::from("__super__"), sc);
2198                }
2199
2200                // Add static methods directly on the class object
2201                for (k, v) in statics {
2202                    class_obj.insert(k, v);
2203                }
2204
2205                self.push(Value::Object(class_obj))?;
2206            }
2207
2208            Instruction::Construct(arg_count) => {
2209                let mut args = Vec::with_capacity(arg_count);
2210                for _ in 0..arg_count {
2211                    args.push(self.pop()?);
2212                }
2213                args.reverse();
2214
2215                let callee = self.pop()?;
2216
2217                match &callee {
2218                    Value::Object(class_obj) if class_obj.contains_key("__class_name__") => {
2219                        // Create a new instance object
2220                        let mut instance = IndexMap::new();
2221
2222                        // Copy prototype methods onto the instance
2223                        if let Some(Value::Object(proto)) = class_obj.get("__prototype__") {
2224                            for (k, v) in proto {
2225                                instance.insert(k.clone(), v.clone());
2226                            }
2227                        }
2228
2229                        // Store class reference for instanceof
2230                        if let Some(class_name) = class_obj.get("__class_name__") {
2231                            instance.insert(Arc::from("__class__"), class_name.clone());
2232                        }
2233
2234                        let instance_val = Value::Object(instance);
2235
2236                        // Call the constructor with `this` bound to the instance
2237                        if let Some(ctor) = class_obj.get("__constructor__") {
2238                            if let Value::Function(closure) = ctor {
2239                                // Clear receiver source — constructors should not
2240                                // write back to a receiver variable.
2241                                self.last_receiver_source = None;
2242                                self.push_call_frame(closure, &args, Some(instance_val))?;
2243                                self.last_receiver = None;
2244                            } else {
2245                                // No valid constructor, just return the instance
2246                                self.push(instance_val)?;
2247                            }
2248                        } else {
2249                            // No constructor, just return the instance
2250                            self.push(instance_val)?;
2251                        }
2252                    }
2253                    Value::Function(closure) => {
2254                        // `new` on a plain function — just call it
2255                        self.push_call_frame(closure, &args, None)?;
2256                        self.last_receiver = None;
2257                    }
2258                    _ => {
2259                        return Err(ZapcodeError::TypeError(format!(
2260                            "{} is not a constructor",
2261                            callee.to_js_string()
2262                        )));
2263                    }
2264                }
2265            }
2266
2267            Instruction::LoadThis => {
2268                // Walk frames from top to find the nearest `this` value
2269                let this_val = self
2270                    .frames
2271                    .iter()
2272                    .rev()
2273                    .find_map(|f| f.this_value.clone())
2274                    .unwrap_or(Value::Undefined);
2275                self.push(this_val)?;
2276            }
2277            Instruction::StoreThis => {
2278                let val = self.pop()?;
2279                // Update this_value in the nearest frame that has one
2280                for frame in self.frames.iter_mut().rev() {
2281                    if frame.this_value.is_some() {
2282                        frame.this_value = Some(val);
2283                        break;
2284                    }
2285                }
2286            }
2287            Instruction::CallSuper(arg_count) => {
2288                let mut args = Vec::with_capacity(arg_count);
2289                for _ in 0..arg_count {
2290                    args.push(self.pop()?);
2291                }
2292                args.reverse();
2293
2294                // Get current `this` value (the instance being constructed)
2295                let this_val = self
2296                    .frames
2297                    .iter()
2298                    .rev()
2299                    .find_map(|f| f.this_value.clone())
2300                    .unwrap_or(Value::Undefined);
2301
2302                // Find the super class constructor from the class that's being constructed.
2303                // We need to look it up from the globals — the class with __super__ key.
2304                // The super class info is stored on the class object.
2305                // We'll look through globals for the class that has __super__.
2306                let mut super_ctor = None;
2307                for val in self.globals.values() {
2308                    if let Value::Object(obj) = val {
2309                        if let Some(Value::Object(super_class)) = obj.get("__super__") {
2310                            if let Some(ctor) = super_class.get("__constructor__") {
2311                                super_ctor = Some(ctor.clone());
2312                                break;
2313                            }
2314                        }
2315                    }
2316                }
2317
2318                if let Some(Value::Function(closure)) = super_ctor {
2319                    self.last_receiver_source = None;
2320                    self.push_call_frame(&closure, &args, Some(this_val))?;
2321                    self.last_receiver = None;
2322                } else {
2323                    // No super constructor found — push undefined
2324                    self.push(Value::Undefined)?;
2325                }
2326            }
2327        }
2328
2329        Ok(None)
2330    }
2331
2332    fn get_property(&self, obj: &Value, name: &str) -> Result<Value> {
2333        // Property access on null/undefined throws TypeError (like JS)
2334        if matches!(obj, Value::Null | Value::Undefined) {
2335            return Err(ZapcodeError::TypeError(format!(
2336                "Cannot read properties of {} (reading '{}')",
2337                obj.to_js_string(),
2338                name
2339            )));
2340        }
2341        match obj {
2342            Value::Object(map) => {
2343                // Check if property exists as a real value on the object
2344                if let Some(val) = map.get(name) {
2345                    if !matches!(val, Value::Undefined) {
2346                        return Ok(val.clone());
2347                    }
2348                }
2349                // Check if this is a promise instance — expose .then/.catch/.finally
2350                if builtins::is_promise(obj) && is_promise_method(name) {
2351                    return Ok(Value::BuiltinMethod {
2352                        object_name: Arc::from("__promise__"),
2353                        method_name: Arc::from(name),
2354                    });
2355                }
2356                // Check if this is a known global object — return builtin method handle
2357                if let Some(global_name) = &self.last_global_name {
2358                    let known_globals = ["console", "Math", "JSON", "Object", "Array", "Promise"];
2359                    if known_globals.contains(&global_name.as_str()) {
2360                        return Ok(Value::BuiltinMethod {
2361                            object_name: Arc::from(global_name.as_str()),
2362                            method_name: Arc::from(name),
2363                        });
2364                    }
2365                }
2366                Ok(Value::Undefined)
2367            }
2368            Value::Array(arr) => match name {
2369                "length" => Ok(Value::Int(arr.len() as i64)),
2370                _ if is_array_method(name) => Ok(Value::BuiltinMethod {
2371                    object_name: Arc::from("__array__"),
2372                    method_name: Arc::from(name),
2373                }),
2374                _ => {
2375                    if let Ok(idx) = name.parse::<usize>() {
2376                        Ok(arr.get(idx).cloned().unwrap_or(Value::Undefined))
2377                    } else {
2378                        Ok(Value::Undefined)
2379                    }
2380                }
2381            },
2382            Value::String(s) => match name {
2383                "length" => Ok(Value::Int(s.chars().count() as i64)),
2384                _ if is_string_method(name) => Ok(Value::BuiltinMethod {
2385                    object_name: Arc::from("__string__"),
2386                    method_name: Arc::from(name),
2387                }),
2388                _ => Ok(Value::Undefined),
2389            },
2390            Value::Generator(_) => match name {
2391                "next" | "return" | "throw" => Ok(Value::BuiltinMethod {
2392                    object_name: Arc::from("__generator__"),
2393                    method_name: Arc::from(name),
2394                }),
2395                _ => Ok(Value::Undefined),
2396            },
2397            _ => Ok(Value::Undefined),
2398        }
2399    }
2400}
2401
2402// Re-export for the ParamPattern type used in function calls
2403use crate::parser::ir::ParamPattern;
2404
2405fn is_array_method(name: &str) -> bool {
2406    matches!(
2407        name,
2408        "push"
2409            | "pop"
2410            | "shift"
2411            | "unshift"
2412            | "splice"
2413            | "slice"
2414            | "concat"
2415            | "join"
2416            | "reverse"
2417            | "sort"
2418            | "indexOf"
2419            | "lastIndexOf"
2420            | "includes"
2421            | "find"
2422            | "findIndex"
2423            | "map"
2424            | "filter"
2425            | "reduce"
2426            | "forEach"
2427            | "every"
2428            | "some"
2429            | "flat"
2430            | "flatMap"
2431            | "fill"
2432            | "at"
2433            | "entries"
2434            | "keys"
2435            | "values"
2436    )
2437}
2438
2439fn is_string_method(name: &str) -> bool {
2440    matches!(
2441        name,
2442        "charAt"
2443            | "charCodeAt"
2444            | "indexOf"
2445            | "lastIndexOf"
2446            | "includes"
2447            | "startsWith"
2448            | "endsWith"
2449            | "slice"
2450            | "substring"
2451            | "substr"
2452            | "toUpperCase"
2453            | "toLowerCase"
2454            | "trim"
2455            | "trimStart"
2456            | "trimEnd"
2457            | "trimLeft"
2458            | "trimRight"
2459            | "padStart"
2460            | "padEnd"
2461            | "repeat"
2462            | "replace"
2463            | "replaceAll"
2464            | "split"
2465            | "concat"
2466            | "at"
2467            | "match"
2468            | "search"
2469            | "normalize"
2470    )
2471}
2472
2473fn is_promise_method(name: &str) -> bool {
2474    matches!(name, "then" | "catch" | "finally")
2475}
2476
2477/// Main entry point: compile and run TypeScript code.
2478pub struct ZapcodeRun {
2479    source: String,
2480    #[allow(dead_code)]
2481    inputs: Vec<String>,
2482    external_functions: Vec<String>,
2483    limits: ResourceLimits,
2484}
2485
2486impl ZapcodeRun {
2487    pub fn new(
2488        source: String,
2489        inputs: Vec<String>,
2490        external_functions: Vec<String>,
2491        limits: ResourceLimits,
2492    ) -> Result<Self> {
2493        Ok(Self {
2494            source,
2495            inputs,
2496            external_functions,
2497            limits,
2498        })
2499    }
2500
2501    pub fn run(&self, input_values: Vec<(String, Value)>) -> Result<RunResult> {
2502        let mut root_span = SpanBuilder::new("zapcode.run");
2503
2504        // Parse
2505        let parse_span = SpanBuilder::new("parse");
2506        let program = match crate::parser::parse(&self.source) {
2507            Ok(p) => {
2508                root_span.add_child(parse_span.finish_ok());
2509                p
2510            }
2511            Err(e) => {
2512                root_span.add_child(parse_span.finish_error(&e.to_string()));
2513                let _trace = ExecutionTrace {
2514                    root: root_span.finish(TraceStatus::Error),
2515                };
2516                return Err(e);
2517            }
2518        };
2519
2520        // Compile
2521        let compile_span = SpanBuilder::new("compile");
2522        let ext_set: HashSet<String> = self.external_functions.iter().cloned().collect();
2523        let compiled = match crate::compiler::compile_with_externals(&program, ext_set.clone()) {
2524            Ok(c) => {
2525                root_span.add_child(compile_span.finish_ok());
2526                c
2527            }
2528            Err(e) => {
2529                root_span.add_child(compile_span.finish_error(&e.to_string()));
2530                let _trace = ExecutionTrace {
2531                    root: root_span.finish(TraceStatus::Error),
2532                };
2533                return Err(e);
2534            }
2535        };
2536
2537        // Execute
2538        let execute_span = SpanBuilder::new("execute");
2539        let mut vm = Vm::new(compiled, self.limits.clone(), ext_set);
2540
2541        for (name, value) in input_values {
2542            vm.globals.insert(name, value);
2543        }
2544
2545        let state = match vm.run() {
2546            Ok(s) => {
2547                let status = match &s {
2548                    VmState::Complete(_) => TraceStatus::Ok,
2549                    VmState::Suspended {
2550                        function_name,
2551                        args,
2552                        ..
2553                    } => {
2554                        let mut span = execute_span;
2555                        span.set_attr("zapcode.suspended_on", function_name);
2556                        span.set_attr("zapcode.args_count", args.len());
2557                        root_span.add_child(span.finish(TraceStatus::Ok));
2558                        let trace = ExecutionTrace {
2559                            root: root_span.finish_ok(),
2560                        };
2561                        return Ok(RunResult {
2562                            state: s,
2563                            stdout: vm.stdout,
2564                            trace,
2565                        });
2566                    }
2567                };
2568                root_span.add_child(execute_span.finish(status));
2569                s
2570            }
2571            Err(e) => {
2572                root_span.add_child(execute_span.finish_error(&e.to_string()));
2573                let _trace = ExecutionTrace {
2574                    root: root_span.finish(TraceStatus::Error),
2575                };
2576                return Err(e);
2577            }
2578        };
2579
2580        let trace = ExecutionTrace {
2581            root: root_span.finish_ok(),
2582        };
2583
2584        Ok(RunResult {
2585            state,
2586            stdout: vm.stdout,
2587            trace,
2588        })
2589    }
2590
2591    /// Start execution. Like `run()`, but returns the raw `VmState` directly
2592    /// instead of wrapping it in a `RunResult`. This is the primary entry point
2593    /// for code that needs to handle suspension / snapshot / resume.
2594    pub fn start(&self, input_values: Vec<(String, Value)>) -> Result<VmState> {
2595        let result = self.run(input_values)?;
2596        Ok(result.state)
2597    }
2598
2599    pub fn run_simple(&self) -> Result<Value> {
2600        let result = self.run(Vec::new())?;
2601        match result.state {
2602            VmState::Complete(v) => Ok(v),
2603            VmState::Suspended { function_name, .. } => Err(ZapcodeError::RuntimeError(format!(
2604                "execution suspended on external function '{}' — use run() instead",
2605                function_name
2606            ))),
2607        }
2608    }
2609}
2610
2611/// Result of running a Zapcode program.
2612pub struct RunResult {
2613    pub state: VmState,
2614    pub stdout: String,
2615    /// Execution trace covering parse → compile → execute.
2616    pub trace: ExecutionTrace,
2617}
2618
2619/// Quick helper to evaluate a TypeScript expression.
2620pub fn eval_ts(source: &str) -> Result<Value> {
2621    let runner = ZapcodeRun::new(
2622        source.to_string(),
2623        Vec::new(),
2624        Vec::new(),
2625        ResourceLimits::default(),
2626    )?;
2627    runner.run_simple()
2628}
2629
2630/// Evaluate TypeScript and return both the value and stdout output.
2631pub fn eval_ts_with_output(source: &str) -> Result<(Value, String)> {
2632    let runner = ZapcodeRun::new(
2633        source.to_string(),
2634        Vec::new(),
2635        Vec::new(),
2636        ResourceLimits::default(),
2637    )?;
2638    let result = runner.run(Vec::new())?;
2639    match result.state {
2640        VmState::Complete(v) => Ok((v, result.stdout)),
2641        VmState::Suspended { function_name, .. } => Err(ZapcodeError::RuntimeError(format!(
2642            "execution suspended on external function '{}'",
2643            function_name
2644        ))),
2645    }
2646}