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