Skip to main content

aver/vm/execute/
mod.rs

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
14/// The Aver bytecode virtual machine.
15pub 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    /// Set CLI arguments for Args.get().
70    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    /// Set the runtime policy loaded from `aver.toml`.
79    pub fn set_runtime_policy(&mut self, config: crate::config::ProjectConfig) {
80        self.runtime.set_runtime_policy(config);
81    }
82
83    /// Start recording effectful calls.
84    pub fn start_recording(&mut self) {
85        self.runtime.start_recording();
86    }
87
88    /// Start replaying from recorded effects.
89    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}