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}
24
25enum ReturnControl {
26 Done(NanValue),
27 Resume {
28 result: NanValue,
29 fn_id: u32,
30 ip: usize,
31 bp: usize,
32 },
33}
34
35impl VM {
36 pub fn new(code: CodeStore, globals: Vec<NanValue>, arena: Arena) -> Self {
37 VM {
38 stack: Vec::with_capacity(1024),
39 frames: Vec::with_capacity(64),
40 globals,
41 code,
42 arena,
43 runtime: VmRuntime::new(),
44 profile: None,
45 }
46 }
47
48 pub fn start_profiling(&mut self) {
49 self.profile = Some(VmProfileState::new(self.code.functions.len()));
50 }
51
52 pub fn clear_profile(&mut self) {
53 self.profile = None;
54 }
55
56 pub fn profile_report(&self) -> Option<VmProfileReport> {
57 self.profile
58 .as_ref()
59 .map(|profile| profile.report(&self.code))
60 }
61
62 pub fn profile_top_bigrams(&self, n: usize) -> Vec<((u8, u8), u64)> {
63 self.profile
64 .as_ref()
65 .map(|p| p.top_bigrams(n))
66 .unwrap_or_default()
67 }
68
69 pub fn set_cli_args(&mut self, args: Vec<String>) {
71 self.runtime.set_cli_args(args);
72 }
73
74 pub fn set_silent_console(&mut self, silent: bool) {
75 self.runtime.set_silent_console(silent);
76 }
77
78 pub fn set_runtime_policy(&mut self, config: crate::config::ProjectConfig) {
80 self.runtime.set_runtime_policy(config);
81 }
82
83 pub fn start_recording(&mut self) {
85 self.runtime.start_recording();
86 }
87
88 pub fn start_replay(
90 &mut self,
91 effects: Vec<crate::replay::session::EffectRecord>,
92 validate_args: bool,
93 ) {
94 self.runtime.start_replay(effects, validate_args);
95 }
96
97 pub fn recorded_effects(&self) -> &[crate::replay::session::EffectRecord] {
98 self.runtime.recorded_effects()
99 }
100
101 pub fn replay_progress(&self) -> (usize, usize) {
102 self.runtime.replay_progress()
103 }
104
105 pub fn ensure_replay_consumed(&self) -> Result<(), VmError> {
106 self.runtime.ensure_replay_consumed()
107 }
108
109 pub fn run(&mut self) -> Result<NanValue, VmError> {
110 self.run_top_level()?;
111 self.run_named_function("main", &[])
112 }
113
114 pub fn run_top_level(&mut self) -> Result<(), VmError> {
115 if let Some(top_id) = self.code.find("__top_level__") {
116 let _ = self.call_function(top_id, &[])?;
117 }
118 Ok(())
119 }
120
121 pub fn run_named_function(
122 &mut self,
123 name: &str,
124 args: &[NanValue],
125 ) -> Result<NanValue, VmError> {
126 let fn_id = self
127 .code
128 .symbols
129 .find(name)
130 .and_then(|symbol_id| self.code.symbols.resolve_function(symbol_id))
131 .or_else(|| self.code.find(name))
132 .ok_or_else(|| VmError::Runtime(format!("function '{}' not found", name)))?;
133 self.runtime
134 .set_allowed_effects(self.code.get(fn_id).effects.clone());
135 self.call_function(fn_id, args)
136 }
137
138 pub fn call_function(&mut self, fn_id: u32, args: &[NanValue]) -> Result<NanValue, VmError> {
139 let chunk = self.code.get(fn_id);
140 let caller_depth = self.frames.len();
141 let arena_mark = self.arena.young_len() as u32;
142 let yard_mark = self.arena.yard_len() as u32;
143 let handoff_mark = self.arena.handoff_len() as u32;
144 let bp = self.stack.len() as u32;
145 for arg in args {
146 self.stack.push(*arg);
147 }
148 for _ in args.len()..(chunk.local_count as usize) {
149 self.stack.push(NanValue::UNIT);
150 }
151 self.frames.push(CallFrame {
152 fn_id,
153 ip: 0,
154 bp,
155 local_count: chunk.local_count,
156 arena_mark,
157 yard_base: yard_mark,
158 yard_mark,
159 handoff_mark,
160 globals_dirty: false,
161 yard_dirty: false,
162 handoff_dirty: false,
163 thin: chunk.thin,
164 parent_thin: chunk.parent_thin,
165 });
166 if let Some(profile) = self.profile.as_mut() {
167 profile.record_function_entry(chunk, fn_id);
168 }
169 self.execute_until(caller_depth)
170 }
171}