Implement vtables and method resolution
Types now can have vtable elements which are used by instances to bind themselves to methods. When Op::GetAttr is executed, it calls a new function, Obj::get_attr_lazy. This will search: * attributes on the object * vtable on the object's type * vtable on the object's type's type, * etc. This searches up the type tree for a named value. If it exists as an attribute, it will be returned immediately. If it exists in the type's vtable, then it will be inserted as an attribute. If the vtable value is a function, the object that it is being called on will be bound to that method as the `self` parameter. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
368
src/obj.rs
368
src/obj.rs
@@ -1,5 +1,5 @@
|
|||||||
// TODO obj.rs - remove the warning suppression
|
// TODO obj.rs - remove the warning suppression
|
||||||
#![allow(unused_variables, dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
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<T, Out>(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::<T>() {
|
||||||
|
closure(obj)
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"could not downcast '{:?}' to {}",
|
||||||
|
ptr,
|
||||||
|
std::any::type_name::<T>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn obj_is_inst<T>(ptr: &ObjP) -> bool
|
pub fn obj_is_inst<T>(ptr: &ObjP) -> bool
|
||||||
where
|
where
|
||||||
T: Obj + 'static,
|
T: Obj + 'static,
|
||||||
@@ -50,34 +67,60 @@ pub fn upcast_obj<T: Obj>(ptr: Ptr<T>) -> ObjP {
|
|||||||
pub fn init_types(builtins: &mut HashMap<String, ObjP>) {
|
pub fn init_types(builtins: &mut HashMap<String, ObjP>) {
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
macro_rules! types {
|
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)));
|
let $name = make_ptr(TypeInst::new(stringify!($name)));
|
||||||
)*
|
)*
|
||||||
|
|
||||||
$(
|
// We have to instantiate these objects all by hand. This is because the `instantiate`
|
||||||
$name.borrow_mut().instantiate(upcast_obj(Gc::clone(&$base_type)));
|
// 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! {
|
types! {
|
||||||
// base type
|
// base type
|
||||||
base_type: Type,
|
base_type: Type,
|
||||||
// type definitions
|
// type definitions
|
||||||
Type,
|
Type {
|
||||||
Obj,
|
to_string => builtins.create_builtin_function("to_string", BaseObjInst::to_string, 1),
|
||||||
Str,
|
},
|
||||||
Int,
|
Obj { },
|
||||||
Float,
|
Str { },
|
||||||
Bool,
|
Int { },
|
||||||
Nil,
|
Float { },
|
||||||
BuiltinFunction,
|
Bool { },
|
||||||
UserFunction,
|
Nil { },
|
||||||
Method,
|
BuiltinFunction { },
|
||||||
|
UserFunction { },
|
||||||
|
Method { },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,50 +128,31 @@ pub trait ObjFactory {
|
|||||||
fn builtins(&self) -> &HashMap<String, ObjP>;
|
fn builtins(&self) -> &HashMap<String, ObjP>;
|
||||||
|
|
||||||
fn create_obj(&self) -> ObjP {
|
fn create_obj(&self) -> ObjP {
|
||||||
upcast_obj(ObjInst::create(ObjP::clone(
|
ObjInst::create(self.builtins().get("Obj").unwrap().clone())
|
||||||
self.builtins().get("Obj").unwrap(),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_type(&self, name: impl ToString) -> ObjP {
|
fn create_type(&self, name: impl ToString) -> ObjP {
|
||||||
upcast_obj(TypeInst::create(
|
TypeInst::create(self.builtins().get("Type").unwrap().clone(), name)
|
||||||
ObjP::clone(self.builtins().get("Type").unwrap()),
|
|
||||||
name,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_str(&self, str_value: impl ToString) -> ObjP {
|
fn create_str(&self, str_value: impl ToString) -> ObjP {
|
||||||
upcast_obj(StrInst::create(
|
StrInst::create(self.builtins().get("Str").unwrap().clone(), str_value)
|
||||||
ObjP::clone(self.builtins().get("Str").unwrap()),
|
|
||||||
str_value,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_int(&self, int_value: i64) -> ObjP {
|
fn create_int(&self, int_value: i64) -> ObjP {
|
||||||
upcast_obj(IntInst::create(
|
IntInst::create(self.builtins().get("Int").unwrap().clone(), int_value)
|
||||||
ObjP::clone(self.builtins().get("Int").unwrap()),
|
|
||||||
int_value,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_float(&self, float_value: f64) -> ObjP {
|
fn create_float(&self, float_value: f64) -> ObjP {
|
||||||
upcast_obj(FloatInst::create(
|
FloatInst::create(self.builtins().get("Float").unwrap().clone(), float_value)
|
||||||
ObjP::clone(self.builtins().get("Float").unwrap()),
|
|
||||||
float_value,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_bool(&self, bool_value: bool) -> ObjP {
|
fn create_bool(&self, bool_value: bool) -> ObjP {
|
||||||
upcast_obj(BoolInst::create(
|
BoolInst::create(self.builtins().get("Bool").unwrap().clone(), bool_value)
|
||||||
ObjP::clone(self.builtins().get("Bool").unwrap()),
|
|
||||||
bool_value,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_nil(&self) -> ObjP {
|
fn create_nil(&self) -> ObjP {
|
||||||
upcast_obj(NilInst::create(ObjP::clone(
|
NilInst::create(self.builtins().get("Nil").unwrap().clone())
|
||||||
self.builtins().get("Nil").unwrap(),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_builtin_function(
|
fn create_builtin_function(
|
||||||
@@ -137,28 +161,28 @@ pub trait ObjFactory {
|
|||||||
function: BuiltinFunctionPtr,
|
function: BuiltinFunctionPtr,
|
||||||
arity: Argc,
|
arity: Argc,
|
||||||
) -> ObjP {
|
) -> ObjP {
|
||||||
upcast_obj(BuiltinFunctionInst::create(
|
BuiltinFunctionInst::create(
|
||||||
ObjP::clone(self.builtins().get("BuiltinFunction").unwrap()),
|
self.builtins().get("BuiltinFunction").unwrap().clone(),
|
||||||
name,
|
name,
|
||||||
function,
|
function,
|
||||||
arity,
|
arity,
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_user_function(&self, chunk: Chunk, arity: Argc) -> ObjP {
|
fn create_user_function(&self, chunk: Chunk, arity: Argc) -> ObjP {
|
||||||
upcast_obj(UserFunctionInst::create(
|
UserFunctionInst::create(
|
||||||
ObjP::clone(self.builtins().get("UserFunction").unwrap()),
|
self.builtins().get("UserFunction").unwrap().clone(),
|
||||||
chunk,
|
chunk,
|
||||||
arity,
|
arity,
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_method(&self, self_binding: ObjP, function: ObjP) -> ObjP {
|
fn create_method(&self, self_binding: ObjP, function: ObjP) -> ObjP {
|
||||||
upcast_obj(MethodInst::create(
|
MethodInst::create(
|
||||||
ObjP::clone(self.builtins().get("Method").unwrap()),
|
self.builtins().get("Method").unwrap().clone(),
|
||||||
self_binding,
|
self_binding,
|
||||||
function,
|
function,
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,8 +197,8 @@ impl ObjFactory for HashMap<String, ObjP> {
|
|||||||
///
|
///
|
||||||
/// I would implement this as a `From<T>` but it doesn't seem to work for a foreign type, and I'm
|
/// I would implement this as a `From<T>` but it doesn't seem to work for a foreign type, and I'm
|
||||||
/// not sure why.
|
/// not sure why.
|
||||||
pub fn make_ptr<T: Obj>(obj: T) -> Ptr<T> {
|
pub fn make_ptr<T: Obj>(obj: T) -> ObjP {
|
||||||
Ptr::new(GcCell::new(obj))
|
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<ObjP> {
|
fn get_attr(&self, name: &str) -> Option<ObjP> {
|
||||||
|
// check attrs, then check vtable
|
||||||
self.attrs().get(name).map(Ptr::clone)
|
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<ObjP> {
|
||||||
|
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::<BuiltinFunctionInst>(&vtable_entry)
|
||||||
|
|| obj_is_inst::<UserFunctionInst>(&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 {
|
fn type_inst(&self) -> ObjP {
|
||||||
self.get_attr("__type__").unwrap()
|
self.get_attr("__type__").unwrap()
|
||||||
}
|
}
|
||||||
@@ -210,7 +274,7 @@ pub trait Obj: Debug + Display + Any + Trace {
|
|||||||
None
|
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
|
// TODO Obj::call - need to handle "this object cannot be called" errors
|
||||||
// BLOCKED-ON: exceptions
|
// BLOCKED-ON: exceptions
|
||||||
todo!("Raise some kind of not implemented/not callable error for non-callable objects")
|
todo!("Raise some kind of not implemented/not callable error for non-callable objects")
|
||||||
@@ -237,6 +301,13 @@ struct BaseObjInst {
|
|||||||
is_instantiated: bool,
|
is_instantiated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BaseObjInst {
|
||||||
|
fn to_string(vm: &mut Vm, args: Vec<ObjP>) -> ObjP {
|
||||||
|
let str_value = format!("{}", &args[0].borrow());
|
||||||
|
vm.create_str(str_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Finalize for BaseObjInst {
|
impl Finalize for BaseObjInst {
|
||||||
fn finalize(&self) {}
|
fn finalize(&self) {}
|
||||||
}
|
}
|
||||||
@@ -259,31 +330,6 @@ impl Display for BaseObjInst {
|
|||||||
impl Obj for BaseObjInst {
|
impl Obj for BaseObjInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
fn instantiate(&mut self, ty: ObjP) {
|
||||||
self.set_attr("__type__", ty);
|
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::<BuiltinFunctionInst>(&value_ptr)
|
|
||||||
|| obj_is_inst::<UserFunctionInst>(&value_ptr)
|
|
||||||
{
|
|
||||||
self.set_attr(key, MethodInst::create(value_ptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
self.is_instantiated = true;
|
self.is_instantiated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +371,10 @@ impl Obj for BaseObjInst {
|
|||||||
|
|
||||||
macro_rules! impl_base_obj {
|
macro_rules! impl_base_obj {
|
||||||
($base_name:ident) => {
|
($base_name:ident) => {
|
||||||
|
fn instantiate(&mut self, ty: ObjP) {
|
||||||
|
self.$base_name.instantiate(ty);
|
||||||
|
}
|
||||||
|
|
||||||
fn is_instantiated(&self) -> bool {
|
fn is_instantiated(&self) -> bool {
|
||||||
self.$base_name.is_instantiated()
|
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
|
// ObjInst
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -366,11 +426,7 @@ impl ObjInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP) -> Ptr<Self> {
|
impl_create!();
|
||||||
let mut new = Self::new();
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Finalize for ObjInst {
|
impl Finalize for ObjInst {
|
||||||
@@ -384,10 +440,6 @@ impl Display for ObjInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for ObjInst {
|
impl Obj for ObjInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn equals(&self, other: &dyn Obj) -> bool {
|
fn equals(&self, other: &dyn Obj) -> bool {
|
||||||
if let Some(other) = other.as_any().downcast_ref::<ObjInst>() {
|
if let Some(other) = other.as_any().downcast_ref::<ObjInst>() {
|
||||||
self.base.equals(&other.base)
|
self.base.equals(&other.base)
|
||||||
@@ -424,11 +476,7 @@ impl TypeInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP, name: impl ToString) -> Ptr<Self> {
|
impl_create!(name: impl ToString);
|
||||||
let mut new = Self::new(name);
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> &Rc<String> {
|
pub fn name(&self) -> &Rc<String> {
|
||||||
&self.name
|
&self.name
|
||||||
@@ -458,10 +506,6 @@ impl Display for TypeInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for TypeInst {
|
impl Obj for TypeInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn equals(&self, other: &dyn Obj) -> bool {
|
fn equals(&self, other: &dyn Obj) -> bool {
|
||||||
if let Some(other) = other.as_any().downcast_ref::<TypeInst>() {
|
if let Some(other) = other.as_any().downcast_ref::<TypeInst>() {
|
||||||
// TODO TypeInst::equals : something more robust than this
|
// TODO TypeInst::equals : something more robust than this
|
||||||
@@ -498,11 +542,7 @@ impl StrInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP, str_value: impl ToString) -> Ptr<Self> {
|
impl_create!(str_value: impl ToString);
|
||||||
let mut new = Self::new(str_value);
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn str_value(&self) -> &Rc<String> {
|
pub fn str_value(&self) -> &Rc<String> {
|
||||||
&self.str_value
|
&self.str_value
|
||||||
@@ -520,10 +560,6 @@ impl Display for StrInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for StrInst {
|
impl Obj for StrInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_truthy(&self) -> bool {
|
fn is_truthy(&self) -> bool {
|
||||||
!self.str_value.is_empty()
|
!self.str_value.is_empty()
|
||||||
}
|
}
|
||||||
@@ -557,11 +593,7 @@ impl IntInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP, int_value: i64) -> Ptr<Self> {
|
impl_create!(int_value: i64);
|
||||||
let mut new = Self::new(int_value);
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_value(&self) -> i64 {
|
pub fn int_value(&self) -> i64 {
|
||||||
self.int_value
|
self.int_value
|
||||||
@@ -579,10 +611,6 @@ impl Display for IntInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for IntInst {
|
impl Obj for IntInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_truthy(&self) -> bool {
|
fn is_truthy(&self) -> bool {
|
||||||
self.int_value != 0
|
self.int_value != 0
|
||||||
}
|
}
|
||||||
@@ -618,11 +646,7 @@ impl FloatInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP, float_value: f64) -> Ptr<Self> {
|
impl_create!(float_value: f64);
|
||||||
let mut new = Self::new(float_value);
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_value(&self) -> f64 {
|
pub fn float_value(&self) -> f64 {
|
||||||
self.float_value
|
self.float_value
|
||||||
@@ -640,10 +664,6 @@ impl Display for FloatInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for FloatInst {
|
impl Obj for FloatInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_truthy(&self) -> bool {
|
fn is_truthy(&self) -> bool {
|
||||||
self.float_value != 0.0
|
self.float_value != 0.0
|
||||||
}
|
}
|
||||||
@@ -679,12 +699,7 @@ impl BoolInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP, bool_value: bool) -> Ptr<Self> {
|
impl_create!(bool_value: bool);
|
||||||
// TODO BoolInst::create : interning
|
|
||||||
let mut new = Self::new(bool_value);
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bool_value(&self) -> bool {
|
pub fn bool_value(&self) -> bool {
|
||||||
self.bool_value
|
self.bool_value
|
||||||
@@ -702,10 +717,6 @@ impl Display for BoolInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for BoolInst {
|
impl Obj for BoolInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_truthy(&self) -> bool {
|
fn is_truthy(&self) -> bool {
|
||||||
self.bool_value
|
self.bool_value
|
||||||
}
|
}
|
||||||
@@ -735,12 +746,7 @@ impl NilInst {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP) -> Ptr<Self> {
|
impl_create!();
|
||||||
// TODO NilInst::create : interning
|
|
||||||
let mut new = Self::new();
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Finalize for NilInst {
|
impl Finalize for NilInst {
|
||||||
@@ -754,10 +760,6 @@ impl Display for NilInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for NilInst {
|
impl Obj for NilInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_truthy(&self) -> bool {
|
fn is_truthy(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -794,16 +796,11 @@ impl BuiltinFunctionInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(
|
impl_create!(
|
||||||
ty: ObjP,
|
|
||||||
name: impl ToString,
|
name: impl ToString,
|
||||||
function: BuiltinFunctionPtr,
|
function: BuiltinFunctionPtr,
|
||||||
arity: Argc,
|
arity: Argc,
|
||||||
) -> Ptr<Self> {
|
);
|
||||||
let mut new = Self::new(name, function, arity);
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> &String {
|
pub fn name(&self) -> &String {
|
||||||
&self.name
|
&self.name
|
||||||
@@ -827,10 +824,6 @@ impl Display for BuiltinFunctionInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for BuiltinFunctionInst {
|
impl Obj for BuiltinFunctionInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn arity(&self) -> Option<Argc> {
|
fn arity(&self) -> Option<Argc> {
|
||||||
Some(self.arity)
|
Some(self.arity)
|
||||||
}
|
}
|
||||||
@@ -887,11 +880,7 @@ impl UserFunctionInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP, chunk: Chunk, arity: Argc) -> Ptr<Self> {
|
impl_create!(chunk: Chunk, arity: Argc);
|
||||||
let mut new = Self::new(chunk, arity);
|
|
||||||
new.instantiate(ty);
|
|
||||||
make_ptr(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> &String {
|
pub fn name(&self) -> &String {
|
||||||
&self.name
|
&self.name
|
||||||
@@ -927,10 +916,6 @@ impl Display for UserFunctionInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for UserFunctionInst {
|
impl Obj for UserFunctionInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn arity(&self) -> Option<Argc> {
|
fn arity(&self) -> Option<Argc> {
|
||||||
Some(self.arity)
|
Some(self.arity)
|
||||||
}
|
}
|
||||||
@@ -981,10 +966,10 @@ impl MethodInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> Ptr<Self> {
|
pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> ObjP {
|
||||||
let mut new = Self::new(self_binding, function);
|
let ptr = make_ptr(Self::new(self_binding, function));
|
||||||
new.instantiate(ty);
|
ptr.borrow_mut().instantiate(ty.clone());
|
||||||
make_ptr(new)
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn self_binding(&self) -> &ObjP {
|
pub fn self_binding(&self) -> &ObjP {
|
||||||
@@ -1003,10 +988,6 @@ impl Display for MethodInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Obj for MethodInst {
|
impl Obj for MethodInst {
|
||||||
fn instantiate(&mut self, ty: ObjP) {
|
|
||||||
self.base.instantiate(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn arity(&self) -> Option<Argc> {
|
fn arity(&self) -> Option<Argc> {
|
||||||
self.function.borrow().arity()
|
self.function.borrow().arity()
|
||||||
}
|
}
|
||||||
@@ -1083,10 +1064,47 @@ fn test_obj_equals() {
|
|||||||
assert!(obj1.borrow().equals(&*obj2.borrow()));
|
assert!(obj1.borrow().equals(&*obj2.borrow()));
|
||||||
|
|
||||||
// these objects aren't equal anymore
|
// 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()));
|
assert!(!obj1.borrow().equals(&*obj2.borrow()));
|
||||||
|
|
||||||
// but now they are!
|
// 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()));
|
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::<MethodInst>(&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::<MethodInst>(&method_to_string_ptr));
|
||||||
|
with_obj_downcast(method_to_string_ptr.clone(), |method: &MethodInst| {
|
||||||
|
assert!(method
|
||||||
|
.self_binding
|
||||||
|
.borrow()
|
||||||
|
.equals(&*to_string_ptr.borrow()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
10
src/vm.rs
10
src/vm.rs
@@ -197,6 +197,9 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
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 {
|
loop {
|
||||||
match self.next() {
|
match self.next() {
|
||||||
Op::Pop => {
|
Op::Pop => {
|
||||||
@@ -232,7 +235,10 @@ impl Vm {
|
|||||||
let name =
|
let name =
|
||||||
with_obj_downcast(name_obj, |name: &StrInst| Rc::clone(&name.str_value()));
|
with_obj_downcast(name_obj, |name: &StrInst| Rc::clone(&name.str_value()));
|
||||||
let owner = self.pop();
|
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 {
|
if let Some(value) = value {
|
||||||
self.push(value);
|
self.push(value);
|
||||||
} else {
|
} else {
|
||||||
@@ -339,7 +345,7 @@ impl Vm {
|
|||||||
let stack_base = self.frames[frame_index].stack_base;
|
let stack_base = self.frames[frame_index].stack_base;
|
||||||
let value = Ptr::clone(&self.stack[stack_base + (slot as usize)]);
|
let value = Ptr::clone(&self.stack[stack_base + (slot as usize)]);
|
||||||
fun.push_capture(value);
|
fun.push_capture(value);
|
||||||
self.push(upcast_obj(make_ptr(fun)));
|
self.push(make_ptr(fun));
|
||||||
}
|
}
|
||||||
Op::Halt => {
|
Op::Halt => {
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user