// TODO obj.rs - remove the warning suppression #![allow(unused_variables, dead_code)] use std::any::Any; use std::collections::HashMap; use std::fmt::{self, Debug, Display}; use std::ptr; use std::sync::{Arc, LazyLock, Mutex, RwLock}; use common_macros::hash_map; use crate::vm::{Argc, Chunk, Frame, Vm}; pub type Ptr = Arc>; pub type ObjP = Ptr; pub type Attrs = HashMap; /// Downcast an object pointer to a concrete type, and do something with that object. pub fn with_obj_downcast(ptr: ObjP, closure: impl FnOnce(&T) -> Out) -> Out where T: Obj + 'static, { let borrowed = ptr.try_read().expect("could not lock object for reading"); if let Some(obj) = borrowed.as_any().downcast_ref::() { closure(obj) } else { panic!( "could not downcast '{:?}' to {}", borrowed, std::any::type_name::() ) } } pub fn obj_is_inst(ptr: &ObjP) -> bool where T: Obj + 'static, { let borrowed = ptr.try_read().expect("could not lock object for reading"); borrowed.as_any().downcast_ref::().is_some() } /// Builtin types macro macro_rules! builtin_types { ( $( $type_name:ident => { $( $vtable_name:ident => $vtable_value:expr ),* $(,)? } ),+ $(,)? ) => { pub static TYPES: LazyLock>>> = LazyLock::new(|| { RwLock::new(hash_map! { $( stringify!($type_name).to_string() => make_ptr(TypeInst::new(stringify!($type_name))) ),+ }) }); static TYPE_SYSTEM_INIT: LazyLock> = LazyLock::new(|| Mutex::new(false)); /// Initialize types. /// /// This should only be called once. fn init_base_types() { // instantiate $( if stringify!($type_name) != "Type" { let ty = Ptr::clone(&TYPES.try_read().unwrap()[stringify!($type_name)]); ty.try_write().unwrap().instantiate(); } )+ // __name__ $( { let name = StrInst::create(stringify!($type_name)); let ty = Ptr::clone(&TYPES.try_read().unwrap()[stringify!($type_name)]); ty.try_write() .unwrap() .set_attr("__name__", name); } )+ // vtable $( { let ptr = Ptr::clone(&TYPES.try_read().unwrap()[stringify!($type_name)]); let ty = ptr.try_write().unwrap(); $( ty.vtable.insert($vtable_name.into(), $vtable_value); )* } )+ } }; } pub(crate) fn init_types() { // Taking the lock here will lock the entire function from being run twice // simulataneously and prevent race conditions. // // Race conditions really can only happen during testing so this is just a precaution. let mut lock_guard = TYPE_SYSTEM_INIT.lock().unwrap(); if *lock_guard { if cfg!(test) { return; } else { panic!("do not initialize type system twice"); } } // Init type_type here { let type_type_ptr = Ptr::clone(&TYPES.try_read().unwrap()["Type"]); let mut type_type = type_type_ptr.try_write().unwrap(); type_type.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Type"]) as ObjP, ); type_type.base.is_instantiated = true; } // Init the rest of the types init_base_types(); *lock_guard = true; } fn placeholder(_: &mut Vm, _: Vec) -> ObjP { NilInst::create() } fn to_string(_: &mut Vm, args: Vec) -> ObjP { let str_value = format!("{}", args[0].try_read().unwrap()); StrInst::create(str_value) } builtin_types! { Type => {}, Obj => {}, Str => {}, Int => {}, Float => {}, Bool => {}, Nil => {}, BuiltinFunction => {}, UserFunction => {}, Method => {}, } /// Convenience function for creating pointers, in case the `Arc>` pointer type has to /// change. /// /// 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 { Arc::new(RwLock::new(obj)) } //////////////////////////////////////////////////////////////////////////////// // Obj //////////////////////////////////////////////////////////////////////////////// pub trait Obj: Debug + Display + Any + Send + Sync { fn instantiate(&mut self); fn is_instantiated(&self) -> bool; fn attrs(&self) -> &Attrs; fn attrs_mut(&mut self) -> &mut Attrs; fn set_attr(&mut self, name: &str, value: ObjP) { self.attrs_mut().insert(name.to_string(), value); } fn get_attr(&self, name: &str) -> Option { self.attrs().get(name).map(Arc::clone) } fn type_inst(&self) -> ObjP { self.get_attr("__type__").unwrap() } fn type_name(&self) -> Arc { with_obj_downcast(self.type_inst(), |type_inst: &TypeInst| { Arc::clone(&type_inst.name) }) } fn arity(&self) -> Option { None } 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") } fn is_truthy(&self) -> bool { true } fn equals(&self, other: &dyn Obj) -> bool; fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; } //////////////////////////////////////////////////////////////////////////////// // BaseObjInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Default, Clone)] struct BaseObjInst { attrs: HashMap, is_instantiated: bool, } impl Display for BaseObjInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "", (self as *const _ as usize)) } } impl Obj for BaseObjInst { fn instantiate(&mut self) { if self.get_attr("__type__").is_none() { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Obj"]) as ObjP, ); } // 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; } fn is_instantiated(&self) -> bool { self.is_instantiated } fn attrs(&self) -> &Attrs { &self.attrs } fn attrs_mut(&mut self) -> &mut Attrs { &mut self.attrs } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { // compare all attrs self.attrs.iter().all(|(k1, v1)| { other .attrs .get(k1) .map(|v2| v2.try_read().unwrap().equals(&*v1.try_read().unwrap())) .unwrap_or(false) }) && self.is_instantiated == other.is_instantiated } else { false } } fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } } macro_rules! impl_base_obj { ($base_name:ident) => { fn is_instantiated(&self) -> bool { self.$base_name.is_instantiated() } fn attrs(&self) -> &Attrs { self.$base_name.attrs() } fn attrs_mut(&mut self) -> &mut Attrs { self.$base_name.attrs_mut() } fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } }; () => { impl_base_obj! { base } }; } //////////////////////////////////////////////////////////////////////////////// // ObjInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] pub struct ObjInst { base: BaseObjInst, } impl ObjInst { pub fn new() -> Self { Self { base: Default::default(), } } pub fn create() -> Ptr { let mut new = Self::new(); new.instantiate(); make_ptr(new) } } impl Display for ObjInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "", (self as *const _ as usize)) } } impl Obj for ObjInst { fn instantiate(&mut self) { self.base.instantiate(); } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { self.base.equals(&other.base) } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // TypeInst //////////////////////////////////////////////////////////////////////////////// pub struct TypeInst { name: Arc, base: BaseObjInst, vtable: HashMap, } impl TypeInst { pub fn new(name: impl ToString) -> Self { Self { name: Arc::new(name.to_string()), base: Default::default(), vtable: Default::default(), } } pub fn create(name: impl ToString) -> Ptr { let mut new = Self::new(name); new.instantiate(); make_ptr(new) } pub fn name(&self) -> &Arc { &self.name } } impl Debug for TypeInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( fmt, "", self.name, (self as *const _ as usize) ) } } impl Display for TypeInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( fmt, "", self.name, (self as *const _ as usize) ) } } impl Obj for TypeInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Type"]) as ObjP, ); self.base.instantiate(); } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { // TODO TypeInst::equals : something more robust than this // Types should hold equality if they have the same name // the problem is that Type.get_attr("__type__") is going to return itself, so we have // to go through attributes to specially exclude to the __type__ attribute if it points // to ourself. // How do we detect that it's pointing to ourself? I suppose pointers are the way self.name == other.name } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // StrInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] pub struct StrInst { str_value: Arc, base: BaseObjInst, } impl Display for StrInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.str_value) } } impl StrInst { pub fn new(str_value: impl ToString) -> Self { Self { str_value: Arc::new(str_value.to_string()), base: Default::default(), } } pub fn create(str_value: impl ToString) -> Ptr { let mut new = Self::new(str_value); new.instantiate(); make_ptr(new) } pub fn str_value(&self) -> &Arc { &self.str_value } } impl Obj for StrInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Str"]) as ObjP, ); self.base.instantiate(); } fn is_truthy(&self) -> bool { !self.str_value.is_empty() } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { self.str_value == other.str_value } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // IntInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] pub struct IntInst { int_value: i64, base: BaseObjInst, } impl IntInst { pub fn new(int_value: i64) -> Self { Self { int_value, base: Default::default(), } } pub fn create(int_value: i64) -> Ptr { let mut new = Self::new(int_value); new.instantiate(); make_ptr(new) } pub fn int_value(&self) -> i64 { self.int_value } } impl Display for IntInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.int_value) } } impl Obj for IntInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Int"]) as ObjP, ); self.base.instantiate(); } fn is_truthy(&self) -> bool { self.int_value != 0 } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { self.int_value == other.int_value } else if let Some(other) = other.as_any().downcast_ref::() { self.int_value as f64 == other.float_value } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // FloatInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] pub struct FloatInst { float_value: f64, base: BaseObjInst, } impl FloatInst { pub fn new(float_value: f64) -> Self { Self { float_value, base: Default::default(), } } pub fn create(float_value: f64) -> Ptr { let mut new = Self::new(float_value); new.instantiate(); make_ptr(new) } pub fn float_value(&self) -> f64 { self.float_value } } impl Display for FloatInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.float_value) } } impl Obj for FloatInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Float"]) as ObjP, ); self.base.instantiate(); } fn is_truthy(&self) -> bool { self.float_value != 0.0 } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { self.float_value == other.float_value } else if let Some(other) = other.as_any().downcast_ref::() { self.float_value == other.int_value as f64 } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // BoolInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] pub struct BoolInst { bool_value: bool, base: BaseObjInst, } impl BoolInst { pub fn new(bool_value: bool) -> Self { Self { bool_value, base: Default::default(), } } pub fn create(bool_value: bool) -> Ptr { // TODO BoolInst::create : interning let mut new = Self::new(bool_value); new.instantiate(); make_ptr(new) } pub fn bool_value(&self) -> bool { self.bool_value } } impl Display for BoolInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.bool_value) } } impl Obj for BoolInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Bool"]) as ObjP, ); self.base.instantiate(); } fn is_truthy(&self) -> bool { self.bool_value } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { self.bool_value == other.bool_value } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // NilInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Default)] pub struct NilInst { base: BaseObjInst, } impl NilInst { pub fn new() -> Self { Default::default() } pub fn create() -> Ptr { // TODO NilInst::create : interning let mut new = Self::new(); new.instantiate(); make_ptr(new) } } impl Display for NilInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "nil") } } impl Obj for NilInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Nil"]) as ObjP, ); self.base.instantiate(); } fn is_truthy(&self) -> bool { false } fn equals(&self, other: &dyn Obj) -> bool { other.as_any().downcast_ref::().is_some() } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // BuiltinFunctionInst //////////////////////////////////////////////////////////////////////////////// pub type BuiltinFunctionPtr = fn(vm: &mut Vm, args: Vec) -> ObjP; #[derive(Debug)] pub struct BuiltinFunctionInst { base: BaseObjInst, name: String, function: BuiltinFunctionPtr, arity: Argc, } impl BuiltinFunctionInst { pub fn new(name: String, function: BuiltinFunctionPtr, arity: Argc) -> Self { Self { base: Default::default(), name, function, arity, } } pub fn create(name: String, function: BuiltinFunctionPtr, arity: Argc) -> Ptr { let mut new = Self::new(name, function, arity); new.instantiate(); make_ptr(new) } pub fn name(&self) -> &String { &self.name } } impl Display for BuiltinFunctionInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( fmt, "", self.name(), self.arity().unwrap(), self.function as *const BuiltinFunctionPtr as usize ) } } impl Obj for BuiltinFunctionInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["UserFunction"]) as ObjP, ); self.base.instantiate(); } fn arity(&self) -> Option { Some(self.arity) } fn call(&self, vm: &mut Vm, argc: Argc) { // args let mut args = Vec::with_capacity(argc as usize); for _ in 0..argc { args.push(vm.pop()); } args.reverse(); // callee (self) vm.pop(); let result = (self.function)(vm, args); vm.push(result); } fn equals(&self, other: &dyn Obj) -> bool { // TODO BuiltinFunctionInst::equals : need something more robust than checking addr_eq, // maybe check the self_binding pointer too? if let Some(other) = other.as_any().downcast_ref::() { ptr::addr_eq(self, other) } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // UserFunctionInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Clone)] pub struct UserFunctionInst { base: BaseObjInst, name: Arc, chunk: Arc, arity: Argc, captures: Vec, } impl UserFunctionInst { pub fn new(chunk: Chunk, arity: Argc) -> Self { Self { base: Default::default(), name: Arc::new("(anonymous)".to_string()), chunk: Arc::new(chunk), arity, captures: Default::default(), } } pub fn create(chunk: Chunk, arity: Argc) -> Ptr { let mut new = Self::new(chunk, arity); new.instantiate(); make_ptr(new) } pub fn name(&self) -> &String { &self.name } pub fn set_name(&mut self, name: Arc) { self.name = name; } pub fn chunk(&self) -> &Chunk { &self.chunk } pub fn push_capture(&mut self, value: ObjP) { self.captures.push(value); } } impl Display for UserFunctionInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( fmt, "", self.name(), self.arity().unwrap(), self as *const _ as usize ) } } impl Obj for UserFunctionInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["UserFunction"]) as ObjP, ); self.base.instantiate(); } fn arity(&self) -> Option { Some(self.arity) } fn call(&self, vm: &mut Vm, argc: Argc) { assert_eq!(argc, self.arity, "argc must match arity"); let new_frame = Frame { name: Arc::clone(&self.name), chunk: Arc::clone(&self.chunk), ip: 0, stack_base: vm.stack().len() - (argc as usize), }; vm.push_frame(new_frame); for capture in &self.captures { vm.push(Ptr::clone(&capture)); } } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { // TODO UserFunctionInst::equals : need something more robust than checking addr_eq. ptr::addr_eq(self, other) } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // MethodInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] pub struct MethodInst { base: BaseObjInst, self_binding: ObjP, function: ObjP, } impl MethodInst { pub fn new(self_binding: ObjP, function: ObjP) -> Self { Self { base: Default::default(), self_binding, function, } } pub fn create(self_binding: ObjP, function: ObjP) -> Ptr { let mut new = Self::new(self_binding, function); new.instantiate(); make_ptr(new) } pub fn self_binding(&self) -> &ObjP { &self.self_binding } } impl Display for MethodInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.function.try_read().unwrap()) } } impl Obj for MethodInst { fn instantiate(&mut self) { self.set_attr( "__type__", Ptr::clone(&TYPES.try_read().unwrap()["Method"]) as ObjP, ); self.base.instantiate(); } fn arity(&self) -> Option { self.function.try_read().unwrap().arity() } fn call(&self, vm: &mut Vm, argc: Argc) { self.function.try_read().unwrap().call(vm, argc) } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { ptr::addr_eq(&*self.self_binding, &*other.self_binding) && ptr::addr_eq(&*self.function, &*other.function) } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // Tests //////////////////////////////////////////////////////////////////////////////// #[test] fn test_new_objects() { init_types(); let type_value = TypeInst::create("Type"); assert_eq!(&*type_value.try_read().unwrap().type_name(), "Type"); let str_value = StrInst::create("asdfasdfasdfasdfasdf"); assert_eq!(&*str_value.try_read().unwrap().type_name(), "Str"); let int_value = IntInst::create(1234); assert_eq!(&*int_value.try_read().unwrap().type_name(), "Int"); let float_value = FloatInst::create(1234.5678); assert_eq!(&*float_value.try_read().unwrap().type_name(), "Float"); let nil_value = NilInst::create(); assert_eq!(&*nil_value.try_read().unwrap().type_name(), "Nil"); } #[test] fn test_obj_equals() { init_types(); let int1 = IntInst::create(1234); let int2 = IntInst::create(1234); assert!(int1.try_read().unwrap().equals(&*int2.try_read().unwrap())); assert!(int2.try_read().unwrap().equals(&*int1.try_read().unwrap())); let float1 = FloatInst::create(1234.0); assert!(int1 .try_read() .unwrap() .equals(&*float1.try_read().unwrap())); assert!(float1 .try_read() .unwrap() .equals(&*int2.try_read().unwrap())); // self-equality let str1 = StrInst::create("1234"); assert!(str1.try_read().unwrap().equals(&*str1.try_read().unwrap())); let str2 = StrInst::create("1234"); assert!(str1.try_read().unwrap().equals(&*str2.try_read().unwrap())); assert!(str2.try_read().unwrap().equals(&*str1.try_read().unwrap())); assert!(!str1 .try_read() .unwrap() .equals(&*float1.try_read().unwrap())); assert!(!str1.try_read().unwrap().equals(&*int1.try_read().unwrap())); let obj1 = ObjInst::create(); let obj2 = ObjInst::create(); assert!(obj1.try_read().unwrap().equals(&*obj2.try_read().unwrap())); // these objects aren't equal anymore obj1.try_write() .unwrap() .set_attr("my_attr", Ptr::clone(&str2) as ObjP); assert!(!obj1.try_read().unwrap().equals(&*obj2.try_read().unwrap())); // but now they are! obj2.try_write() .unwrap() .set_attr("my_attr", Ptr::clone(&str2) as ObjP); assert!(obj2.try_read().unwrap().equals(&*obj1.try_read().unwrap())); }