11use crate :: { PyObject , with_vm} ;
22use core:: ffi:: { CStr , c_char, c_int, c_uint, c_ulong, c_void} ;
33use core:: ptr:: NonNull ;
4- use rustpython_vm:: builtins:: { PyDict , PyStr , PyTuple , PyType } ;
4+ use rustpython_vm:: builtins:: { PyDict , PyGetSet , PyStr , PyTuple , PyType } ;
5+ use rustpython_vm:: convert:: IntoObject ;
56use rustpython_vm:: function:: FuncArgs ;
67use rustpython_vm:: types:: { PyTypeFlags , PyTypeSlots , SlotAccessor } ;
7- use rustpython_vm:: { AsObject , Context , Py , PyObjectRef } ;
8+ use rustpython_vm:: { AsObject , Context , Py , PyObjectRef , PyRef , PyResult , VirtualMachine } ;
89
910const PY_TPFLAGS_LONG_SUBCLASS : c_ulong = 1 << 24 ;
1011const PY_TPFLAGS_LIST_SUBCLASS : c_ulong = 1 << 25 ;
@@ -186,6 +187,15 @@ pub struct PyType_Spec {
186187 slots : * mut PyType_Slot ,
187188}
188189
190+ #[ repr( C ) ]
191+ pub struct PyGetSetDef {
192+ name : * const c_char ,
193+ get : extern "C" fn ( * mut PyObject , usize ) -> * mut PyObject ,
194+ set : Option < extern "C" fn ( * mut PyObject , * mut PyObject , usize ) -> c_int > ,
195+ doc : * const c_char ,
196+ closure : usize ,
197+ }
198+
189199#[ derive( Default ) ]
190200struct TypeVTable {
191201 new_func : Option < newfunc > ,
@@ -201,7 +211,7 @@ type newfunc = unsafe extern "C" fn(
201211pub extern "C" fn PyType_FromSpec ( spec : * mut PyType_Spec ) -> * mut PyObject {
202212 with_vm ( |vm| {
203213 let spec = unsafe { & * spec } ;
204- let name = unsafe {
214+ let class_name = unsafe {
205215 CStr :: from_ptr ( spec. name )
206216 . to_str ( )
207217 . expect ( "type name must be valid UTF-8" )
@@ -213,6 +223,7 @@ pub extern "C" fn PyType_FromSpec(spec: *mut PyType_Spec) -> *mut PyObject {
213223 slots. itemsize = spec. itemsize as _ ;
214224 slots. flags = PyTypeFlags :: from_bits ( spec. flags as u64 ) . expect ( "invalid flags value" ) ;
215225
226+ let mut attributes: & [ PyGetSetDef ] = & [ ] ;
216227 let mut vtable = TypeVTable :: default ( ) ;
217228 let mut slot_ptr = spec. slots ;
218229 while let slot = unsafe { & * slot_ptr }
@@ -228,7 +239,16 @@ pub extern "C" fn PyType_FromSpec(spec: *mut PyType_Spec) -> *mut PyObject {
228239 } ) ) ;
229240 }
230241 SlotAccessor :: TpBase => base = unsafe { & * slot. pfunc . cast :: < PyTypeObject > ( ) } ,
231- SlotAccessor :: TpGetset => { }
242+ SlotAccessor :: TpGetset => {
243+ let start = slot. pfunc . cast :: < PyGetSetDef > ( ) ;
244+ let mut end = start;
245+ while unsafe { !( * end) . name . is_null ( ) } {
246+ end = unsafe { end. add ( 1 ) }
247+ }
248+ attributes = unsafe {
249+ core:: slice:: from_raw_parts ( start, end. offset_from ( start) as usize )
250+ } ;
251+ }
232252 SlotAccessor :: TpMethods => { }
233253 SlotAccessor :: TpNew => {
234254 vtable. new_func = Some ( unsafe { core:: mem:: transmute ( slot. pfunc ) } ) ;
@@ -250,15 +270,60 @@ pub extern "C" fn PyType_FromSpec(spec: *mut PyType_Spec) -> *mut PyObject {
250270 unsafe { Ok ( PyObjectRef :: from_raw ( NonNull :: new ( result) . unwrap ( ) ) ) }
251271 } ) ) ;
252272 }
253- SlotAccessor :: TpDoc => { }
273+ SlotAccessor :: TpDoc => {
274+ let doc = unsafe {
275+ CStr :: from_ptr ( slot. pfunc . cast :: < c_char > ( ) )
276+ . to_str ( )
277+ . expect ( "tp_doc must be a valid UTF-8 string" )
278+ } ;
279+ slots. doc = Some ( doc) ;
280+ }
254281 _ => todo ! ( "Slot {accessor:?} is not yet supported in PyType_FromSpec" ) ,
255282 }
256283
257284 slot_ptr = unsafe { slot_ptr. add ( 1 ) } ;
258285 }
259286
260- let class = vm. ctx . new_class ( None , name , base. to_owned ( ) , slots) ;
287+ let class = vm. ctx . new_class ( None , class_name , base. to_owned ( ) , slots) ;
261288 class. init_type_data ( vtable) . unwrap ( ) ;
289+ for attribute in attributes {
290+ let name = unsafe {
291+ CStr :: from_ptr ( attribute. name )
292+ . to_str ( )
293+ . expect ( "attribute name must be valid UTF-8" )
294+ } ;
295+ let closure = attribute. closure ;
296+ let getter = attribute. get ;
297+ let getset = if let Some ( setter) = attribute. set {
298+ todo ! ( ) ;
299+ unsafe {
300+ vm. ctx . new_getset (
301+ name,
302+ & class,
303+ |obj : PyObjectRef , vm : & VirtualMachine | { } ,
304+ |obj : PyObjectRef , value : PyObjectRef , vm : & VirtualMachine | { } ,
305+ )
306+ }
307+ } else {
308+ let class = unsafe { & * ( ( & * class) as * const _ ) } ;
309+ vm. ctx . new_readonly_getset (
310+ name,
311+ & class,
312+ move |obj : PyObjectRef , vm : & VirtualMachine | {
313+ let result = getter ( obj. as_raw ( ) . cast_mut ( ) , closure) ;
314+ unsafe {
315+ PyObjectRef :: from_raw (
316+ NonNull :: new ( result) . expect ( "TODO handle error from c function" ) ,
317+ )
318+ }
319+ } ,
320+ )
321+ } ;
322+ class
323+ . attributes
324+ . write ( )
325+ . insert ( vm. ctx . intern_str ( name) , getset. into_object ( ) ) ;
326+ }
262327 class
263328 } )
264329}
@@ -510,6 +575,11 @@ mod tests {
510575
511576 Python :: attach ( |py| {
512577 let obj = Bound :: new ( py, MyClass { num : 3 } ) . unwrap ( ) ;
578+
579+ let globals = PyDict :: new ( py) ;
580+ globals. set_item ( "instance" , obj) . unwrap ( ) ;
581+ py. run ( c"assert instance.num == 3" , Some ( & globals) , None )
582+ . unwrap ( ) ;
513583 } ) ;
514584 }
515585}
0 commit comments