From 7bf8378a18dfa1ee0d7ecf4c0244b488ca33716d Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 4 Nov 2018 15:57:50 +0000 Subject: [PATCH 1/2] type.__getattribute__ should be bound to type! --- tests/snippets/class.py | 9 +++------ vm/src/obj/objtype.rs | 3 ++- vm/src/vm.rs | 19 +++++++++++++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/snippets/class.py b/tests/snippets/class.py index e226f9c4b4..f5dce24257 100644 --- a/tests/snippets/class.py +++ b/tests/snippets/class.py @@ -42,12 +42,9 @@ def kungfu(x): bar = Bar() -bar.fubar(2) -# TODO: make below work: -# Bar.fubar(2) +bar.fubar(2) +Bar.fubar(2) bar.kungfu(3) -# TODO: make below work: -# Bar.kungfu(3) - +Bar.kungfu(3) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 7f68a37e63..86818cf98f 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -29,6 +29,7 @@ pub fn init(context: &PyContext) { type_type.set_attr("__class__", context.new_member_descriptor(type_new)); type_type.set_attr("__repr__", context.new_rustfunc(type_repr)); type_type.set_attr("__prepare__", context.new_rustfunc(type_prepare)); + type_type.set_attr("__getattribute__", context.new_rustfunc(type_getattribute)); } fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -85,7 +86,7 @@ pub fn get_type_name(typ: &PyObjectRef) -> String { } pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - debug!("type.__new__{:?}", args); + debug!("type.__new__ {:?}", args); if args.args.len() == 2 { arg_check!( vm, diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 05ecade75d..5f73ffe4cb 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -17,8 +17,8 @@ use super::obj::objsequence; use super::obj::objstr; use super::obj::objtype; use super::pyobject::{ - AttributeProtocol, DictProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, - TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, + PyResult, TypeProtocol, }; use super::stdlib; use super::sysmodule; @@ -93,7 +93,11 @@ impl VirtualMachine { } pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { - self.ctx.new_bound_method(function, object) + if object.is(&self.get_none()) { + function + } else { + self.ctx.new_bound_method(function, object) + } } pub fn get_type(&self) -> PyObjectRef { @@ -202,7 +206,13 @@ impl VirtualMachine { let cls = obj.typ(); match cls.get_attr(method_name) { Some(func) => { - trace!("vm.call_method {:?} {:?} -> {:?}", obj, method_name, func); + trace!( + "vm.call_method {:?} {:?} {:?} -> {:?}", + obj, + cls, + method_name, + func + ); let wrapped = self.call_get_descriptor(func, obj.clone())?; self.invoke(wrapped, args) } @@ -423,6 +433,7 @@ impl VirtualMachine { // get_attribute should be used for full attribute access (usually from user code). pub fn get_attribute(&mut self, obj: PyObjectRef, attr_name: PyObjectRef) -> PyResult { + trace!("vm.__getattribute__: {:?} {:?}", obj, attr_name); self.call_method(&obj, "__getattribute__", vec![attr_name]) } From a2ce4c8045cfa22b6db005e893ea083bcdd8321e Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 4 Nov 2018 20:11:07 +0000 Subject: [PATCH 2/2] Add special case handling for __get__(None, NoneType) --- vm/src/obj/objfunction.rs | 18 ++++++++++++++---- vm/src/obj/objproperty.rs | 2 +- vm/src/vm.rs | 12 ++---------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index d4e4dafce2..955f2643e5 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,6 +1,6 @@ use super::super::pyobject::{ - AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, - TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, + PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -22,7 +22,17 @@ pub fn init(context: &PyContext) { } fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - Ok(vm.new_bound_method(args.args[0].clone(), args.args[1].clone())) + arg_check!( + vm, + args, + required = [(function, None), (obj, None), (cls, None)] + ); + + if obj.is(&vm.get_none()) && !cls.is(&obj.typ()) { + Ok(function.clone()) + } else { + Ok(vm.ctx.new_bound_method(function.clone(), obj.clone())) + } } fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { @@ -50,7 +60,7 @@ fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match cls.get_attr("function") { Some(function) => { let py_obj = owner.clone(); - let py_method = vm.new_bound_method(function, py_obj); + let py_method = vm.ctx.new_bound_method(function, py_obj); Ok(py_method) } None => { diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index d2972a2686..1876915cb0 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -31,7 +31,7 @@ fn property_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match cls.get_attr("fget") { Some(getter) => { - let py_method = vm.new_bound_method(getter, inst.clone()); + let py_method = vm.ctx.new_bound_method(getter, inst.clone()); vm.invoke(py_method, PyFuncArgs::default()) } None => { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 5f73ffe4cb..17c89ba0b6 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -17,8 +17,8 @@ use super::obj::objsequence; use super::obj::objstr; use super::obj::objtype; use super::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, - PyResult, TypeProtocol, + AttributeProtocol, DictProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, + TypeProtocol, }; use super::stdlib; use super::sysmodule; @@ -92,14 +92,6 @@ impl VirtualMachine { self.ctx.none() } - pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { - if object.is(&self.get_none()) { - function - } else { - self.ctx.new_bound_method(function, object) - } - } - pub fn get_type(&self) -> PyObjectRef { self.ctx.type_type() }