@@ -10,6 +10,7 @@ use crate::{
1010use alloc:: fmt;
1111
1212// PyCFunctionObject in CPython
13+ #[ repr( C ) ]
1314#[ pyclass( name = "builtin_function_or_method" , module = false ) ]
1415pub struct PyNativeFunction {
1516 pub ( crate ) value : & ' static PyMethodDef ,
@@ -68,13 +69,65 @@ impl Callable for PyNativeFunction {
6869 #[ inline]
6970 fn call ( zelf : & Py < Self > , mut args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
7071 if let Some ( z) = & zelf. zelf {
71- args. prepend_arg ( z. clone ( ) ) ;
72+ // STATIC methods store the class in zelf for qualname/repr purposes,
73+ // but should not prepend it to args (the Rust function doesn't expect it).
74+ if !zelf. value . flags . contains ( PyMethodFlags :: STATIC ) {
75+ args. prepend_arg ( z. clone ( ) ) ;
76+ }
7277 }
7378 ( zelf. value . func ) ( vm, args)
7479 }
7580}
7681
77- #[ pyclass( with( Callable ) , flags( HAS_DICT , DISALLOW_INSTANTIATION ) ) ]
82+ // meth_richcompare in CPython
83+ impl Comparable for PyNativeFunction {
84+ fn cmp (
85+ zelf : & Py < Self > ,
86+ other : & PyObject ,
87+ op : PyComparisonOp ,
88+ _vm : & VirtualMachine ,
89+ ) -> PyResult < PyComparisonValue > {
90+ op. eq_only ( || {
91+ if let Some ( other) = other. downcast_ref :: < Self > ( ) {
92+ let eq = match ( zelf. zelf . as_ref ( ) , other. zelf . as_ref ( ) ) {
93+ ( Some ( z) , Some ( o) ) => z. is ( o) ,
94+ ( None , None ) => true ,
95+ _ => false ,
96+ } ;
97+ let eq = eq && core:: ptr:: eq ( zelf. value , other. value ) ;
98+ Ok ( eq. into ( ) )
99+ } else {
100+ Ok ( PyComparisonValue :: NotImplemented )
101+ }
102+ } )
103+ }
104+ }
105+
106+ // meth_repr in CPython
107+ impl Representable for PyNativeFunction {
108+ #[ inline]
109+ fn repr_str ( zelf : & Py < Self > , vm : & VirtualMachine ) -> PyResult < String > {
110+ if let Some ( bound) = zelf
111+ . zelf
112+ . as_ref ( )
113+ . filter ( |b| !b. class ( ) . is ( vm. ctx . types . module_type ) )
114+ {
115+ Ok ( format ! (
116+ "<built-in method {} of {} object at {:#x}>" ,
117+ zelf. value. name,
118+ bound. class( ) . name( ) ,
119+ bound. get_id( )
120+ ) )
121+ } else {
122+ Ok ( format ! ( "<built-in function {}>" , zelf. value. name) )
123+ }
124+ }
125+ }
126+
127+ #[ pyclass(
128+ with( Callable , Comparable , Representable ) ,
129+ flags( HAS_DICT , DISALLOW_INSTANTIATION )
130+ ) ]
78131impl PyNativeFunction {
79132 #[ pygetset]
80133 fn __module__ ( zelf : NativeFunctionOrMethod ) -> Option < & ' static PyStrInterned > {
@@ -86,20 +139,19 @@ impl PyNativeFunction {
86139 zelf. 0 . value . name
87140 }
88141
142+ // meth_get__qualname__ in CPython
89143 #[ pygetset]
90144 fn __qualname__ ( zelf : NativeFunctionOrMethod , vm : & VirtualMachine ) -> PyResult < PyStrRef > {
91145 let zelf = zelf. 0 ;
92- let flags = zelf. value . flags ;
93- // if flags.contains(PyMethodFlags::CLASS) || flags.contains(PyMethodFlags::STATIC) {
94146 let qualname = if let Some ( bound) = & zelf. zelf {
95- let prefix = if flags. contains ( PyMethodFlags :: CLASS ) {
96- bound
97- . get_attr ( "__qualname__" , vm)
98- . unwrap ( )
99- . str ( vm)
100- . unwrap ( )
101- . to_string ( )
147+ if bound. class ( ) . is ( vm. ctx . types . module_type ) {
148+ return Ok ( vm. ctx . intern_str ( zelf. value . name ) . to_owned ( ) ) ;
149+ }
150+ let prefix = if bound. class ( ) . is ( vm. ctx . types . type_type ) {
151+ // m_self is a type: use PyType_GetQualName(m_self)
152+ bound. get_attr ( "__qualname__" , vm) ?. str ( vm) ?. to_string ( )
102153 } else {
154+ // m_self is an instance: use Py_TYPE(m_self).__qualname__
103155 bound. class ( ) . name ( ) . to_string ( )
104156 } ;
105157 vm. ctx . new_str ( format ! ( "{}.{}" , prefix, & zelf. value. name) )
@@ -114,15 +166,23 @@ impl PyNativeFunction {
114166 zelf. 0 . value . doc
115167 }
116168
169+ // meth_get__self__ in CPython
117170 #[ pygetset]
118- fn __self__ ( _zelf : PyObjectRef , vm : & VirtualMachine ) -> PyObjectRef {
119- vm. ctx . none ( )
171+ fn __self__ ( zelf : NativeFunctionOrMethod , vm : & VirtualMachine ) -> PyObjectRef {
172+ zelf . 0 . zelf . clone ( ) . unwrap_or_else ( || vm. ctx . none ( ) )
120173 }
121174
175+ // meth_reduce in CPython
122176 #[ pymethod]
123- const fn __reduce__ ( & self ) -> & ' static str {
124- // TODO: return (getattr, (self.object, self.name)) if this is a method
125- self . value . name
177+ fn __reduce__ ( zelf : NativeFunctionOrMethod , vm : & VirtualMachine ) -> PyResult {
178+ let zelf = zelf. 0 ;
179+ if zelf. zelf . is_none ( ) || zelf. module . is_some ( ) {
180+ Ok ( vm. ctx . new_str ( zelf. value . name ) . into ( ) )
181+ } else {
182+ let getattr = vm. builtins . get_attr ( "getattr" , vm) ?;
183+ let target = zelf. zelf . clone ( ) . unwrap ( ) ;
184+ Ok ( vm. new_tuple ( ( getattr, ( target, zelf. value . name ) ) ) . into ( ) )
185+ }
126186 }
127187
128188 #[ pymethod]
@@ -138,54 +198,20 @@ impl PyNativeFunction {
138198 }
139199}
140200
141- impl Representable for PyNativeFunction {
142- #[ inline]
143- fn repr_str ( zelf : & Py < Self > , _vm : & VirtualMachine ) -> PyResult < String > {
144- Ok ( format ! ( "<built-in function {}>" , zelf. value. name) )
145- }
146- }
147-
148- // `PyCMethodObject` in CPython
149- #[ pyclass( name = "builtin_method" , module = false , base = PyNativeFunction , ctx = "builtin_method_type" ) ]
201+ // PyCMethodObject in CPython
202+ // repr(C) ensures `func` is at offset 0, allowing safe cast from PyNativeMethod to PyNativeFunction
203+ #[ repr( C ) ]
204+ #[ pyclass( name = "builtin_function_or_method" , module = false , base = PyNativeFunction , ctx = "builtin_function_or_method_type" ) ]
150205pub struct PyNativeMethod {
151206 pub ( crate ) func : PyNativeFunction ,
152207 pub ( crate ) class : & ' static Py < PyType > , // TODO: the actual life is &'self
153208}
154209
155- #[ pyclass(
156- with( Callable , Comparable , Representable ) ,
157- flags( HAS_DICT , DISALLOW_INSTANTIATION )
158- ) ]
159- impl PyNativeMethod {
160- #[ pygetset]
161- fn __qualname__ ( zelf : PyRef < Self > , vm : & VirtualMachine ) -> PyResult < PyStrRef > {
162- let prefix = zelf. class . name ( ) . to_string ( ) ;
163- Ok ( vm
164- . ctx
165- . new_str ( format ! ( "{}.{}" , prefix, & zelf. func. value. name) ) )
166- }
167-
168- #[ pymethod]
169- fn __reduce__ (
170- & self ,
171- vm : & VirtualMachine ,
172- ) -> PyResult < ( PyObjectRef , ( PyObjectRef , & ' static str ) ) > {
173- // TODO: return (getattr, (self.object, self.name)) if this is a method
174- let getattr = vm. builtins . get_attr ( "getattr" , vm) ?;
175- let target = self
176- . func
177- . zelf
178- . clone ( )
179- . unwrap_or_else ( || self . class . to_owned ( ) . into ( ) ) ;
180- let name = self . func . value . name ;
181- Ok ( ( getattr, ( target, name) ) )
182- }
183-
184- #[ pygetset]
185- fn __self__ ( zelf : PyRef < Self > , _vm : & VirtualMachine ) -> Option < PyObjectRef > {
186- zelf. func . zelf . clone ( )
187- }
188- }
210+ // All Python-visible behavior (getters, slots) is registered by PyNativeFunction::extend_class.
211+ // PyNativeMethod only extends the Rust-side struct with the defining class reference.
212+ // The func field at offset 0 (#[repr(C)]) allows NativeFunctionOrMethod to read it safely.
213+ #[ pyclass( flags( HAS_DICT , DISALLOW_INSTANTIATION ) ) ]
214+ impl PyNativeMethod { }
189215
190216impl fmt:: Debug for PyNativeMethod {
191217 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
@@ -198,63 +224,21 @@ impl fmt::Debug for PyNativeMethod {
198224 }
199225}
200226
201- impl Comparable for PyNativeMethod {
202- fn cmp (
203- zelf : & Py < Self > ,
204- other : & PyObject ,
205- op : PyComparisonOp ,
206- _vm : & VirtualMachine ,
207- ) -> PyResult < PyComparisonValue > {
208- op. eq_only ( || {
209- if let Some ( other) = other. downcast_ref :: < Self > ( ) {
210- let eq = match ( zelf. func . zelf . as_ref ( ) , other. func . zelf . as_ref ( ) ) {
211- ( Some ( z) , Some ( o) ) => z. is ( o) ,
212- ( None , None ) => true ,
213- _ => false ,
214- } ;
215- let eq = eq && core:: ptr:: eq ( zelf. func . value , other. func . value ) ;
216- Ok ( eq. into ( ) )
217- } else {
218- Ok ( PyComparisonValue :: NotImplemented )
219- }
220- } )
221- }
222- }
223-
224- impl Callable for PyNativeMethod {
225- type Args = FuncArgs ;
226-
227- #[ inline]
228- fn call ( zelf : & Py < Self > , mut args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
229- if let Some ( zelf) = & zelf. func . zelf {
230- args. prepend_arg ( zelf. clone ( ) ) ;
231- }
232- ( zelf. func . value . func ) ( vm, args)
233- }
234- }
235-
236- impl Representable for PyNativeMethod {
237- #[ inline]
238- fn repr_str ( zelf : & Py < Self > , _vm : & VirtualMachine ) -> PyResult < String > {
239- Ok ( format ! (
240- "<built-in method {} of {} object at ...>" ,
241- & zelf. func. value. name,
242- zelf. class. name( )
243- ) )
244- }
245- }
246-
247227pub fn init ( context : & Context ) {
248228 PyNativeFunction :: extend_class ( context, context. types . builtin_function_or_method_type ) ;
249- PyNativeMethod :: extend_class ( context, context. types . builtin_method_type ) ;
250229}
251230
231+ /// Wrapper that provides access to the common PyNativeFunction data
232+ /// for both PyNativeFunction and PyNativeMethod (which has func as its first field).
252233struct NativeFunctionOrMethod ( PyRef < PyNativeFunction > ) ;
253234
254235impl TryFromObject for NativeFunctionOrMethod {
255236 fn try_from_object ( vm : & VirtualMachine , obj : PyObjectRef ) -> PyResult < Self > {
256237 let class = vm. ctx . types . builtin_function_or_method_type ;
257238 if obj. fast_isinstance ( class) {
239+ // Both PyNativeFunction and PyNativeMethod share the same type now.
240+ // PyNativeMethod has `func: PyNativeFunction` as its first field,
241+ // so we can safely treat the data pointer as PyNativeFunction for reading.
258242 Ok ( Self ( unsafe { obj. downcast_unchecked ( ) } ) )
259243 } else {
260244 Err ( vm. new_downcast_type_error ( class, & obj) )
0 commit comments