1mod boundary;
2mod dispatch;
3mod host;
4mod ops;
5
6#[cfg(test)]
7mod tests;
8
9use super::runtime::VmRuntime;
10use super::types::{CallFrame, CodeStore, VmError};
11use crate::nan_value::{Arena, NanValue};
12
13pub struct VM {
15 stack: Vec<NanValue>,
16 frames: Vec<CallFrame>,
17 globals: Vec<NanValue>,
18 code: CodeStore,
19 pub arena: Arena,
20 runtime: VmRuntime,
21}
22
23enum ReturnControl {
24 Done(NanValue),
25 Resume {
26 result: NanValue,
27 fn_id: u32,
28 ip: usize,
29 bp: usize,
30 },
31}
32
33impl VM {
34 pub fn new(code: CodeStore, globals: Vec<NanValue>, arena: Arena) -> Self {
35 VM {
36 stack: Vec::with_capacity(1024),
37 frames: Vec::with_capacity(64),
38 globals,
39 code,
40 arena,
41 runtime: VmRuntime::new(),
42 }
43 }
44
45 pub fn set_cli_args(&mut self, args: Vec<String>) {
47 self.runtime.set_cli_args(args);
48 }
49
50 pub fn start_recording(&mut self) {
52 self.runtime.start_recording();
53 }
54
55 pub fn start_replay(
57 &mut self,
58 effects: Vec<crate::replay::session::EffectRecord>,
59 validate_args: bool,
60 ) {
61 self.runtime.start_replay(effects, validate_args);
62 }
63
64 pub fn recorded_effects(&self) -> &[crate::replay::session::EffectRecord] {
65 self.runtime.recorded_effects()
66 }
67
68 pub fn replay_progress(&self) -> (usize, usize) {
69 self.runtime.replay_progress()
70 }
71
72 pub fn ensure_replay_consumed(&self) -> Result<(), VmError> {
73 self.runtime.ensure_replay_consumed()
74 }
75
76 pub fn run(&mut self) -> Result<NanValue, VmError> {
77 self.run_top_level()?;
78 self.run_named_function("main", &[])
79 }
80
81 pub fn run_top_level(&mut self) -> Result<(), VmError> {
82 if let Some(top_id) = self.code.find("__top_level__") {
83 let _ = self.call_function(top_id, &[])?;
84 }
85 Ok(())
86 }
87
88 pub fn run_named_function(
89 &mut self,
90 name: &str,
91 args: &[NanValue],
92 ) -> Result<NanValue, VmError> {
93 let fn_id = self
94 .code
95 .find(name)
96 .ok_or_else(|| VmError::Runtime(format!("function '{}' not found", name)))?;
97 self.runtime
98 .set_allowed_effects(self.code.get(fn_id).effects.clone());
99 self.call_function(fn_id, args)
100 }
101
102 pub fn call_function(&mut self, fn_id: u32, args: &[NanValue]) -> Result<NanValue, VmError> {
103 let chunk = self.code.get(fn_id);
104 let caller_depth = self.frames.len();
105 let arena_mark = self.arena.young_len() as u32;
106 let yard_mark = self.arena.yard_len() as u32;
107 let handoff_mark = self.arena.handoff_len() as u32;
108 let bp = self.stack.len() as u32;
109 for arg in args {
110 self.stack.push(*arg);
111 }
112 for _ in args.len()..(chunk.local_count as usize) {
113 self.stack.push(NanValue::UNIT);
114 }
115 self.frames.push(CallFrame {
116 fn_id,
117 ip: 0,
118 bp,
119 local_count: chunk.local_count,
120 arena_mark,
121 yard_base: yard_mark,
122 yard_mark,
123 handoff_mark,
124 globals_dirty: false,
125 yard_dirty: false,
126 handoff_dirty: false,
127 thin: chunk.thin,
128 parent_thin: chunk.parent_thin,
129 });
130 self.execute_until(caller_depth)
131 }
132}