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 super::{VmProfileReport, profile::VmProfileState};
12use crate::nan_value::{Arena, NanValue};
13
14pub struct VM {
16 stack: Vec<NanValue>,
17 frames: Vec<CallFrame>,
18 globals: Vec<NanValue>,
19 code: CodeStore,
20 pub arena: Arena,
21 runtime: VmRuntime,
22 profile: Option<VmProfileState>,
23 error_fn_id: u32,
25 error_ip: u32,
26 cancelled: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
28 buffer_pool: Vec<Option<String>>,
34}
35
36enum ReturnControl {
37 Done(NanValue),
38 Resume {
39 result: NanValue,
40 fn_id: u32,
41 ip: usize,
42 bp: usize,
43 },
44}
45
46impl VM {
47 pub fn new(code: CodeStore, globals: Vec<NanValue>, arena: Arena) -> Self {
48 VM {
49 stack: Vec::with_capacity(1024),
50 frames: Vec::with_capacity(64),
51 globals,
52 code,
53 arena,
54 runtime: VmRuntime::new(),
55 profile: None,
56 error_fn_id: 0,
57 error_ip: 0,
58 cancelled: None,
59 buffer_pool: Vec::new(),
60 }
61 }
62
63 pub fn start_profiling(&mut self) {
64 self.profile = Some(VmProfileState::new(self.code.functions.len()));
65 }
66
67 pub fn clear_profile(&mut self) {
68 self.profile = None;
69 }
70
71 pub fn profile_report(&self) -> Option<VmProfileReport> {
72 self.profile
73 .as_ref()
74 .map(|profile| profile.report(&self.code))
75 }
76
77 pub fn profile_top_bigrams(&self, n: usize) -> Vec<((u8, u8), u64)> {
78 self.profile
79 .as_ref()
80 .map(|p| p.top_bigrams(n))
81 .unwrap_or_default()
82 }
83
84 pub fn set_cli_args(&mut self, args: Vec<String>) {
86 self.runtime.set_cli_args(args);
87 }
88
89 pub fn set_silent_console(&mut self, silent: bool) {
90 self.runtime.set_silent_console(silent);
91 }
92
93 pub fn set_runtime_policy(&mut self, config: crate::config::ProjectConfig) {
95 self.runtime.set_runtime_policy(config);
96 }
97
98 pub fn start_recording(&mut self) {
100 self.runtime.start_recording();
101 }
102
103 pub fn set_record_cap(&mut self, cap: Option<usize>) {
107 self.runtime.set_record_cap(cap);
108 }
109
110 pub fn start_replay(
112 &mut self,
113 effects: Vec<crate::replay::session::EffectRecord>,
114 validate_args: bool,
115 ) {
116 self.runtime.start_replay(effects, validate_args);
117 }
118
119 pub fn set_allowed_effects(&mut self, effects: Vec<u32>) {
120 self.runtime.set_allowed_effects(effects);
121 }
122
123 pub fn install_oracle_stubs(&mut self, stubs: std::collections::HashMap<String, u32>) {
128 self.runtime.install_oracle_stubs(stubs);
129 }
130
131 pub fn clear_oracle_stubs(&mut self) {
135 self.runtime.clear_oracle_stubs();
136 }
137
138 pub fn find_fn_id(&self, name: &str) -> Option<u32> {
141 self.code.find(name)
142 }
143
144 pub fn start_trace_collection(&mut self) {
148 self.runtime.start_trace_collection();
149 }
150
151 pub fn set_trace_root_fn_id(&mut self, fn_id: Option<u32>) {
157 self.runtime.set_trace_root_fn_id(fn_id);
158 }
159
160 pub fn stop_trace_collection(&mut self) {
162 self.runtime.stop_trace_collection();
163 }
164
165 pub fn take_trace_events(&mut self) -> Vec<crate::value::Value> {
169 let events = self.runtime.take_trace_events();
170 self.runtime.stop_trace_collection();
171 events
172 }
173
174 pub fn take_trace_events_with_coords(
179 &mut self,
180 ) -> (
181 Vec<crate::value::Value>,
182 Vec<crate::vm::runtime::TraceCoord>,
183 ) {
184 let out = self.runtime.take_trace_events_with_coords();
185 self.runtime.stop_trace_collection();
186 out
187 }
188
189 pub fn set_cancelled(&mut self, flag: std::sync::Arc<std::sync::atomic::AtomicBool>) {
190 self.cancelled = Some(flag);
191 }
192
193 fn is_cancelled(&self) -> bool {
195 self.cancelled
196 .as_ref()
197 .is_some_and(|f| f.load(std::sync::atomic::Ordering::Relaxed))
198 }
199
200 pub fn recorded_effects(&self) -> &[crate::replay::session::EffectRecord] {
201 self.runtime.recorded_effects()
202 }
203
204 pub fn replay_progress(&self) -> (usize, usize) {
205 self.runtime.replay_progress()
206 }
207
208 pub fn args_diff_count(&self) -> usize {
209 self.runtime.args_diff_count()
210 }
211
212 pub fn ensure_replay_consumed(&self) -> Result<(), VmError> {
213 self.runtime.ensure_replay_consumed()
214 }
215
216 pub fn run(&mut self) -> Result<NanValue, VmError> {
217 self.run_top_level()?;
218 let has_main = self
220 .code
221 .symbols
222 .find("main")
223 .and_then(|sid| self.code.symbols.resolve_function(sid))
224 .or_else(|| self.code.find("main"))
225 .is_some();
226 if has_main {
227 self.run_named_function("main", &[])
228 } else {
229 Ok(NanValue::UNIT)
230 }
231 }
232
233 pub fn run_top_level(&mut self) -> Result<(), VmError> {
234 if let Some(top_id) = self.code.find("__top_level__") {
235 let _ = self.call_function(top_id, &[])?;
236 }
237 Ok(())
238 }
239
240 pub fn run_named_function(
241 &mut self,
242 name: &str,
243 args: &[NanValue],
244 ) -> Result<NanValue, VmError> {
245 let fn_id = self
246 .code
247 .symbols
248 .find(name)
249 .and_then(|symbol_id| self.code.symbols.resolve_function(symbol_id))
250 .or_else(|| self.code.find(name))
251 .ok_or_else(|| VmError::runtime(format!("function '{}' not found", name)))?;
252 self.runtime
253 .set_allowed_effects(self.code.get(fn_id).effects.clone());
254 self.call_function(fn_id, args)
255 }
256
257 pub fn call_function(&mut self, fn_id: u32, args: &[NanValue]) -> Result<NanValue, VmError> {
258 let chunk = self.code.get(fn_id);
259 let caller_depth = self.frames.len();
260 let arena_mark = self.arena.young_len() as u32;
261 let yard_mark = self.arena.yard_len() as u32;
262 let handoff_mark = self.arena.handoff_len() as u32;
263 let bp = self.stack.len() as u32;
264 for arg in args {
265 self.stack.push(*arg);
266 }
267 for _ in args.len()..(chunk.local_count as usize) {
268 self.stack.push(NanValue::UNIT);
269 }
270 self.frames.push(CallFrame {
271 fn_id,
272 ip: 0,
273 bp,
274 local_count: chunk.local_count,
275 arena_mark,
276 yard_base: yard_mark,
277 yard_mark,
278 handoff_mark,
279 globals_dirty: false,
280 yard_dirty: false,
281 handoff_dirty: false,
282 thin: chunk.thin,
283 parent_thin: chunk.parent_thin,
284 });
285 if let Some(profile) = self.profile.as_mut() {
286 profile.record_function_entry(chunk, fn_id);
287 }
288 self.execute_until(caller_depth).map_err(|err| {
289 let loc = self
291 .code
292 .resolve_source_location(self.error_fn_id, self.error_ip);
293 err.with_location(loc.map(|(file, line)| super::types::VmSourceLoc {
294 file: file.to_string(),
295 line,
296 fn_name: self.code.get(self.error_fn_id).name.clone(),
297 }))
298 })
299 }
300}