1use crate::{
2 Context, Py, PyObjectRef, PyPayload, PyRef, VirtualMachine,
3 builtins::{
4 PyType,
5 builtin_func::{PyNativeFunction, PyNativeMethod},
6 descriptor::PyMethodDescriptor,
7 },
8 function::{IntoPyNativeFn, PyNativeFn},
9};
10
11bitflags::bitflags! {
12 #[derive(Copy, Clone, Debug, PartialEq)]
14 pub struct PyMethodFlags: u32 {
15 const VARARGS = 0x0001;
16 const KEYWORDS = 0x0002;
17 const NOARGS = 0x0004;
19 const O = 0x0008;
20
21 const CLASS = 0x0010;
25 const STATIC = 0x0020;
26
27 const FASTCALL = 0x0080;
35
36 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
121 pub const fn to_function(&'static self) -> PyNativeFunction {
122 PyNativeFunction {
123 zelf: None,
124 value: self,
125 module: None,
126 _method_def_owner: None,
127 }
128 }
129
130 pub fn to_method(
131 &'static self,
132 class: &'static Py<PyType>,
133 ctx: &Context,
134 ) -> PyMethodDescriptor {
135 PyMethodDescriptor::new(self, class, ctx)
136 }
137
138 pub const fn to_bound_method(
139 &'static self,
140 obj: PyObjectRef,
141 class: &'static Py<PyType>,
142 ) -> PyNativeMethod {
143 PyNativeMethod {
144 func: PyNativeFunction {
145 zelf: Some(obj),
146 value: self,
147 module: None,
148 _method_def_owner: None,
149 },
150 class,
151 }
152 }
153
154 pub fn build_function(&'static self, ctx: &Context) -> PyRef<PyNativeFunction> {
155 self.to_function().into_ref(ctx)
156 }
157
158 pub fn build_bound_function(
159 &'static self,
160 ctx: &Context,
161 obj: PyObjectRef,
162 ) -> PyRef<PyNativeFunction> {
163 let function = PyNativeFunction {
164 zelf: Some(obj),
165 value: self,
166 module: None,
167 _method_def_owner: None,
168 };
169 PyRef::new_ref(
170 function,
171 ctx.types.builtin_function_or_method_type.to_owned(),
172 None,
173 )
174 }
175
176 pub fn build_method(
177 &'static self,
178 ctx: &Context,
179 class: &'static Py<PyType>,
180 ) -> PyRef<PyMethodDescriptor> {
181 debug_assert!(self.flags.contains(PyMethodFlags::METHOD));
182 let method = self.to_method(class, ctx);
183 PyRef::new_ref(method, ctx.types.method_descriptor_type.to_owned(), None)
184 }
185
186 pub fn build_bound_method(
187 &'static self,
188 ctx: &Context,
189 obj: PyObjectRef,
190 class: &'static Py<PyType>,
191 ) -> PyRef<PyNativeMethod> {
192 PyRef::new_ref(
193 self.to_bound_method(obj, class),
194 ctx.types.builtin_function_or_method_type.to_owned(),
195 None,
196 )
197 }
198
199 pub fn build_classmethod(
200 &'static self,
201 ctx: &Context,
202 class: &'static Py<PyType>,
203 ) -> PyRef<PyMethodDescriptor> {
204 PyRef::new_ref(
205 self.to_method(class, ctx),
206 ctx.types.method_descriptor_type.to_owned(),
207 None,
208 )
209 }
210
211 pub fn build_staticmethod(
212 &'static self,
213 ctx: &Context,
214 class: &'static Py<PyType>,
215 ) -> PyRef<PyNativeMethod> {
216 debug_assert!(self.flags.contains(PyMethodFlags::STATIC));
217 let func = PyNativeFunction {
220 zelf: Some(class.to_owned().into()),
221 value: self,
222 module: None,
223 _method_def_owner: None,
224 };
225 PyNativeMethod { func, class }.into_ref(ctx)
226 }
227
228 #[doc(hidden)]
229 pub const fn __const_concat_arrays<const SUM_LEN: usize>(
230 method_groups: &[&[Self]],
231 ) -> [Self; SUM_LEN] {
232 const NULL_METHOD: PyMethodDef = PyMethodDef {
233 name: "",
234 func: &|_, _| unreachable!(),
235 flags: PyMethodFlags::empty(),
236 doc: None,
237 };
238 let mut all_methods = [NULL_METHOD; SUM_LEN];
239 let mut all_idx = 0;
240 let mut group_idx = 0;
241 while group_idx < method_groups.len() {
242 let group = method_groups[group_idx];
243 let mut method_idx = 0;
244 while method_idx < group.len() {
245 all_methods[all_idx] = group[method_idx].const_copy();
246 method_idx += 1;
247 all_idx += 1;
248 }
249 group_idx += 1;
250 }
251 all_methods
252 }
253
254 const fn const_copy(&self) -> Self {
255 Self {
256 name: self.name,
257 func: self.func,
258 flags: self.flags,
259 doc: self.doc,
260 }
261 }
262}
263
264impl core::fmt::Debug for PyMethodDef {
265 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
266 f.debug_struct("PyMethodDef")
267 .field("name", &self.name)
268 .field(
269 "func",
270 &(unsafe {
271 core::mem::transmute::<&dyn PyNativeFn, [usize; 2]>(self.func)[1] as *const u8
272 }),
273 )
274 .field("flags", &self.flags)
275 .field("doc", &self.doc)
276 .finish()
277 }
278}
279
280#[pyclass(name, module = false, ctx = "method_def")]
283#[derive(Debug)]
284pub struct HeapMethodDef {
285 method: PyMethodDef,
286}
287
288impl HeapMethodDef {
289 pub const fn new(method: PyMethodDef) -> Self {
290 Self { method }
291 }
292}
293
294impl Py<HeapMethodDef> {
295 pub(crate) unsafe fn method(&self) -> &'static PyMethodDef {
296 unsafe { &*(&self.method as *const _) }
297 }
298
299 pub fn build_function(&self, vm: &VirtualMachine) -> PyRef<PyNativeFunction> {
300 let mut function = unsafe { self.method() }.to_function();
301 function._method_def_owner = Some(self.to_owned().into());
302 PyRef::new_ref(
303 function,
304 vm.ctx.types.builtin_function_or_method_type.to_owned(),
305 None,
306 )
307 }
308
309 pub fn build_method(
310 &self,
311 class: &'static Py<PyType>,
312 vm: &VirtualMachine,
313 ) -> PyRef<PyMethodDescriptor> {
314 let mut function = unsafe { self.method() }.to_method(class, &vm.ctx);
315 function._method_def_owner = Some(self.to_owned().into());
316 PyRef::new_ref(
317 function,
318 vm.ctx.types.method_descriptor_type.to_owned(),
319 None,
320 )
321 }
322}
323
324#[pyclass]
325impl HeapMethodDef {}