1+ use crate :: abstract_:: { dict_to_kwargs, tuple_to_args} ;
2+ use crate :: methodobject:: { PyMethodDef , build_method_def} ;
13use crate :: object:: define_py_check;
24use crate :: pystate:: with_vm;
3- use core:: ffi:: { c_int, c_ulong} ;
4- use rustpython_vm:: builtins:: { PyStr , PyType } ;
5+ use crate :: slots:: { PySlot , PySlotKind } ;
6+ use core:: ffi:: { CStr , c_char, c_int, c_ulong, c_void} ;
7+ use rustpython_vm:: builtins:: { PyDict , PyStr , PyTuple , PyType } ;
8+ use rustpython_vm:: function:: { FuncArgs , PyMethodFlags } ;
9+ use rustpython_vm:: types:: { PyTypeFlags , PyTypeSlots , SlotAccessor } ;
510use rustpython_vm:: { AsObject , Py , PyObject } ;
6- use std:: ffi:: c_void;
711
812pub type PyTypeObject = Py < PyType > ;
913
10- pub struct PyTypeSlot {
14+ define_py_check ! ( fn PyType_Check , types. type_type) ;
15+ define_py_check ! ( exact fn PyType_CheckExact , types. type_type) ;
16+
17+ #[ repr( C ) ]
18+ pub struct PyType_Slot {
1119 pub slot : c_int ,
1220 pub pfunc : * mut c_void ,
1321}
1422
15- define_py_check ! ( fn PyType_Check , types. type_type) ;
16- define_py_check ! ( exact fn PyType_CheckExact , types. type_type) ;
17-
1823#[ unsafe( no_mangle) ]
1924pub unsafe extern "C" fn Py_TYPE ( op : * mut PyObject ) -> * const PyTypeObject {
2025 unsafe { ( * op) . class ( ) }
@@ -77,18 +82,14 @@ pub unsafe extern "C" fn PyType_GetFullyQualifiedName(ptr: *const PyTypeObject)
7782}
7883
7984#[ unsafe( no_mangle) ]
80- #[ cfg( false ) ]
81- pub extern "C" fn PyType_GetSlot ( ty : * const PyTypeObject , slot : c_int ) -> * mut c_void {
85+ pub unsafe extern "C" fn PyType_GetSlot ( ty : * const PyTypeObject , slot : c_int ) -> * mut c_void {
8286 with_vm ( |_vm| {
8387 let ty = unsafe { & * ty } ;
8488 let slot: u8 = slot
8589 . try_into ( )
8690 . expect ( "slot number out of range for SlotAccessor" ) ;
87- let slot_accessor: SlotAccessor = slot
88- . try_into ( )
89- . expect ( "invalid slot number for SlotAccessor" ) ;
9091
91- match slot_accessor {
92+ match slot . try_into ( ) . unwrap ( ) {
9293 SlotAccessor :: TpNew => {
9394 extern "C" fn newfunc_wrapper (
9495 subtype : * mut PyTypeObject ,
@@ -97,61 +98,176 @@ pub extern "C" fn PyType_GetSlot(ty: *const PyTypeObject, slot: c_int) -> *mut c
9798 ) -> * mut PyObject {
9899 with_vm ( |vm| {
99100 let subtype = unsafe { & * subtype } ;
100- let mut func_args = FuncArgs :: default ( ) ;
101101
102- if let Some ( args_obj) = unsafe { args. as_ref ( ) } {
103- let tuple = args_obj. try_downcast_ref :: < PyTuple > ( vm) ?;
104- func_args
105- . args
106- . extend ( tuple. iter ( ) . map ( |arg| arg. to_owned ( ) ) ) ;
107- }
102+ let args = if let Some ( args_obj) = unsafe { args. as_ref ( ) } {
103+ tuple_to_args ( args_obj. try_downcast_ref :: < PyTuple > ( vm) ?)
104+ } else {
105+ ( ) . into ( )
106+ } ;
108107
109- if let Some ( kwargs_obj) = unsafe { kwargs. as_ref ( ) } {
110- let kwargs = kwargs_obj. try_downcast_ref :: < PyDict > ( vm) ?;
111- for ( key, value) in kwargs. items_vec ( ) {
112- let key = key. try_downcast :: < PyStr > ( vm) ?;
113- func_args
114- . kwargs
115- . insert ( key. to_string_lossy ( ) . into_owned ( ) , value) ;
116- }
117- }
108+ let kwargs = unsafe { kwargs. as_ref ( ) }
109+ . map ( |obj| dict_to_kwargs ( vm, obj. try_downcast_ref :: < PyDict > ( vm) ?) )
110+ . transpose ( ) ?
111+ . unwrap_or_default ( ) ;
118112
119113 subtype
120114 . slots
121115 . new
122116 . load ( )
123117 . expect ( "tp_new slot function pointer is null" ) (
124118 subtype. to_owned ( ) ,
125- func_args ,
119+ FuncArgs :: new ( args , kwargs ) ,
126120 vm,
127121 )
128122 } )
129123 }
130124
131- if let Some ( vtable) = ty. get_type_data :: < TypeVTable > ( ) {
132- vtable. new_func . map ( |newfunc| newfunc as * mut c_void )
133- } else {
134- ty. slots . new . load ( ) . map ( |_| newfunc_wrapper as * mut c_void )
135- }
125+ ty. slots . new . load ( ) . map ( |_| newfunc_wrapper as * mut c_void )
136126 }
137127 _ => {
138128 todo ! ( "Slot {slot_accessor:?} for {ty:?} is not yet implemented in PyType_GetSlot" )
139129 }
140130 }
141- . unwrap_or_default ( )
131+ . unwrap ( )
132+ } )
133+ }
134+
135+ #[ unsafe( no_mangle) ]
136+ pub extern "C" fn PyType_FromSlots ( slots : * const PySlot ) -> * mut PyObject {
137+ with_vm ( |vm| {
138+ let mut name = None ;
139+ let mut base = None ;
140+ let mut methods = Vec :: new ( ) ;
141+ let mut type_slots: PyTypeSlots = Default :: default ( ) ;
142+ let attrs = Default :: default ( ) ;
143+
144+ for slot in PySlot :: iter ( slots) {
145+ match ( slot, vm) . try_into ( ) ? {
146+ PySlotKind :: TypeName { value, .. } => {
147+ name = unsafe { Some ( CStr :: from_ptr ( value) . to_str ( ) . unwrap ( ) ) }
148+ }
149+ PySlotKind :: TypeFlags { value } => {
150+ type_slots. flags = PyTypeFlags :: from_bits ( value) . ok_or_else ( || {
151+ vm. new_value_error ( format ! (
152+ "Invalid type flags: {value:#x} for PyType_FromSlots"
153+ ) )
154+ } ) ?;
155+ }
156+ PySlotKind :: TypeSlots { mut value, .. } => {
157+ while let slot = unsafe { & * value }
158+ && slot. slot != 0
159+ {
160+ let slot_id: u8 = slot. slot . try_into ( ) . unwrap ( ) ;
161+ match slot_id. try_into ( ) . unwrap ( ) {
162+ SlotAccessor :: TpDoc => {
163+ let doc = unsafe {
164+ CStr :: from_ptr ( slot. pfunc . cast :: < c_char > ( ) )
165+ . to_str ( )
166+ . expect ( "tp_doc must be a valid UTF-8 string" )
167+ } ;
168+ type_slots. doc = Some ( doc) ;
169+ }
170+ SlotAccessor :: TpNew => {
171+ type_slots. new . store ( Some ( |ty, _args, vm| {
172+ Err ( vm. new_not_implemented_error ( format ! ( "tp_new is not yet implemented in PyType_FromSlots for {ty:?}" ) ) )
173+ } ) ) ;
174+ }
175+ SlotAccessor :: TpBase => {
176+ base = unsafe { Some ( & * slot. pfunc . cast :: < PyTypeObject > ( ) ) }
177+ }
178+ SlotAccessor :: TpDealloc => {
179+ type_slots. del . store ( Some ( |_ty, _vm| {
180+ // TODO
181+ Ok ( ( ) )
182+ } ) ) ;
183+ }
184+ SlotAccessor :: TpMethods => {
185+ let mut def_ptr = slot. pfunc . cast :: < PyMethodDef > ( ) ;
186+ while let def = unsafe { & * def_ptr }
187+ && !def. ml_name . is_null ( )
188+ {
189+ let name = unsafe {
190+ CStr :: from_ptr ( def. ml_name )
191+ . to_str ( )
192+ . expect ( "method name must be valid UTF-8" )
193+ } ;
194+ let is_static =
195+ PyMethodFlags :: from_bits_retain ( def. ml_flags as _ )
196+ . contains ( PyMethodFlags :: STATIC ) ;
197+ let method = build_method_def ( vm, def, !is_static) ?;
198+ methods. push ( ( name, method) ) ;
199+ def_ptr = unsafe { def_ptr. add ( 1 ) }
200+ }
201+ }
202+ slot => {
203+ return Err ( vm. new_not_implemented_error ( format ! (
204+ "PyType_FromSlots with PyType_Slot {slot:?} not implemented yet"
205+ ) ) ) ;
206+ }
207+ }
208+ value = unsafe { value. add ( 1 ) } ;
209+ }
210+ }
211+ PySlotKind :: TypeExtraBasicSize ( size) => {
212+ if size != 0 {
213+ return Err ( vm. new_not_implemented_error (
214+ "PyType_FromSlots with non-zero Py_tp_extra_basicsize is not supported" ,
215+ ) ) ;
216+ }
217+ }
218+ kind => {
219+ return Err ( vm. new_not_implemented_error ( format ! (
220+ "PyType_FromSlots with slot {kind:?} not implemented yet"
221+ ) ) ) ;
222+ }
223+ }
224+ }
225+
226+ let bases = if let Some ( base) = base {
227+ vec ! [ base. to_owned( ) ]
228+ } else {
229+ vec ! [ vm. ctx. types. object_type. to_owned( ) ]
230+ } ;
231+
232+ let metaclass = vm. ctx . types . type_type . to_owned ( ) ;
233+ let class = PyType :: new_heap ( name. unwrap ( ) , bases, attrs, type_slots, metaclass, & vm. ctx )
234+ . map_err ( |msg| {
235+ vm. new_system_error ( format ! ( "Failed to create type from slots: {msg}" ) )
236+ } ) ?;
237+
238+ for ( name, method) in methods {
239+ let class_static = unsafe { & * ( ( & * class) as * const _ ) } ;
240+ class. attributes . write ( ) . insert (
241+ vm. ctx . intern_str ( name) ,
242+ method. build_method ( class_static, vm) . into ( ) ,
243+ ) ;
244+ }
245+
246+ Ok ( class)
142247 } )
143248}
144249
250+ #[ unsafe( no_mangle) ]
251+ pub unsafe extern "C" fn PyObject_GetTypeData (
252+ obj : * mut PyObject ,
253+ cls : * mut PyTypeObject ,
254+ ) -> * mut c_void {
255+ if unsafe { & * cls } . slots . basicsize == 0 {
256+ obj. cast ( )
257+ } else {
258+ todo ! ( "PyObject_GetTypeData for non-zero sized types is not yet implemented" )
259+ }
260+ }
261+
145262#[ unsafe( no_mangle) ]
146263pub extern "C" fn PyType_Freeze ( _ty : * mut PyTypeObject ) -> c_int {
147- // TODO: Implement immutable type freezing semantics.
148264 0
149265}
150266
151267#[ cfg( test) ]
152268mod tests {
153269 use pyo3:: prelude:: * ;
154- use pyo3:: types:: { PyDict , PyInt , PyString , PyTypeMethods } ;
270+ use pyo3:: types:: { PyInt , PyString , PyType , PyTypeMethods } ;
155271
156272 #[ test]
157273 fn type_name ( ) {
@@ -194,22 +310,6 @@ mod tests {
194310 fn method2 ( & self , a : i32 ) -> PyResult < i32 > {
195311 Ok ( self . num + a)
196312 }
197-
198- #[ staticmethod]
199- fn static_method1 ( a : i32 , b : i32 ) -> PyResult < i32 > {
200- Ok ( a + b)
201- }
202-
203- #[ staticmethod]
204- fn static_method2 ( ) -> PyResult < i32 > {
205- Ok ( 0 )
206- }
207-
208- #[ classmethod]
209- fn cls_method ( cls : & Bound < ' _ , PyType > ) -> PyResult < i32 > {
210- assert ! ( cls. is_subclass_of:: <MyClass >( ) ?) ;
211- Ok ( 10 )
212- }
213313 }
214314
215315 Python :: attach ( |py| {
@@ -235,6 +335,40 @@ mod tests {
235335 . unwrap( ) ,
236336 8
237337 ) ;
338+ } ) ;
339+ }
340+
341+ #[ test]
342+ fn test_zero_sized_class ( ) {
343+ #[ pyclass( frozen) ]
344+ struct MyEmptyClass { }
345+
346+ #[ pymethods]
347+ impl MyEmptyClass {
348+ #[ new]
349+ fn new ( ) -> Self {
350+ MyEmptyClass { }
351+ }
352+
353+ #[ staticmethod]
354+ fn static_method1 ( a : i32 , b : i32 ) -> PyResult < i32 > {
355+ Ok ( a + b)
356+ }
357+
358+ #[ staticmethod]
359+ fn static_method2 ( ) -> PyResult < i32 > {
360+ Ok ( 0 )
361+ }
362+
363+ #[ classmethod]
364+ fn cls_method ( cls : & Bound < ' _ , PyType > ) -> PyResult < i32 > {
365+ assert ! ( cls. is_subclass_of:: <MyEmptyClass >( ) ?) ;
366+ Ok ( 10 )
367+ }
368+ }
369+
370+ Python :: attach ( |py| {
371+ let obj = Bound :: new ( py, MyEmptyClass { } ) . unwrap ( ) ;
238372
239373 assert_eq ! (
240374 obj. call_method1( "static_method1" , ( 5 , 8 ) )
0 commit comments