1use crate::{
2 builtins::{
3 builtin_func::{PyNativeFunction, PyNativeMethod},
4 descriptor::PyMethodDescriptor,
5 PyType,
6 },
7 function::{IntoPyNativeFn, PyNativeFn},
8 Context, Py, PyObjectRef, PyPayload, PyRef, VirtualMachine,
9};
10
11bitflags::bitflags! {
12 #[derive(Copy, Clone, Debug, PartialEq)]
14 pub struct PyMethodFlags: u32 {
15 const CLASS = 0x0010;
25 const STATIC = 0x0020;
26
27 const METHOD = 0x0200;
45 }
46}
47
48impl PyMethodFlags {
49 pub const EMPTY: Self = Self::empty();
51}
52
53#[macro_export]
54macro_rules! define_methods {
55 ($($name:literal => $func:ident as $flags:ident),+) => {
57 vec![ $( $crate::function::PyMethodDef {
58 name: $name,
59 func: $crate::function::static_func($func),
60 flags: $crate::function::PyMethodFlags::$flags,
61 doc: None,
62 }),+ ]
63 };
64}
65
66#[derive(Clone)]
67pub struct PyMethodDef {
68 pub name: &'static str, pub func: &'static dyn PyNativeFn,
70 pub flags: PyMethodFlags,
71 pub doc: Option<&'static str>, }
73
74impl PyMethodDef {
75 #[inline]
76 pub const fn new_const<Kind>(
77 name: &'static str,
78 func: impl IntoPyNativeFn<Kind>,
79 flags: PyMethodFlags,
80 doc: Option<&'static str>,
81 ) -> Self {
82 Self {
83 name,
84 func: super::static_func(func),
85 flags,
86 doc,
87 }
88 }
89
90 #[inline]
91 pub const fn new_raw_const(
92 name: &'static str,
93 func: impl PyNativeFn,
94 flags: PyMethodFlags,
95 doc: Option<&'static str>,
96 ) -> Self {
97 Self {
98 name,
99 func: super::static_raw_func(func),
100 flags,
101 doc,
102 }
103 }
104
105 pub fn to_proper_method(
106 &'static self,
107 class: &'static Py<PyType>,
108 ctx: &Context,
109 ) -> PyObjectRef {
110 if self.flags.contains(PyMethodFlags::METHOD) {
111 self.build_method(ctx, class).into()
112 } else if self.flags.contains(PyMethodFlags::CLASS) {
113 self.build_classmethod(ctx, class).into()
114 } else if self.flags.contains(PyMethodFlags::STATIC) {
115 self.build_staticmethod(ctx, class).into()
116 } else {
117 unreachable!();
118 }
119 }
120 pub fn to_function(&'static self) -> PyNativeFunction {
121 PyNativeFunction {
122 zelf: None,
123 value: self,
124 module: None,
125 }
126 }
127 pub fn to_method(
128 &'static self,
129 class: &'static Py<PyType>,
130 ctx: &Context,
131 ) -> PyMethodDescriptor {
132 PyMethodDescriptor::new(self, class, ctx)
133 }
134 pub fn to_bound_method(
135 &'static self,
136 obj: PyObjectRef,
137 class: &'static Py<PyType>,
138 ) -> PyNativeMethod {
139 PyNativeMethod {
140 func: PyNativeFunction {
141 zelf: Some(obj),
142 value: self,
143 module: None,
144 },
145 class,
146 }
147 }
148 pub fn build_function(&'static self, ctx: &Context) -> PyRef<PyNativeFunction> {
149 self.to_function().into_ref(ctx)
150 }
151 pub fn build_bound_function(
152 &'static self,
153 ctx: &Context,
154 obj: PyObjectRef,
155 ) -> PyRef<PyNativeFunction> {
156 let function = PyNativeFunction {
157 zelf: Some(obj),
158 value: self,
159 module: None,
160 };
161 PyRef::new_ref(
162 function,
163 ctx.types.builtin_function_or_method_type.to_owned(),
164 None,
165 )
166 }
167 pub fn build_method(
168 &'static self,
169 ctx: &Context,
170 class: &'static Py<PyType>,
171 ) -> PyRef<PyMethodDescriptor> {
172 debug_assert!(self.flags.contains(PyMethodFlags::METHOD));
173 let method = self.to_method(class, ctx);
174 PyRef::new_ref(method, ctx.types.method_descriptor_type.to_owned(), None)
175 }
176 pub fn build_bound_method(
177 &'static self,
178 ctx: &Context,
179 obj: PyObjectRef,
180 class: &'static Py<PyType>,
181 ) -> PyRef<PyNativeMethod> {
182 PyRef::new_ref(
183 self.to_bound_method(obj, class),
184 ctx.types.builtin_method_type.to_owned(),
185 None,
186 )
187 }
188 pub fn build_classmethod(
189 &'static self,
190 ctx: &Context,
191 class: &'static Py<PyType>,
192 ) -> PyRef<PyMethodDescriptor> {
193 PyRef::new_ref(
194 self.to_method(class, ctx),
195 ctx.types.method_descriptor_type.to_owned(),
196 None,
197 )
198 }
199 pub fn build_staticmethod(
200 &'static self,
201 ctx: &Context,
202 class: &'static Py<PyType>,
203 ) -> PyRef<PyNativeMethod> {
204 debug_assert!(self.flags.contains(PyMethodFlags::STATIC));
205 let func = self.to_function();
206 PyNativeMethod { func, class }.into_ref(ctx)
207 }
208
209 #[doc(hidden)]
210 pub const fn __const_concat_arrays<const SUM_LEN: usize>(
211 method_groups: &[&[Self]],
212 ) -> [Self; SUM_LEN] {
213 const NULL_METHOD: PyMethodDef = PyMethodDef {
214 name: "",
215 func: &|_, _| unreachable!(),
216 flags: PyMethodFlags::empty(),
217 doc: None,
218 };
219 let mut all_methods = [NULL_METHOD; SUM_LEN];
220 let mut all_idx = 0;
221 let mut group_idx = 0;
222 while group_idx < method_groups.len() {
223 let group = method_groups[group_idx];
224 let mut method_idx = 0;
225 while method_idx < group.len() {
226 all_methods[all_idx] = group[method_idx].const_copy();
227 method_idx += 1;
228 all_idx += 1;
229 }
230 group_idx += 1;
231 }
232 all_methods
233 }
234
235 const fn const_copy(&self) -> Self {
236 Self {
237 name: self.name,
238 func: self.func,
239 flags: self.flags,
240 doc: self.doc,
241 }
242 }
243}
244
245impl std::fmt::Debug for PyMethodDef {
246 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247 f.debug_struct("PyMethodDef")
248 .field("name", &self.name)
249 .field(
250 "func",
251 &(unsafe {
252 std::mem::transmute::<&dyn PyNativeFn, [usize; 2]>(self.func)[1] as *const u8
253 }),
254 )
255 .field("flags", &self.flags)
256 .field("doc", &self.doc)
257 .finish()
258 }
259}
260
261#[pyclass(name, module = false, ctx = "method_def")]
264#[derive(Debug)]
265pub struct HeapMethodDef {
266 method: PyMethodDef,
267}
268
269impl HeapMethodDef {
270 pub fn new(method: PyMethodDef) -> Self {
271 Self { method }
272 }
273}
274
275impl Py<HeapMethodDef> {
276 pub(crate) unsafe fn method(&self) -> &'static PyMethodDef {
277 &*(&self.method as *const _)
278 }
279
280 pub fn build_function(&self, vm: &VirtualMachine) -> PyRef<PyNativeFunction> {
281 let function = unsafe { self.method() }.to_function();
282 let dict = vm.ctx.new_dict();
283 dict.set_item("__method_def__", self.to_owned().into(), vm)
284 .unwrap();
285 PyRef::new_ref(
286 function,
287 vm.ctx.types.builtin_function_or_method_type.to_owned(),
288 Some(dict),
289 )
290 }
291
292 pub fn build_method(
293 &self,
294 class: &'static Py<PyType>,
295 vm: &VirtualMachine,
296 ) -> PyRef<PyMethodDescriptor> {
297 let function = unsafe { self.method() }.to_method(class, &vm.ctx);
298 let dict = vm.ctx.new_dict();
299 dict.set_item("__method_def__", self.to_owned().into(), vm)
300 .unwrap();
301 PyRef::new_ref(
302 function,
303 vm.ctx.types.method_descriptor_type.to_owned(),
304 Some(dict),
305 )
306 }
307}
308
309#[pyclass]
310impl HeapMethodDef {}