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.
* 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
* 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![],
*positional_count,
));
let closure_obj: Rc<dyn crate::ast::types::Object> = 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::<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,
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::<crate::ast::vm::Closure>() {
result = vm.run_with_args_observed(&mut observer, closure, next_args);
if next_obj.as_any().downcast_ref::<crate::ast::vm::Closure>().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: {}",
+19 -15
View File
@@ -49,7 +49,7 @@ impl Object for Closure {
#[derive(Debug)]
struct CallFrame {
stack_base: usize,
closure: Option<Rc<Closure>>,
closure: Option<Rc<dyn Object>>,
}
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<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.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<O: VMObserver>(
&mut self,
observer: &mut O,
closure: &Closure,
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.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::<Closure>() {
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<dyn Object>,
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::<Closure>() {
result = match self.run_with_args(closure, next_args) {
if next_obj.as_any().downcast_ref::<Closure>().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::<Closure>().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::<Closure>().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::<Closure>().unwrap();
let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() {
*closure.upvalues[u_idx].borrow_mut() = value;
-1
View File
@@ -1 +0,0 @@
(while (< 1 2) (print 1))