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 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 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 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 if Self::BUILTIN_GLOBAL_NAMES.contains(&name.as_str()) {
813 self.last_load_source = None;
814 } else {
815 self.last_load_source = Some(ReceiverSource::Global(name));
816 }
817 self.push(val)?;
818 }
819 Instruction::StoreGlobal(name) => {
820 let val = self.pop()?;
821 self.globals.insert(name, val);
822 }
823 Instruction::DeclareLocal(_) => {
824 let frame = self.current_frame_mut();
825 frame.locals.push(Value::Undefined);
826 }
827
828 Instruction::Add => {
830 let right = self.pop()?;
831 let left = self.pop()?;
832 let result = match (&left, &right) {
833 (Value::Int(a), Value::Int(b)) => match a.checked_add(*b) {
834 Some(r) => Value::Int(r),
835 None => Value::Float(*a as f64 + *b as f64),
836 },
837 (Value::Float(a), Value::Float(b)) => Value::Float(a + b),
838 (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 + b),
839 (Value::Float(a), Value::Int(b)) => Value::Float(a + *b as f64),
840 (Value::String(a), _) => {
841 let rhs = right.to_js_string();
842 let new_len = a.len().saturating_add(rhs.len());
843 if new_len > 10_000_000 {
844 return Err(ZapcodeError::AllocationLimitExceeded);
845 }
846 let mut s = a.to_string();
847 s.push_str(&rhs);
848 Value::String(Arc::from(s.as_str()))
849 }
850 (_, Value::String(b)) => {
851 let lhs = left.to_js_string();
852 let new_len = lhs.len().saturating_add(b.len());
853 if new_len > 10_000_000 {
854 return Err(ZapcodeError::AllocationLimitExceeded);
855 }
856 let mut s = lhs;
857 s.push_str(b);
858 Value::String(Arc::from(s.as_str()))
859 }
860 _ => Value::Float(left.to_number() + right.to_number()),
861 };
862 self.push(result)?;
863 }
864 Instruction::Sub => {
865 let right = self.pop()?;
866 let left = self.pop()?;
867 let result = match (&left, &right) {
868 (Value::Int(a), Value::Int(b)) => match a.checked_sub(*b) {
869 Some(r) => Value::Int(r),
870 None => Value::Float(*a as f64 - *b as f64),
871 },
872 _ => Value::Float(left.to_number() - right.to_number()),
873 };
874 self.push(result)?;
875 }
876 Instruction::Mul => {
877 let right = self.pop()?;
878 let left = self.pop()?;
879 let result = match (&left, &right) {
880 (Value::Int(a), Value::Int(b)) => match a.checked_mul(*b) {
881 Some(r) => Value::Int(r),
882 None => Value::Float(*a as f64 * *b as f64),
883 },
884 _ => Value::Float(left.to_number() * right.to_number()),
885 };
886 self.push(result)?;
887 }
888 Instruction::Div => {
889 let right = self.pop()?;
890 let left = self.pop()?;
891 let result = Value::Float(left.to_number() / right.to_number());
892 self.push(result)?;
893 }
894 Instruction::Rem => {
895 let right = self.pop()?;
896 let left = self.pop()?;
897 let result = match (&left, &right) {
898 (Value::Int(a), Value::Int(b)) if *b != 0 => Value::Int(a % b),
899 _ => Value::Float(left.to_number() % right.to_number()),
900 };
901 self.push(result)?;
902 }
903 Instruction::Pow => {
904 let right = self.pop()?;
905 let left = self.pop()?;
906 let result = Value::Float(left.to_number().powf(right.to_number()));
907 self.push(result)?;
908 }
909 Instruction::Neg => {
910 let val = self.pop()?;
911 let result = match val {
912 Value::Int(n) => Value::Int(-n),
913 _ => Value::Float(-val.to_number()),
914 };
915 self.push(result)?;
916 }
917 Instruction::BitNot => {
918 let val = self.pop()?;
919 let n = val.to_number() as i32;
920 self.push(Value::Int(!n as i64))?;
921 }
922 Instruction::BitAnd => {
923 let right = self.pop()?;
924 let left = self.pop()?;
925 let result = (left.to_number() as i32) & (right.to_number() as i32);
926 self.push(Value::Int(result as i64))?;
927 }
928 Instruction::BitOr => {
929 let right = self.pop()?;
930 let left = self.pop()?;
931 let result = (left.to_number() as i32) | (right.to_number() as i32);
932 self.push(Value::Int(result as i64))?;
933 }
934 Instruction::BitXor => {
935 let right = self.pop()?;
936 let left = self.pop()?;
937 let result = (left.to_number() as i32) ^ (right.to_number() as i32);
938 self.push(Value::Int(result as i64))?;
939 }
940 Instruction::Shl => {
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::Shr => {
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 i32) >> shift;
952 self.push(Value::Int(result as i64))?;
953 }
954 Instruction::Ushr => {
955 let right = self.pop()?;
956 let left = self.pop()?;
957 let shift = (right.to_number() as u32) & 0x1f;
958 let result = (left.to_number() as u32) >> shift;
959 self.push(Value::Int(result as i64))?;
960 }
961
962 Instruction::Eq | Instruction::StrictEq => {
964 let right = self.pop()?;
965 let left = self.pop()?;
966 self.push(Value::Bool(left.strict_eq(&right)))?;
967 }
968 Instruction::Neq | Instruction::StrictNeq => {
969 let right = self.pop()?;
970 let left = self.pop()?;
971 self.push(Value::Bool(!left.strict_eq(&right)))?;
972 }
973 Instruction::Lt => {
974 let right = self.pop()?;
975 let left = self.pop()?;
976 self.push(Value::Bool(left.to_number() < right.to_number()))?;
977 }
978 Instruction::Lte => {
979 let right = self.pop()?;
980 let left = self.pop()?;
981 self.push(Value::Bool(left.to_number() <= right.to_number()))?;
982 }
983 Instruction::Gt => {
984 let right = self.pop()?;
985 let left = self.pop()?;
986 self.push(Value::Bool(left.to_number() > right.to_number()))?;
987 }
988 Instruction::Gte => {
989 let right = self.pop()?;
990 let left = self.pop()?;
991 self.push(Value::Bool(left.to_number() >= right.to_number()))?;
992 }
993
994 Instruction::Not => {
996 let val = self.pop()?;
997 self.push(Value::Bool(!val.is_truthy()))?;
998 }
999
1000 Instruction::CreateArray(count) => {
1002 self.tracker.track_allocation(&self.limits)?;
1003 let mut arr = Vec::with_capacity(count);
1004 for _ in 0..count {
1005 arr.push(self.pop()?);
1006 }
1007 arr.reverse();
1008 self.push(Value::Array(arr))?;
1009 }
1010 Instruction::CreateObject(count) => {
1011 self.tracker.track_allocation(&self.limits)?;
1012 let mut obj = IndexMap::new();
1013 let mut entries = Vec::new();
1015 for _ in 0..count {
1016 let val = self.pop()?;
1017 let key = self.pop()?;
1018 entries.push((key, val));
1019 }
1020 entries.reverse();
1021 for (key, val) in entries {
1022 match key {
1023 Value::String(k) => {
1024 obj.insert(k, val);
1025 }
1026 _ => {
1027 let k: Arc<str> = Arc::from(key.to_js_string().as_str());
1028 obj.insert(k, val);
1029 }
1030 }
1031 }
1032 self.push(Value::Object(obj))?;
1033 }
1034 Instruction::GetProperty(name) => {
1035 let obj = self.pop()?;
1036 let result = self.get_property(&obj, &name)?;
1037 if matches!(result, Value::BuiltinMethod { .. } | Value::Function(_)) {
1039 self.last_receiver = Some(obj);
1040 self.last_receiver_source = self.last_load_source.take();
1041 } else {
1042 self.last_receiver_source = None;
1043 }
1044 self.push(result)?;
1045 }
1046 Instruction::SetProperty(name) => {
1047 let obj_val = self.pop()?;
1050 let value = self.pop()?;
1051 match obj_val {
1052 Value::Object(mut obj) => {
1053 obj.insert(Arc::from(name.as_str()), value);
1054 self.push(Value::Object(obj))?;
1056 }
1057 _ => {
1058 return Err(ZapcodeError::TypeError(format!(
1059 "cannot set property '{}' on {}",
1060 name,
1061 obj_val.type_name()
1062 )));
1063 }
1064 }
1065 }
1066 Instruction::GetIndex => {
1067 let index = self.pop()?;
1068 let obj = self.pop()?;
1069 let result = match (&obj, &index) {
1070 (Value::Array(arr), Value::Int(i)) => {
1071 arr.get(*i as usize).cloned().unwrap_or(Value::Undefined)
1072 }
1073 (Value::Array(arr), Value::Float(f)) => {
1074 arr.get(*f as usize).cloned().unwrap_or(Value::Undefined)
1075 }
1076 (Value::Object(map), Value::String(key)) => {
1077 map.get(key.as_ref()).cloned().unwrap_or(Value::Undefined)
1078 }
1079 (Value::Object(map), _) => {
1080 let key: Arc<str> = Arc::from(index.to_js_string().as_str());
1081 map.get(key.as_ref()).cloned().unwrap_or(Value::Undefined)
1082 }
1083 (Value::String(s), Value::Int(i)) => s
1084 .chars()
1085 .nth(*i as usize)
1086 .map(|c| Value::String(Arc::from(c.to_string().as_str())))
1087 .unwrap_or(Value::Undefined),
1088 _ => Value::Undefined,
1089 };
1090 self.push(result)?;
1091 }
1092 Instruction::SetIndex => {
1093 let index = self.pop()?;
1094 let mut obj = self.pop()?;
1095 let value = self.pop()?;
1096 match &mut obj {
1097 Value::Array(arr) => {
1098 let idx = match &index {
1099 Value::Int(i) if *i >= 0 => *i as usize,
1100 Value::Float(f) if *f >= 0.0 && *f == (*f as usize as f64) => {
1101 *f as usize
1102 }
1103 _ => {
1104 self.push(obj)?;
1106 return Ok(None);
1107 }
1108 };
1109 if idx > arr.len() + 1024 {
1111 return Err(ZapcodeError::RuntimeError(format!(
1112 "array index {} too far beyond length {}",
1113 idx,
1114 arr.len()
1115 )));
1116 }
1117 while arr.len() <= idx {
1118 arr.push(Value::Undefined);
1119 }
1120 arr[idx] = value;
1121 }
1122 Value::Object(map) => {
1123 let key: Arc<str> = Arc::from(index.to_js_string().as_str());
1124 map.insert(key, value);
1125 }
1126 _ => {}
1127 }
1128 self.push(obj)?;
1130 }
1131 Instruction::Spread => {
1132 }
1134 Instruction::In => {
1135 let right = self.pop()?;
1136 let left = self.pop()?;
1137 let result = match &right {
1138 Value::Object(map) => {
1139 let key = left.to_js_string();
1140 map.contains_key(key.as_str())
1141 }
1142 Value::Array(arr) => {
1143 if let Value::Int(i) = left {
1144 (i as usize) < arr.len()
1145 } else {
1146 false
1147 }
1148 }
1149 _ => false,
1150 };
1151 self.push(Value::Bool(result))?;
1152 }
1153 Instruction::InstanceOf => {
1154 let right = self.pop()?;
1155 let left = self.pop()?;
1156 let result = match (&left, &right) {
1158 (Value::Object(instance), Value::Object(class_obj)) => {
1159 if let (Some(inst_class), Some(class_name)) =
1160 (instance.get("__class__"), class_obj.get("__class_name__"))
1161 {
1162 inst_class == class_name
1163 } else {
1164 false
1165 }
1166 }
1167 _ => false,
1168 };
1169 self.push(Value::Bool(result))?;
1170 }
1171
1172 Instruction::CreateClosure(func_idx) => {
1174 let mut captured = Vec::new();
1176 for frame in &self.frames {
1178 let local_names = if let Some(fidx) = frame.func_index {
1179 &self.program.functions[fidx].local_names
1180 } else {
1181 &self.program.local_names
1182 };
1183 for (i, val) in frame.locals.iter().enumerate() {
1184 if let Some(name) = local_names.get(i) {
1185 captured.push((name.clone(), val.clone()));
1186 }
1187 }
1188 }
1189 let builtins = ["console", "Math", "JSON", "Object", "Array"];
1191 for (name, val) in &self.globals {
1192 if !builtins.contains(&name.as_str()) {
1193 captured.push((name.clone(), val.clone()));
1194 }
1195 }
1196 let closure = Closure {
1197 func_id: FunctionId(func_idx),
1198 captured,
1199 };
1200 self.push(Value::Function(closure))?;
1201 }
1202 Instruction::Call(arg_count) => {
1203 let mut args = Vec::with_capacity(arg_count);
1204 for _ in 0..arg_count {
1205 args.push(self.pop()?);
1206 }
1207 args.reverse();
1208
1209 let callee = self.pop()?;
1210 match callee {
1211 Value::Function(closure) => {
1212 let func_idx = closure.func_id.0;
1213 let is_generator = self.program.functions[func_idx].is_generator;
1214
1215 if is_generator {
1217 let params = self.program.functions[func_idx].params.clone();
1218 let gen_id = self.alloc_generator_id();
1219 let mut captured = closure.captured.clone();
1221 for (i, param) in params.iter().enumerate() {
1222 match param {
1223 ParamPattern::Ident(name) => {
1224 captured.push((
1225 name.clone(),
1226 args.get(i).cloned().unwrap_or(Value::Undefined),
1227 ));
1228 }
1229 ParamPattern::Rest(name) => {
1230 let rest: Vec<Value> = args[i..].to_vec();
1231 captured.push((name.clone(), Value::Array(rest)));
1232 }
1233 _ => {}
1234 }
1235 }
1236 let gen_obj = GeneratorObject {
1237 id: gen_id,
1238 func_id: closure.func_id,
1239 captured,
1240 suspended: None,
1241 done: false,
1242 };
1243 self.globals.insert(
1245 format!("__gen_{}", gen_id),
1246 Value::Generator(gen_obj.clone()),
1247 );
1248 self.push(Value::Generator(gen_obj))?;
1249 self.last_receiver = None;
1250 self.last_receiver_source = None;
1251 } else {
1252 let this_value = self.last_receiver.take();
1253 self.push_call_frame(&closure, &args, this_value)?;
1254 }
1255 }
1256 Value::BuiltinMethod {
1257 object_name,
1258 method_name,
1259 } => {
1260 let receiver = self.last_receiver.take();
1261 let result = match object_name.as_ref() {
1262 "__array__" => {
1263 if let Some(Value::Array(arr)) = &receiver {
1264 match method_name.as_ref() {
1266 "map" | "filter" | "forEach" | "find" | "findIndex"
1267 | "every" | "some" | "reduce" | "sort" | "flatMap" => {
1268 let result = self.execute_array_callback_method(
1269 arr.clone(),
1270 &method_name,
1271 args,
1272 )?;
1273 Some(result)
1274 }
1275 _ => builtins::call_builtin(
1276 &Value::Array(arr.clone()),
1277 &method_name,
1278 &args,
1279 &mut self.stdout,
1280 )?,
1281 }
1282 } else {
1283 None
1284 }
1285 }
1286 "__string__" => {
1287 if let Some(Value::String(s)) = &receiver {
1288 builtins::call_builtin(
1289 &Value::String(s.clone()),
1290 &method_name,
1291 &args,
1292 &mut self.stdout,
1293 )?
1294 } else {
1295 None
1296 }
1297 }
1298 "__generator__" => {
1299 if let Some(Value::Generator(gen_obj)) = receiver {
1300 match method_name.as_ref() {
1301 "next" => {
1302 let arg =
1303 args.into_iter().next().unwrap_or(Value::Undefined);
1304 let gen_key = format!("__gen_{}", gen_obj.id);
1308 if let Some(Value::Generator(g)) =
1309 self.globals.remove(&gen_key)
1310 {
1311 let result = self.generator_next(g, arg)?;
1312 Some(result)
1313 } else {
1314 Some(
1315 self.make_iterator_result(
1316 Value::Undefined,
1317 true,
1318 ),
1319 )
1320 }
1321 }
1322 "return" => {
1323 let val =
1324 args.into_iter().next().unwrap_or(Value::Undefined);
1325 let gen_key = format!("__gen_{}", gen_obj.id);
1326 if let Some(Value::Generator(g)) =
1327 self.globals.remove(&gen_key)
1328 {
1329 let result = self.finish_generator(g, val);
1330 Some(result)
1331 } else {
1332 Some(self.make_iterator_result(val, true))
1333 }
1334 }
1335 _ => None,
1336 }
1337 } else {
1338 None
1339 }
1340 }
1341 global_name => builtins::call_global_method(
1342 global_name,
1343 &method_name,
1344 &args,
1345 &mut self.stdout,
1346 )?,
1347 };
1348 match result {
1349 Some(val) => self.push(val)?,
1350 None => {
1351 return Err(ZapcodeError::TypeError(format!(
1352 "{}.{} is not a function",
1353 object_name, method_name
1354 )));
1355 }
1356 }
1357 }
1358 _ => {
1359 return Err(ZapcodeError::TypeError(format!(
1360 "{} is not a function",
1361 callee.to_js_string()
1362 )));
1363 }
1364 }
1365 }
1366 Instruction::Return => {
1367 let return_val = self.pop().unwrap_or(Value::Undefined);
1368
1369 if self.frames.len() <= 1 {
1370 return Ok(Some(VmState::Complete(return_val)));
1371 }
1372
1373 let frame = self.frames.pop().unwrap();
1374 self.tracker.pop_frame();
1375
1376 let actual_return = if let Some(ref this_val) = frame.this_value {
1380 if let Some(parent) = self.frames.last_mut() {
1382 if parent.this_value.is_some() {
1383 parent.this_value = Some(this_val.clone());
1384 }
1385 }
1386 if let Some(ref source) = frame.receiver_source {
1391 match source {
1392 ReceiverSource::Global(name) => {
1393 self.globals.insert(name.clone(), this_val.clone());
1394 }
1395 ReceiverSource::Local { frame_index, slot } => {
1396 if let Some(target_frame) = self.frames.get_mut(*frame_index) {
1397 while target_frame.locals.len() <= *slot {
1398 target_frame.locals.push(Value::Undefined);
1399 }
1400 target_frame.locals[*slot] = this_val.clone();
1401 }
1402 }
1403 }
1404 }
1405 if matches!(return_val, Value::Undefined) {
1406 this_val.clone()
1407 } else {
1408 return_val
1409 }
1410 } else {
1411 return_val
1412 };
1413
1414 self.stack.truncate(frame.stack_base);
1415 self.push(actual_return)?;
1416 }
1417 Instruction::CallExternal(name, arg_count) => {
1418 if !self.external_functions.contains(&name) {
1419 return Err(ZapcodeError::UnknownExternalFunction(name));
1420 }
1421 let mut args = Vec::with_capacity(arg_count);
1422 for _ in 0..arg_count {
1423 args.push(self.pop()?);
1424 }
1425 args.reverse();
1426 let snapshot = ZapcodeSnapshot::capture(self)?;
1428 return Ok(Some(VmState::Suspended {
1429 function_name: name,
1430 args,
1431 snapshot,
1432 }));
1433 }
1434
1435 Instruction::Jump(target) => {
1437 self.current_frame_mut().ip = target;
1438 }
1439 Instruction::JumpIfFalse(target) => {
1440 let val = self.pop()?;
1441 if !val.is_truthy() {
1442 self.current_frame_mut().ip = target;
1443 }
1444 }
1445 Instruction::JumpIfTrue(target) => {
1446 let val = self.pop()?;
1447 if val.is_truthy() {
1448 self.current_frame_mut().ip = target;
1449 }
1450 }
1451 Instruction::JumpIfNullish(target) => {
1452 let val = self.peek()?;
1453 if matches!(val, Value::Null | Value::Undefined) {
1454 self.current_frame_mut().ip = target;
1455 }
1456 }
1457
1458 Instruction::SetupLoop => {}
1460 Instruction::Break | Instruction::Continue => {
1461 }
1463
1464 Instruction::GetIterator => {
1466 let val = self.pop()?;
1467 match val {
1468 Value::Array(arr) => {
1469 let iter_obj = Value::Array(vec![Value::Array(arr), Value::Int(0)]);
1471 self.push(iter_obj)?;
1472 }
1473 Value::String(s) => {
1474 let chars: Vec<Value> = s
1475 .chars()
1476 .map(|c| Value::String(Arc::from(c.to_string().as_str())))
1477 .collect();
1478 let iter_obj = Value::Array(vec![Value::Array(chars), Value::Int(0)]);
1479 self.push(iter_obj)?;
1480 }
1481 Value::Generator(gen_obj) => {
1482 let iter_obj = Value::Array(vec![
1483 Value::String(Arc::from("__gen__")),
1484 Value::Int(gen_obj.id as i64),
1485 Value::Bool(false),
1486 ]);
1487 self.push(iter_obj)?;
1488 }
1489 _ => {
1490 return Err(ZapcodeError::TypeError(format!(
1491 "{} is not iterable",
1492 val.type_name()
1493 )));
1494 }
1495 }
1496 }
1497 Instruction::IteratorNext => {
1498 let iter = self.pop()?;
1499 if let Value::Array(ref items) = iter {
1501 if items.len() == 3 {
1502 if let Value::String(ref s) = items[0] {
1503 if s.as_ref() == "__gen__" {
1504 let gen_id = match &items[1] {
1505 Value::Int(id) => *id as u64,
1506 _ => {
1507 return Err(ZapcodeError::RuntimeError(
1508 "bad gen iter".into(),
1509 ))
1510 }
1511 };
1512 let gen_key = format!("__gen_{}", gen_id);
1513 let gen_obj = if let Some(Value::Generator(g)) =
1514 self.globals.remove(&gen_key)
1515 {
1516 g
1517 } else {
1518 self.push(Value::Array(vec![
1519 Value::String(Arc::from("__gen__")),
1520 Value::Int(gen_id as i64),
1521 Value::Bool(true),
1522 ]))?;
1523 self.push(Value::Undefined)?;
1524 return Ok(None);
1525 };
1526 let result = self.generator_next(gen_obj, Value::Undefined)?;
1527 if let Value::Object(ref obj) = result {
1528 let done = obj
1529 .get("done")
1530 .is_some_and(|v| matches!(v, Value::Bool(true)));
1531 let value =
1532 obj.get("value").cloned().unwrap_or(Value::Undefined);
1533 self.push(Value::Array(vec![
1534 Value::String(Arc::from("__gen__")),
1535 Value::Int(gen_id as i64),
1536 Value::Bool(done),
1537 ]))?;
1538 self.push(value)?;
1539 } else {
1540 self.push(iter)?;
1541 self.push(Value::Undefined)?;
1542 }
1543 return Ok(None);
1544 }
1545 }
1546 }
1547 }
1548 match iter {
1549 Value::Array(ref items) if items.len() == 2 => {
1550 let arr = match &items[0] {
1551 Value::Array(a) => a,
1552 _ => return Err(ZapcodeError::RuntimeError("invalid iterator".into())),
1553 };
1554 let idx = match &items[1] {
1555 Value::Int(i) => *i as usize,
1556 _ => return Err(ZapcodeError::RuntimeError("invalid iterator".into())),
1557 };
1558 if idx < arr.len() {
1559 let value = arr[idx].clone();
1560 let new_iter =
1562 Value::Array(vec![items[0].clone(), Value::Int((idx + 1) as i64)]);
1563 self.push(new_iter)?;
1565 self.push(value)?;
1566 } else {
1567 self.push(iter)?;
1569 self.push(Value::Undefined)?;
1570 }
1571 }
1572 _ => {
1573 return Err(ZapcodeError::RuntimeError("invalid iterator state".into()));
1574 }
1575 }
1576 }
1577 Instruction::IteratorDone => {
1578 let value = self.pop()?;
1579 let iter = self.peek()?;
1580 if let Value::Array(items) = iter {
1582 if items.len() == 3 {
1583 if let Value::String(ref s) = items[0] {
1584 if s.as_ref() == "__gen__" {
1585 let done = matches!(&items[2], Value::Bool(true));
1586 if !done {
1587 self.push(value)?;
1588 }
1589 self.push(Value::Bool(done))?;
1590 return Ok(None);
1591 }
1592 }
1593 }
1594 }
1595 let iter = self.peek()?;
1596 match iter {
1597 Value::Array(items) if items.len() == 2 => {
1598 let arr = match &items[0] {
1599 Value::Array(a) => a,
1600 _ => {
1601 self.push(value)?;
1602 self.push(Value::Bool(true))?;
1603 return Ok(None);
1604 }
1605 };
1606 let idx = match &items[1] {
1607 Value::Int(i) => *i as usize,
1608 _ => {
1609 self.push(value)?;
1610 self.push(Value::Bool(true))?;
1611 return Ok(None);
1612 }
1613 };
1614 let done = idx > arr.len();
1615 if !done {
1616 self.push(value)?;
1618 }
1619 self.push(Value::Bool(done))?;
1620 }
1621 _ => {
1622 self.push(value)?;
1623 self.push(Value::Bool(true))?;
1624 }
1625 }
1626 }
1627
1628 Instruction::SetupTry(catch_ip, _) => {
1630 self.try_stack.push(TryInfo {
1631 catch_ip,
1632 frame_depth: self.frames.len(),
1633 stack_depth: self.stack.len(),
1634 });
1635 }
1636 Instruction::Throw => {
1637 let val = self.pop()?;
1638 let msg = val.to_js_string();
1639 return Err(ZapcodeError::RuntimeError(msg));
1640 }
1641 Instruction::EndTry => {
1642 self.try_stack.pop();
1643 }
1644
1645 Instruction::TypeOf => {
1647 let val = self.pop()?;
1648 let type_str = val.type_name();
1649 self.push(Value::String(Arc::from(type_str)))?;
1650 }
1651
1652 Instruction::Void => {
1654 self.pop()?;
1655 self.push(Value::Undefined)?;
1656 }
1657
1658 Instruction::Increment => {
1660 let val = self.pop()?;
1661 let result = match val {
1662 Value::Int(n) => Value::Int(n + 1),
1663 _ => Value::Float(val.to_number() + 1.0),
1664 };
1665 self.push(result)?;
1666 }
1667 Instruction::Decrement => {
1668 let val = self.pop()?;
1669 let result = match val {
1670 Value::Int(n) => Value::Int(n - 1),
1671 _ => Value::Float(val.to_number() - 1.0),
1672 };
1673 self.push(result)?;
1674 }
1675
1676 Instruction::ConcatStrings(count) => {
1678 let mut parts = Vec::with_capacity(count);
1679 for _ in 0..count {
1680 parts.push(self.pop()?);
1681 }
1682 parts.reverse();
1683 let result: String = parts.iter().map(|v| v.to_js_string()).collect();
1684 self.push(Value::String(Arc::from(result.as_str())))?;
1685 }
1686
1687 Instruction::DestructureObject(keys) => {
1689 let obj = self.pop()?;
1690 for key in keys {
1691 let val = self.get_property(&obj, &key)?;
1692 self.push(val)?;
1693 }
1694 }
1695 Instruction::DestructureArray(count) => {
1696 let arr = self.pop()?;
1697 match arr {
1698 Value::Array(items) => {
1699 for i in 0..count {
1700 self.push(items.get(i).cloned().unwrap_or(Value::Undefined))?;
1701 }
1702 }
1703 _ => {
1704 for _ in 0..count {
1705 self.push(Value::Undefined)?;
1706 }
1707 }
1708 }
1709 }
1710
1711 Instruction::Nop => {}
1712
1713 Instruction::CreateGenerator(_func_idx) => {
1715 }
1717 Instruction::Yield => {
1718 return Err(ZapcodeError::RuntimeError(
1721 "yield can only be used inside a generator function".to_string(),
1722 ));
1723 }
1724
1725 Instruction::Await => {
1726 let val = self.pop()?;
1730 if builtins::is_promise(&val) {
1731 if let Value::Object(map) = &val {
1732 let status = map.get("status").cloned().unwrap_or(Value::Undefined);
1733 match status {
1734 Value::String(s) if s.as_ref() == "resolved" => {
1735 let inner = map.get("value").cloned().unwrap_or(Value::Undefined);
1736 self.push(inner)?;
1737 }
1738 Value::String(s) if s.as_ref() == "rejected" => {
1739 let reason = map.get("reason").cloned().unwrap_or(Value::Undefined);
1740 return Err(ZapcodeError::RuntimeError(format!(
1741 "Unhandled promise rejection: {}",
1742 reason.to_js_string()
1743 )));
1744 }
1745 _ => {
1746 self.push(val)?;
1748 }
1749 }
1750 } else {
1751 self.push(val)?;
1752 }
1753 } else {
1754 self.push(val)?;
1756 }
1757 }
1758
1759 Instruction::CreateClass {
1761 name,
1762 n_methods,
1763 n_statics,
1764 has_super,
1765 } => {
1766 let constructor = self.pop()?;
1773
1774 let mut prototype = IndexMap::new();
1776 for _ in 0..n_methods {
1777 let method_closure = self.pop()?;
1778 let method_name = self.pop()?;
1779 if let Value::String(mn) = method_name {
1780 prototype.insert(mn, method_closure);
1781 }
1782 }
1783
1784 let mut statics = IndexMap::new();
1786 for _ in 0..n_statics {
1787 let method_closure = self.pop()?;
1788 let method_name = self.pop()?;
1789 if let Value::String(mn) = method_name {
1790 statics.insert(mn, method_closure);
1791 }
1792 }
1793
1794 let super_class = if has_super { Some(self.pop()?) } else { None };
1796
1797 if let Some(Value::Object(ref sc)) = super_class {
1799 if let Some(Value::Object(super_proto)) = sc.get("__prototype__").cloned() {
1800 let mut merged = super_proto;
1802 for (k, v) in prototype {
1803 merged.insert(k, v);
1804 }
1805 prototype = merged;
1806 }
1807 }
1808
1809 let mut class_obj = IndexMap::new();
1811 class_obj.insert(
1812 Arc::from("__class_name__"),
1813 Value::String(Arc::from(name.as_str())),
1814 );
1815 class_obj.insert(Arc::from("__constructor__"), constructor);
1816 class_obj.insert(Arc::from("__prototype__"), Value::Object(prototype));
1817
1818 if let Some(sc) = super_class {
1820 class_obj.insert(Arc::from("__super__"), sc);
1821 }
1822
1823 for (k, v) in statics {
1825 class_obj.insert(k, v);
1826 }
1827
1828 self.push(Value::Object(class_obj))?;
1829 }
1830
1831 Instruction::Construct(arg_count) => {
1832 let mut args = Vec::with_capacity(arg_count);
1833 for _ in 0..arg_count {
1834 args.push(self.pop()?);
1835 }
1836 args.reverse();
1837
1838 let callee = self.pop()?;
1839
1840 match &callee {
1841 Value::Object(class_obj) if class_obj.contains_key("__class_name__") => {
1842 let mut instance = IndexMap::new();
1844
1845 if let Some(Value::Object(proto)) = class_obj.get("__prototype__") {
1847 for (k, v) in proto {
1848 instance.insert(k.clone(), v.clone());
1849 }
1850 }
1851
1852 if let Some(class_name) = class_obj.get("__class_name__") {
1854 instance.insert(Arc::from("__class__"), class_name.clone());
1855 }
1856
1857 let instance_val = Value::Object(instance);
1858
1859 if let Some(ctor) = class_obj.get("__constructor__") {
1861 if let Value::Function(closure) = ctor {
1862 self.last_receiver_source = None;
1865 self.push_call_frame(closure, &args, Some(instance_val))?;
1866 self.last_receiver = None;
1867 } else {
1868 self.push(instance_val)?;
1870 }
1871 } else {
1872 self.push(instance_val)?;
1874 }
1875 }
1876 Value::Function(closure) => {
1877 self.push_call_frame(closure, &args, None)?;
1879 self.last_receiver = None;
1880 }
1881 _ => {
1882 return Err(ZapcodeError::TypeError(format!(
1883 "{} is not a constructor",
1884 callee.to_js_string()
1885 )));
1886 }
1887 }
1888 }
1889
1890 Instruction::LoadThis => {
1891 let this_val = self
1893 .frames
1894 .iter()
1895 .rev()
1896 .find_map(|f| f.this_value.clone())
1897 .unwrap_or(Value::Undefined);
1898 self.push(this_val)?;
1899 }
1900 Instruction::StoreThis => {
1901 let val = self.pop()?;
1902 for frame in self.frames.iter_mut().rev() {
1904 if frame.this_value.is_some() {
1905 frame.this_value = Some(val);
1906 break;
1907 }
1908 }
1909 }
1910 Instruction::CallSuper(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 this_val = self
1919 .frames
1920 .iter()
1921 .rev()
1922 .find_map(|f| f.this_value.clone())
1923 .unwrap_or(Value::Undefined);
1924
1925 let mut super_ctor = None;
1930 for val in self.globals.values() {
1931 if let Value::Object(obj) = val {
1932 if let Some(Value::Object(super_class)) = obj.get("__super__") {
1933 if let Some(ctor) = super_class.get("__constructor__") {
1934 super_ctor = Some(ctor.clone());
1935 break;
1936 }
1937 }
1938 }
1939 }
1940
1941 if let Some(Value::Function(closure)) = super_ctor {
1942 self.last_receiver_source = None;
1943 self.push_call_frame(&closure, &args, Some(this_val))?;
1944 self.last_receiver = None;
1945 } else {
1946 self.push(Value::Undefined)?;
1948 }
1949 }
1950 }
1951
1952 Ok(None)
1953 }
1954
1955 fn get_property(&self, obj: &Value, name: &str) -> Result<Value> {
1956 if matches!(obj, Value::Null | Value::Undefined) {
1958 return Err(ZapcodeError::TypeError(format!(
1959 "Cannot read properties of {} (reading '{}')",
1960 obj.to_js_string(),
1961 name
1962 )));
1963 }
1964 match obj {
1965 Value::Object(map) => {
1966 if let Some(val) = map.get(name) {
1968 if !matches!(val, Value::Undefined) {
1969 return Ok(val.clone());
1970 }
1971 }
1972 if let Some(global_name) = &self.last_global_name {
1974 let known_globals = ["console", "Math", "JSON", "Object", "Array", "Promise"];
1975 if known_globals.contains(&global_name.as_str()) {
1976 return Ok(Value::BuiltinMethod {
1977 object_name: Arc::from(global_name.as_str()),
1978 method_name: Arc::from(name),
1979 });
1980 }
1981 }
1982 Ok(Value::Undefined)
1983 }
1984 Value::Array(arr) => match name {
1985 "length" => Ok(Value::Int(arr.len() as i64)),
1986 _ if is_array_method(name) => Ok(Value::BuiltinMethod {
1987 object_name: Arc::from("__array__"),
1988 method_name: Arc::from(name),
1989 }),
1990 _ => {
1991 if let Ok(idx) = name.parse::<usize>() {
1992 Ok(arr.get(idx).cloned().unwrap_or(Value::Undefined))
1993 } else {
1994 Ok(Value::Undefined)
1995 }
1996 }
1997 },
1998 Value::String(s) => match name {
1999 "length" => Ok(Value::Int(s.chars().count() as i64)),
2000 _ if is_string_method(name) => Ok(Value::BuiltinMethod {
2001 object_name: Arc::from("__string__"),
2002 method_name: Arc::from(name),
2003 }),
2004 _ => Ok(Value::Undefined),
2005 },
2006 Value::Generator(_) => match name {
2007 "next" | "return" | "throw" => Ok(Value::BuiltinMethod {
2008 object_name: Arc::from("__generator__"),
2009 method_name: Arc::from(name),
2010 }),
2011 _ => Ok(Value::Undefined),
2012 },
2013 _ => Ok(Value::Undefined),
2014 }
2015 }
2016}
2017
2018use crate::parser::ir::ParamPattern;
2020
2021fn is_array_method(name: &str) -> bool {
2022 matches!(
2023 name,
2024 "push"
2025 | "pop"
2026 | "shift"
2027 | "unshift"
2028 | "splice"
2029 | "slice"
2030 | "concat"
2031 | "join"
2032 | "reverse"
2033 | "sort"
2034 | "indexOf"
2035 | "lastIndexOf"
2036 | "includes"
2037 | "find"
2038 | "findIndex"
2039 | "map"
2040 | "filter"
2041 | "reduce"
2042 | "forEach"
2043 | "every"
2044 | "some"
2045 | "flat"
2046 | "flatMap"
2047 | "fill"
2048 | "at"
2049 | "entries"
2050 | "keys"
2051 | "values"
2052 )
2053}
2054
2055fn is_string_method(name: &str) -> bool {
2056 matches!(
2057 name,
2058 "charAt"
2059 | "charCodeAt"
2060 | "indexOf"
2061 | "lastIndexOf"
2062 | "includes"
2063 | "startsWith"
2064 | "endsWith"
2065 | "slice"
2066 | "substring"
2067 | "substr"
2068 | "toUpperCase"
2069 | "toLowerCase"
2070 | "trim"
2071 | "trimStart"
2072 | "trimEnd"
2073 | "trimLeft"
2074 | "trimRight"
2075 | "padStart"
2076 | "padEnd"
2077 | "repeat"
2078 | "replace"
2079 | "replaceAll"
2080 | "split"
2081 | "concat"
2082 | "at"
2083 | "match"
2084 | "search"
2085 | "normalize"
2086 )
2087}
2088
2089pub struct ZapcodeRun {
2091 source: String,
2092 #[allow(dead_code)]
2093 inputs: Vec<String>,
2094 external_functions: Vec<String>,
2095 limits: ResourceLimits,
2096}
2097
2098impl ZapcodeRun {
2099 pub fn new(
2100 source: String,
2101 inputs: Vec<String>,
2102 external_functions: Vec<String>,
2103 limits: ResourceLimits,
2104 ) -> Result<Self> {
2105 Ok(Self {
2106 source,
2107 inputs,
2108 external_functions,
2109 limits,
2110 })
2111 }
2112
2113 pub fn run(&self, input_values: Vec<(String, Value)>) -> Result<RunResult> {
2114 let program = crate::parser::parse(&self.source)?;
2115 let ext_set: HashSet<String> = self.external_functions.iter().cloned().collect();
2116 let compiled = crate::compiler::compile_with_externals(&program, ext_set.clone())?;
2117 let mut vm = Vm::new(compiled, self.limits.clone(), ext_set);
2118
2119 for (name, value) in input_values {
2121 vm.globals.insert(name, value);
2122 }
2123
2124 let state = vm.run()?;
2125 Ok(RunResult {
2126 state,
2127 stdout: vm.stdout,
2128 })
2129 }
2130
2131 pub fn start(&self, input_values: Vec<(String, Value)>) -> Result<VmState> {
2135 let program = crate::parser::parse(&self.source)?;
2136 let ext_set: HashSet<String> = self.external_functions.iter().cloned().collect();
2137 let compiled = crate::compiler::compile_with_externals(&program, ext_set.clone())?;
2138 let mut vm = Vm::new(compiled, self.limits.clone(), ext_set);
2139
2140 for (name, value) in input_values {
2141 vm.globals.insert(name, value);
2142 }
2143
2144 vm.run()
2145 }
2146
2147 pub fn run_simple(&self) -> Result<Value> {
2148 let result = self.run(Vec::new())?;
2149 match result.state {
2150 VmState::Complete(v) => Ok(v),
2151 VmState::Suspended { function_name, .. } => Err(ZapcodeError::RuntimeError(format!(
2152 "execution suspended on external function '{}' — use run() instead",
2153 function_name
2154 ))),
2155 }
2156 }
2157}
2158
2159pub struct RunResult {
2161 pub state: VmState,
2162 pub stdout: String,
2163}
2164
2165pub fn eval_ts(source: &str) -> Result<Value> {
2167 let runner = ZapcodeRun::new(
2168 source.to_string(),
2169 Vec::new(),
2170 Vec::new(),
2171 ResourceLimits::default(),
2172 )?;
2173 runner.run_simple()
2174}
2175
2176pub fn eval_ts_with_output(source: &str) -> Result<(Value, String)> {
2178 let runner = ZapcodeRun::new(
2179 source.to_string(),
2180 Vec::new(),
2181 Vec::new(),
2182 ResourceLimits::default(),
2183 )?;
2184 let result = runner.run(Vec::new())?;
2185 match result.state {
2186 VmState::Complete(v) => Ok((v, result.stdout)),
2187 VmState::Suspended { function_name, .. } => Err(ZapcodeError::RuntimeError(format!(
2188 "execution suspended on external function '{}'",
2189 function_name
2190 ))),
2191 }
2192}