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:
+19
-15
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user