diff --git a/src/obj.rs b/src/obj.rs index b797f8e..75257e2 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -1,5 +1,5 @@ // TODO obj.rs - remove the warning suppression -#![allow(unused_variables, dead_code)] +#![allow(dead_code)] use std::any::Any; use std::collections::HashMap; @@ -32,6 +32,23 @@ where } } +/// Downcast an object pointer to a concrete type, and do something with that object. +pub fn with_obj_downcast_mut(ptr: ObjP, closure: impl FnOnce(&mut T) -> Out) -> Out +where + T: Obj + 'static, +{ + let mut borrowed = ptr.borrow_mut(); + if let Some(obj) = borrowed.as_any_mut().downcast_mut::() { + closure(obj) + } else { + panic!( + "could not downcast '{:?}' to {}", + ptr, + std::any::type_name::() + ) + } +} + pub fn obj_is_inst(ptr: &ObjP) -> bool where T: Obj + 'static, @@ -50,34 +67,60 @@ pub fn upcast_obj(ptr: Ptr) -> ObjP { pub fn init_types(builtins: &mut HashMap) { #![allow(non_snake_case)] macro_rules! types { - (base_type: $base_type:ident, $($name:ident),* $(,)?) => { + ( + base_type: $base_type:ident, + $( + $name:ident { + $( $vtable_name:ident => $vtable_value:expr ),* $(,)? + } + ),* $(,)? + ) => {{ $( let $name = make_ptr(TypeInst::new(stringify!($name))); )* - $( - $name.borrow_mut().instantiate(upcast_obj(Gc::clone(&$base_type))); - )* + // We have to instantiate these objects all by hand. This is because the `instantiate` + // function does some stuff that may accidentally cause infinite recursion while we are + // setting up these fundamental types. + $({ + let base_type = $base_type.clone(); + $name.borrow_mut().set_attr("__type__", base_type); + with_obj_downcast_mut($name.clone(), |type_inst: &mut TypeInst| { type_inst.base.is_instantiated = true; }); + //$name.borrow_mut().base.is_instantiated = true; + })* $( - builtins.insert(stringify!($name).to_string(), upcast_obj($name)); + builtins.insert(stringify!($name).to_string(), $name.clone()); )* - }; + + $({ + $( + let vtable_name = stringify!($vtable_name); + let vtable_value = $vtable_value; + with_obj_downcast_mut($name, |type_inst: &mut TypeInst| { + type_inst.vtable.insert(vtable_name.to_string(), vtable_value); + }); + )* + })* + }}; } + types! { // base type base_type: Type, // type definitions - Type, - Obj, - Str, - Int, - Float, - Bool, - Nil, - BuiltinFunction, - UserFunction, - Method, + Type { + to_string => builtins.create_builtin_function("to_string", BaseObjInst::to_string, 1), + }, + Obj { }, + Str { }, + Int { }, + Float { }, + Bool { }, + Nil { }, + BuiltinFunction { }, + UserFunction { }, + Method { }, } } @@ -85,50 +128,31 @@ pub trait ObjFactory { fn builtins(&self) -> &HashMap; fn create_obj(&self) -> ObjP { - upcast_obj(ObjInst::create(ObjP::clone( - self.builtins().get("Obj").unwrap(), - ))) + ObjInst::create(self.builtins().get("Obj").unwrap().clone()) } fn create_type(&self, name: impl ToString) -> ObjP { - upcast_obj(TypeInst::create( - ObjP::clone(self.builtins().get("Type").unwrap()), - name, - )) + TypeInst::create(self.builtins().get("Type").unwrap().clone(), name) } fn create_str(&self, str_value: impl ToString) -> ObjP { - upcast_obj(StrInst::create( - ObjP::clone(self.builtins().get("Str").unwrap()), - str_value, - )) + StrInst::create(self.builtins().get("Str").unwrap().clone(), str_value) } fn create_int(&self, int_value: i64) -> ObjP { - upcast_obj(IntInst::create( - ObjP::clone(self.builtins().get("Int").unwrap()), - int_value, - )) + IntInst::create(self.builtins().get("Int").unwrap().clone(), int_value) } fn create_float(&self, float_value: f64) -> ObjP { - upcast_obj(FloatInst::create( - ObjP::clone(self.builtins().get("Float").unwrap()), - float_value, - )) + FloatInst::create(self.builtins().get("Float").unwrap().clone(), float_value) } fn create_bool(&self, bool_value: bool) -> ObjP { - upcast_obj(BoolInst::create( - ObjP::clone(self.builtins().get("Bool").unwrap()), - bool_value, - )) + BoolInst::create(self.builtins().get("Bool").unwrap().clone(), bool_value) } fn create_nil(&self) -> ObjP { - upcast_obj(NilInst::create(ObjP::clone( - self.builtins().get("Nil").unwrap(), - ))) + NilInst::create(self.builtins().get("Nil").unwrap().clone()) } fn create_builtin_function( @@ -137,28 +161,28 @@ pub trait ObjFactory { function: BuiltinFunctionPtr, arity: Argc, ) -> ObjP { - upcast_obj(BuiltinFunctionInst::create( - ObjP::clone(self.builtins().get("BuiltinFunction").unwrap()), + BuiltinFunctionInst::create( + self.builtins().get("BuiltinFunction").unwrap().clone(), name, function, arity, - )) + ) } fn create_user_function(&self, chunk: Chunk, arity: Argc) -> ObjP { - upcast_obj(UserFunctionInst::create( - ObjP::clone(self.builtins().get("UserFunction").unwrap()), + UserFunctionInst::create( + self.builtins().get("UserFunction").unwrap().clone(), chunk, arity, - )) + ) } fn create_method(&self, self_binding: ObjP, function: ObjP) -> ObjP { - upcast_obj(MethodInst::create( - ObjP::clone(self.builtins().get("Method").unwrap()), + MethodInst::create( + self.builtins().get("Method").unwrap().clone(), self_binding, function, - )) + ) } } @@ -173,8 +197,8 @@ impl ObjFactory for HashMap { /// /// I would implement this as a `From` but it doesn't seem to work for a foreign type, and I'm /// not sure why. -pub fn make_ptr(obj: T) -> Ptr { - Ptr::new(GcCell::new(obj)) +pub fn make_ptr(obj: T) -> ObjP { + upcast_obj(Ptr::new(GcCell::new(obj))) } //////////////////////////////////////////////////////////////////////////////// @@ -193,9 +217,49 @@ pub trait Obj: Debug + Display + Any + Trace { } fn get_attr(&self, name: &str) -> Option { + // check attrs, then check vtable self.attrs().get(name).map(Ptr::clone) } + /// This tries to get an attribute using these sources: + /// * `self.attrs()` + /// * `self.type_inst().vtable` + /// * `self.type_inst().type_inst().vtable` - our type's type's vtable (search up the type + /// inheritance tree) + /// + /// If the value is found in a vtable, then it is inserted as an attribute. If it is a + /// function (BuiltinFunctionInst, UserFunctionInst), then it is wrapped in a `MethodInst` + /// first. + fn get_attr_lazy(&mut self, self_ptr: ObjP, method_ty: ObjP, name: &str) -> Option { + let attr = self.get_attr(name); + if attr.is_some() { + return attr; + } + + let mut type_inst = self.type_inst(); + loop { + with_obj_downcast_mut(type_inst.clone(), |type_inst: &mut TypeInst| { + type_inst.vtable.get(name).cloned() + }) + .map(|vtable_entry| { + let ptr = if obj_is_inst::(&vtable_entry) + || obj_is_inst::(&vtable_entry) + { + MethodInst::create(method_ty.clone(), self_ptr.clone(), vtable_entry) + } else { + vtable_entry + }; + self.set_attr(name, ptr.clone()); + ptr + })?; + let type_inst_copy = type_inst.borrow().type_inst(); + if type_inst.borrow().equals(&*type_inst_copy.borrow()) { + return None; + } + type_inst = type_inst_copy; + } + } + fn type_inst(&self) -> ObjP { self.get_attr("__type__").unwrap() } @@ -210,7 +274,7 @@ pub trait Obj: Debug + Display + Any + Trace { None } - fn call(&self, vm: &mut Vm, argc: Argc) { + fn call(&self, _vm: &mut Vm, _argc: Argc) { // TODO Obj::call - need to handle "this object cannot be called" errors // BLOCKED-ON: exceptions todo!("Raise some kind of not implemented/not callable error for non-callable objects") @@ -237,6 +301,13 @@ struct BaseObjInst { is_instantiated: bool, } +impl BaseObjInst { + fn to_string(vm: &mut Vm, args: Vec) -> ObjP { + let str_value = format!("{}", &args[0].borrow()); + vm.create_str(str_value) + } +} + impl Finalize for BaseObjInst { fn finalize(&self) {} } @@ -259,31 +330,6 @@ impl Display for BaseObjInst { impl Obj for BaseObjInst { fn instantiate(&mut self, ty: ObjP) { self.set_attr("__type__", ty); - - // TODO BaseObjInst::instantiate - instantiate VTable - // Okay, we are running into a little snag here: - // * TypeInst::vtable holds a collection of named objects that will get copied into the - // attributes during instatiation. - // * If an object that gets copied is a function (UserFunctionInst, BuiltinFunctionInst), - // it will be wrapped by a MethodInst - // * MethodInst requires a pointer to the function being wrapped, as well as a pointer to - // the "self" object. - // * This is the root of the problem - ***we need the pointer to the object that we are - // currently instantiating.*** - /* - let type_inst_ptr = Ptr::clone(&self.type_inst()); - with_obj_downcast(type_inst_ptr, |type_inst: &TypeInst| { - for (key, value_ptr) in type_inst.vtable.iter() { - // copy functions over as MethodInst - if obj_is_inst::(&value_ptr) - || obj_is_inst::(&value_ptr) - { - self.set_attr(key, MethodInst::create(value_ptr)); - } - } - }); - */ - self.is_instantiated = true; } @@ -325,6 +371,10 @@ impl Obj for BaseObjInst { macro_rules! impl_base_obj { ($base_name:ident) => { + fn instantiate(&mut self, ty: ObjP) { + self.$base_name.instantiate(ty); + } + fn is_instantiated(&self) -> bool { self.$base_name.is_instantiated() } @@ -350,6 +400,16 @@ macro_rules! impl_base_obj { }; } +macro_rules! impl_create { + ($($arg:ident : $ty:ty),* $(,)?) => { + pub fn create(ty: ObjP $(, $arg : $ty )*) -> ObjP { + let ptr = make_ptr(Self::new($($arg),*)); + ptr.borrow_mut().instantiate(ty); + ptr + } + } +} + //////////////////////////////////////////////////////////////////////////////// // ObjInst //////////////////////////////////////////////////////////////////////////////// @@ -366,11 +426,7 @@ impl ObjInst { } } - pub fn create(ty: ObjP) -> Ptr { - let mut new = Self::new(); - new.instantiate(ty); - make_ptr(new) - } + impl_create!(); } impl Finalize for ObjInst { @@ -384,10 +440,6 @@ impl Display for ObjInst { } impl Obj for ObjInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { self.base.equals(&other.base) @@ -424,11 +476,7 @@ impl TypeInst { } } - pub fn create(ty: ObjP, name: impl ToString) -> Ptr { - let mut new = Self::new(name); - new.instantiate(ty); - make_ptr(new) - } + impl_create!(name: impl ToString); pub fn name(&self) -> &Rc { &self.name @@ -458,10 +506,6 @@ impl Display for TypeInst { } impl Obj for TypeInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { // TODO TypeInst::equals : something more robust than this @@ -498,11 +542,7 @@ impl StrInst { } } - pub fn create(ty: ObjP, str_value: impl ToString) -> Ptr { - let mut new = Self::new(str_value); - new.instantiate(ty); - make_ptr(new) - } + impl_create!(str_value: impl ToString); pub fn str_value(&self) -> &Rc { &self.str_value @@ -520,10 +560,6 @@ impl Display for StrInst { } impl Obj for StrInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn is_truthy(&self) -> bool { !self.str_value.is_empty() } @@ -557,11 +593,7 @@ impl IntInst { } } - pub fn create(ty: ObjP, int_value: i64) -> Ptr { - let mut new = Self::new(int_value); - new.instantiate(ty); - make_ptr(new) - } + impl_create!(int_value: i64); pub fn int_value(&self) -> i64 { self.int_value @@ -579,10 +611,6 @@ impl Display for IntInst { } impl Obj for IntInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn is_truthy(&self) -> bool { self.int_value != 0 } @@ -618,11 +646,7 @@ impl FloatInst { } } - pub fn create(ty: ObjP, float_value: f64) -> Ptr { - let mut new = Self::new(float_value); - new.instantiate(ty); - make_ptr(new) - } + impl_create!(float_value: f64); pub fn float_value(&self) -> f64 { self.float_value @@ -640,10 +664,6 @@ impl Display for FloatInst { } impl Obj for FloatInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn is_truthy(&self) -> bool { self.float_value != 0.0 } @@ -679,12 +699,7 @@ impl BoolInst { } } - pub fn create(ty: ObjP, bool_value: bool) -> Ptr { - // TODO BoolInst::create : interning - let mut new = Self::new(bool_value); - new.instantiate(ty); - make_ptr(new) - } + impl_create!(bool_value: bool); pub fn bool_value(&self) -> bool { self.bool_value @@ -702,10 +717,6 @@ impl Display for BoolInst { } impl Obj for BoolInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn is_truthy(&self) -> bool { self.bool_value } @@ -735,12 +746,7 @@ impl NilInst { Default::default() } - pub fn create(ty: ObjP) -> Ptr { - // TODO NilInst::create : interning - let mut new = Self::new(); - new.instantiate(ty); - make_ptr(new) - } + impl_create!(); } impl Finalize for NilInst { @@ -754,10 +760,6 @@ impl Display for NilInst { } impl Obj for NilInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn is_truthy(&self) -> bool { false } @@ -794,16 +796,11 @@ impl BuiltinFunctionInst { } } - pub fn create( - ty: ObjP, + impl_create!( name: impl ToString, function: BuiltinFunctionPtr, arity: Argc, - ) -> Ptr { - let mut new = Self::new(name, function, arity); - new.instantiate(ty); - make_ptr(new) - } + ); pub fn name(&self) -> &String { &self.name @@ -827,10 +824,6 @@ impl Display for BuiltinFunctionInst { } impl Obj for BuiltinFunctionInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn arity(&self) -> Option { Some(self.arity) } @@ -887,11 +880,7 @@ impl UserFunctionInst { } } - pub fn create(ty: ObjP, chunk: Chunk, arity: Argc) -> Ptr { - let mut new = Self::new(chunk, arity); - new.instantiate(ty); - make_ptr(new) - } + impl_create!(chunk: Chunk, arity: Argc); pub fn name(&self) -> &String { &self.name @@ -927,10 +916,6 @@ impl Display for UserFunctionInst { } impl Obj for UserFunctionInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn arity(&self) -> Option { Some(self.arity) } @@ -981,10 +966,10 @@ impl MethodInst { } } - pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> Ptr { - let mut new = Self::new(self_binding, function); - new.instantiate(ty); - make_ptr(new) + pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> ObjP { + let ptr = make_ptr(Self::new(self_binding, function)); + ptr.borrow_mut().instantiate(ty.clone()); + ptr } pub fn self_binding(&self) -> &ObjP { @@ -1003,10 +988,6 @@ impl Display for MethodInst { } impl Obj for MethodInst { - fn instantiate(&mut self, ty: ObjP) { - self.base.instantiate(ty); - } - fn arity(&self) -> Option { self.function.borrow().arity() } @@ -1083,10 +1064,47 @@ fn test_obj_equals() { assert!(obj1.borrow().equals(&*obj2.borrow())); // these objects aren't equal anymore - obj1.borrow_mut().set_attr("my_attr", ObjP::clone(&str2)); + obj1.borrow_mut().set_attr("my_attr", str2.clone()); assert!(!obj1.borrow().equals(&*obj2.borrow())); // but now they are! - obj2.borrow_mut().set_attr("my_attr", ObjP::clone(&str2)); + obj2.borrow_mut().set_attr("my_attr", str2.clone()); assert!(obj2.borrow().equals(&*obj1.borrow())); } + +fn test_obj_vtable() { + let mut builtins = HashMap::new(); + init_types(&mut builtins); + let str1 = builtins.create_str("asdfasdfasdf"); + + let to_string_ptr = str1.borrow_mut().get_attr_lazy( + str1.clone(), + builtins.get("Method").unwrap().clone(), + "to_string", + ); + assert!(to_string_ptr.is_some()); + + let to_string_ptr = to_string_ptr.unwrap(); + assert!(obj_is_inst::(&to_string_ptr)); + with_obj_downcast(to_string_ptr.clone(), |method: &MethodInst| { + assert!(method.self_binding.borrow().equals(&*str1.borrow())); + }); + + // now get the method's to_string ptr + let method_to_string_ptr = to_string_ptr.borrow_mut().get_attr_lazy( + to_string_ptr.clone(), + builtins.get("Method").unwrap().clone(), + "to_string", + ); + assert!(method_to_string_ptr.is_some()); + + // this is like doing "asdfasdfasdf".to_string().to_string() + let method_to_string_ptr = method_to_string_ptr.unwrap(); + assert!(obj_is_inst::(&method_to_string_ptr)); + with_obj_downcast(method_to_string_ptr.clone(), |method: &MethodInst| { + assert!(method + .self_binding + .borrow() + .equals(&*to_string_ptr.borrow())); + }); +} diff --git a/src/vm.rs b/src/vm.rs index 49d8a6e..e0b9e20 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -197,6 +197,9 @@ impl Vm { } pub fn run(&mut self) { + // Cached pointers that we just always want to have on hand + let method_type = self.builtins().get("Method").unwrap().clone(); + loop { match self.next() { Op::Pop => { @@ -232,7 +235,10 @@ impl Vm { let name = with_obj_downcast(name_obj, |name: &StrInst| Rc::clone(&name.str_value())); let owner = self.pop(); - let value = owner.borrow().get_attr(&name); + let value = + owner + .borrow_mut() + .get_attr_lazy(owner.clone(), method_type.clone(), &name); if let Some(value) = value { self.push(value); } else { @@ -339,7 +345,7 @@ impl Vm { let stack_base = self.frames[frame_index].stack_base; let value = Ptr::clone(&self.stack[stack_base + (slot as usize)]); fun.push_capture(value); - self.push(upcast_obj(make_ptr(fun))); + self.push(make_ptr(fun)); } Op::Halt => { break;