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);
|
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
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user