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