Refactor VM to use trait objects for closures

The `CallFrame` struct and various VM methods now use `Rc<dyn Object>`
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<dyn Trait>` with local `downcast_ref` where
appropriate.

Additionally, a documentation note has been added to `gemini.md`
regarding this performance preference in Rust.
This commit is contained in:
Michael Schimmel
2026-02-28 14:26:29 +01:00
parent 3daf8ef94d
commit 0dfbda5e15
4 changed files with 29 additions and 21 deletions
+4
View File
@@ -33,6 +33,10 @@ Wichtig: zentraler Einstiegspunkt ist Delphi/Myc.Ast.Environment.pasund die dort
* Dokumentation nach Rust-Regeln. * Dokumentation nach Rust-Regeln.
* Im Code und den Kommentaren muss alles auf englisch sein. * Im Code und den Kommentaren muss alles auf englisch sein.
## Rust
* Performance: Bevorzuge `Rc<dyn Trait>` + lokales `downcast_ref` gegenüber Deep Copies via `Rc::new(obj.clone())`.
## Interaktion ## Interaktion
* Ich kann Rust noch nicht so gut. Erkläre mir, was du machst und welche Konzepte du nutzt. Ich will was dazulernen. * Ich kann Rust noch nicht so gut. Erkläre mir, was du machst und welche Konzepte du nutzt. Ich will was dazulernen.
+6 -5
View File
@@ -261,12 +261,13 @@ impl Environment {
vec![], vec![],
*positional_count, *positional_count,
)); ));
let closure_obj: Rc<dyn crate::ast::types::Object> = closure;
return Rc::new(crate::ast::types::NativeFunction { return Rc::new(crate::ast::types::NativeFunction {
purity: Purity::Impure, purity: Purity::Impure,
func: Rc::new(move |args| { func: Rc::new(move |args| {
let mut vm = VM::new(global_values.clone()); 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, Ok(v) => v,
Err(e) => panic!("Myc Runtime Error: {}", e), Err(e) => panic!("Myc Runtime Error: {}", e),
}; };
@@ -287,9 +288,9 @@ impl Environment {
let mut final_res = res; let mut final_res = res;
if let Value::Object(obj) = &final_res if let Value::Object(obj) = &final_res
&& let Some(closure) = obj.as_any().downcast_ref::<crate::ast::vm::Closure>() && obj.as_any().downcast_ref::<crate::ast::vm::Closure>().is_some()
{ {
final_res = match vm.run_with_args(closure, args) { final_res = match vm.run_with_args(obj.clone(), args) {
Ok(v) => v, Ok(v) => v,
Err(e) => panic!("Myc Runtime Error (Closure): {}", e), Err(e) => panic!("Myc Runtime Error (Closure): {}", e),
}; };
@@ -396,8 +397,8 @@ impl Environment {
while let Ok(Value::TailCallRequest(payload)) = result { while let Ok(Value::TailCallRequest(payload)) = result {
let (next_obj, next_args) = *payload; let (next_obj, next_args) = *payload;
if let Some(closure) = next_obj.as_any().downcast_ref::<crate::ast::vm::Closure>() { if next_obj.as_any().downcast_ref::<crate::ast::vm::Closure>().is_some() {
result = vm.run_with_args_observed(&mut observer, closure, next_args); result = vm.run_with_args_observed(&mut observer, next_obj, next_args);
} else { } else {
result = Err(format!( result = Err(format!(
"Tail call target is not a closure: {}", "Tail call target is not a closure: {}",
+19 -15
View File
@@ -49,7 +49,7 @@ impl Object for Closure {
#[derive(Debug)] #[derive(Debug)]
struct CallFrame { struct CallFrame {
stack_base: usize, stack_base: usize,
closure: Option<Rc<Closure>>, closure: Option<Rc<dyn Object>>,
} }
pub trait VMObserver { pub trait VMObserver {
@@ -154,7 +154,7 @@ impl VM {
self.stack.clear(); self.stack.clear();
self.frames.push(CallFrame { self.frames.push(CallFrame {
stack_base: 0, stack_base: 0,
closure: Some(Rc::new(closure.clone())), closure: Some(next_obj.clone()),
}); });
if let Some(count) = closure.positional_count if let Some(count) = closure.positional_count
&& next_args.len() == count as usize && next_args.len() == count as usize
@@ -177,12 +177,13 @@ impl VM {
} }
} }
pub fn run_with_args(&mut self, closure: &Closure, args: Vec<Value>) -> Result<Value, String> { pub fn run_with_args(&mut self, closure_obj: Rc<dyn Object>, args: Vec<Value>) -> Result<Value, String> {
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
self.stack.clear(); self.stack.clear();
self.frames.clear(); self.frames.clear();
self.frames.push(CallFrame { self.frames.push(CallFrame {
stack_base: 0, stack_base: 0,
closure: Some(Rc::new(closure.clone())), closure: Some(closure_obj.clone()),
}); });
if let Some(count) = closure.positional_count if let Some(count) = closure.positional_count
&& args.len() == count as usize && args.len() == count as usize
@@ -197,14 +198,15 @@ impl VM {
pub fn run_with_args_observed<O: VMObserver>( pub fn run_with_args_observed<O: VMObserver>(
&mut self, &mut self,
observer: &mut O, observer: &mut O,
closure: &Closure, closure_obj: Rc<dyn Object>,
args: Vec<Value>, args: Vec<Value>,
) -> Result<Value, String> { ) -> Result<Value, String> {
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
self.stack.clear(); self.stack.clear();
self.frames.clear(); self.frames.clear();
self.frames.push(CallFrame { self.frames.push(CallFrame {
stack_base: 0, stack_base: 0,
closure: Some(Rc::new(closure.clone())), closure: Some(closure_obj.clone()),
}); });
if let Some(count) = closure.positional_count if let Some(count) = closure.positional_count
&& args.len() == count as usize && args.len() == count as usize
@@ -445,10 +447,9 @@ impl VM {
Value::Object(obj) => { Value::Object(obj) => {
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() { if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
let old_stack_top = self.stack.len(); let old_stack_top = self.stack.len();
let closure_rc = Rc::new(closure.clone());
self.frames.push(CallFrame { self.frames.push(CallFrame {
stack_base: old_stack_top, stack_base: old_stack_top,
closure: Some(closure_rc.clone()), closure: Some(obj.clone()),
}); });
if let Some(count) = closure.positional_count if let Some(count) = closure.positional_count
&& arg_vals.len() == count as usize && arg_vals.len() == count as usize
@@ -509,9 +510,9 @@ impl VM {
}; };
let frame = self.frames.last().ok_or("No call frame for 'again'")?; 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(( Ok(Value::TailCallRequest(Box::new((
closure.clone() as Rc<dyn Object>, closure_obj.clone(),
arg_vals, arg_vals,
)))) ))))
} else { } else {
@@ -551,8 +552,8 @@ impl VM {
pub fn resolve_tail_calls(&mut self, mut result: Value) -> Value { pub fn resolve_tail_calls(&mut self, mut result: Value) -> Value {
while let Value::TailCallRequest(payload) = result { while let Value::TailCallRequest(payload) = result {
let (next_obj, next_args) = *payload; let (next_obj, next_args) = *payload;
if let Some(closure) = next_obj.as_any().downcast_ref::<Closure>() { if next_obj.as_any().downcast_ref::<Closure>().is_some() {
result = match self.run_with_args(closure, next_args) { result = match self.run_with_args(next_obj, next_args) {
Ok(v) => v, Ok(v) => v,
Err(e) => panic!("Myc Runtime Error (TailCall): {}", e), Err(e) => panic!("Myc Runtime Error (TailCall): {}", e),
}; };
@@ -586,7 +587,8 @@ impl VM {
} }
Address::Upvalue(idx) => { Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?; 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::<Closure>().unwrap();
let u_idx = idx.0 as usize; let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() { if u_idx < closure.upvalues.len() {
Ok(closure.upvalues[u_idx].clone()) Ok(closure.upvalues[u_idx].clone())
@@ -626,7 +628,8 @@ impl VM {
} }
Address::Upvalue(idx) => { Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?; 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::<Closure>().unwrap();
let u_idx = idx.0 as usize; let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() { if u_idx < closure.upvalues.len() {
Ok(closure.upvalues[u_idx].borrow().clone()) Ok(closure.upvalues[u_idx].borrow().clone())
@@ -669,7 +672,8 @@ impl VM {
} }
Address::Upvalue(idx) => { Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?; 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::<Closure>().unwrap();
let u_idx = idx.0 as usize; let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() { if u_idx < closure.upvalues.len() {
*closure.upvalues[u_idx].borrow_mut() = value; *closure.upvalues[u_idx].borrow_mut() = value;
-1
View File
@@ -1 +0,0 @@
(while (< 1 2) (print 1))