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:
48
src/obj.rs
48
src/obj.rs
@@ -452,6 +452,54 @@ impl Object for 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
macro_rules! impl_base_obj {
|
||||
($base_name:ident, $type_name:ident) => {
|
||||
fn instantiate(&mut self) {
|
||||
let ty = $crate::obj::BUILTINS.with_borrow(|builtins| {
|
||||
builtins
|
||||
.get(stringify!($type_name))
|
||||
.expect(concat!("no ", stringify!($type_name)))
|
||||
.clone()
|
||||
});
|
||||
self.set_attr("__ty__", ty);
|
||||
if self.get_attr("__ty__").is_none() {
|
||||
let ty = $crate::obj::BUILTINS.with_borrow(|builtins| {
|
||||
builtins
|
||||
.get(stringify!($type_name))
|
||||
.expect(concat!("no ", stringify!($type_name)))
|
||||
.clone()
|
||||
});
|
||||
self.set_attr("__ty__", ty);
|
||||
}
|
||||
self.$base_name.instantiate();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
// vtable value.
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
let index = vm.stack().len() - 1 - argc as usize;
|
||||
let this = vm.stack()[index].clone();
|
||||
assert!(
|
||||
std::ptr::addr_eq(&*this.borrow(), self),
|
||||
"calling {}.__call__ on type that is not ourselves",
|
||||
self.ty_name()
|
||||
);
|
||||
}
|
||||
let index = vm.stack().len() - 1 - argc as usize;
|
||||
let this = vm.stack()[index].clone();
|
||||
|
||||
assert!(
|
||||
std::ptr::addr_eq(&*this.borrow(), self),
|
||||
"calling {}.__call__ on type that is not ourselves",
|
||||
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
|
||||
.vtable
|
||||
.get("__call__")
|
||||
.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> {
|
||||
@@ -185,7 +201,9 @@ pub fn init_types() {
|
||||
len => BuiltinFunction::create("len", BaseObj::not_implemented_un, 1),
|
||||
},
|
||||
Obj {
|
||||
//__call__ => BuiltinFunction::create("__call__",
|
||||
// Constructor
|
||||
__call__ => BuiltinFunction::create("__call__", Obj::do_call, 1),
|
||||
__init__ => BuiltinFunction::create("__init__", Obj::init, 1),
|
||||
// Methods
|
||||
},
|
||||
List {
|
||||
|
||||
Reference in New Issue
Block a user