From 0dfbda5e15d1b4f4958e35218e96558be4fc89ca Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sat, 28 Feb 2026 14:26:29 +0100 Subject: [PATCH] Refactor VM to use trait objects for closures The `CallFrame` struct and various VM methods now use `Rc` to hold closures, allowing for more flexibility and avoiding unnecessary cloning. This change also addresses the performance concern regarding deep copies of closures, preferring `Rc` with local `downcast_ref` where appropriate. Additionally, a documentation note has been added to `gemini.md` regarding this performance preference in Rust. --- gemini.md | 4 ++++ src/ast/environment.rs | 11 ++++++----- src/ast/vm.rs | 34 +++++++++++++++++++--------------- test_while.myc | 1 - 4 files changed, 29 insertions(+), 21 deletions(-) delete mode 100644 test_while.myc diff --git a/gemini.md b/gemini.md index e116a6c..b630178 100644 --- a/gemini.md +++ b/gemini.md @@ -33,6 +33,10 @@ Wichtig: zentraler Einstiegspunkt ist Delphi/Myc.Ast.Environment.pasund die dort * Dokumentation nach Rust-Regeln. * Im Code und den Kommentaren muss alles auf englisch sein. +## Rust + +* Performance: Bevorzuge `Rc` + lokales `downcast_ref` gegenüber Deep Copies via `Rc::new(obj.clone())`. + ## Interaktion * Ich kann Rust noch nicht so gut. Erkläre mir, was du machst und welche Konzepte du nutzt. Ich will was dazulernen. diff --git a/src/ast/environment.rs b/src/ast/environment.rs index c253467..45d2e7f 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -261,12 +261,13 @@ impl Environment { vec![], *positional_count, )); + let closure_obj: Rc = closure; return Rc::new(crate::ast::types::NativeFunction { purity: Purity::Impure, func: Rc::new(move |args| { let mut vm = VM::new(global_values.clone()); - let res = match vm.run_with_args(&closure, args) { + let res = match vm.run_with_args(closure_obj.clone(), args) { Ok(v) => v, Err(e) => panic!("Myc Runtime Error: {}", e), }; @@ -287,9 +288,9 @@ impl Environment { let mut final_res = res; if let Value::Object(obj) = &final_res - && let Some(closure) = obj.as_any().downcast_ref::() + && obj.as_any().downcast_ref::().is_some() { - final_res = match vm.run_with_args(closure, args) { + final_res = match vm.run_with_args(obj.clone(), args) { Ok(v) => v, Err(e) => panic!("Myc Runtime Error (Closure): {}", e), }; @@ -396,8 +397,8 @@ impl Environment { while let Ok(Value::TailCallRequest(payload)) = result { let (next_obj, next_args) = *payload; - if let Some(closure) = next_obj.as_any().downcast_ref::() { - result = vm.run_with_args_observed(&mut observer, closure, next_args); + if next_obj.as_any().downcast_ref::().is_some() { + result = vm.run_with_args_observed(&mut observer, next_obj, next_args); } else { result = Err(format!( "Tail call target is not a closure: {}", diff --git a/src/ast/vm.rs b/src/ast/vm.rs index 366c281..0da56ad 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -49,7 +49,7 @@ impl Object for Closure { #[derive(Debug)] struct CallFrame { stack_base: usize, - closure: Option>, + closure: Option>, } pub trait VMObserver { @@ -154,7 +154,7 @@ impl VM { self.stack.clear(); self.frames.push(CallFrame { stack_base: 0, - closure: Some(Rc::new(closure.clone())), + closure: Some(next_obj.clone()), }); if let Some(count) = closure.positional_count && next_args.len() == count as usize @@ -177,12 +177,13 @@ impl VM { } } - pub fn run_with_args(&mut self, closure: &Closure, args: Vec) -> Result { + pub fn run_with_args(&mut self, closure_obj: Rc, args: Vec) -> Result { + let closure = closure_obj.as_any().downcast_ref::().unwrap(); self.stack.clear(); self.frames.clear(); self.frames.push(CallFrame { stack_base: 0, - closure: Some(Rc::new(closure.clone())), + closure: Some(closure_obj.clone()), }); if let Some(count) = closure.positional_count && args.len() == count as usize @@ -197,14 +198,15 @@ impl VM { pub fn run_with_args_observed( &mut self, observer: &mut O, - closure: &Closure, + closure_obj: Rc, args: Vec, ) -> Result { + let closure = closure_obj.as_any().downcast_ref::().unwrap(); self.stack.clear(); self.frames.clear(); self.frames.push(CallFrame { stack_base: 0, - closure: Some(Rc::new(closure.clone())), + closure: Some(closure_obj.clone()), }); if let Some(count) = closure.positional_count && args.len() == count as usize @@ -445,10 +447,9 @@ impl VM { Value::Object(obj) => { if let Some(closure) = obj.as_any().downcast_ref::() { let old_stack_top = self.stack.len(); - let closure_rc = Rc::new(closure.clone()); self.frames.push(CallFrame { stack_base: old_stack_top, - closure: Some(closure_rc.clone()), + closure: Some(obj.clone()), }); if let Some(count) = closure.positional_count && arg_vals.len() == count as usize @@ -509,9 +510,9 @@ impl VM { }; let frame = self.frames.last().ok_or("No call frame for 'again'")?; - if let Some(closure) = &frame.closure { + if let Some(closure_obj) = &frame.closure { Ok(Value::TailCallRequest(Box::new(( - closure.clone() as Rc, + closure_obj.clone(), arg_vals, )))) } else { @@ -551,8 +552,8 @@ impl VM { pub fn resolve_tail_calls(&mut self, mut result: Value) -> Value { while let Value::TailCallRequest(payload) = result { let (next_obj, next_args) = *payload; - if let Some(closure) = next_obj.as_any().downcast_ref::() { - result = match self.run_with_args(closure, next_args) { + if next_obj.as_any().downcast_ref::().is_some() { + result = match self.run_with_args(next_obj, next_args) { Ok(v) => v, Err(e) => panic!("Myc Runtime Error (TailCall): {}", e), }; @@ -586,7 +587,8 @@ impl VM { } Address::Upvalue(idx) => { let frame = self.frames.last().ok_or("No call frame")?; - if let Some(closure) = &frame.closure { + if let Some(closure_obj) = &frame.closure { + let closure = closure_obj.as_any().downcast_ref::().unwrap(); let u_idx = idx.0 as usize; if u_idx < closure.upvalues.len() { Ok(closure.upvalues[u_idx].clone()) @@ -626,7 +628,8 @@ impl VM { } Address::Upvalue(idx) => { let frame = self.frames.last().ok_or("No call frame")?; - if let Some(closure) = &frame.closure { + if let Some(closure_obj) = &frame.closure { + let closure = closure_obj.as_any().downcast_ref::().unwrap(); let u_idx = idx.0 as usize; if u_idx < closure.upvalues.len() { Ok(closure.upvalues[u_idx].borrow().clone()) @@ -669,7 +672,8 @@ impl VM { } Address::Upvalue(idx) => { let frame = self.frames.last().ok_or("No call frame")?; - if let Some(closure) = &frame.closure { + if let Some(closure_obj) = &frame.closure { + let closure = closure_obj.as_any().downcast_ref::().unwrap(); let u_idx = idx.0 as usize; if u_idx < closure.upvalues.len() { *closure.upvalues[u_idx].borrow_mut() = value; diff --git a/test_while.myc b/test_while.myc deleted file mode 100644 index a1c2bc6..0000000 --- a/test_while.myc +++ /dev/null @@ -1 +0,0 @@ -(while (< 1 2) (print 1)) \ No newline at end of file