Add Obj.__init__ and Obj.__call__, fix how __call__ works on Ty

* Obj.__call__ should generically call Obj.__init__, passing along the
  arguments given to __call__
* Ty::call function (in Rust) sets up the stack frame correctly now.
  Before, the only arguments visible to a `Ty.__call__` function would
  have been the values passed to it. However, the Ty.__call__ also needs
  *itself* passed to the __call__ function so we can retrieve the
  __init__ function and eventually end up calling that.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-10-07 15:24:06 -07:00
parent 16ab9d718b
commit 40a10fa00e
3 changed files with 86 additions and 18 deletions

View File

@@ -452,6 +452,54 @@ impl Object for Obj {
impl_base_obj!(Obj); impl_base_obj!(Obj);
} }
//
// Obj methods
//
impl Obj {
pub(crate) fn do_call(vm: &mut Vm, state: FunctionState) -> FunctionResult {
let ty = vm.frame_stack()[0].clone();
match state {
FunctionState::Begin => {
let argc = vm.frame_stack().len() - 1;
let mut obj = Obj::new();
obj.set_attr("__ty__", ty.clone());
obj.instantiate();
let obj_ptr = make_ptr(obj);
let init = obj_ptr
.borrow()
.get_vtable_attr(obj_ptr.clone(), "__init__")
.expect("no __init__");
vm.push(obj_ptr);
vm.push(init.clone());
// duplicate arguments that were pushed to the frame
for i in 0..argc {
let arg = vm.frame_stack()[i + 1].clone();
vm.push(arg);
}
init.borrow().call(vm, argc as Argc);
FunctionResult::Yield(0)
}
FunctionState::Resume(0) => {
vm.pop();
FunctionResult::Return
}
_ => unreachable!(),
}
}
pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult {
// no-op
Nil::create().into()
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Nil // Nil
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,13 +1,15 @@
macro_rules! impl_base_obj { macro_rules! impl_base_obj {
($base_name:ident, $type_name:ident) => { ($base_name:ident, $type_name:ident) => {
fn instantiate(&mut self) { fn instantiate(&mut self) {
let ty = $crate::obj::BUILTINS.with_borrow(|builtins| { if self.get_attr("__ty__").is_none() {
builtins let ty = $crate::obj::BUILTINS.with_borrow(|builtins| {
.get(stringify!($type_name)) builtins
.expect(concat!("no ", stringify!($type_name))) .get(stringify!($type_name))
.clone() .expect(concat!("no ", stringify!($type_name)))
}); .clone()
self.set_attr("__ty__", ty); });
self.set_attr("__ty__", ty);
}
self.$base_name.instantiate(); self.$base_name.instantiate();
} }

View File

@@ -77,21 +77,37 @@ impl Object for Ty {
// If you do e.g. `Int.__call__`, Int is an object, so it should be doing `__call__` as a // If you do e.g. `Int.__call__`, Int is an object, so it should be doing `__call__` as a
// vtable value. // vtable value.
if cfg!(debug_assertions) { let index = vm.stack().len() - 1 - argc as usize;
let index = vm.stack().len() - 1 - argc as usize; let this = vm.stack()[index].clone();
let this = vm.stack()[index].clone();
assert!( assert!(
std::ptr::addr_eq(&*this.borrow(), self), std::ptr::addr_eq(&*this.borrow(), self),
"calling {}.__call__ on type that is not ourselves", "calling {}.__call__ on type that is not ourselves",
self.ty_name() self.ty_name()
); );
}
// Additionally, we want to duplicate the type argument on the stack.
//
// When you call a new constructor, e.g. `MyType(arg1, arg2, arg3)`, the stack will look like this:
//
// -- stack top
// arg3
// arg2
// arg1
// <Ty MyType> <---- NOTE - this is what we are actually calling, and is being treated as a function
// ...
// -- stack bottom
//
// With a custom type, we want to be getting the __init__ function off of the type. Since
// we don't have access to the function in the call stack (in this case, it's our type),
// we duplicate the type to be the first argument.
vm.stack_mut().insert(index, this);
let function = self let function = self
.vtable .vtable
.get("__call__") .get("__call__")
.expect("Why does a type not have a __call__ member?"); .expect("Why does a type not have a __call__ member?");
function.borrow().call(vm, argc); function.borrow().call(vm, argc + 1);
} }
fn arity(&self) -> Option<Argc> { fn arity(&self) -> Option<Argc> {
@@ -185,7 +201,9 @@ pub fn init_types() {
len => BuiltinFunction::create("len", BaseObj::not_implemented_un, 1), len => BuiltinFunction::create("len", BaseObj::not_implemented_un, 1),
}, },
Obj { Obj {
//__call__ => BuiltinFunction::create("__call__", // Constructor
__call__ => BuiltinFunction::create("__call__", Obj::do_call, 1),
__init__ => BuiltinFunction::create("__init__", Obj::init, 1),
// Methods // Methods
}, },
List { List {