1use std::collections::{BTreeMap, HashSet};
2use std::rc::Rc;
3use std::time::Instant;
4
5use crate::chunk::{Chunk, Constant};
6use crate::value::{
7 ModuleFunctionRegistry, VmAsyncBuiltinFn, VmBuiltinFn, VmEnv, VmError, VmTaskHandle, VmValue,
8};
9
10use super::debug::DebugHook;
11use super::modules::LoadedModule;
12
13pub(crate) struct ScopeSpan(u64);
15
16impl ScopeSpan {
17 pub(crate) fn new(kind: crate::tracing::SpanKind, name: String) -> Self {
18 Self(crate::tracing::span_start(kind, name))
19 }
20}
21
22impl Drop for ScopeSpan {
23 fn drop(&mut self) {
24 crate::tracing::span_end(self.0);
25 }
26}
27
28pub(crate) struct CallFrame {
30 pub(crate) chunk: Chunk,
31 pub(crate) ip: usize,
32 pub(crate) stack_base: usize,
33 pub(crate) saved_env: VmEnv,
34 pub(crate) initial_env: Option<VmEnv>,
42 pub(crate) saved_iterator_depth: usize,
44 pub(crate) fn_name: String,
46 pub(crate) argc: usize,
48 pub(crate) saved_source_dir: Option<std::path::PathBuf>,
51 pub(crate) module_functions: Option<ModuleFunctionRegistry>,
53 pub(crate) module_state: Option<crate::value::ModuleState>,
59}
60
61pub(crate) struct ExceptionHandler {
63 pub(crate) catch_ip: usize,
64 pub(crate) stack_depth: usize,
65 pub(crate) frame_depth: usize,
66 pub(crate) env_scope_depth: usize,
67 pub(crate) error_type: String,
69}
70
71pub(crate) enum IterState {
73 Vec {
74 items: Vec<VmValue>,
75 idx: usize,
76 },
77 Channel {
78 receiver: std::sync::Arc<tokio::sync::Mutex<tokio::sync::mpsc::Receiver<VmValue>>>,
79 closed: std::sync::Arc<std::sync::atomic::AtomicBool>,
80 },
81 Generator {
82 gen: crate::value::VmGenerator,
83 },
84 Range {
88 next: i64,
89 stop: i64,
90 },
91 VmIter {
92 handle: std::rc::Rc<std::cell::RefCell<crate::vm::iter::VmIter>>,
93 },
94}
95
96pub struct Vm {
98 pub(crate) stack: Vec<VmValue>,
99 pub(crate) env: VmEnv,
100 pub(crate) output: String,
101 pub(crate) builtins: BTreeMap<String, VmBuiltinFn>,
102 pub(crate) async_builtins: BTreeMap<String, VmAsyncBuiltinFn>,
103 pub(crate) iterators: Vec<IterState>,
105 pub(crate) frames: Vec<CallFrame>,
107 pub(crate) exception_handlers: Vec<ExceptionHandler>,
109 pub(crate) spawned_tasks: BTreeMap<String, VmTaskHandle>,
111 pub(crate) task_counter: u64,
113 pub(crate) deadlines: Vec<(Instant, usize)>,
115 pub(crate) breakpoints: BTreeMap<String, std::collections::BTreeSet<usize>>,
120 pub(crate) function_breakpoints: std::collections::BTreeSet<String>,
126 pub(crate) pending_function_bp: Option<String>,
131 pub(crate) step_mode: bool,
133 pub(crate) step_frame_depth: usize,
135 pub(crate) stopped: bool,
137 pub(crate) last_line: usize,
139 pub(crate) source_dir: Option<std::path::PathBuf>,
141 pub(crate) imported_paths: Vec<std::path::PathBuf>,
143 pub(crate) module_cache: BTreeMap<std::path::PathBuf, LoadedModule>,
145 pub(crate) source_file: Option<String>,
147 pub(crate) source_text: Option<String>,
149 pub(crate) bridge: Option<Rc<crate::bridge::HostBridge>>,
151 pub(crate) denied_builtins: HashSet<String>,
153 pub(crate) cancel_token: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
155 pub(crate) error_stack_trace: Vec<(String, usize, usize, Option<String>)>,
157 pub(crate) yield_sender: Option<tokio::sync::mpsc::Sender<VmValue>>,
160 pub(crate) project_root: Option<std::path::PathBuf>,
163 pub(crate) globals: BTreeMap<String, VmValue>,
166 pub(crate) debug_hook: Option<Box<DebugHook>>,
168}
169
170impl Vm {
171 pub fn new() -> Self {
172 Self {
173 stack: Vec::with_capacity(256),
174 env: VmEnv::new(),
175 output: String::new(),
176 builtins: BTreeMap::new(),
177 async_builtins: BTreeMap::new(),
178 iterators: Vec::new(),
179 frames: Vec::new(),
180 exception_handlers: Vec::new(),
181 spawned_tasks: BTreeMap::new(),
182 task_counter: 0,
183 deadlines: Vec::new(),
184 breakpoints: BTreeMap::new(),
185 function_breakpoints: std::collections::BTreeSet::new(),
186 pending_function_bp: None,
187 step_mode: false,
188 step_frame_depth: 0,
189 stopped: false,
190 last_line: 0,
191 source_dir: None,
192 imported_paths: Vec::new(),
193 module_cache: BTreeMap::new(),
194 source_file: None,
195 source_text: None,
196 bridge: None,
197 denied_builtins: HashSet::new(),
198 cancel_token: None,
199 error_stack_trace: Vec::new(),
200 yield_sender: None,
201 project_root: None,
202 globals: BTreeMap::new(),
203 debug_hook: None,
204 }
205 }
206
207 pub fn set_bridge(&mut self, bridge: Rc<crate::bridge::HostBridge>) {
209 self.bridge = Some(bridge);
210 }
211
212 pub fn set_denied_builtins(&mut self, denied: HashSet<String>) {
215 self.denied_builtins = denied;
216 }
217
218 pub fn set_source_info(&mut self, file: &str, text: &str) {
220 self.source_file = Some(file.to_string());
221 self.source_text = Some(text.to_string());
222 }
223
224 pub fn start(&mut self, chunk: &Chunk) {
226 let initial_env = self.env.clone();
227 self.frames.push(CallFrame {
228 chunk: chunk.clone(),
229 ip: 0,
230 stack_base: self.stack.len(),
231 saved_env: self.env.clone(),
232 initial_env: Some(initial_env),
237 saved_iterator_depth: self.iterators.len(),
238 fn_name: String::new(),
239 argc: 0,
240 saved_source_dir: None,
241 module_functions: None,
242 module_state: None,
243 });
244 }
245
246 pub(crate) fn child_vm(&self) -> Vm {
249 Vm {
250 stack: Vec::with_capacity(64),
251 env: self.env.clone(),
252 output: String::new(),
253 builtins: self.builtins.clone(),
254 async_builtins: self.async_builtins.clone(),
255 iterators: Vec::new(),
256 frames: Vec::new(),
257 exception_handlers: Vec::new(),
258 spawned_tasks: BTreeMap::new(),
259 task_counter: 0,
260 deadlines: self.deadlines.clone(),
261 breakpoints: BTreeMap::new(),
262 function_breakpoints: std::collections::BTreeSet::new(),
263 pending_function_bp: None,
264 step_mode: false,
265 step_frame_depth: 0,
266 stopped: false,
267 last_line: 0,
268 source_dir: self.source_dir.clone(),
269 imported_paths: Vec::new(),
270 module_cache: self.module_cache.clone(),
271 source_file: self.source_file.clone(),
272 source_text: self.source_text.clone(),
273 bridge: self.bridge.clone(),
274 denied_builtins: self.denied_builtins.clone(),
275 cancel_token: None,
276 error_stack_trace: Vec::new(),
277 yield_sender: None,
278 project_root: self.project_root.clone(),
279 globals: self.globals.clone(),
280 debug_hook: None,
281 }
282 }
283
284 pub(crate) fn child_vm_for_host(&self) -> Vm {
287 self.child_vm()
288 }
289
290 pub fn set_source_dir(&mut self, dir: &std::path::Path) {
293 self.source_dir = Some(dir.to_path_buf());
294 crate::stdlib::set_thread_source_dir(dir);
295 if self.project_root.is_none() {
297 self.project_root = crate::stdlib::process::find_project_root(dir);
298 }
299 }
300
301 pub fn set_project_root(&mut self, root: &std::path::Path) {
304 self.project_root = Some(root.to_path_buf());
305 }
306
307 pub fn project_root(&self) -> Option<&std::path::Path> {
309 self.project_root.as_deref().or(self.source_dir.as_deref())
310 }
311
312 pub fn builtin_names(&self) -> Vec<String> {
314 let mut names: Vec<String> = self.builtins.keys().cloned().collect();
315 names.extend(self.async_builtins.keys().cloned());
316 names
317 }
318
319 pub fn set_global(&mut self, name: &str, value: VmValue) {
322 self.globals.insert(name.to_string(), value);
323 }
324
325 pub fn output(&self) -> &str {
327 &self.output
328 }
329
330 pub(crate) fn pop(&mut self) -> Result<VmValue, VmError> {
331 self.stack.pop().ok_or(VmError::StackUnderflow)
332 }
333
334 pub(crate) fn peek(&self) -> Result<&VmValue, VmError> {
335 self.stack.last().ok_or(VmError::StackUnderflow)
336 }
337
338 pub(crate) fn const_string(c: &Constant) -> Result<String, VmError> {
339 match c {
340 Constant::String(s) => Ok(s.clone()),
341 _ => Err(VmError::TypeError("expected string constant".into())),
342 }
343 }
344}
345
346impl Default for Vm {
347 fn default() -> Self {
348 Self::new()
349 }
350}