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 step_limit: Option<u64>,
37 buffer_pool: Vec<Option<String>>,
43}
44
45enum ReturnControl {
46 Done(NanValue),
47 Resume {
48 result: NanValue,
49 fn_id: u32,
50 ip: usize,
51 bp: usize,
52 },
53}
54
55impl VM {
56 pub fn new(code: CodeStore, globals: Vec<NanValue>, arena: Arena) -> Self {
57 VM {
58 stack: Vec::with_capacity(1024),
59 frames: Vec::with_capacity(64),
60 globals,
61 code,
62 arena,
63 runtime: VmRuntime::new(),
64 profile: None,
65 error_fn_id: 0,
66 error_ip: 0,
67 cancelled: None,
68 buffer_pool: Vec::new(),
69 step_limit: None,
70 }
71 }
72
73 pub fn set_step_limit(&mut self, limit: Option<u64>) {
79 self.step_limit = limit;
80 }
81
82 pub fn start_profiling(&mut self) {
83 self.profile = Some(VmProfileState::new(self.code.functions.len()));
84 }
85
86 pub fn clear_profile(&mut self) {
87 self.profile = None;
88 }
89
90 pub fn profile_report(&self) -> Option<VmProfileReport> {
91 self.profile
92 .as_ref()
93 .map(|profile| profile.report(&self.code))
94 }
95
96 pub fn profile_top_bigrams(&self, n: usize) -> Vec<((u8, u8), u64)> {
97 self.profile
98 .as_ref()
99 .map(|p| p.top_bigrams(n))
100 .unwrap_or_default()
101 }
102
103 pub fn set_cli_args(&mut self, args: Vec<String>) {
105 self.runtime.set_cli_args(args);
106 }
107
108 pub fn set_silent_console(&mut self, silent: bool) {
109 self.runtime.set_silent_console(silent);
110 }
111
112 pub fn set_runtime_policy(&mut self, config: crate::config::ProjectConfig) {
114 self.runtime.set_runtime_policy(config);
115 }
116
117 pub fn start_recording(&mut self) {
119 self.runtime.start_recording();
120 }
121
122 pub fn set_record_cap(&mut self, cap: Option<usize>) {
126 self.runtime.set_record_cap(cap);
127 }
128
129 pub fn start_replay(
131 &mut self,
132 effects: Vec<crate::replay::session::EffectRecord>,
133 validate_args: bool,
134 ) {
135 self.runtime.start_replay(effects, validate_args);
136 }
137
138 pub fn set_allowed_effects(&mut self, effects: Vec<u32>) {
139 self.runtime.set_allowed_effects(effects);
140 }
141
142 pub fn install_oracle_stubs(&mut self, stubs: std::collections::HashMap<String, u32>) {
147 self.runtime.install_oracle_stubs(stubs);
148 }
149
150 pub fn clear_oracle_stubs(&mut self) {
154 self.runtime.clear_oracle_stubs();
155 }
156
157 pub fn set_reverse_independent_eval(&mut self, value: bool) {
167 self.runtime.set_reverse_independent_eval(value);
168 }
169
170 pub fn find_fn_id(&self, name: &str) -> Option<u32> {
173 self.code.find(name)
174 }
175
176 pub fn start_trace_collection(&mut self) {
180 self.runtime.start_trace_collection();
181 }
182
183 pub fn set_trace_root_fn_id(&mut self, fn_id: Option<u32>) {
189 self.runtime.set_trace_root_fn_id(fn_id);
190 }
191
192 pub fn stop_trace_collection(&mut self) {
194 self.runtime.stop_trace_collection();
195 }
196
197 pub fn take_trace_events(&mut self) -> Vec<crate::value::Value> {
201 let events = self.runtime.take_trace_events();
202 self.runtime.stop_trace_collection();
203 events
204 }
205
206 pub fn take_trace_events_with_coords(
211 &mut self,
212 ) -> (
213 Vec<crate::value::Value>,
214 Vec<crate::vm::runtime::TraceCoord>,
215 ) {
216 let out = self.runtime.take_trace_events_with_coords();
217 self.runtime.stop_trace_collection();
218 out
219 }
220
221 pub fn set_cancelled(&mut self, flag: std::sync::Arc<std::sync::atomic::AtomicBool>) {
222 self.cancelled = Some(flag);
223 }
224
225 fn is_cancelled(&self) -> bool {
227 self.cancelled
228 .as_ref()
229 .is_some_and(|f| f.load(std::sync::atomic::Ordering::Relaxed))
230 }
231
232 pub fn recorded_effects(&self) -> &[crate::replay::session::EffectRecord] {
233 self.runtime.recorded_effects()
234 }
235
236 pub fn replay_progress(&self) -> (usize, usize) {
237 self.runtime.replay_progress()
238 }
239
240 pub fn args_diff_count(&self) -> usize {
241 self.runtime.args_diff_count()
242 }
243
244 pub fn ensure_replay_consumed(&self) -> Result<(), VmError> {
245 self.runtime.ensure_replay_consumed()
246 }
247
248 pub fn run(&mut self) -> Result<NanValue, VmError> {
249 self.run_top_level()?;
250 let has_main = self
252 .code
253 .symbols
254 .find("main")
255 .and_then(|sid| self.code.symbols.resolve_function(sid))
256 .or_else(|| self.code.find("main"))
257 .is_some();
258 if has_main {
259 self.run_named_function("main", &[])
260 } else {
261 Ok(NanValue::UNIT)
262 }
263 }
264
265 pub fn run_top_level(&mut self) -> Result<(), VmError> {
266 if let Some(top_id) = self.code.find("__top_level__") {
267 let _ = self.call_function(top_id, &[])?;
268 }
269 Ok(())
270 }
271
272 pub fn run_named_function(
273 &mut self,
274 name: &str,
275 args: &[NanValue],
276 ) -> Result<NanValue, VmError> {
277 let fn_id = self
278 .code
279 .symbols
280 .find(name)
281 .and_then(|symbol_id| self.code.symbols.resolve_function(symbol_id))
282 .or_else(|| self.code.find(name))
283 .ok_or_else(|| VmError::runtime(format!("function '{}' not found", name)))?;
284 self.runtime
285 .set_allowed_effects(self.code.get(fn_id).effects.clone());
286 self.call_function(fn_id, args)
287 }
288
289 pub fn call_function(&mut self, fn_id: u32, args: &[NanValue]) -> Result<NanValue, VmError> {
290 let chunk = self.code.get(fn_id);
291 let caller_depth = self.frames.len();
292 let arena_mark = self.arena.young_len() as u32;
293 let yard_mark = self.arena.yard_len() as u32;
294 let handoff_mark = self.arena.handoff_len() as u32;
295 let bp = self.stack.len() as u32;
296 for arg in args {
297 self.stack.push(*arg);
298 }
299 for _ in args.len()..(chunk.local_count as usize) {
300 self.stack.push(NanValue::UNIT);
301 }
302 self.frames.push(CallFrame {
303 fn_id,
304 ip: 0,
305 bp,
306 local_count: chunk.local_count,
307 arena_mark,
308 yard_base: yard_mark,
309 yard_mark,
310 handoff_mark,
311 globals_dirty: false,
312 yard_dirty: false,
313 handoff_dirty: false,
314 thin: chunk.thin,
315 parent_thin: chunk.parent_thin,
316 });
317 if let Some(profile) = self.profile.as_mut() {
318 profile.record_function_entry(chunk, fn_id);
319 }
320 self.execute_until(caller_depth).map_err(|err| {
321 let loc = self
323 .code
324 .resolve_source_location(self.error_fn_id, self.error_ip);
325 err.with_location(loc.map(|(file, line)| super::types::VmSourceLoc {
326 file: file.to_string(),
327 line,
328 fn_name: self.code.get(self.error_fn_id).name.clone(),
329 }))
330 })
331 }
332}