|
1 | 1 | use crate::{PyObject, with_vm}; |
2 | 2 | use core::ffi::{CStr, c_char, c_int, c_uint, c_ulong, c_void}; |
3 | 3 | use core::ptr::NonNull; |
4 | | -use rustpython_vm::builtins::{PyStr, PyType}; |
5 | | -use rustpython_vm::{AsObject, Context, Py}; |
| 4 | +use rustpython_vm::builtins::{PyDict, PyStr, PyTuple, PyType}; |
| 5 | +use rustpython_vm::function::FuncArgs; |
| 6 | +use rustpython_vm::types::{PyTypeFlags, PyTypeSlots, SlotAccessor}; |
| 7 | +use rustpython_vm::{AsObject, Context, Py, PyObjectRef}; |
6 | 8 |
|
7 | 9 | const PY_TPFLAGS_LONG_SUBCLASS: c_ulong = 1 << 24; |
8 | 10 | const PY_TPFLAGS_LIST_SUBCLASS: c_ulong = 1 << 25; |
@@ -105,6 +107,168 @@ pub extern "C" fn PyType_IsSubtype(a: *const PyTypeObject, b: *const PyTypeObjec |
105 | 107 | }) |
106 | 108 | } |
107 | 109 |
|
| 110 | +#[unsafe(no_mangle)] |
| 111 | +pub extern "C" fn PyType_GetSlot(ty: *const PyTypeObject, slot: c_int) -> *mut c_void { |
| 112 | + with_vm(|_vm| -> Option<*mut c_void> { |
| 113 | + let ty = unsafe { &*ty }; |
| 114 | + let slot: u8 = slot |
| 115 | + .try_into() |
| 116 | + .expect("slot number out of range for SlotAccessor"); |
| 117 | + let slot_accessor: SlotAccessor = slot |
| 118 | + .try_into() |
| 119 | + .expect("invalid slot number for SlotAccessor"); |
| 120 | + |
| 121 | + match slot_accessor { |
| 122 | + SlotAccessor::TpNew => { |
| 123 | + extern "C" fn newfunc_wrapper( |
| 124 | + subtype: *mut PyTypeObject, |
| 125 | + args: *mut PyObject, |
| 126 | + kwargs: *mut PyObject, |
| 127 | + ) -> *mut PyObject { |
| 128 | + with_vm(|vm| { |
| 129 | + let subtype = unsafe { &*subtype }; |
| 130 | + let mut func_args = FuncArgs::default(); |
| 131 | + |
| 132 | + if let Some(args_obj) = unsafe { args.as_ref() } { |
| 133 | + let tuple = args_obj.try_downcast_ref::<PyTuple>(vm)?; |
| 134 | + func_args |
| 135 | + .args |
| 136 | + .extend(tuple.iter().map(|arg| arg.to_owned())); |
| 137 | + } |
| 138 | + |
| 139 | + if let Some(kwargs_obj) = unsafe { kwargs.as_ref() } { |
| 140 | + let kwargs = kwargs_obj.try_downcast_ref::<PyDict>(vm)?; |
| 141 | + for (key, value) in kwargs.items_vec() { |
| 142 | + let key = key.try_downcast::<PyStr>(vm)?; |
| 143 | + func_args |
| 144 | + .kwargs |
| 145 | + .insert(key.to_string_lossy().into_owned(), value); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + subtype |
| 150 | + .slots |
| 151 | + .new |
| 152 | + .load() |
| 153 | + .expect("tp_new slot function pointer is null")( |
| 154 | + subtype.to_owned(), |
| 155 | + func_args, |
| 156 | + vm, |
| 157 | + ) |
| 158 | + }) |
| 159 | + } |
| 160 | + |
| 161 | + if let Some(vtable) = ty.get_type_data::<TypeVTable>() { |
| 162 | + vtable.new_func.map(|newfunc| newfunc as *mut c_void) |
| 163 | + } else { |
| 164 | + ty.slots.new.load().map(|_| newfunc_wrapper as *mut c_void) |
| 165 | + } |
| 166 | + } |
| 167 | + _ => { |
| 168 | + todo!("Slot {slot_accessor:?} for {ty:?} is not yet implemented in PyType_GetSlot") |
| 169 | + } |
| 170 | + } |
| 171 | + }) |
| 172 | +} |
| 173 | + |
| 174 | +#[repr(C)] |
| 175 | +pub struct PyType_Slot { |
| 176 | + slot: c_int, |
| 177 | + pfunc: *mut c_void, |
| 178 | +} |
| 179 | + |
| 180 | +#[repr(C)] |
| 181 | +pub struct PyType_Spec { |
| 182 | + name: *const c_char, |
| 183 | + basicsize: c_int, |
| 184 | + itemsize: c_int, |
| 185 | + flags: c_uint, |
| 186 | + slots: *mut PyType_Slot, |
| 187 | +} |
| 188 | + |
| 189 | +#[derive(Default)] |
| 190 | +struct TypeVTable { |
| 191 | + new_func: Option<newfunc>, |
| 192 | +} |
| 193 | + |
| 194 | +type newfunc = unsafe extern "C" fn( |
| 195 | + ty: *mut PyTypeObject, |
| 196 | + args: *mut PyObject, |
| 197 | + kwargs: *mut PyObject, |
| 198 | +) -> *mut PyObject; |
| 199 | + |
| 200 | +#[unsafe(no_mangle)] |
| 201 | +pub extern "C" fn PyType_FromSpec(spec: *mut PyType_Spec) -> *mut PyObject { |
| 202 | + with_vm(|vm| { |
| 203 | + let spec = unsafe { &*spec }; |
| 204 | + let name = unsafe { |
| 205 | + CStr::from_ptr(spec.name) |
| 206 | + .to_str() |
| 207 | + .expect("type name must be valid UTF-8") |
| 208 | + }; |
| 209 | + let mut base = vm.ctx.types.object_type; |
| 210 | + let mut slots = PyTypeSlots::heap_default(); |
| 211 | + |
| 212 | + slots.basicsize = spec.basicsize as _; |
| 213 | + slots.itemsize = spec.itemsize as _; |
| 214 | + slots.flags = PyTypeFlags::from_bits(spec.flags as u64).expect("invalid flags value"); |
| 215 | + |
| 216 | + let mut vtable = TypeVTable::default(); |
| 217 | + let mut slot_ptr = spec.slots; |
| 218 | + while let slot = unsafe { &*slot_ptr } |
| 219 | + && slot.slot != 0 |
| 220 | + { |
| 221 | + let accessor = SlotAccessor::try_from(slot.slot as u8) |
| 222 | + .expect("invalid slot number in PyType_Spec"); |
| 223 | + |
| 224 | + match accessor { |
| 225 | + SlotAccessor::TpDealloc => { |
| 226 | + slots.del.store(Some(|ty, _vm| { |
| 227 | + todo!("tp_dealloc is not yet implemented in PyType_FromSpec for {ty:?}") |
| 228 | + })); |
| 229 | + } |
| 230 | + SlotAccessor::TpBase => base = unsafe { &*slot.pfunc.cast::<PyTypeObject>() }, |
| 231 | + SlotAccessor::TpGetset => {} |
| 232 | + SlotAccessor::TpMethods => {} |
| 233 | + SlotAccessor::TpNew => { |
| 234 | + vtable.new_func = Some(unsafe { core::mem::transmute(slot.pfunc) }); |
| 235 | + slots.new.store(Some(|ty, args, vm| { |
| 236 | + let new_func = ty.get_type_data::<TypeVTable>().unwrap().new_func.unwrap(); |
| 237 | + let kwargs = vm.ctx.new_dict(); |
| 238 | + for (name, value) in &args.kwargs { |
| 239 | + kwargs.set_item(&*vm.ctx.new_str(name.clone()), value.clone(), vm)?; |
| 240 | + } |
| 241 | + let args = vm.ctx.new_tuple(args.args); |
| 242 | + let result = unsafe { |
| 243 | + new_func( |
| 244 | + (&*ty) as *const _ as *mut _, |
| 245 | + args.as_object().as_raw().cast_mut(), |
| 246 | + kwargs.as_object().as_raw().cast_mut(), |
| 247 | + ) |
| 248 | + }; |
| 249 | + |
| 250 | + unsafe { Ok(PyObjectRef::from_raw(NonNull::new(result).unwrap())) } |
| 251 | + })); |
| 252 | + } |
| 253 | + SlotAccessor::TpDoc => {} |
| 254 | + _ => todo!("Slot {accessor:?} is not yet supported in PyType_FromSpec"), |
| 255 | + } |
| 256 | + |
| 257 | + slot_ptr = unsafe { slot_ptr.add(1) }; |
| 258 | + } |
| 259 | + |
| 260 | + let class = vm.ctx.new_class(None, name, base.to_owned(), slots); |
| 261 | + class.init_type_data(vtable).unwrap(); |
| 262 | + class |
| 263 | + }) |
| 264 | +} |
| 265 | + |
| 266 | +#[unsafe(no_mangle)] |
| 267 | +pub extern "C" fn PyType_Freeze(_ty: *mut PyTypeObject) -> c_int { |
| 268 | + // TODO: Implement immutable type freezing semantics. |
| 269 | + 0 |
| 270 | +} |
| 271 | + |
108 | 272 | #[unsafe(no_mangle)] |
109 | 273 | pub extern "C" fn PyObject_GetAttr(obj: *mut PyObject, name: *mut PyObject) -> *mut PyObject { |
110 | 274 | with_vm(|vm| { |
@@ -323,4 +487,29 @@ mod tests { |
323 | 487 | assert!(dict.get_item("foo").is_ok()); |
324 | 488 | }) |
325 | 489 | } |
| 490 | + |
| 491 | + #[test] |
| 492 | + fn test_rust_class() { |
| 493 | + #[pyclass] |
| 494 | + struct MyClass { |
| 495 | + #[pyo3(get)] |
| 496 | + num: i32, |
| 497 | + } |
| 498 | + |
| 499 | + #[pymethods] |
| 500 | + impl MyClass { |
| 501 | + #[new] |
| 502 | + fn new(value: i32) -> Self { |
| 503 | + MyClass { num: value } |
| 504 | + } |
| 505 | + |
| 506 | + fn method1(&self) -> PyResult<i32> { |
| 507 | + Ok(10) |
| 508 | + } |
| 509 | + } |
| 510 | + |
| 511 | + Python::attach(|py| { |
| 512 | + let obj = Bound::new(py, MyClass { num: 3 }).unwrap(); |
| 513 | + }); |
| 514 | + } |
326 | 515 | } |
0 commit comments