Skip to main content

onion_vm/types/lambda/
context.rs

1//! Onion 虚拟机执行上下文与帧管理。
2//!
3//! - `Frame`:单个执行帧,包含变量表和操作数栈。
4//! - `Context`:多帧调用栈,支持变量作用域、栈操作、帧切换等。
5//! - 提供丰富的栈操作、变量管理与调试辅助方法。
6
7use rustc_hash::FxHashMap as HashMap;
8
9use crate::{
10    lambda::runnable::RuntimeError,
11    types::{
12        lambda::vm_instructions::instruction_set::VMInstructionPackage, object::OnionStaticObject,
13    },
14    utils::format_object_summary,
15};
16
17/// 虚拟机执行帧。
18///
19/// 包含变量表和操作数栈,代表一次函数调用或执行环境。
20#[derive(Clone, Debug)]
21pub struct Frame {
22    /// 变量表,key 为常量池索引
23    variables: HashMap<usize, OnionStaticObject>,
24    /// 操作数栈
25    stack: Vec<OnionStaticObject>,
26}
27
28impl Frame {
29    pub fn new() -> Self {
30        Frame {
31            variables: HashMap::default(),
32            stack: Vec::new(),
33        }
34    }
35    #[inline(always)]
36    pub fn get_stack(&self) -> &Vec<OnionStaticObject> {
37        &self.stack
38    }
39    #[inline(always)]
40    pub fn get_stack_mut(&mut self) -> &mut Vec<OnionStaticObject> {
41        &mut self.stack
42    }
43
44    pub fn format_context(&self, package: &VMInstructionPackage) -> String {
45        let mut parts = Vec::new();
46        let string_pool = package.get_string_pool();
47
48        // --- Part 1: Format Variables ---
49        if self.variables.is_empty() {
50            parts.push("  - Variables: (none)".to_string());
51        } else {
52            parts.push("  - Variables:".to_string());
53            for (id, value) in &self.variables {
54                // [关键修改] 使用 id 从 string_pool 中查找变量名
55                let var_name = string_pool
56                    .get(*id)
57                    .map(|s| s.as_ref())
58                    .unwrap_or("<Unknown Var>");
59
60                let value_summary = format_object_summary(value.weak());
61                parts.push(format!("    - {}: {}", var_name, value_summary));
62            }
63        }
64
65        // --- Part 2: Format Operand Stack ---
66        if self.stack.is_empty() {
67            parts.push("  - Operand Stack: (empty)".to_string());
68        } else {
69            parts.push(format!("  - Operand Stack ({} items):", self.stack.len()));
70            for (i, value) in self.stack.iter().rev().enumerate() {
71                // [修改点] 在这里也使用辅助函数
72                let value_summary = format_object_summary(value.weak());
73                parts.push(format!("    - [Top - {}]: {}", i, value_summary));
74            }
75        }
76
77        parts.join("\n")
78    }
79}
80
81/// Onion 虚拟机执行上下文。
82///
83/// 管理多帧调用栈,支持作用域、变量、栈和帧的各种操作。
84#[derive(Clone)]
85pub struct Context {
86    /// 调用帧栈,栈顶为当前活跃帧
87    frames: Vec<Frame>,
88}
89
90impl Context {
91    pub fn new() -> Self {
92        Context { frames: Vec::new() }
93    }
94
95    pub fn push_frame(&mut self, frame: Frame) {
96        self.frames.push(frame);
97    }
98
99    pub fn pop_frame(&mut self) -> Result<Frame, RuntimeError> {
100        match self.frames.pop() {
101            Some(frame) => Ok(frame),
102            None => Err(RuntimeError::DetailedError(
103                "Cannot pop frame from empty context".into(),
104            )),
105        }
106    }
107    pub fn concat_last_frame(&mut self) -> Result<(), RuntimeError> {
108        if self.frames.len() < 2 {
109            return Ok(());
110        }
111
112        let last_frame = self.frames.pop().unwrap();
113        let second_last_frame = self.frames.last_mut().unwrap();
114        second_last_frame.stack.extend(last_frame.stack);
115        Ok(())
116    }
117    pub fn clear_stack(&mut self) {
118        if self.frames.len() > 0 {
119            self.frames.last_mut().unwrap().stack.clear();
120        }
121    }
122
123    pub fn push_object(&mut self, object: OnionStaticObject) -> Result<(), RuntimeError> {
124        if self.frames.len() == 0 {
125            return Err(RuntimeError::DetailedError(
126                "Cannot push object to empty context".into(),
127            ));
128        }
129        self.frames.last_mut().unwrap().stack.push(object);
130        Ok(())
131    }
132
133    pub fn pop(&mut self) -> Result<OnionStaticObject, RuntimeError> {
134        if self.frames.len() == 0 {
135            return Err(RuntimeError::DetailedError(
136                "Cannot pop object from empty context".into(),
137            ));
138        }
139        let last_frame = self.frames.last_mut().unwrap();
140        if last_frame.get_stack().len() == 0 {
141            return Err(RuntimeError::DetailedError(
142                "Cannot pop object from empty stack".into(),
143            ));
144        }
145        let stack = last_frame.get_stack_mut();
146        Ok(stack.pop().unwrap())
147    }
148
149    pub fn discard_objects(&mut self, count: usize) -> Result<(), RuntimeError> {
150        if self.frames.len() == 0 {
151            return Err(RuntimeError::DetailedError(
152                "Cannot discard objects from empty context"
153                    .to_string()
154                    .into(),
155            ));
156        }
157        let last_frame = self.frames.last_mut().unwrap();
158        let stack = last_frame.get_stack_mut();
159        if stack.len() < count {
160            return Err(RuntimeError::DetailedError(
161                "Cannot discard more objects than available in stack"
162                    .to_string()
163                    .into(),
164            ));
165        }
166        stack.truncate(stack.len() - count);
167        Ok(())
168    }
169
170    pub fn discard_objects_offset(
171        &mut self,
172        offset: usize,
173        count: usize,
174    ) -> Result<(), RuntimeError> {
175        if self.frames.len() == 0 {
176            return Err(RuntimeError::DetailedError(
177                "Cannot discard objects from empty context"
178                    .to_string()
179                    .into(),
180            ));
181        }
182        let last_frame = self.frames.last_mut().unwrap();
183        let stack = last_frame.get_stack_mut();
184        if stack.len() < offset + count {
185            return Err(RuntimeError::DetailedError(
186                "Cannot discard more objects than available in stack"
187                    .to_string()
188                    .into(),
189            ));
190        }
191
192        // 使用 drain 一次性删除范围内的元素
193        let remove_start = stack.len() - offset - count;
194        let remove_end = stack.len() - offset;
195        stack.drain(remove_start..remove_end);
196        Ok(())
197    }
198
199    pub fn get_object_rev(&self, idx: usize) -> Result<&OnionStaticObject, RuntimeError> {
200        if self.frames.len() == 0 {
201            return Err(RuntimeError::DetailedError(
202                "Cannot get object from empty context".into(),
203            ));
204        }
205        let last_frame = self.frames.last().unwrap();
206        if last_frame.get_stack().len() <= idx {
207            return Err(RuntimeError::DetailedError(
208                "Cannot get object from empty stack".into(),
209            ));
210        }
211        let stack = last_frame.get_stack();
212        match stack.get(stack.len() - 1 - idx) {
213            None => Err(RuntimeError::DetailedError("Index out of bounds".into())),
214            Some(o) => Ok(o),
215        }
216    }
217
218    pub fn get_object_rev_mut(
219        &mut self,
220        idx: usize,
221    ) -> Result<&mut OnionStaticObject, RuntimeError> {
222        if self.frames.len() == 0 {
223            return Err(RuntimeError::DetailedError(
224                "Cannot get object from empty context".into(),
225            ));
226        }
227        let last_frame = self.frames.last_mut().unwrap();
228        if last_frame.get_stack().len() <= idx {
229            return Err(RuntimeError::DetailedError(
230                "Cannot get object from empty stack".into(),
231            ));
232        }
233        let stack = last_frame.get_stack_mut();
234        let idx = stack.len() - 1 - idx;
235        match stack.get_mut(idx) {
236            None => Err(RuntimeError::DetailedError("Index out of bounds".into())),
237            Some(o) => Ok(o),
238        }
239    }
240
241    #[inline(always)]
242    pub fn let_variable(
243        &mut self,
244        name: usize,
245        value: OnionStaticObject,
246    ) -> Result<(), RuntimeError> {
247        if self.frames.len() == 0 {
248            return Err(RuntimeError::InvalidOperation(
249                "Cannot let variable in empty context".into(),
250            ));
251        }
252
253        let last_frame = self.frames.last_mut().unwrap();
254        last_frame.variables.insert(name, value);
255        Ok(())
256    }
257
258    #[inline(always)]
259    pub fn get_variable(&self, name: usize) -> Option<&OnionStaticObject> {
260        if self.frames.len() == 0 {
261            return None;
262        } // 反向遍历所有帧,从最新的帧开始查找
263        for frame in self.frames.iter().rev() {
264            if let Some(value) = frame.variables.get(&name) {
265                return Some(value);
266            }
267        }
268        None
269    }
270
271    fn _debug_print(&self) {
272        println!("Context Debug Print:");
273        for (i, frame) in self.frames.iter().enumerate() {
274            println!("Frame {}: {:?}", i, frame);
275        }
276    }
277
278    pub fn get_variable_mut(&mut self, name: usize) -> Option<&mut OnionStaticObject> {
279        if self.frames.len() == 0 {
280            return None;
281        } // 反向遍历所有帧,从最新的帧开始查找
282        for frame in self.frames.iter_mut().rev() {
283            if let Some(value) = frame.variables.get_mut(&name) {
284                return Some(value);
285            }
286        }
287        None
288    }
289
290    pub fn swap(&mut self, idx1: usize, idx2: usize) -> Result<(), RuntimeError> {
291        if self.frames.len() == 0 {
292            return Err(RuntimeError::DetailedError(
293                "Cannot swap objects in empty context".into(),
294            ));
295        }
296        let last_frame = self.frames.last_mut().unwrap();
297        let stack = last_frame.get_stack_mut();
298        if stack.len() <= idx1 || stack.len() <= idx2 {
299            return Err(RuntimeError::DetailedError(
300                "Cannot swap objects in empty stack".into(),
301            ));
302        }
303        let len = stack.len();
304        stack.swap(len - 1 - idx1, len - 1 - idx2);
305        Ok(())
306    }
307
308    pub fn get_current_stack_mut(&mut self) -> Result<&mut Vec<OnionStaticObject>, RuntimeError> {
309        if self.frames.len() == 0 {
310            return Err(RuntimeError::DetailedError(
311                "Cannot get stack from empty context".into(),
312            ));
313        }
314        let last_frame = self.frames.last_mut().unwrap();
315        Ok(last_frame.get_stack_mut())
316    }
317
318    #[inline(always)]
319    pub fn push_to_stack(stack: &mut Vec<OnionStaticObject>, object: OnionStaticObject) {
320        stack.push(object);
321    }
322
323    pub fn pop_from_stack(
324        stack: &mut Vec<OnionStaticObject>,
325    ) -> Result<OnionStaticObject, RuntimeError> {
326        if stack.is_empty() {
327            return Err(RuntimeError::DetailedError(
328                "Cannot pop from empty stack".into(),
329            ));
330        }
331        Ok(stack.pop().unwrap())
332    }
333
334    #[inline(always)]
335    pub fn discard_from_stack(
336        stack: &mut Vec<OnionStaticObject>,
337        count: usize,
338    ) -> Result<(), RuntimeError> {
339        if stack.len() < count {
340            return Err(RuntimeError::DetailedError(
341                "Cannot discard more objects than available in stack"
342                    .to_string()
343                    .into(),
344            ));
345        }
346        stack.truncate(stack.len() - count);
347        Ok(())
348    }
349
350    #[inline(always)]
351    pub fn get_object_from_stack(
352        stack: &Vec<OnionStaticObject>,
353        idx: usize,
354    ) -> Result<&OnionStaticObject, RuntimeError> {
355        if stack.len() <= idx {
356            return Err(RuntimeError::DetailedError("Index out of bounds".into()));
357        }
358        Ok(&stack[stack.len() - 1 - idx])
359    }
360
361    #[inline(always)]
362    pub fn get_objects_slice(
363        stack: &Vec<OnionStaticObject>,
364        idx: usize,
365        len: usize,
366    ) -> Result<&[OnionStaticObject], RuntimeError> {
367        // 如果长度为0,直接返回空切片
368        if len == 0 {
369            return Ok(&[]);
370        }
371
372        if stack.len() <= idx {
373            return Err(RuntimeError::DetailedError("Index out of bounds".into()));
374        }
375
376        if idx + len > stack.len() {
377            return Err(RuntimeError::DetailedError(
378                "Slice length exceeds available elements".into(),
379            ));
380        }
381
382        let start = stack.len() - idx - len;
383        let end = stack.len() - idx;
384        Ok(&stack[start..end])
385    }
386    pub fn replace_last_object(stack: &mut Vec<OnionStaticObject>, object: OnionStaticObject) {
387        let last_index = stack.len() - 1;
388        stack[last_index] = object;
389    }
390
391    pub fn format_context(&self, package: &VMInstructionPackage) -> String {
392        if self.frames.is_empty() {
393            return "Context: (No active frames)".to_string();
394        }
395
396        let mut parts = Vec::new();
397        parts.push(format!("Call Stack ({} frames):", self.frames.len()));
398
399        // 从栈顶(最近的调用帧)开始打印
400        for (i, frame) in self.frames.iter().rev().enumerate() {
401            // 你需要一种方法来命名你的帧。这通常与函数名相关联。
402            // 暂时我们用索引代替。
403            parts.push(format!("--- Frame #{} (most recent) ---", i));
404
405            // 调用我们刚刚为 Frame 实现的 format_context
406            let frame_context = frame.format_context(package);
407            parts.push(frame_context);
408        }
409
410        parts.join("\n")
411    }
412}