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