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#[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#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
29pub(crate) enum ReceiverSource {
30 Global(String),
32 Local { frame_index: usize, slot: usize },
35}
36
37#[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 pub(crate) this_value: Option<Value>,
46 pub(crate) receiver_source: Option<ReceiverSource>,
48}
49
50pub 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 last_receiver: Option<Value>,
63 last_receiver_source: Option<ReceiverSource>,
65 last_global_name: Option<String>,
67 last_load_source: Option<ReceiverSource>,
69 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 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 pub(crate) const BUILTIN_GLOBAL_NAMES: &'static [&'static str] =
111 &["console", "JSON", "Object", "Array", "Math", "Promise"];
112
113 #[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 builtins::register_globals(&mut globals);
130 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 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 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 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 locals.push(val);
215 }
216 _ => {
217 locals.push(args.get(i).cloned().unwrap_or(Value::Undefined));
218 }
219 }
220 }
221 locals
222 }
223
224 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 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 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 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 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 if self.frames.len() <= 1 {
294 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 let frame = self.frames.pop().unwrap();
304 self.tracker.pop_frame();
305 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 if let Some(try_info) = self.try_stack.pop() {
325 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 let error_val = Value::String(Arc::from(err.to_string()));
334 self.push(error_val)?;
335
336 self.current_frame_mut().ip = try_info.catch_ip;
338 } else {
339 return Err(err);
340 }
341 }
342 }
343 }
344 }
345
346 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 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 if self.frames.len() > target_frame_depth + 1 {
375 self.frames.pop();
377 self.tracker.pop_frame();
378 self.push(Value::Undefined)?;
379 continue;
380 } else {
381 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 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 if self.frames.len() == target_frame_depth {
405 return Ok(self.pop().unwrap_or(Value::Undefined));
407 }
408 }
409 Err(err) => {
410 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 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 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 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 execute_promise_method(
571 &mut self,
572 promise: Value,
573 method: &str,
574 args: Vec<Value>,
575 ) -> Result<Option<Value>> {
576 let (status, value, reason) = if let Value::Object(ref map) = promise {
577 let status = match map.get("status") {
578 Some(Value::String(s)) => s.to_string(),
579 _ => "pending".to_string(),
580 };
581 let value = map.get("value").cloned().unwrap_or(Value::Undefined);
582 let reason = map.get("reason").cloned().unwrap_or(Value::Undefined);
583 (status, value, reason)
584 } else {
585 return Ok(None);
586 };
587
588 let on_fulfilled = args.first().cloned().unwrap_or(Value::Undefined);
589 let on_rejected = args.get(1).cloned().unwrap_or(Value::Undefined);
590
591 match method {
592 "then" => {
593 if status == "resolved" {
594 if matches!(on_fulfilled, Value::Function(_)) {
595 let result = self.call_function_internal(&on_fulfilled, vec![value])?;
596 Ok(Some(builtins::make_resolved_promise(result)))
597 } else {
598 Ok(Some(promise))
600 }
601 } else if status == "rejected" {
602 if matches!(on_rejected, Value::Function(_)) {
603 let result = self.call_function_internal(&on_rejected, vec![reason])?;
604 Ok(Some(builtins::make_resolved_promise(result)))
605 } else {
606 Ok(Some(promise))
608 }
609 } else {
610 Ok(Some(promise))
611 }
612 }
613 "catch" => {
614 if status == "rejected" {
615 let handler = args.first().cloned().unwrap_or(Value::Undefined);
616 if matches!(handler, Value::Function(_)) {
617 let result = self.call_function_internal(&handler, vec![reason])?;
618 Ok(Some(builtins::make_resolved_promise(result)))
619 } else {
620 Ok(Some(promise))
621 }
622 } else {
623 Ok(Some(promise))
625 }
626 }
627 "finally" => {
628 let handler = args.first().cloned().unwrap_or(Value::Undefined);
629 if matches!(handler, Value::Function(_)) {
630 self.call_function_internal(&handler, vec![])?;
632 }
633 Ok(Some(promise))
635 }
636 _ => Ok(None),
637 }
638 }
639
640 fn alloc_generator_id(&mut self) -> u64 {
641 let id = self.next_generator_id;
642 self.next_generator_id += 1;
643 id
644 }
645
646 fn generator_next(&mut self, mut gen_obj: GeneratorObject, arg: Value) -> Result<Value> {
647 if gen_obj.done {
648 return Ok(self.make_iterator_result(Value::Undefined, true));
649 }
650 for (name, val) in &gen_obj.captured {
651 if !self.globals.contains_key(name) {
652 self.globals.insert(name.clone(), val.clone());
653 }
654 }
655 let func_idx = gen_obj.func_id.0;
656 match gen_obj.suspended.take() {
657 None => {
658 let func = &self.program.functions[func_idx];
659 self.tracker.push_frame();
660 let mut locals = Vec::with_capacity(func.local_count);
661 for param in func.params.iter() {
662 match param {
663 ParamPattern::Ident(name) => {
664 let val = gen_obj
665 .captured
666 .iter()
667 .find(|(n, _)| n == name)
668 .map(|(_, v)| v.clone())
669 .unwrap_or(Value::Undefined);
670 locals.push(val);
671 }
672 ParamPattern::Rest(name) => {
673 let val = gen_obj
674 .captured
675 .iter()
676 .find(|(n, _)| n == name)
677 .map(|(_, v)| v.clone())
678 .unwrap_or(Value::Array(Vec::new()));
679 locals.push(val);
680 }
681 _ => {
682 locals.push(Value::Undefined);
683 }
684 }
685 }
686 let stack_base = self.stack.len();
687 self.frames.push(CallFrame {
688 func_index: Some(func_idx),
689 ip: 0,
690 locals,
691 stack_base,
692 this_value: None,
693 receiver_source: None,
694 });
695 self.run_generator_until_yield_or_return(gen_obj)
696 }
697 Some(suspended) => {
698 self.tracker.push_frame();
699 let stack_base = self.stack.len();
700 for val in &suspended.stack {
701 self.push(val.clone())?;
702 }
703 self.push(arg)?;
704 self.frames.push(CallFrame {
705 func_index: Some(func_idx),
706 ip: suspended.ip,
707 locals: suspended.locals,
708 stack_base,
709 this_value: None,
710 receiver_source: None,
711 });
712 self.run_generator_until_yield_or_return(gen_obj)
713 }
714 }
715 }
716
717 fn store_generator(&mut self, gen_obj: GeneratorObject) {
720 let gen_key = format!("__gen_{}", gen_obj.id);
721 if gen_obj.done {
722 self.globals.remove(&gen_key);
723 } else {
724 self.globals.insert(gen_key, Value::Generator(gen_obj));
725 }
726 }
727
728 fn finish_generator(&mut self, mut gen_obj: GeneratorObject, value: Value) -> Value {
730 gen_obj.done = true;
731 gen_obj.suspended = None;
732 self.store_generator(gen_obj);
733 self.make_iterator_result(value, true)
734 }
735
736 fn run_generator_until_yield_or_return(
737 &mut self,
738 mut gen_obj: GeneratorObject,
739 ) -> Result<Value> {
740 let target_frame_depth = self.frames.len() - 1;
741 loop {
742 self.tracker.check_time(&self.limits)?;
743 let frame = self.frames.last().unwrap();
744 let instructions = match frame.func_index {
745 Some(idx) => &self.program.functions[idx].instructions,
746 None => &self.program.instructions,
747 };
748 if frame.ip >= instructions.len() {
749 if self.frames.len() > target_frame_depth + 1 {
750 let frame = self.frames.pop().unwrap();
751 self.tracker.pop_frame();
752 if let Some(this_val) = frame.this_value {
753 self.stack.truncate(frame.stack_base);
754 self.push(this_val)?;
755 } else {
756 self.push(Value::Undefined)?;
757 }
758 continue;
759 }
760 let frame = self.frames.pop().unwrap();
761 self.tracker.pop_frame();
762 self.stack.truncate(frame.stack_base);
763 let result = self.finish_generator(gen_obj, Value::Undefined);
764 return Ok(result);
765 }
766 let instr = instructions[frame.ip].clone();
767 if matches!(instr, Instruction::Yield) {
768 self.current_frame_mut().ip += 1;
769 let yielded_value = self.pop()?;
770 let frame = self.frames.pop().unwrap();
771 self.tracker.pop_frame();
772 let frame_stack: Vec<Value> = self.stack.drain(frame.stack_base..).collect();
773 gen_obj.suspended = Some(SuspendedFrame {
774 ip: frame.ip,
775 locals: frame.locals,
776 stack: frame_stack,
777 });
778 gen_obj.done = false;
779 self.store_generator(gen_obj);
780 return Ok(self.make_iterator_result(yielded_value, false));
781 }
782 if matches!(instr, Instruction::Return) {
783 self.current_frame_mut().ip += 1;
784 let return_val = self.pop().unwrap_or(Value::Undefined);
785 if self.frames.len() > target_frame_depth + 1 {
786 let frame = self.frames.pop().unwrap();
787 self.tracker.pop_frame();
788 self.stack.truncate(frame.stack_base);
789 self.push(return_val)?;
790 continue;
791 }
792 let frame = self.frames.pop().unwrap();
793 self.tracker.pop_frame();
794 self.stack.truncate(frame.stack_base);
795 let result = self.finish_generator(gen_obj, return_val);
796 return Ok(result);
797 }
798 let result = self.dispatch(instr);
799 match result {
800 Ok(Some(VmState::Complete(val))) => return Ok(val),
801 Ok(Some(VmState::Suspended { .. })) => {
802 return Err(ZapcodeError::RuntimeError(
803 "cannot suspend inside a generator".to_string(),
804 ));
805 }
806 Ok(None) => {
807 if self.frames.len() == target_frame_depth {
808 let return_val = self.pop().unwrap_or(Value::Undefined);
809 let result = self.finish_generator(gen_obj, return_val);
810 return Ok(result);
811 }
812 }
813 Err(err) => {
814 if let Some(try_info) = self.try_stack.pop() {
815 while self.frames.len() > try_info.frame_depth {
816 self.frames.pop();
817 self.tracker.pop_frame();
818 }
819 self.stack.truncate(try_info.stack_depth);
820 let error_val = Value::String(Arc::from(err.to_string()));
821 self.push(error_val)?;
822 self.current_frame_mut().ip = try_info.catch_ip;
823 } else {
824 return Err(err);
825 }
826 }
827 }
828 }
829 }
830
831 fn make_iterator_result(&self, value: Value, done: bool) -> Value {
832 let mut obj = IndexMap::new();
833 obj.insert(Arc::from("value"), value);
834 obj.insert(Arc::from("done"), Value::Bool(done));
835 Value::Object(obj)
836 }
837
838 fn dispatch(&mut self, instr: Instruction) -> Result<Option<VmState>> {
839 self.current_frame_mut().ip += 1;
840
841 match instr {
842 Instruction::Push(constant) => {
843 let value = match constant {
844 Constant::Undefined => Value::Undefined,
845 Constant::Null => Value::Null,
846 Constant::Bool(b) => Value::Bool(b),
847 Constant::Int(n) => Value::Int(n),
848 Constant::Float(n) => Value::Float(n),
849 Constant::String(s) => Value::String(Arc::from(s.as_str())),
850 };
851 self.push(value)?;
852 }
853 Instruction::Pop => {
854 self.pop()?;
855 }
856 Instruction::Dup => {
857 let val = self.peek()?.clone();
858 self.push(val)?;
859 }
860 Instruction::LoadLocal(idx) => {
861 let frame_index = self.frames.len() - 1;
862 let frame = self.current_frame();
863 let val = frame.locals.get(idx).cloned().unwrap_or(Value::Undefined);
864 self.last_load_source = Some(ReceiverSource::Local {
865 frame_index,
866 slot: idx,
867 });
868 self.push(val)?;
869 }
870 Instruction::StoreLocal(idx) => {
871 let val = self.pop()?;
872 let frame = self.current_frame_mut();
873 while frame.locals.len() <= idx {
874 frame.locals.push(Value::Undefined);
875 }
876 frame.locals[idx] = val;
877 }
878 Instruction::LoadGlobal(name) => {
879 let val = self.globals.get(&name).cloned().unwrap_or(Value::Undefined);
880 self.last_global_name = Some(name.clone());
881 if Self::BUILTIN_GLOBAL_NAMES.contains(&name.as_str()) {
885 self.last_load_source = None;
886 } else {
887 self.last_load_source = Some(ReceiverSource::Global(name));
888 }
889 self.push(val)?;
890 }
891 Instruction::StoreGlobal(name) => {
892 let val = self.pop()?;
893 self.globals.insert(name, val);
894 }
895 Instruction::DeclareLocal(_) => {
896 let frame = self.current_frame_mut();
897 frame.locals.push(Value::Undefined);
898 }
899
900 Instruction::Add => {
902 let right = self.pop()?;
903 let left = self.pop()?;
904 let result = match (&left, &right) {
905 (Value::Int(a), Value::Int(b)) => match a.checked_add(*b) {
906 Some(r) => Value::Int(r),
907 None => Value::Float(*a as f64 + *b as f64),
908 },
909 (Value::Float(a), Value::Float(b)) => Value::Float(a + b),
910 (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 + b),
911 (Value::Float(a), Value::Int(b)) => Value::Float(a + *b as f64),
912 (Value::String(a), _) => {
913 let rhs = right.to_js_string();
914 let new_len = a.len().saturating_add(rhs.len());
915 if new_len > 10_000_000 {
916 return Err(ZapcodeError::AllocationLimitExceeded);
917 }
918 let mut s = a.to_string();
919 s.push_str(&rhs);
920 Value::String(Arc::from(s.as_str()))
921 }
922 (_, Value::String(b)) => {
923 let lhs = left.to_js_string();
924 let new_len = lhs.len().saturating_add(b.len());
925 if new_len > 10_000_000 {
926 return Err(ZapcodeError::AllocationLimitExceeded);
927 }
928 let mut s = lhs;
929 s.push_str(b);
930 Value::String(Arc::from(s.as_str()))
931 }
932 _ => Value::Float(left.to_number() + right.to_number()),
933 };
934 self.push(result)?;
935 }
936 Instruction::Sub => {
937 let right = self.pop()?;
938 let left = self.pop()?;
939 let result = match (&left, &right) {
940 (Value::Int(a), Value::Int(b)) => match a.checked_sub(*b) {
941 Some(r) => Value::Int(r),
942 None => Value::Float(*a as f64 - *b as f64),
943 },
944 _ => Value::Float(left.to_number() - right.to_number()),
945 };
946 self.push(result)?;
947 }
948 Instruction::Mul => {
949 let right = self.pop()?;
950 let left = self.pop()?;
951 let result = match (&left, &right) {
952 (Value::Int(a), Value::Int(b)) => match a.checked_mul(*b) {
953 Some(r) => Value::Int(r),
954 None => Value::Float(*a as f64 * *b as f64),
955 },
956 _ => Value::Float(left.to_number() * right.to_number()),
957 };
958 self.push(result)?;
959 }
960 Instruction::Div => {
961 let right = self.pop()?;
962 let left = self.pop()?;
963 let result = Value::Float(left.to_number() / right.to_number());
964 self.push(result)?;
965 }
966 Instruction::Rem => {
967 let right = self.pop()?;
968 let left = self.pop()?;
969 let result = match (&left, &right) {
970 (Value::Int(a), Value::Int(b)) if *b != 0 => Value::Int(a % b),
971 _ => Value::Float(left.to_number() % right.to_number()),
972 };
973 self.push(result)?;
974 }
975 Instruction::Pow => {
976 let right = self.pop()?;
977 let left = self.pop()?;
978 let result = Value::Float(left.to_number().powf(right.to_number()));
979 self.push(result)?;
980 }
981 Instruction::Neg => {
982 let val = self.pop()?;
983 let result = match val {
984 Value::Int(n) => Value::Int(-n),
985 _ => Value::Float(-val.to_number()),
986 };
987 self.push(result)?;
988 }
989 Instruction::BitNot => {
990 let val = self.pop()?;
991 let n = val.to_number() as i32;
992 self.push(Value::Int(!n as i64))?;
993 }
994 Instruction::BitAnd => {
995 let right = self.pop()?;
996 let left = self.pop()?;
997 let result = (left.to_number() as i32) & (right.to_number() as i32);
998 self.push(Value::Int(result as i64))?;
999 }
1000 Instruction::BitOr => {
1001 let right = self.pop()?;
1002 let left = self.pop()?;
1003 let result = (left.to_number() as i32) | (right.to_number() as i32);
1004 self.push(Value::Int(result as i64))?;
1005 }
1006 Instruction::BitXor => {
1007 let right = self.pop()?;
1008 let left = self.pop()?;
1009 let result = (left.to_number() as i32) ^ (right.to_number() as i32);
1010 self.push(Value::Int(result as i64))?;
1011 }
1012 Instruction::Shl => {
1013 let right = self.pop()?;
1014 let left = self.pop()?;
1015 let shift = (right.to_number() as u32) & 0x1f;
1016 let result = (left.to_number() as i32) << shift;
1017 self.push(Value::Int(result as i64))?;
1018 }
1019 Instruction::Shr => {
1020 let right = self.pop()?;
1021 let left = self.pop()?;
1022 let shift = (right.to_number() as u32) & 0x1f;
1023 let result = (left.to_number() as i32) >> shift;
1024 self.push(Value::Int(result as i64))?;
1025 }
1026 Instruction::Ushr => {
1027 let right = self.pop()?;
1028 let left = self.pop()?;
1029 let shift = (right.to_number() as u32) & 0x1f;
1030 let result = (left.to_number() as u32) >> shift;
1031 self.push(Value::Int(result as i64))?;
1032 }
1033
1034 Instruction::Eq | Instruction::StrictEq => {
1036 let right = self.pop()?;
1037 let left = self.pop()?;
1038 self.push(Value::Bool(left.strict_eq(&right)))?;
1039 }
1040 Instruction::Neq | Instruction::StrictNeq => {
1041 let right = self.pop()?;
1042 let left = self.pop()?;
1043 self.push(Value::Bool(!left.strict_eq(&right)))?;
1044 }
1045 Instruction::Lt => {
1046 let right = self.pop()?;
1047 let left = self.pop()?;
1048 self.push(Value::Bool(left.to_number() < right.to_number()))?;
1049 }
1050 Instruction::Lte => {
1051 let right = self.pop()?;
1052 let left = self.pop()?;
1053 self.push(Value::Bool(left.to_number() <= right.to_number()))?;
1054 }
1055 Instruction::Gt => {
1056 let right = self.pop()?;
1057 let left = self.pop()?;
1058 self.push(Value::Bool(left.to_number() > right.to_number()))?;
1059 }
1060 Instruction::Gte => {
1061 let right = self.pop()?;
1062 let left = self.pop()?;
1063 self.push(Value::Bool(left.to_number() >= right.to_number()))?;
1064 }
1065
1066 Instruction::Not => {
1068 let val = self.pop()?;
1069 self.push(Value::Bool(!val.is_truthy()))?;
1070 }
1071
1072 Instruction::CreateArray(count) => {
1074 self.tracker.track_allocation(&self.limits)?;
1075 let mut arr = Vec::with_capacity(count);
1076 for _ in 0..count {
1077 arr.push(self.pop()?);
1078 }
1079 arr.reverse();
1080 self.push(Value::Array(arr))?;
1081 }
1082 Instruction::CreateObject(count) => {
1083 self.tracker.track_allocation(&self.limits)?;
1084 let mut obj = IndexMap::new();
1085 let mut entries = Vec::new();
1087 for _ in 0..count {
1088 let val = self.pop()?;
1089 let key = self.pop()?;
1090 entries.push((key, val));
1091 }
1092 entries.reverse();
1093 for (key, val) in entries {
1094 match key {
1095 Value::String(k) => {
1096 obj.insert(k, val);
1097 }
1098 _ => {
1099 let k: Arc<str> = Arc::from(key.to_js_string().as_str());
1100 obj.insert(k, val);
1101 }
1102 }
1103 }
1104 self.push(Value::Object(obj))?;
1105 }
1106 Instruction::GetProperty(name) => {
1107 let obj = self.pop()?;
1108 let result = self.get_property(&obj, &name)?;
1109 if matches!(result, Value::BuiltinMethod { .. } | Value::Function(_)) {
1111 self.last_receiver = Some(obj);
1112 self.last_receiver_source = self.last_load_source.take();
1113 } else {
1114 self.last_receiver_source = None;
1115 }
1116 self.push(result)?;
1117 }
1118 Instruction::SetProperty(name) => {
1119 let obj_val = self.pop()?;
1122 let value = self.pop()?;
1123 match obj_val {
1124 Value::Object(mut obj) => {
1125 obj.insert(Arc::from(name.as_str()), value);
1126 self.push(Value::Object(obj))?;
1128 }
1129 _ => {
1130 return Err(ZapcodeError::TypeError(format!(
1131 "cannot set property '{}' on {}",
1132 name,
1133 obj_val.type_name()
1134 )));
1135 }
1136 }
1137 }
1138 Instruction::GetIndex => {
1139 let index = self.pop()?;
1140 let obj = self.pop()?;
1141 let result = match (&obj, &index) {
1142 (Value::Array(arr), Value::Int(i)) => {
1143 arr.get(*i as usize).cloned().unwrap_or(Value::Undefined)
1144 }
1145 (Value::Array(arr), Value::Float(f)) => {
1146 arr.get(*f as usize).cloned().unwrap_or(Value::Undefined)
1147 }
1148 (Value::Object(map), Value::String(key)) => {
1149 map.get(key.as_ref()).cloned().unwrap_or(Value::Undefined)
1150 }
1151 (Value::Object(map), _) => {
1152 let key: Arc<str> = Arc::from(index.to_js_string().as_str());
1153 map.get(key.as_ref()).cloned().unwrap_or(Value::Undefined)
1154 }
1155 (Value::String(s), Value::Int(i)) => s
1156 .chars()
1157 .nth(*i as usize)
1158 .map(|c| Value::String(Arc::from(c.to_string().as_str())))
1159 .unwrap_or(Value::Undefined),
1160 _ => Value::Undefined,
1161 };
1162 self.push(result)?;
1163 }
1164 Instruction::SetIndex => {
1165 let index = self.pop()?;
1166 let mut obj = self.pop()?;
1167 let value = self.pop()?;
1168 match &mut obj {
1169 Value::Array(arr) => {
1170 let idx = match &index {
1171 Value::Int(i) if *i >= 0 => *i as usize,
1172 Value::Float(f) if *f >= 0.0 && *f == (*f as usize as f64) => {
1173 *f as usize
1174 }
1175 _ => {
1176 self.push(obj)?;
1178 return Ok(None);
1179 }
1180 };
1181 if idx > arr.len() + 1024 {
1183 return Err(ZapcodeError::RuntimeError(format!(
1184 "array index {} too far beyond length {}",
1185 idx,
1186 arr.len()
1187 )));
1188 }
1189 while arr.len() <= idx {
1190 arr.push(Value::Undefined);
1191 }
1192 arr[idx] = value;
1193 }
1194 Value::Object(map) => {
1195 let key: Arc<str> = Arc::from(index.to_js_string().as_str());
1196 map.insert(key, value);
1197 }
1198 _ => {}
1199 }
1200 self.push(obj)?;
1202 }
1203 Instruction::Spread => {
1204 }
1206 Instruction::In => {
1207 let right = self.pop()?;
1208 let left = self.pop()?;
1209 let result = match &right {
1210 Value::Object(map) => {
1211 let key = left.to_js_string();
1212 map.contains_key(key.as_str())
1213 }
1214 Value::Array(arr) => {
1215 if let Value::Int(i) = left {
1216 (i as usize) < arr.len()
1217 } else {
1218 false
1219 }
1220 }
1221 _ => false,
1222 };
1223 self.push(Value::Bool(result))?;
1224 }
1225 Instruction::InstanceOf => {
1226 let right = self.pop()?;
1227 let left = self.pop()?;
1228 let result = match (&left, &right) {
1230 (Value::Object(instance), Value::Object(class_obj)) => {
1231 if let (Some(inst_class), Some(class_name)) =
1232 (instance.get("__class__"), class_obj.get("__class_name__"))
1233 {
1234 inst_class == class_name
1235 } else {
1236 false
1237 }
1238 }
1239 _ => false,
1240 };
1241 self.push(Value::Bool(result))?;
1242 }
1243
1244 Instruction::CreateClosure(func_idx) => {
1246 let mut captured = Vec::new();
1248 for frame in &self.frames {
1250 let local_names = if let Some(fidx) = frame.func_index {
1251 &self.program.functions[fidx].local_names
1252 } else {
1253 &self.program.local_names
1254 };
1255 for (i, val) in frame.locals.iter().enumerate() {
1256 if let Some(name) = local_names.get(i) {
1257 captured.push((name.clone(), val.clone()));
1258 }
1259 }
1260 }
1261 let builtins = ["console", "Math", "JSON", "Object", "Array"];
1263 for (name, val) in &self.globals {
1264 if !builtins.contains(&name.as_str()) {
1265 captured.push((name.clone(), val.clone()));
1266 }
1267 }
1268 let closure = Closure {
1269 func_id: FunctionId(func_idx),
1270 captured,
1271 };
1272 self.push(Value::Function(closure))?;
1273 }
1274 Instruction::Call(arg_count) => {
1275 let mut args = Vec::with_capacity(arg_count);
1276 for _ in 0..arg_count {
1277 args.push(self.pop()?);
1278 }
1279 args.reverse();
1280
1281 let callee = self.pop()?;
1282 match callee {
1283 Value::Function(closure) => {
1284 let func_idx = closure.func_id.0;
1285 let is_generator = self.program.functions[func_idx].is_generator;
1286
1287 if is_generator {
1289 let params = self.program.functions[func_idx].params.clone();
1290 let gen_id = self.alloc_generator_id();
1291 let mut captured = closure.captured.clone();
1293 for (i, param) in params.iter().enumerate() {
1294 match param {
1295 ParamPattern::Ident(name) => {
1296 captured.push((
1297 name.clone(),
1298 args.get(i).cloned().unwrap_or(Value::Undefined),
1299 ));
1300 }
1301 ParamPattern::Rest(name) => {
1302 let rest: Vec<Value> = args[i..].to_vec();
1303 captured.push((name.clone(), Value::Array(rest)));
1304 }
1305 _ => {}
1306 }
1307 }
1308 let gen_obj = GeneratorObject {
1309 id: gen_id,
1310 func_id: closure.func_id,
1311 captured,
1312 suspended: None,
1313 done: false,
1314 };
1315 self.globals.insert(
1317 format!("__gen_{}", gen_id),
1318 Value::Generator(gen_obj.clone()),
1319 );
1320 self.push(Value::Generator(gen_obj))?;
1321 self.last_receiver = None;
1322 self.last_receiver_source = None;
1323 } else {
1324 let this_value = self.last_receiver.take();
1325 self.push_call_frame(&closure, &args, this_value)?;
1326 }
1327 }
1328 Value::BuiltinMethod {
1329 object_name,
1330 method_name,
1331 } => {
1332 let receiver = self.last_receiver.take();
1333 let result = match object_name.as_ref() {
1334 "__array__" => {
1335 if let Some(Value::Array(arr)) = &receiver {
1336 match method_name.as_ref() {
1338 "map" | "filter" | "forEach" | "find" | "findIndex"
1339 | "every" | "some" | "reduce" | "sort" | "flatMap" => {
1340 let result = self.execute_array_callback_method(
1341 arr.clone(),
1342 &method_name,
1343 args,
1344 )?;
1345 Some(result)
1346 }
1347 _ => builtins::call_builtin(
1348 &Value::Array(arr.clone()),
1349 &method_name,
1350 &args,
1351 &mut self.stdout,
1352 )?,
1353 }
1354 } else {
1355 None
1356 }
1357 }
1358 "__string__" => {
1359 if let Some(Value::String(s)) = &receiver {
1360 builtins::call_builtin(
1361 &Value::String(s.clone()),
1362 &method_name,
1363 &args,
1364 &mut self.stdout,
1365 )?
1366 } else {
1367 None
1368 }
1369 }
1370 "__generator__" => {
1371 if let Some(Value::Generator(gen_obj)) = receiver {
1372 match method_name.as_ref() {
1373 "next" => {
1374 let arg =
1375 args.into_iter().next().unwrap_or(Value::Undefined);
1376 let gen_key = format!("__gen_{}", gen_obj.id);
1380 if let Some(Value::Generator(g)) =
1381 self.globals.remove(&gen_key)
1382 {
1383 let result = self.generator_next(g, arg)?;
1384 Some(result)
1385 } else {
1386 Some(
1387 self.make_iterator_result(
1388 Value::Undefined,
1389 true,
1390 ),
1391 )
1392 }
1393 }
1394 "return" => {
1395 let val =
1396 args.into_iter().next().unwrap_or(Value::Undefined);
1397 let gen_key = format!("__gen_{}", gen_obj.id);
1398 if let Some(Value::Generator(g)) =
1399 self.globals.remove(&gen_key)
1400 {
1401 let result = self.finish_generator(g, val);
1402 Some(result)
1403 } else {
1404 Some(self.make_iterator_result(val, true))
1405 }
1406 }
1407 _ => None,
1408 }
1409 } else {
1410 None
1411 }
1412 }
1413 "__promise__" => {
1414 if let Some(promise) = receiver {
1415 self.execute_promise_method(promise, &method_name, args)?
1416 } else {
1417 None
1418 }
1419 }
1420 global_name => builtins::call_global_method(
1421 global_name,
1422 &method_name,
1423 &args,
1424 &mut self.stdout,
1425 )?,
1426 };
1427 match result {
1428 Some(val) => self.push(val)?,
1429 None => {
1430 return Err(ZapcodeError::TypeError(format!(
1431 "{}.{} is not a function",
1432 object_name, method_name
1433 )));
1434 }
1435 }
1436 }
1437 _ => {
1438 return Err(ZapcodeError::TypeError(format!(
1439 "{} is not a function",
1440 callee.to_js_string()
1441 )));
1442 }
1443 }
1444 }
1445 Instruction::Return => {
1446 let return_val = self.pop().unwrap_or(Value::Undefined);
1447
1448 if self.frames.len() <= 1 {
1449 return Ok(Some(VmState::Complete(return_val)));
1450 }
1451
1452 let frame = self.frames.pop().unwrap();
1453 self.tracker.pop_frame();
1454
1455 let actual_return = if let Some(ref this_val) = frame.this_value {
1459 if let Some(parent) = self.frames.last_mut() {
1461 if parent.this_value.is_some() {
1462 parent.this_value = Some(this_val.clone());
1463 }
1464 }
1465 if let Some(ref source) = frame.receiver_source {
1470 match source {
1471 ReceiverSource::Global(name) => {
1472 self.globals.insert(name.clone(), this_val.clone());
1473 }
1474 ReceiverSource::Local { frame_index, slot } => {
1475 if let Some(target_frame) = self.frames.get_mut(*frame_index) {
1476 while target_frame.locals.len() <= *slot {
1477 target_frame.locals.push(Value::Undefined);
1478 }
1479 target_frame.locals[*slot] = this_val.clone();
1480 }
1481 }
1482 }
1483 }
1484 if matches!(return_val, Value::Undefined) {
1485 this_val.clone()
1486 } else {
1487 return_val
1488 }
1489 } else {
1490 return_val
1491 };
1492
1493 self.stack.truncate(frame.stack_base);
1494 self.push(actual_return)?;
1495 }
1496 Instruction::CallExternal(name, arg_count) => {
1497 if !self.external_functions.contains(&name) {
1498 return Err(ZapcodeError::UnknownExternalFunction(name));
1499 }
1500 let mut args = Vec::with_capacity(arg_count);
1501 for _ in 0..arg_count {
1502 args.push(self.pop()?);
1503 }
1504 args.reverse();
1505 let snapshot = ZapcodeSnapshot::capture(self)?;
1507 return Ok(Some(VmState::Suspended {
1508 function_name: name,
1509 args,
1510 snapshot,
1511 }));
1512 }
1513
1514 Instruction::Jump(target) => {
1516 self.current_frame_mut().ip = target;
1517 }
1518 Instruction::JumpIfFalse(target) => {
1519 let val = self.pop()?;
1520 if !val.is_truthy() {
1521 self.current_frame_mut().ip = target;
1522 }
1523 }
1524 Instruction::JumpIfTrue(target) => {
1525 let val = self.pop()?;
1526 if val.is_truthy() {
1527 self.current_frame_mut().ip = target;
1528 }
1529 }
1530 Instruction::JumpIfNullish(target) => {
1531 let val = self.peek()?;
1532 if matches!(val, Value::Null | Value::Undefined) {
1533 self.current_frame_mut().ip = target;
1534 }
1535 }
1536
1537 Instruction::SetupLoop => {}
1539 Instruction::Break | Instruction::Continue => {
1540 }
1542
1543 Instruction::GetIterator => {
1545 let val = self.pop()?;
1546 match val {
1547 Value::Array(arr) => {
1548 let iter_obj = Value::Array(vec![Value::Array(arr), Value::Int(0)]);
1550 self.push(iter_obj)?;
1551 }
1552 Value::String(s) => {
1553 let chars: Vec<Value> = s
1554 .chars()
1555 .map(|c| Value::String(Arc::from(c.to_string().as_str())))
1556 .collect();
1557 let iter_obj = Value::Array(vec![Value::Array(chars), Value::Int(0)]);
1558 self.push(iter_obj)?;
1559 }
1560 Value::Generator(gen_obj) => {
1561 let iter_obj = Value::Array(vec![
1562 Value::String(Arc::from("__gen__")),
1563 Value::Int(gen_obj.id as i64),
1564 Value::Bool(false),
1565 ]);
1566 self.push(iter_obj)?;
1567 }
1568 _ => {
1569 return Err(ZapcodeError::TypeError(format!(
1570 "{} is not iterable",
1571 val.type_name()
1572 )));
1573 }
1574 }
1575 }
1576 Instruction::IteratorNext => {
1577 let iter = self.pop()?;
1578 if let Value::Array(ref items) = iter {
1580 if items.len() == 3 {
1581 if let Value::String(ref s) = items[0] {
1582 if s.as_ref() == "__gen__" {
1583 let gen_id = match &items[1] {
1584 Value::Int(id) => *id as u64,
1585 _ => {
1586 return Err(ZapcodeError::RuntimeError(
1587 "bad gen iter".into(),
1588 ))
1589 }
1590 };
1591 let gen_key = format!("__gen_{}", gen_id);
1592 let gen_obj = if let Some(Value::Generator(g)) =
1593 self.globals.remove(&gen_key)
1594 {
1595 g
1596 } else {
1597 self.push(Value::Array(vec![
1598 Value::String(Arc::from("__gen__")),
1599 Value::Int(gen_id as i64),
1600 Value::Bool(true),
1601 ]))?;
1602 self.push(Value::Undefined)?;
1603 return Ok(None);
1604 };
1605 let result = self.generator_next(gen_obj, Value::Undefined)?;
1606 if let Value::Object(ref obj) = result {
1607 let done = obj
1608 .get("done")
1609 .is_some_and(|v| matches!(v, Value::Bool(true)));
1610 let value =
1611 obj.get("value").cloned().unwrap_or(Value::Undefined);
1612 self.push(Value::Array(vec![
1613 Value::String(Arc::from("__gen__")),
1614 Value::Int(gen_id as i64),
1615 Value::Bool(done),
1616 ]))?;
1617 self.push(value)?;
1618 } else {
1619 self.push(iter)?;
1620 self.push(Value::Undefined)?;
1621 }
1622 return Ok(None);
1623 }
1624 }
1625 }
1626 }
1627 match iter {
1628 Value::Array(ref items) if items.len() == 2 => {
1629 let arr = match &items[0] {
1630 Value::Array(a) => a,
1631 _ => return Err(ZapcodeError::RuntimeError("invalid iterator".into())),
1632 };
1633 let idx = match &items[1] {
1634 Value::Int(i) => *i as usize,
1635 _ => return Err(ZapcodeError::RuntimeError("invalid iterator".into())),
1636 };
1637 if idx < arr.len() {
1638 let value = arr[idx].clone();
1639 let new_iter =
1641 Value::Array(vec![items[0].clone(), Value::Int((idx + 1) as i64)]);
1642 self.push(new_iter)?;
1644 self.push(value)?;
1645 } else {
1646 self.push(iter)?;
1648 self.push(Value::Undefined)?;
1649 }
1650 }
1651 _ => {
1652 return Err(ZapcodeError::RuntimeError("invalid iterator state".into()));
1653 }
1654 }
1655 }
1656 Instruction::IteratorDone => {
1657 let value = self.pop()?;
1658 let iter = self.peek()?;
1659 if let Value::Array(items) = iter {
1661 if items.len() == 3 {
1662 if let Value::String(ref s) = items[0] {
1663 if s.as_ref() == "__gen__" {
1664 let done = matches!(&items[2], Value::Bool(true));
1665 if !done {
1666 self.push(value)?;
1667 }
1668 self.push(Value::Bool(done))?;
1669 return Ok(None);
1670 }
1671 }
1672 }
1673 }
1674 let iter = self.peek()?;
1675 match iter {
1676 Value::Array(items) if items.len() == 2 => {
1677 let arr = match &items[0] {
1678 Value::Array(a) => a,
1679 _ => {
1680 self.push(value)?;
1681 self.push(Value::Bool(true))?;
1682 return Ok(None);
1683 }
1684 };
1685 let idx = match &items[1] {
1686 Value::Int(i) => *i as usize,
1687 _ => {
1688 self.push(value)?;
1689 self.push(Value::Bool(true))?;
1690 return Ok(None);
1691 }
1692 };
1693 let done = idx > arr.len();
1694 if !done {
1695 self.push(value)?;
1697 }
1698 self.push(Value::Bool(done))?;
1699 }
1700 _ => {
1701 self.push(value)?;
1702 self.push(Value::Bool(true))?;
1703 }
1704 }
1705 }
1706
1707 Instruction::SetupTry(catch_ip, _) => {
1709 self.try_stack.push(TryInfo {
1710 catch_ip,
1711 frame_depth: self.frames.len(),
1712 stack_depth: self.stack.len(),
1713 });
1714 }
1715 Instruction::Throw => {
1716 let val = self.pop()?;
1717 let msg = val.to_js_string();
1718 return Err(ZapcodeError::RuntimeError(msg));
1719 }
1720 Instruction::EndTry => {
1721 self.try_stack.pop();
1722 }
1723
1724 Instruction::TypeOf => {
1726 let val = self.pop()?;
1727 let type_str = val.type_name();
1728 self.push(Value::String(Arc::from(type_str)))?;
1729 }
1730
1731 Instruction::Void => {
1733 self.pop()?;
1734 self.push(Value::Undefined)?;
1735 }
1736
1737 Instruction::Increment => {
1739 let val = self.pop()?;
1740 let result = match val {
1741 Value::Int(n) => Value::Int(n + 1),
1742 _ => Value::Float(val.to_number() + 1.0),
1743 };
1744 self.push(result)?;
1745 }
1746 Instruction::Decrement => {
1747 let val = self.pop()?;
1748 let result = match val {
1749 Value::Int(n) => Value::Int(n - 1),
1750 _ => Value::Float(val.to_number() - 1.0),
1751 };
1752 self.push(result)?;
1753 }
1754
1755 Instruction::ConcatStrings(count) => {
1757 let mut parts = Vec::with_capacity(count);
1758 for _ in 0..count {
1759 parts.push(self.pop()?);
1760 }
1761 parts.reverse();
1762 let result: String = parts.iter().map(|v| v.to_js_string()).collect();
1763 self.push(Value::String(Arc::from(result.as_str())))?;
1764 }
1765
1766 Instruction::DestructureObject(keys) => {
1768 let obj = self.pop()?;
1769 for key in keys {
1770 let val = self.get_property(&obj, &key)?;
1771 self.push(val)?;
1772 }
1773 }
1774 Instruction::DestructureArray(count) => {
1775 let arr = self.pop()?;
1776 match arr {
1777 Value::Array(items) => {
1778 for i in 0..count {
1779 self.push(items.get(i).cloned().unwrap_or(Value::Undefined))?;
1780 }
1781 }
1782 _ => {
1783 for _ in 0..count {
1784 self.push(Value::Undefined)?;
1785 }
1786 }
1787 }
1788 }
1789
1790 Instruction::Nop => {}
1791
1792 Instruction::CreateGenerator(_func_idx) => {
1794 }
1796 Instruction::Yield => {
1797 return Err(ZapcodeError::RuntimeError(
1800 "yield can only be used inside a generator function".to_string(),
1801 ));
1802 }
1803
1804 Instruction::Await => {
1805 let val = self.pop()?;
1809 if builtins::is_promise(&val) {
1810 if let Value::Object(map) = &val {
1811 let status = map.get("status").cloned().unwrap_or(Value::Undefined);
1812 match status {
1813 Value::String(s) if s.as_ref() == "resolved" => {
1814 let inner = map.get("value").cloned().unwrap_or(Value::Undefined);
1815 self.push(inner)?;
1816 }
1817 Value::String(s) if s.as_ref() == "rejected" => {
1818 let reason = map.get("reason").cloned().unwrap_or(Value::Undefined);
1819 return Err(ZapcodeError::RuntimeError(format!(
1820 "Unhandled promise rejection: {}",
1821 reason.to_js_string()
1822 )));
1823 }
1824 _ => {
1825 self.push(val)?;
1827 }
1828 }
1829 } else {
1830 self.push(val)?;
1831 }
1832 } else {
1833 self.push(val)?;
1835 }
1836 }
1837
1838 Instruction::CreateClass {
1840 name,
1841 n_methods,
1842 n_statics,
1843 has_super,
1844 } => {
1845 let constructor = self.pop()?;
1852
1853 let mut prototype = IndexMap::new();
1855 for _ in 0..n_methods {
1856 let method_closure = self.pop()?;
1857 let method_name = self.pop()?;
1858 if let Value::String(mn) = method_name {
1859 prototype.insert(mn, method_closure);
1860 }
1861 }
1862
1863 let mut statics = IndexMap::new();
1865 for _ in 0..n_statics {
1866 let method_closure = self.pop()?;
1867 let method_name = self.pop()?;
1868 if let Value::String(mn) = method_name {
1869 statics.insert(mn, method_closure);
1870 }
1871 }
1872
1873 let super_class = if has_super { Some(self.pop()?) } else { None };
1875
1876 if let Some(Value::Object(ref sc)) = super_class {
1878 if let Some(Value::Object(super_proto)) = sc.get("__prototype__").cloned() {
1879 let mut merged = super_proto;
1881 for (k, v) in prototype {
1882 merged.insert(k, v);
1883 }
1884 prototype = merged;
1885 }
1886 }
1887
1888 let mut class_obj = IndexMap::new();
1890 class_obj.insert(
1891 Arc::from("__class_name__"),
1892 Value::String(Arc::from(name.as_str())),
1893 );
1894 class_obj.insert(Arc::from("__constructor__"), constructor);
1895 class_obj.insert(Arc::from("__prototype__"), Value::Object(prototype));
1896
1897 if let Some(sc) = super_class {
1899 class_obj.insert(Arc::from("__super__"), sc);
1900 }
1901
1902 for (k, v) in statics {
1904 class_obj.insert(k, v);
1905 }
1906
1907 self.push(Value::Object(class_obj))?;
1908 }
1909
1910 Instruction::Construct(arg_count) => {
1911 let mut args = Vec::with_capacity(arg_count);
1912 for _ in 0..arg_count {
1913 args.push(self.pop()?);
1914 }
1915 args.reverse();
1916
1917 let callee = self.pop()?;
1918
1919 match &callee {
1920 Value::Object(class_obj) if class_obj.contains_key("__class_name__") => {
1921 let mut instance = IndexMap::new();
1923
1924 if let Some(Value::Object(proto)) = class_obj.get("__prototype__") {
1926 for (k, v) in proto {
1927 instance.insert(k.clone(), v.clone());
1928 }
1929 }
1930
1931 if let Some(class_name) = class_obj.get("__class_name__") {
1933 instance.insert(Arc::from("__class__"), class_name.clone());
1934 }
1935
1936 let instance_val = Value::Object(instance);
1937
1938 if let Some(ctor) = class_obj.get("__constructor__") {
1940 if let Value::Function(closure) = ctor {
1941 self.last_receiver_source = None;
1944 self.push_call_frame(closure, &args, Some(instance_val))?;
1945 self.last_receiver = None;
1946 } else {
1947 self.push(instance_val)?;
1949 }
1950 } else {
1951 self.push(instance_val)?;
1953 }
1954 }
1955 Value::Function(closure) => {
1956 self.push_call_frame(closure, &args, None)?;
1958 self.last_receiver = None;
1959 }
1960 _ => {
1961 return Err(ZapcodeError::TypeError(format!(
1962 "{} is not a constructor",
1963 callee.to_js_string()
1964 )));
1965 }
1966 }
1967 }
1968
1969 Instruction::LoadThis => {
1970 let this_val = self
1972 .frames
1973 .iter()
1974 .rev()
1975 .find_map(|f| f.this_value.clone())
1976 .unwrap_or(Value::Undefined);
1977 self.push(this_val)?;
1978 }
1979 Instruction::StoreThis => {
1980 let val = self.pop()?;
1981 for frame in self.frames.iter_mut().rev() {
1983 if frame.this_value.is_some() {
1984 frame.this_value = Some(val);
1985 break;
1986 }
1987 }
1988 }
1989 Instruction::CallSuper(arg_count) => {
1990 let mut args = Vec::with_capacity(arg_count);
1991 for _ in 0..arg_count {
1992 args.push(self.pop()?);
1993 }
1994 args.reverse();
1995
1996 let this_val = self
1998 .frames
1999 .iter()
2000 .rev()
2001 .find_map(|f| f.this_value.clone())
2002 .unwrap_or(Value::Undefined);
2003
2004 let mut super_ctor = None;
2009 for val in self.globals.values() {
2010 if let Value::Object(obj) = val {
2011 if let Some(Value::Object(super_class)) = obj.get("__super__") {
2012 if let Some(ctor) = super_class.get("__constructor__") {
2013 super_ctor = Some(ctor.clone());
2014 break;
2015 }
2016 }
2017 }
2018 }
2019
2020 if let Some(Value::Function(closure)) = super_ctor {
2021 self.last_receiver_source = None;
2022 self.push_call_frame(&closure, &args, Some(this_val))?;
2023 self.last_receiver = None;
2024 } else {
2025 self.push(Value::Undefined)?;
2027 }
2028 }
2029 }
2030
2031 Ok(None)
2032 }
2033
2034 fn get_property(&self, obj: &Value, name: &str) -> Result<Value> {
2035 if matches!(obj, Value::Null | Value::Undefined) {
2037 return Err(ZapcodeError::TypeError(format!(
2038 "Cannot read properties of {} (reading '{}')",
2039 obj.to_js_string(),
2040 name
2041 )));
2042 }
2043 match obj {
2044 Value::Object(map) => {
2045 if let Some(val) = map.get(name) {
2047 if !matches!(val, Value::Undefined) {
2048 return Ok(val.clone());
2049 }
2050 }
2051 if builtins::is_promise(obj) && is_promise_method(name) {
2053 return Ok(Value::BuiltinMethod {
2054 object_name: Arc::from("__promise__"),
2055 method_name: Arc::from(name),
2056 });
2057 }
2058 if let Some(global_name) = &self.last_global_name {
2060 let known_globals = ["console", "Math", "JSON", "Object", "Array", "Promise"];
2061 if known_globals.contains(&global_name.as_str()) {
2062 return Ok(Value::BuiltinMethod {
2063 object_name: Arc::from(global_name.as_str()),
2064 method_name: Arc::from(name),
2065 });
2066 }
2067 }
2068 Ok(Value::Undefined)
2069 }
2070 Value::Array(arr) => match name {
2071 "length" => Ok(Value::Int(arr.len() as i64)),
2072 _ if is_array_method(name) => Ok(Value::BuiltinMethod {
2073 object_name: Arc::from("__array__"),
2074 method_name: Arc::from(name),
2075 }),
2076 _ => {
2077 if let Ok(idx) = name.parse::<usize>() {
2078 Ok(arr.get(idx).cloned().unwrap_or(Value::Undefined))
2079 } else {
2080 Ok(Value::Undefined)
2081 }
2082 }
2083 },
2084 Value::String(s) => match name {
2085 "length" => Ok(Value::Int(s.chars().count() as i64)),
2086 _ if is_string_method(name) => Ok(Value::BuiltinMethod {
2087 object_name: Arc::from("__string__"),
2088 method_name: Arc::from(name),
2089 }),
2090 _ => Ok(Value::Undefined),
2091 },
2092 Value::Generator(_) => match name {
2093 "next" | "return" | "throw" => Ok(Value::BuiltinMethod {
2094 object_name: Arc::from("__generator__"),
2095 method_name: Arc::from(name),
2096 }),
2097 _ => Ok(Value::Undefined),
2098 },
2099 _ => Ok(Value::Undefined),
2100 }
2101 }
2102}
2103
2104use crate::parser::ir::ParamPattern;
2106
2107fn is_array_method(name: &str) -> bool {
2108 matches!(
2109 name,
2110 "push"
2111 | "pop"
2112 | "shift"
2113 | "unshift"
2114 | "splice"
2115 | "slice"
2116 | "concat"
2117 | "join"
2118 | "reverse"
2119 | "sort"
2120 | "indexOf"
2121 | "lastIndexOf"
2122 | "includes"
2123 | "find"
2124 | "findIndex"
2125 | "map"
2126 | "filter"
2127 | "reduce"
2128 | "forEach"
2129 | "every"
2130 | "some"
2131 | "flat"
2132 | "flatMap"
2133 | "fill"
2134 | "at"
2135 | "entries"
2136 | "keys"
2137 | "values"
2138 )
2139}
2140
2141fn is_string_method(name: &str) -> bool {
2142 matches!(
2143 name,
2144 "charAt"
2145 | "charCodeAt"
2146 | "indexOf"
2147 | "lastIndexOf"
2148 | "includes"
2149 | "startsWith"
2150 | "endsWith"
2151 | "slice"
2152 | "substring"
2153 | "substr"
2154 | "toUpperCase"
2155 | "toLowerCase"
2156 | "trim"
2157 | "trimStart"
2158 | "trimEnd"
2159 | "trimLeft"
2160 | "trimRight"
2161 | "padStart"
2162 | "padEnd"
2163 | "repeat"
2164 | "replace"
2165 | "replaceAll"
2166 | "split"
2167 | "concat"
2168 | "at"
2169 | "match"
2170 | "search"
2171 | "normalize"
2172 )
2173}
2174
2175fn is_promise_method(name: &str) -> bool {
2176 matches!(name, "then" | "catch" | "finally")
2177}
2178
2179pub struct ZapcodeRun {
2181 source: String,
2182 #[allow(dead_code)]
2183 inputs: Vec<String>,
2184 external_functions: Vec<String>,
2185 limits: ResourceLimits,
2186}
2187
2188impl ZapcodeRun {
2189 pub fn new(
2190 source: String,
2191 inputs: Vec<String>,
2192 external_functions: Vec<String>,
2193 limits: ResourceLimits,
2194 ) -> Result<Self> {
2195 Ok(Self {
2196 source,
2197 inputs,
2198 external_functions,
2199 limits,
2200 })
2201 }
2202
2203 pub fn run(&self, input_values: Vec<(String, Value)>) -> Result<RunResult> {
2204 let program = crate::parser::parse(&self.source)?;
2205 let ext_set: HashSet<String> = self.external_functions.iter().cloned().collect();
2206 let compiled = crate::compiler::compile_with_externals(&program, ext_set.clone())?;
2207 let mut vm = Vm::new(compiled, self.limits.clone(), ext_set);
2208
2209 for (name, value) in input_values {
2211 vm.globals.insert(name, value);
2212 }
2213
2214 let state = vm.run()?;
2215 Ok(RunResult {
2216 state,
2217 stdout: vm.stdout,
2218 })
2219 }
2220
2221 pub fn start(&self, input_values: Vec<(String, Value)>) -> Result<VmState> {
2225 let program = crate::parser::parse(&self.source)?;
2226 let ext_set: HashSet<String> = self.external_functions.iter().cloned().collect();
2227 let compiled = crate::compiler::compile_with_externals(&program, ext_set.clone())?;
2228 let mut vm = Vm::new(compiled, self.limits.clone(), ext_set);
2229
2230 for (name, value) in input_values {
2231 vm.globals.insert(name, value);
2232 }
2233
2234 vm.run()
2235 }
2236
2237 pub fn run_simple(&self) -> Result<Value> {
2238 let result = self.run(Vec::new())?;
2239 match result.state {
2240 VmState::Complete(v) => Ok(v),
2241 VmState::Suspended { function_name, .. } => Err(ZapcodeError::RuntimeError(format!(
2242 "execution suspended on external function '{}' — use run() instead",
2243 function_name
2244 ))),
2245 }
2246 }
2247}
2248
2249pub struct RunResult {
2251 pub state: VmState,
2252 pub stdout: String,
2253}
2254
2255pub fn eval_ts(source: &str) -> Result<Value> {
2257 let runner = ZapcodeRun::new(
2258 source.to_string(),
2259 Vec::new(),
2260 Vec::new(),
2261 ResourceLimits::default(),
2262 )?;
2263 runner.run_simple()
2264}
2265
2266pub fn eval_ts_with_output(source: &str) -> Result<(Value, String)> {
2268 let runner = ZapcodeRun::new(
2269 source.to_string(),
2270 Vec::new(),
2271 Vec::new(),
2272 ResourceLimits::default(),
2273 )?;
2274 let result = runner.run(Vec::new())?;
2275 match result.state {
2276 VmState::Complete(v) => Ok((v, result.stdout)),
2277 VmState::Suspended { function_name, .. } => Err(ZapcodeError::RuntimeError(format!(
2278 "execution suspended on external function '{}'",
2279 function_name
2280 ))),
2281 }
2282}