From 201089008a4d40997e68271a1421417a1e6c69de Mon Sep 17 00:00:00 2001 From: Hanif Ariffin Date: Sun, 2 Mar 2025 22:19:39 +0800 Subject: [PATCH 1/6] Implemented the ability to del __dict__ This PR is based on the work by key262yek. The original PR is here: https://github.com/RustPython/RustPython/pull/5509 --- vm/src/builtins/object.rs | 9 +++++++-- vm/src/builtins/type.rs | 11 ++++++++--- vm/src/object/core.rs | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index cce1422d56..afcebc7584 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -495,9 +495,14 @@ pub fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult PyResult<()> { + +pub fn object_set_dict( + obj: PyObjectRef, + dict: PySetterValue, + vm: &VirtualMachine, +) -> PyResult<()> { obj.set_dict(dict) - .map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned())) + .ok_or_else(|| vm.new_type_error("cannot delete __dict__".to_owned())) } pub fn init(ctx: &Context) { diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index e8ad67a666..5345eb93c3 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -1272,7 +1272,11 @@ fn subtype_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { Ok(ret) } -fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { +fn subtype_set_dict( + obj: PyObjectRef, + value: PySetterValue, + vm: &VirtualMachine, +) -> PyResult<()> { let cls = obj.class(); match find_base_dict_descr(cls, vm) { Some(descr) => { @@ -1285,10 +1289,11 @@ fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) - cls.name() )) })?; - descr_set(&descr, obj, PySetterValue::Assign(value), vm) + descr_set(&descr, obj, value, vm) } None => { - object::object_set_dict(obj, value.try_into_value(vm)?, vm)?; + let dict = value.map(|s| s.try_into_value(vm)).transpose()?; + object::object_set_dict(obj, dict, vm)?; Ok(()) } } diff --git a/vm/src/object/core.rs b/vm/src/object/core.rs index 56ab419c01..c76b5426da 100644 --- a/vm/src/object/core.rs +++ b/vm/src/object/core.rs @@ -25,6 +25,7 @@ use crate::{ lock::{PyMutex, PyMutexGuard, PyRwLock}, refcount::RefCount, }, + function::PySetterValue, vm::VirtualMachine, }; use itertools::Itertools; @@ -711,14 +712,37 @@ impl PyObject { /// Set the dict field. Returns `Err(dict)` if this object does not have a dict field /// in the first place. - pub fn set_dict(&self, dict: PyDictRef) -> Result<(), PyDictRef> { - match self.instance_dict() { - Some(d) => { + pub fn set_dict(&self, dict: PySetterValue) -> Option<()> { + // NOTE(hanif) - So far, this is the only error condition that I know of so we can use Option + // for now. + if self.payload_is::() { + return None; + } + + match (self.instance_dict(), dict) { + (Some(d), PySetterValue::Assign(dict)) => { d.set(dict); - Ok(()) } - None => Err(dict), - } + (None, PySetterValue::Assign(dict)) => { + // self.0.dict = Some(InstanceDict::new(dict)); + unsafe { + let ptr = self as *const _ as *mut PyObject; + (*ptr).0.dict = Some(InstanceDict::new(dict)); + } + } + (Some(_), PySetterValue::Delete) => { + // self.0.dict = None; + unsafe { + let ptr = self as *const _ as *mut PyObject; + (*ptr).0.dict = None; + } + } + (None, PySetterValue::Delete) => { + // NOTE(hanif) - noop? + } + }; + + Some(()) } #[inline(always)] From 1abd2040ea12b93c84017a3d000e636270ad11cb Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Mon, 10 Feb 2025 10:30:17 +0900 Subject: [PATCH 2/6] Implement delete object dictionary - Add support for deleting object dictionaries - Modify object_set_dict to handle dictionary deletion - Update setter value handling to support delete operations --- vm/src/builtins/object.rs | 18 +++++++++++------- vm/src/builtins/type.rs | 11 +++-------- vm/src/function/getset.rs | 6 ++++-- vm/src/object/core.rs | 13 +++++++++++++ 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index afcebc7584..9df870f75b 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -496,13 +496,17 @@ pub fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult, - vm: &VirtualMachine, -) -> PyResult<()> { - obj.set_dict(dict) - .ok_or_else(|| vm.new_type_error("cannot delete __dict__".to_owned())) +pub fn object_set_dict(obj: PyObjectRef, dict: PySetterValue, vm: &VirtualMachine) -> PyResult<()> { + match dict { + PySetterValue::Assign(dict) => { + obj.set_dict(dict) + .map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned())) + } + PySetterValue::Delete => { + obj.delete_dict() + .map_err(|_| vm.new_attribute_error("This object has no deletable __dict__".to_owned())) + } + } } pub fn init(ctx: &Context) { diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index 5345eb93c3..0b888b3f53 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -1272,11 +1272,7 @@ fn subtype_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { Ok(ret) } -fn subtype_set_dict( - obj: PyObjectRef, - value: PySetterValue, - vm: &VirtualMachine, -) -> PyResult<()> { +fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { let cls = obj.class(); match find_base_dict_descr(cls, vm) { Some(descr) => { @@ -1289,11 +1285,10 @@ fn subtype_set_dict( cls.name() )) })?; - descr_set(&descr, obj, value, vm) + descr_set(&descr, obj, PySetterValue::Assign(value), vm) } None => { - let dict = value.map(|s| s.try_into_value(vm)).transpose()?; - object::object_set_dict(obj, dict, vm)?; + object::object_set_dict(obj, PySetterValue::Delete, vm)?; Ok(()) } } diff --git a/vm/src/function/getset.rs b/vm/src/function/getset.rs index 66e668ace6..068b7472bc 100644 --- a/vm/src/function/getset.rs +++ b/vm/src/function/getset.rs @@ -36,8 +36,10 @@ where { #[inline] fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult { - let obj = obj.ok_or_else(|| vm.new_type_error("can't delete attribute".to_owned()))?; - T::try_from_object(vm, obj) + match obj { + PySetterValue::Assign(obj) => T::try_from_object(vm, obj), + PySetterValue::Delete => T::try_from_object(vm, vm.ctx.none()), + } } } diff --git a/vm/src/object/core.rs b/vm/src/object/core.rs index c76b5426da..a70d7cfc18 100644 --- a/vm/src/object/core.rs +++ b/vm/src/object/core.rs @@ -745,6 +745,19 @@ impl PyObject { Some(()) } + pub fn delete_dict(&self) -> Result<(), ()> { + match self.instance_dict() { + Some(_) => { + unsafe { + let ptr = self as *const _ as *mut PyObject; + (*ptr).0.dict = None; + } + Ok(()) + } + None => Err(()), + } + } + #[inline(always)] pub fn payload_if_subclass(&self, vm: &VirtualMachine) -> Option<&T> { if self.class().fast_issubclass(T::class(&vm.ctx)) { From c43327edb41e523b48d568a9a37bc1f45e206dd8 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Sat, 15 Feb 2025 00:12:19 +0900 Subject: [PATCH 3/6] Refactor object dictionary setter to handle delete and assign operations - Simplify object_set_dict method in object.rs - Update set_dict method in core.rs to handle both assignment and deletion - Consolidate dictionary modification logic --- vm/src/builtins/object.rs | 12 ++---------- vm/src/object/core.rs | 36 ++++-------------------------------- 2 files changed, 6 insertions(+), 42 deletions(-) diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index 9df870f75b..6fbb8f13a5 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -497,16 +497,8 @@ pub fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult, vm: &VirtualMachine) -> PyResult<()> { - match dict { - PySetterValue::Assign(dict) => { - obj.set_dict(dict) - .map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned())) - } - PySetterValue::Delete => { - obj.delete_dict() - .map_err(|_| vm.new_attribute_error("This object has no deletable __dict__".to_owned())) - } - } + obj.set_dict(dict) + .map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned())) } pub fn init(ctx: &Context) { diff --git a/vm/src/object/core.rs b/vm/src/object/core.rs index a70d7cfc18..fbedd9067e 100644 --- a/vm/src/object/core.rs +++ b/vm/src/object/core.rs @@ -712,49 +712,21 @@ impl PyObject { /// Set the dict field. Returns `Err(dict)` if this object does not have a dict field /// in the first place. - pub fn set_dict(&self, dict: PySetterValue) -> Option<()> { - // NOTE(hanif) - So far, this is the only error condition that I know of so we can use Option - // for now. - if self.payload_is::() { - return None; - } - + pub fn set_dict(&self, dict: PySetterValue) -> Result<(), PyDictRef> { match (self.instance_dict(), dict) { (Some(d), PySetterValue::Assign(dict)) => { d.set(dict); + Ok(()) } - (None, PySetterValue::Assign(dict)) => { - // self.0.dict = Some(InstanceDict::new(dict)); - unsafe { - let ptr = self as *const _ as *mut PyObject; - (*ptr).0.dict = Some(InstanceDict::new(dict)); - } - } + (None, PySetterValue::Assign(dict)) => Err(dict), (Some(_), PySetterValue::Delete) => { - // self.0.dict = None; - unsafe { - let ptr = self as *const _ as *mut PyObject; - (*ptr).0.dict = None; - } - } - (None, PySetterValue::Delete) => { - // NOTE(hanif) - noop? - } - }; - - Some(()) - } - - pub fn delete_dict(&self) -> Result<(), ()> { - match self.instance_dict() { - Some(_) => { unsafe { let ptr = self as *const _ as *mut PyObject; (*ptr).0.dict = None; } Ok(()) } - None => Err(()), + (None, PySetterValue::Delete) => Ok(()), } } From 79d70e3d39a7e8170fbd0dd6390c86348ce965db Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Sat, 15 Feb 2025 00:56:39 +0900 Subject: [PATCH 4/6] Enhance object dictionary handling in type and object modules - Modify set_dict method to allow assigning dict to objects without existing dict - Update error handling in object_set_dict to provide more precise error message - Refactor type module to correctly handle dictionary assignment --- vm/src/builtins/object.rs | 8 ++++++-- vm/src/builtins/type.rs | 2 +- vm/src/object/core.rs | 12 +++++++++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index 6fbb8f13a5..6f0a8268d5 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -496,9 +496,13 @@ pub fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult, vm: &VirtualMachine) -> PyResult<()> { +pub fn object_set_dict( + obj: PyObjectRef, + dict: PySetterValue, + vm: &VirtualMachine, +) -> PyResult<()> { obj.set_dict(dict) - .map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned())) + .map_err(|_| vm.new_attribute_error("This object has no __dict__ to delete".to_owned())) } pub fn init(ctx: &Context) { diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index 0b888b3f53..e8d3441f99 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -1288,7 +1288,7 @@ fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) - descr_set(&descr, obj, PySetterValue::Assign(value), vm) } None => { - object::object_set_dict(obj, PySetterValue::Delete, vm)?; + object::object_set_dict(obj, PySetterValue::Assign(value.try_into_value(vm)?), vm)?; Ok(()) } } diff --git a/vm/src/object/core.rs b/vm/src/object/core.rs index fbedd9067e..e130ed5949 100644 --- a/vm/src/object/core.rs +++ b/vm/src/object/core.rs @@ -712,13 +712,19 @@ impl PyObject { /// Set the dict field. Returns `Err(dict)` if this object does not have a dict field /// in the first place. - pub fn set_dict(&self, dict: PySetterValue) -> Result<(), PyDictRef> { + pub fn set_dict(&self, dict: PySetterValue) -> Result<(), PySetterValue> { match (self.instance_dict(), dict) { (Some(d), PySetterValue::Assign(dict)) => { d.set(dict); Ok(()) } - (None, PySetterValue::Assign(dict)) => Err(dict), + (None, PySetterValue::Assign(dict)) => { + unsafe { + let ptr = self as *const _ as *mut PyObject; + (*ptr).0.dict = Some(InstanceDict::new(dict)); + } + Ok(()) + } (Some(_), PySetterValue::Delete) => { unsafe { let ptr = self as *const _ as *mut PyObject; @@ -726,7 +732,7 @@ impl PyObject { } Ok(()) } - (None, PySetterValue::Delete) => Ok(()), + (None, PySetterValue::Delete) => Err(PySetterValue::Delete), } } From 02ce85a54d098f2d972e4b648a3f478dac72eaf9 Mon Sep 17 00:00:00 2001 From: Hanif Ariffin Date: Sat, 8 Mar 2025 18:53:40 +0800 Subject: [PATCH 5/6] Implemented the ability to del __dict__ This PR is based on the work by key262yek. Signed-off-by: Hanif Ariffin --- vm/src/builtins/object.rs | 2 +- vm/src/builtins/type.rs | 11 ++++++++--- vm/src/function/getset.rs | 6 ++---- vm/src/object/core.rs | 21 +++++++++++++++------ 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index 6f0a8268d5..afcebc7584 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -502,7 +502,7 @@ pub fn object_set_dict( vm: &VirtualMachine, ) -> PyResult<()> { obj.set_dict(dict) - .map_err(|_| vm.new_attribute_error("This object has no __dict__ to delete".to_owned())) + .ok_or_else(|| vm.new_type_error("cannot delete __dict__".to_owned())) } pub fn init(ctx: &Context) { diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index e8d3441f99..5345eb93c3 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -1272,7 +1272,11 @@ fn subtype_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { Ok(ret) } -fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { +fn subtype_set_dict( + obj: PyObjectRef, + value: PySetterValue, + vm: &VirtualMachine, +) -> PyResult<()> { let cls = obj.class(); match find_base_dict_descr(cls, vm) { Some(descr) => { @@ -1285,10 +1289,11 @@ fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) - cls.name() )) })?; - descr_set(&descr, obj, PySetterValue::Assign(value), vm) + descr_set(&descr, obj, value, vm) } None => { - object::object_set_dict(obj, PySetterValue::Assign(value.try_into_value(vm)?), vm)?; + let dict = value.map(|s| s.try_into_value(vm)).transpose()?; + object::object_set_dict(obj, dict, vm)?; Ok(()) } } diff --git a/vm/src/function/getset.rs b/vm/src/function/getset.rs index 068b7472bc..66e668ace6 100644 --- a/vm/src/function/getset.rs +++ b/vm/src/function/getset.rs @@ -36,10 +36,8 @@ where { #[inline] fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult { - match obj { - PySetterValue::Assign(obj) => T::try_from_object(vm, obj), - PySetterValue::Delete => T::try_from_object(vm, vm.ctx.none()), - } + let obj = obj.ok_or_else(|| vm.new_type_error("can't delete attribute".to_owned()))?; + T::try_from_object(vm, obj) } } diff --git a/vm/src/object/core.rs b/vm/src/object/core.rs index e130ed5949..c76b5426da 100644 --- a/vm/src/object/core.rs +++ b/vm/src/object/core.rs @@ -712,28 +712,37 @@ impl PyObject { /// Set the dict field. Returns `Err(dict)` if this object does not have a dict field /// in the first place. - pub fn set_dict(&self, dict: PySetterValue) -> Result<(), PySetterValue> { + pub fn set_dict(&self, dict: PySetterValue) -> Option<()> { + // NOTE(hanif) - So far, this is the only error condition that I know of so we can use Option + // for now. + if self.payload_is::() { + return None; + } + match (self.instance_dict(), dict) { (Some(d), PySetterValue::Assign(dict)) => { d.set(dict); - Ok(()) } (None, PySetterValue::Assign(dict)) => { + // self.0.dict = Some(InstanceDict::new(dict)); unsafe { let ptr = self as *const _ as *mut PyObject; (*ptr).0.dict = Some(InstanceDict::new(dict)); } - Ok(()) } (Some(_), PySetterValue::Delete) => { + // self.0.dict = None; unsafe { let ptr = self as *const _ as *mut PyObject; (*ptr).0.dict = None; } - Ok(()) } - (None, PySetterValue::Delete) => Err(PySetterValue::Delete), - } + (None, PySetterValue::Delete) => { + // NOTE(hanif) - noop? + } + }; + + Some(()) } #[inline(always)] From 425fd4f673f888272be2707c308794111ce0f45e Mon Sep 17 00:00:00 2001 From: Hanif Ariffin Date: Sat, 15 Mar 2025 12:10:52 +0800 Subject: [PATCH 6/6] Update vm/src/object/core.rs Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com> --- vm/src/object/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/object/core.rs b/vm/src/object/core.rs index c76b5426da..e69bd65e80 100644 --- a/vm/src/object/core.rs +++ b/vm/src/object/core.rs @@ -713,7 +713,7 @@ impl PyObject { /// Set the dict field. Returns `Err(dict)` if this object does not have a dict field /// in the first place. pub fn set_dict(&self, dict: PySetterValue) -> Option<()> { - // NOTE(hanif) - So far, this is the only error condition that I know of so we can use Option + // NOTE: So far, this is the only error condition that I know of so we can use Option // for now. if self.payload_is::() { return None;