From 3e56b3f284e15dbc8eba9e6999d585f6218a42ff Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 1 Feb 2020 16:54:33 +0900 Subject: [PATCH] PyBaseObject --- vm/src/obj/objobject.rs | 429 ++++++++++++++++++++-------------------- vm/src/pyobject.rs | 4 +- vm/src/vm.rs | 2 +- 3 files changed, 215 insertions(+), 220 deletions(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index f77b24989..671dcd9e6 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,84 +1,238 @@ use super::objbool; use super::objdict::PyDictRef; use super::objlist::PyList; -use super::objproperty::PropertyBuilder; use super::objstr::PyStringRef; use super::objtype::{self, PyClassRef}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyhash; use crate::pyobject::{ - IdProtocol, ItemProtocol, PyArithmaticValue::*, PyAttributes, PyComparisonValue, PyContext, - PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol, + IdProtocol, ItemProtocol, PyArithmaticValue::*, PyAttributes, PyClassImpl, PyComparisonValue, + PyContext, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; -use crate::slots::PyTpFlags; use crate::vm::VirtualMachine; +/// The most base type +#[pyclass] #[derive(Debug)] -pub struct PyInstance; +pub struct PyBaseObject; -impl PyValue for PyInstance { +impl PyValue for PyBaseObject { fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.object() } } -pub fn new_instance(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { - // more or less __new__ operator - let cls = PyClassRef::try_from_object(vm, args.shift())?; - let dict = if cls.is(&vm.ctx.object()) { - None - } else { - Some(vm.ctx.new_dict()) - }; - Ok(PyObject::new(PyInstance, cls, dict)) -} +#[pyimpl(flags(BASETYPE))] +impl PyBaseObject { + #[pyslot] + fn tp_new(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { + // more or less __new__ operator + let cls = PyClassRef::try_from_object(vm, args.shift())?; + let dict = if cls.is(&vm.ctx.object()) { + None + } else { + Some(vm.ctx.new_dict()) + }; + Ok(PyObject::new(PyBaseObject, cls, dict)) + } -fn object_eq(zelf: PyObjectRef, other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { - if zelf.is(&other) { - Implemented(true) - } else { + #[pymethod(magic)] + fn eq(zelf: PyObjectRef, other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { + if zelf.is(&other) { + Implemented(true) + } else { + NotImplemented + } + } + + #[pymethod(magic)] + fn ne( + zelf: PyObjectRef, + other: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult { + let eq_method = match vm.get_method(zelf, "__eq__") { + Some(func) => func?, + None => return Ok(NotImplemented), // XXX: is this a possible case? + }; + let eq = vm.invoke(&eq_method, vec![other])?; + if eq.is(&vm.ctx.not_implemented()) { + return Ok(NotImplemented); + } + let bool_eq = objbool::boolval(vm, eq)?; + Ok(Implemented(!bool_eq)) + } + + #[pymethod(magic)] + fn lt(_zelf: PyObjectRef, _other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { NotImplemented } -} -fn object_ne( - zelf: PyObjectRef, - other: PyObjectRef, - vm: &VirtualMachine, -) -> PyResult { - let eq_method = match vm.get_method(zelf, "__eq__") { - Some(func) => func?, - None => return Ok(NotImplemented), // XXX: is this a possible case? - }; - let eq = vm.invoke(&eq_method, vec![other])?; - if eq.is(&vm.ctx.not_implemented()) { - return Ok(NotImplemented); + #[pymethod(magic)] + fn le(_zelf: PyObjectRef, _other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { + NotImplemented + } + + #[pymethod(magic)] + fn gt(_zelf: PyObjectRef, _other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { + NotImplemented + } + + #[pymethod(magic)] + fn ge(_zelf: PyObjectRef, _other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { + NotImplemented + } + + #[pymethod(magic)] + fn hash(zelf: PyObjectRef, _vm: &VirtualMachine) -> pyhash::PyHash { + zelf.get_id() as pyhash::PyHash + } + + #[pymethod(magic)] + pub(crate) fn setattr( + obj: PyObjectRef, + attr_name: PyStringRef, + value: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult<()> { + setattr(obj, attr_name, value, vm) + } + + #[pymethod(magic)] + fn delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) -> PyResult<()> { + let cls = obj.class(); + + if let Some(attr) = cls.get_attr(attr_name.as_str()) { + if let Some(descriptor) = attr.class().get_attr("__delete__") { + return vm.invoke(&descriptor, vec![attr, obj.clone()]).map(|_| ()); + } + } + + if let Some(ref dict) = obj.dict { + dict.borrow().del_item(attr_name.as_str(), vm)?; + Ok(()) + } else { + Err(vm.new_attribute_error(format!( + "'{}' object has no attribute '{}'", + obj.class().name, + attr_name.as_str() + ))) + } + } + + #[pymethod(magic)] + fn str(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + vm.call_method(&zelf, "__repr__", vec![]) + } + + #[pymethod(magic)] + fn repr(zelf: PyObjectRef, _vm: &VirtualMachine) -> String { + format!("<{} object at 0x{:x}>", zelf.class().name, zelf.get_id()) + } + + #[pyclassmethod(magic)] + fn subclasshook(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { + Ok(vm.ctx.not_implemented()) + } + + #[pymethod(magic)] + pub fn dir(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let attributes: PyAttributes = obj.class().get_attributes(); + + let dict = PyDictRef::from_attributes(attributes, vm)?; + + // Get instance attributes: + if let Some(object_dict) = &obj.dict { + vm.invoke( + &vm.get_attribute(dict.clone().into_object(), "update")?, + object_dict.borrow().clone().into_object(), + )?; + } + + let attributes: Vec<_> = dict.into_iter().map(|(k, _v)| k).collect(); + + Ok(PyList::from(attributes)) + } + + #[pymethod(magic)] + fn format( + obj: PyObjectRef, + format_spec: PyStringRef, + vm: &VirtualMachine, + ) -> PyResult { + if format_spec.as_str().is_empty() { + vm.to_str(&obj) + } else { + Err(vm.new_type_error( + "unsupported format string passed to object.__format__".to_string(), + )) + } + } + + #[pymethod(magic)] + fn init(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { + Ok(vm.ctx.none()) + } + + #[pyproperty(name = "__class__")] + fn _class(obj: PyObjectRef, _vm: &VirtualMachine) -> PyObjectRef { + obj.class().into_object() + } + + #[pyproperty(name = "__class__", setter)] + fn _set_class(instance: PyObjectRef, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let type_repr = vm.to_pystr(&instance.class())?; + Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) + } + + #[pyproperty(magic)] + fn dict(object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if let Some(ref dict) = object.dict { + Ok(dict.borrow().clone()) + } else { + Err(vm.new_attribute_error("no dictionary.".to_string())) + } + } + + #[pyproperty(magic, setter)] + fn set_dict(instance: PyObjectRef, value: PyDictRef, vm: &VirtualMachine) -> PyResult<()> { + if let Some(dict) = &instance.dict { + *dict.borrow_mut() = value; + Ok(()) + } else { + Err(vm.new_attribute_error(format!( + "'{}' object has no attribute '__dict__'", + instance.class().name + ))) + } + } + + #[pymethod(magic)] + fn getattribute(obj: PyObjectRef, name: PyStringRef, vm: &VirtualMachine) -> PyResult { + vm_trace!("object.__getattribute__({:?}, {:?})", obj, name); + vm.generic_getattribute(obj.clone(), name.clone())? + .ok_or_else(|| vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name))) + } + + #[pymethod(magic)] + fn reduce(obj: PyObjectRef, proto: OptionalArg, vm: &VirtualMachine) -> PyResult { + common_reduce(obj, proto.unwrap_or(0), vm) + } + + #[pymethod(magic)] + fn reduce_ex(obj: PyObjectRef, proto: usize, vm: &VirtualMachine) -> PyResult { + let cls = obj.class(); + if let Some(reduce) = cls.get_attr("__reduce__") { + let object_reduce = vm.ctx.types.object_type.get_attr("__reduce__").unwrap(); + if !reduce.is(&object_reduce) { + return vm.invoke(&reduce, vec![]); + } + } + common_reduce(obj, proto, vm) } - let bool_eq = objbool::boolval(vm, eq)?; - Ok(Implemented(!bool_eq)) } -fn object_lt(_zelf: PyObjectRef, _other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { - NotImplemented -} - -fn object_le(_zelf: PyObjectRef, _other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { - NotImplemented -} - -fn object_gt(_zelf: PyObjectRef, _other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { - NotImplemented -} - -fn object_ge(_zelf: PyObjectRef, _other: PyObjectRef, _vm: &VirtualMachine) -> PyComparisonValue { - NotImplemented -} - -fn object_hash(zelf: PyObjectRef, _vm: &VirtualMachine) -> pyhash::PyHash { - zelf.get_id() as pyhash::PyHash -} - -pub(crate) fn object_setattr( +pub(crate) fn setattr( obj: PyObjectRef, attr_name: PyStringRef, value: PyObjectRef, @@ -107,173 +261,14 @@ pub(crate) fn object_setattr( } } -fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) -> PyResult<()> { - let cls = obj.class(); - - if let Some(attr) = cls.get_attr(attr_name.as_str()) { - if let Some(descriptor) = attr.class().get_attr("__delete__") { - return vm.invoke(&descriptor, vec![attr, obj.clone()]).map(|_| ()); - } - } - - if let Some(ref dict) = obj.dict { - dict.borrow().del_item(attr_name.as_str(), vm)?; - Ok(()) - } else { - Err(vm.new_attribute_error(format!( - "'{}' object has no attribute '{}'", - obj.class().name, - attr_name.as_str() - ))) - } -} - -fn object_str(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { - vm.call_method(&zelf, "__repr__", vec![]) -} - -fn object_repr(zelf: PyObjectRef, _vm: &VirtualMachine) -> String { - format!("<{} object at 0x{:x}>", zelf.class().name, zelf.get_id()) -} - -fn object_subclasshook(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { - Ok(vm.ctx.not_implemented()) -} - -pub fn object_dir(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let attributes: PyAttributes = obj.class().get_attributes(); - - let dict = PyDictRef::from_attributes(attributes, vm)?; - - // Get instance attributes: - if let Some(object_dict) = &obj.dict { - vm.invoke( - &vm.get_attribute(dict.clone().into_object(), "update")?, - object_dict.borrow().clone().into_object(), - )?; - } - - let attributes: Vec<_> = dict.into_iter().map(|(k, _v)| k).collect(); - - Ok(PyList::from(attributes)) -} - -fn object_format( - obj: PyObjectRef, - format_spec: PyStringRef, - vm: &VirtualMachine, -) -> PyResult { - if format_spec.as_str().is_empty() { - vm.to_str(&obj) - } else { - Err(vm.new_type_error("unsupported format string passed to object.__format__".to_string())) - } -} - pub fn init(context: &PyContext) { - let object = &context.types.object_type; - let object_doc = "The most base type"; - - object.slots.borrow_mut().flags |= PyTpFlags::BASETYPE; - - extend_class!(context, object, { - (slot new) => new_instance, + PyBaseObject::extend_class(context, &context.types.object_type); + extend_class!(context, &context.types.object_type, { // yeah, it's `type_new`, but we're putting here so it's available on every object "__new__" => context.new_classmethod(objtype::type_new), - "__init__" => context.new_method(object_init), - "__class__" => - PropertyBuilder::new(context) - .add_getter(object_class) - .add_setter(object_class_setter) - .create(), - "__eq__" => context.new_method(object_eq), - "__ne__" => context.new_method(object_ne), - "__lt__" => context.new_method(object_lt), - "__le__" => context.new_method(object_le), - "__gt__" => context.new_method(object_gt), - "__ge__" => context.new_method(object_ge), - "__setattr__" => context.new_method(object_setattr), - "__delattr__" => context.new_method(object_delattr), - "__dict__" => - PropertyBuilder::new(context) - .add_getter(object_dict) - .add_setter(object_dict_setter) - .create(), - "__dir__" => context.new_method(object_dir), - "__hash__" => context.new_method(object_hash), - "__str__" => context.new_method(object_str), - "__repr__" => context.new_method(object_repr), - "__format__" => context.new_method(object_format), - "__getattribute__" => context.new_method(object_getattribute), - "__subclasshook__" => context.new_classmethod(object_subclasshook), - "__reduce__" => context.new_method(object_reduce), - "__reduce_ex__" => context.new_method(object_reduce_ex), - "__doc__" => context.new_str(object_doc.to_string()), }); } -fn object_init(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { - Ok(vm.ctx.none()) -} - -fn object_class(obj: PyObjectRef, _vm: &VirtualMachine) -> PyObjectRef { - obj.class().into_object() -} - -fn object_class_setter( - instance: PyObjectRef, - _value: PyObjectRef, - vm: &VirtualMachine, -) -> PyResult<()> { - let type_repr = vm.to_pystr(&instance.class())?; - Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) -} - -fn object_dict(object: PyObjectRef, vm: &VirtualMachine) -> PyResult { - if let Some(ref dict) = object.dict { - Ok(dict.borrow().clone()) - } else { - Err(vm.new_attribute_error("no dictionary.".to_string())) - } -} - -fn object_dict_setter( - instance: PyObjectRef, - value: PyDictRef, - vm: &VirtualMachine, -) -> PyResult<()> { - if let Some(dict) = &instance.dict { - *dict.borrow_mut() = value; - Ok(()) - } else { - Err(vm.new_attribute_error(format!( - "'{}' object has no attribute '__dict__'", - instance.class().name - ))) - } -} - -fn object_getattribute(obj: PyObjectRef, name: PyStringRef, vm: &VirtualMachine) -> PyResult { - vm_trace!("object.__getattribute__({:?}, {:?})", obj, name); - vm.generic_getattribute(obj.clone(), name.clone())? - .ok_or_else(|| vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name))) -} - -fn object_reduce(obj: PyObjectRef, proto: OptionalArg, vm: &VirtualMachine) -> PyResult { - common_reduce(obj, proto.unwrap_or(0), vm) -} - -fn object_reduce_ex(obj: PyObjectRef, proto: usize, vm: &VirtualMachine) -> PyResult { - let cls = obj.class(); - if let Some(reduce) = cls.get_attr("__reduce__") { - let object_reduce = vm.ctx.types.object_type.get_attr("__reduce__").unwrap(); - if !reduce.is(&object_reduce) { - return vm.invoke(&reduce, vec![]); - } - } - common_reduce(obj, proto, vm) -} - fn common_reduce(obj: PyObjectRef, proto: usize, vm: &VirtualMachine) -> PyResult { if proto >= 2 { let reducelib = vm.import("__reducelib", &[], 0)?; diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index de379df4b..db0df14a1 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -534,11 +534,11 @@ impl PyContext { ) } - pub fn new_instance(&self, class: PyClassRef, dict: Option) -> PyObjectRef { + pub fn new_base_object(&self, class: PyClassRef, dict: Option) -> PyObjectRef { PyObject { typ: class, dict: dict.map(RefCell::new), - payload: objobject::PyInstance, + payload: objobject::PyBaseObject, } .into_ref() } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 75f862f38..fa405e807 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -1298,7 +1298,7 @@ impl VirtualMachine { attr_value: impl Into, ) -> PyResult<()> { let val = attr_value.into(); - objobject::object_setattr(module.clone(), attr_name.try_into_ref(self)?, val, self) + objobject::setattr(module.clone(), attr_name.try_into_ref(self)?, val, self) } }