From cc4ae0831c657bdd6986f555c98d9f18e79129ca Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Wed, 12 Aug 2020 00:54:23 -0500 Subject: [PATCH] Make HAVE_DICT a type flag --- vm/src/exceptions.rs | 6 ++-- vm/src/obj/objbuiltinfunc.rs | 3 +- vm/src/obj/objclassmethod.rs | 4 +-- vm/src/obj/objfunction.rs | 4 +-- vm/src/obj/objmodule.rs | 4 +-- vm/src/obj/objnamespace.rs | 3 +- vm/src/obj/objobject.rs | 34 ++++++------------- vm/src/obj/objstaticmethod.rs | 2 +- vm/src/obj/objtype.rs | 62 ++++++++++++++++++++++++++--------- vm/src/pyobject.rs | 30 ++++++++++++----- vm/src/slots.rs | 1 + vm/src/stdlib/ast.rs | 2 ++ vm/src/types.rs | 2 ++ 13 files changed, 92 insertions(+), 65 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index dc604616e..f1dab5195 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -45,14 +45,12 @@ pub trait IntoPyException { } impl PyValue for PyBaseException { - const HAVE_DICT: bool = true; - fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.exceptions.base_exception_type.clone() } } -#[pyimpl(flags(BASETYPE))] +#[pyimpl(flags(BASETYPE, HAS_DICT))] impl PyBaseException { pub(crate) fn new(args: Vec, vm: &VirtualMachine) -> PyBaseException { PyBaseException { @@ -467,7 +465,7 @@ impl ExceptionZoo { pub fn new(type_type: &PyClassRef, object_type: &PyClassRef) -> Self { let create_exception_type = |name: &str, base: &PyClassRef| { let typ = create_type(name, type_type, base); - typ.slots.write().flags |= PyTpFlags::BASETYPE; + typ.slots.write().flags |= PyTpFlags::BASETYPE | PyTpFlags::HAS_DICT; typ }; // Sorted By Hierarchy then alphabetized. diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index 28638ea8c..ed5898149 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -20,7 +20,6 @@ impl PyValue for PyBuiltinFunction { fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.builtin_function_or_method_type() } - const HAVE_DICT: bool = true; } impl fmt::Debug for PyBuiltinFunction { @@ -59,7 +58,7 @@ impl SlotCall for PyBuiltinFunction { } } -#[pyimpl(with(SlotCall))] +#[pyimpl(with(SlotCall), flags(HAS_DICT))] impl PyBuiltinFunction { #[pyproperty(magic)] fn module(&self) -> Option { diff --git a/vm/src/obj/objclassmethod.rs b/vm/src/obj/objclassmethod.rs index 5b833aec0..294d246fd 100644 --- a/vm/src/obj/objclassmethod.rs +++ b/vm/src/obj/objclassmethod.rs @@ -40,8 +40,6 @@ impl From for PyClassMethod { } impl PyValue for PyClassMethod { - const HAVE_DICT: bool = true; - fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.classmethod_type() } @@ -60,7 +58,7 @@ impl SlotDescriptor for PyClassMethod { } } -#[pyimpl(with(SlotDescriptor), flags(BASETYPE))] +#[pyimpl(with(SlotDescriptor), flags(BASETYPE, HAS_DICT))] impl PyClassMethod { #[pyslot] fn tp_new( diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 2658cf3ca..d8b1725c6 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -259,7 +259,7 @@ impl PyValue for PyFunction { } } -#[pyimpl(with(SlotDescriptor))] +#[pyimpl(with(SlotDescriptor), flags(HAS_DICT))] impl PyFunction { #[pyslot] #[pymethod(magic)] @@ -309,7 +309,7 @@ impl PyBoundMethod { } } -#[pyimpl(with(SlotCall))] +#[pyimpl(with(SlotCall), flags(HAS_DICT))] impl PyBoundMethod { #[pymethod(magic)] fn repr(&self, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 33faedbb7..6c8ce6623 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -13,8 +13,6 @@ pub struct PyModule {} pub type PyModuleRef = PyRef; impl PyValue for PyModule { - const HAVE_DICT: bool = true; - fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.module_type() } @@ -43,7 +41,7 @@ pub fn init_module_dict( .expect("Failed to set __spec__ on module"); } -#[pyimpl(flags(BASETYPE))] +#[pyimpl(flags(BASETYPE, HAS_DICT))] impl PyModuleRef { #[pyslot] fn tp_new(cls: PyClassRef, _args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/obj/objnamespace.rs b/vm/src/obj/objnamespace.rs index aa6107840..ddf1f9feb 100644 --- a/vm/src/obj/objnamespace.rs +++ b/vm/src/obj/objnamespace.rs @@ -11,13 +11,12 @@ use crate::vm::VirtualMachine; pub struct PyNamespace; impl PyValue for PyNamespace { - const HAVE_DICT: bool = true; fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.namespace_type() } } -#[pyimpl(flags(BASETYPE))] +#[pyimpl(flags(BASETYPE, HAS_DICT))] impl PyNamespace { #[pyslot] fn tp_new(cls: PyClassRef, kwargs: KwArgs, vm: &VirtualMachine) -> PyResult> { diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index c93871f1d..82ac139c3 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -151,10 +151,7 @@ impl PyBaseObject { // Get instance attributes: if let Some(object_dict) = obj.dict() { - vm.invoke( - &vm.get_attribute(dict.clone().into_object(), "update")?, - object_dict.into_object(), - )?; + vm.call_method(dict.as_object(), "update", vec![object_dict.into_object()])?; } let attributes: Vec<_> = dict.into_iter().map(|(k, _v)| k).collect(); @@ -211,26 +208,6 @@ impl PyBaseObject { } } - #[pyproperty(magic)] - fn dict(object: PyObjectRef, vm: &VirtualMachine) -> PyResult { - object.dict().ok_or_else(|| { - vm.new_attribute_error(format!( - "'{}' object has no attribute '__dict__'", - object.lease_class().name - )) - }) - } - - #[pyproperty(magic, setter)] - fn set_dict(instance: PyObjectRef, value: PyDictRef, vm: &VirtualMachine) -> PyResult<()> { - instance.set_dict(value).map_err(|_| { - vm.new_attribute_error(format!( - "'{}' object has no attribute '__dict__'", - instance.lease_class().name - )) - }) - } - #[pymethod(magic)] fn getattribute(obj: PyObjectRef, name: PyStringRef, vm: &VirtualMachine) -> PyResult { vm_trace!("object.__getattribute__({:?}, {:?})", obj, name); @@ -254,6 +231,15 @@ impl PyBaseObject { } } +pub fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { + obj.dict() + .ok_or_else(|| vm.new_attribute_error("This object has no __dict__".to_owned())) +} +pub fn object_set_dict(obj: PyObjectRef, dict: PyDictRef, vm: &VirtualMachine) -> PyResult<()> { + obj.set_dict(dict) + .map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned())) +} + #[cfg_attr(feature = "flame-it", flame)] pub(crate) fn setattr( obj: PyObjectRef, diff --git a/vm/src/obj/objstaticmethod.rs b/vm/src/obj/objstaticmethod.rs index e9c3715e9..6fed00a8c 100644 --- a/vm/src/obj/objstaticmethod.rs +++ b/vm/src/obj/objstaticmethod.rs @@ -35,7 +35,7 @@ impl From for PyStaticMethod { } } -#[pyimpl(with(SlotDescriptor), flags(BASETYPE))] +#[pyimpl(with(SlotDescriptor), flags(BASETYPE, HAS_DICT))] impl PyStaticMethod { #[pyslot] fn tp_new( diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 5bcabb47b..9c8b84d4b 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -6,13 +6,14 @@ use super::objclassmethod::PyClassMethod; use super::objdict::PyDictRef; use super::objlist::PyList; use super::objmappingproxy::PyMappingProxy; +use super::objobject; use super::objstaticmethod::PyStaticMethod; use super::objstr::PyStringRef; use super::objtuple::PyTuple; use super::objweakref::PyWeak; use crate::function::{KwArgs, OptionalArg, PyFuncArgs}; use crate::pyobject::{ - BorrowValue, IdProtocol, PyAttributes, PyClassImpl, PyContext, PyIterable, PyLease, PyObject, + BorrowValue, IdProtocol, PyAttributes, PyClassImpl, PyContext, PyIterable, PyLease, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::slots::{PyClassSlots, PyTpFlags}; @@ -311,16 +312,41 @@ impl PyClassRef { } } + if !attributes.contains_key("__dict__") { + attributes.insert( + "__dict__".to_owned(), + vm.ctx.new_getset( + "__dict__", + objobject::object_get_dict, + objobject::object_set_dict, + ), + ); + } + + let slots = PyClassSlots { + // TODO: how do we know if it should have a dict? + flags: base.slots.read().flags | PyTpFlags::HAS_DICT, + ..Default::default() + }; + + // TODO: is this correct behavior? + let cls_dict = if metatype.is(&vm.ctx.types.type_type) { + None + } else { + Some(vm.ctx.new_dict()) + }; + let typ = new( metatype, name.borrow_value(), - base.clone(), + base, bases, attributes, + slots, + cls_dict, ) .map_err(|e| vm.new_type_error(e))?; - typ.slots.write().flags = base.slots.read().flags; vm.ctx.add_tp_new_wrapper(&typ); for (name, obj) in typ.attributes.read().clone().iter() { @@ -582,9 +608,11 @@ fn linearise_mro(mut bases: Vec>) -> Result, Str pub fn new( typ: PyClassRef, name: &str, - _base: PyClassRef, + base: PyClassRef, bases: Vec, - dict: HashMap, + attrs: HashMap, + mut slots: PyClassSlots, + dict: Option, ) -> Result { // Check for duplicates in bases. let mut unique_bases = HashSet::new(); @@ -599,21 +627,21 @@ pub fn new( .map(|x| x.iter_mro().cloned().collect()) .collect(); let mro = linearise_mro(mros)?; - let new_type = PyObject { - payload: PyClass { + if base.slots.read().flags.has_feature(PyTpFlags::HAS_DICT) { + slots.flags |= PyTpFlags::HAS_DICT + } + let new_type = PyRef::new_ref( + PyClass { name: String::from(name), bases, mro, subclasses: PyRwLock::default(), - attributes: PyRwLock::new(dict), - slots: PyRwLock::default(), + attributes: PyRwLock::new(attrs), + slots: PyRwLock::new(slots), }, - dict: None, - typ: PyRwLock::new(typ.into_typed_pyobj()), - } - .into_ref(); - - let new_type: PyClassRef = new_type.downcast().unwrap(); + typ, + dict, + ); for base in &new_type.bases { base.subclasses @@ -718,6 +746,8 @@ mod tests { object.clone(), vec![object.clone()], HashMap::new(), + Default::default(), + None, ) .unwrap(); let b = new( @@ -726,6 +756,8 @@ mod tests { object.clone(), vec![object.clone()], HashMap::new(), + Default::default(), + None, ) .unwrap(); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 646363543..3ffad6d9d 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1334,8 +1334,6 @@ cfg_if::cfg_if! { } pub trait PyValue: fmt::Debug + PyThreadingConstraint + Sized + 'static { - const HAVE_DICT: bool = false; - fn class(vm: &VirtualMachine) -> PyClassRef; fn into_simple_object(self, vm: &VirtualMachine) -> PyObjectRef { @@ -1352,18 +1350,13 @@ pub trait PyValue: fmt::Debug + PyThreadingConstraint + Sized + 'static { } fn into_ref(self, vm: &VirtualMachine) -> PyRef { - PyRef::new_ref(self, Self::class(vm), None) + pyvalue_into_ref(self, Self::class(vm), vm) } fn into_ref_with_type(self, vm: &VirtualMachine, cls: PyClassRef) -> PyResult> { let class = Self::class(vm); if objtype::issubclass(&cls, &class) { - let dict = if !Self::HAVE_DICT && cls.is(&class) { - None - } else { - Some(vm.ctx.new_dict()) - }; - PyRef::from_obj(self.into_object(vm, cls, dict), vm) + Ok(pyvalue_into_ref(self, cls, vm)) } else { let subtype = vm.to_str(&cls.obj)?; let basetype = vm.to_str(&class.obj)?; @@ -1372,6 +1365,15 @@ pub trait PyValue: fmt::Debug + PyThreadingConstraint + Sized + 'static { } } +fn pyvalue_into_ref(value: T, cls: PyClassRef, vm: &VirtualMachine) -> PyRef { + let dict = if cls.slots.read().flags.has_feature(PyTpFlags::HAS_DICT) { + Some(vm.ctx.new_dict()) + } else { + None + }; + PyRef::new_ref(value, cls, dict) +} + pub trait BorrowValue<'a>: PyValue { type Borrowed: 'a + Deref; fn borrow_value(&'a self) -> Self::Borrowed; @@ -1457,6 +1459,16 @@ pub trait PyClassImpl: PyClassDef { fn impl_extend_class(ctx: &PyContext, class: &PyClassRef); fn extend_class(ctx: &PyContext, class: &PyClassRef) { + if Self::TP_FLAGS.has_feature(PyTpFlags::HAS_DICT) { + class.set_str_attr( + "__dict__", + ctx.new_getset( + "__dict__", + objobject::object_get_dict, + objobject::object_set_dict, + ), + ); + } Self::impl_extend_class(ctx, class); class.slots.write().flags = Self::TP_FLAGS; ctx.add_tp_new_wrapper(&class); diff --git a/vm/src/slots.rs b/vm/src/slots.rs index 87b5b73fb..848ba4ff5 100644 --- a/vm/src/slots.rs +++ b/vm/src/slots.rs @@ -5,6 +5,7 @@ use crate::VirtualMachine; bitflags! { pub struct PyTpFlags: u64 { const BASETYPE = 1 << 10; + const HAS_DICT = 1 << 40; } } diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index ca3bd9be4..2e4c3cb78 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -12,6 +12,7 @@ use rustpython_parser::{ast, mode::Mode, parser}; use crate::obj::objlist::PyListRef; use crate::obj::objtype::PyClassRef; use crate::pyobject::{PyObjectRef, PyRef, PyResult, PyValue}; +use crate::slots::PyTpFlags; use crate::vm::VirtualMachine; #[derive(Debug)] @@ -644,6 +645,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { let ctx = &vm.ctx; let ast_base = py_class!(ctx, "AST", ctx.object(), {}); + ast_base.slots.write().flags |= PyTpFlags::HAS_DICT; py_module!(vm, MODULE_NAME, { // TODO: There's got to be a better way! "alias" => py_class!(ctx, "alias", ast_base.clone(), {}), diff --git a/vm/src/types.rs b/vm/src/types.rs index efa5949b8..2e1a0e664 100644 --- a/vm/src/types.rs +++ b/vm/src/types.rs @@ -264,6 +264,8 @@ pub fn create_type(name: &str, type_type: &PyClassRef, base: &PyClassRef) -> PyC base.clone(), vec![base.clone()], dict, + Default::default(), + None, ) .expect("Failed to create a new type in internal code.") }