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