Add Vm::exit_module

This does all of the routines necessary for exiting a module's
execution.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-10-04 10:50:40 -07:00
parent f0de5f7850
commit c5f95f4d87
3 changed files with 46 additions and 65 deletions

View File

@@ -47,8 +47,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
// run // run
let module = upcast_obj(module);
let mut vm = vm::Vm::new(&constants); let mut vm = vm::Vm::new(&constants);
vm.enter_module(upcast_obj(module)); // VM needs the module to be on top of the stack when it executes
vm.push(module.clone());
vm.enter_module(module);
vm.run(); vm.run();
Ok(()) Ok(())

View File

@@ -81,43 +81,3 @@ impl Object for Module {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Module method implementations // Module method implementations
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/*
impl Module {
pub(crate) fn execute(vm: &mut Vm, state: FunctionState) -> FunctionResult {
match state {
FunctionState::Begin => {
let this = vm.frame_stack()[0].clone();
let value = with_obj_downcast(this.clone(), |module: &Module| {
module.evaluated_value.clone()
});
// don't evaluate this twice
if let Some(value) = value {
return FunctionResult::ReturnPush(value);
}
vm.enter_module(this.clone());
FunctionResult::Yield(0)
}
FunctionState::Resume(0) => {
let this = vm.frame_stack()[0].clone();
let obj = with_obj_downcast(this.clone(), |module: &Module| {
assert_eq!(module.globals().len(), vm.globals().len());
let obj = Obj::create();
module
.globals()
.iter()
.zip(vm.globals())
.for_each(|(name, value)| {
obj.borrow_mut().set_attr(name, value.clone());
});
obj
});
FunctionResult::ReturnPush(obj)
}
_ => unreachable!(),
}
}
}
*/

View File

@@ -198,7 +198,17 @@ impl<'c> Vm<'c> {
} }
*/ */
pub fn enter_module(&mut self, module_ptr: ObjP) { /// Performs module entrance routines.
///
/// This **will not** check if the module has already been evaluated - this is up to the caller
/// to do so.
///
/// This function will set the VM up for execution of a new module by:
///
/// 1. Create a new globals stack for the new module to use, and populate it with all of the
/// globals that are used in the module.
/// 2. Create and push a new module stack frame.
pub(crate) fn enter_module(&mut self, module_ptr: ObjP) {
with_obj_downcast(module_ptr.clone(), |module: &Module| { with_obj_downcast(module_ptr.clone(), |module: &Module| {
let mut globals: Vec<_> = module.globals().iter().map(|_| Nil::create()).collect(); let mut globals: Vec<_> = module.globals().iter().map(|_| Nil::create()).collect();
@@ -227,6 +237,36 @@ impl<'c> Vm<'c> {
}); });
} }
/// Performs module exit routines.
///
/// This expects that the module object that was executed is still on top of the stack. This
/// will be popped, and the object represented by this module will be pushed to the stack.
fn exit_module(&mut self) {
let globals = self.globals.pop().expect("no globals");
let old_frame = self.pop_frame();
self.stack
.resize_with(old_frame.stack_base, || unreachable!());
let module = self.pop();
// create the object and assign it as the module's evaluated value
let obj = with_obj_downcast_mut(module, |module: &mut Module| {
assert_eq!(module.globals().len(), globals.len());
let obj = Obj::create();
module
.globals()
.iter()
.zip(globals)
.for_each(|(name, value)| {
obj.borrow_mut().set_attr(name, value.clone());
});
module.set_evaluated_value(Some(obj.clone()));
obj
});
self.push(obj);
}
/// Get the current instruction and advance the IP. /// Get the current instruction and advance the IP.
/// ///
/// This may actually end up calling a function on top of the stack, if it's a builtin function. /// This may actually end up calling a function on top of the stack, if it's a builtin function.
@@ -447,34 +487,12 @@ impl<'c> Vm<'c> {
} }
} }
Op::ExitModule => { Op::ExitModule => {
let globals = self.globals.pop().unwrap(); self.exit_module();
let old_frame = self.pop_frame();
self.stack
.resize_with(old_frame.stack_base, || unreachable!());
// halt execution if there aren't any globals left // halt execution if there aren't any globals left
if self.globals.is_empty() { if self.globals.is_empty() {
break; break;
} }
let module = self.pop();
// create the object and assign it as the module's evaluated value
let obj = with_obj_downcast_mut(module, |module: &mut Module| {
assert_eq!(module.globals().len(), globals.len());
let obj = Obj::create();
module
.globals()
.iter()
.zip(globals)
.for_each(|(name, value)| {
obj.borrow_mut().set_attr(name, value.clone());
});
module.set_evaluated_value(Some(obj.clone()));
obj
});
self.push(obj);
} }
} }
} }