1use crate::{
2 Context, Py, PyObjectRef, PyPayload, PyRef, VirtualMachine,
3 builtins::{
4 PyType,
5 builtin_func::{PyNativeFunction, PyNativeMethod},
6 descriptor::{PyClassMethodDescriptor, PyMethodDescriptor},
7 },
8 class::PyClassDef,
9 function::{IntoPyNativeFn, PyNativeFn},
10};
11
12bitflags::bitflags! {
13 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
15 pub struct PyMethodFlags: u32 {
16 const VARARGS = 0x0001;
17 const KEYWORDS = 0x0002;
18 const NOARGS = 0x0004;
20 const O = 0x0008;
21
22 const CLASS = 0x0010;
26 const STATIC = 0x0020;
27
28 const COEXIST = 0x0040;
33
34 const FASTCALL = 0x0080;
36
37 const METHOD = 0x0200;
46 }
47}
48
49impl PyMethodFlags {
50 pub const EMPTY: Self = Self::empty();
52}
53
54#[macro_export]
55macro_rules! define_methods {
56 ($($name:literal => $func:ident as $flags:ident),+) => {
58 vec![ $( $crate::function::PyMethodDef {
59 name: $name,
60 func: $crate::function::static_func($func),
61 flags: $crate::function::PyMethodFlags::$flags,
62 #[cfg(feature = "doc")]
63 doc_off: 0,
64 #[cfg(feature = "doc")]
65 doc_len: 0,
66 #[cfg(feature = "doc")]
67 doc_body_pending: false,
68 doc: None,
69 }),+ ]
70 };
71}
72
73#[derive(Clone)]
74pub struct PyMethodDef {
75 pub name: &'static str, pub func: &'static dyn PyNativeFn,
77 pub flags: PyMethodFlags,
78 #[cfg(feature = "doc")]
81 pub doc_off: u32,
82 #[cfg(feature = "doc")]
85 pub doc_len: u32,
86 #[cfg(feature = "doc")]
93 pub doc_body_pending: bool,
94 pub doc: Option<&'static str>,
99}
100
101impl PyMethodDef {
102 #[must_use]
103 pub fn item_doc(&self) -> super::ItemDoc {
104 super::ItemDoc {
105 text: self.doc,
106 #[cfg(feature = "doc")]
107 offset: self.doc_off,
108 #[cfg(feature = "doc")]
109 len: self.doc_len,
110 #[cfg(not(feature = "doc"))]
111 offset: 0,
112 #[cfg(not(feature = "doc"))]
113 len: 0,
114 }
115 }
116
117 #[inline]
118 pub const fn new_const<Kind>(
119 name: &'static str,
120 func: impl IntoPyNativeFn<Kind>,
121 flags: PyMethodFlags,
122 doc: super::ItemDoc,
123 ) -> Self {
124 Self {
125 name,
126 func: super::static_func(func),
127 flags,
128 #[cfg(feature = "doc")]
129 doc_off: doc.offset,
130 #[cfg(feature = "doc")]
131 doc_len: doc.len,
132 #[cfg(feature = "doc")]
133 doc_body_pending: false,
134 doc: doc.text,
135 }
136 }
137
138 #[inline]
139 pub const fn new_raw_const(
140 name: &'static str,
141 func: impl PyNativeFn,
142 flags: PyMethodFlags,
143 doc: super::ItemDoc,
144 ) -> Self {
145 Self {
146 name,
147 func: super::static_raw_func(func),
148 flags,
149 #[cfg(feature = "doc")]
150 doc_off: doc.offset,
151 #[cfg(feature = "doc")]
152 doc_len: doc.len,
153 #[cfg(feature = "doc")]
154 doc_body_pending: false,
155 doc: doc.text,
156 }
157 }
158
159 pub fn to_proper_method(
160 &'static self,
161 class: &'static Py<PyType>,
162 ctx: &Context,
163 ) -> PyObjectRef {
164 if self.flags.contains(PyMethodFlags::METHOD) {
165 self.build_method(ctx, class).into()
166 } else if self.flags.contains(PyMethodFlags::CLASS) {
167 self.build_classmethod(ctx, class).into()
168 } else if self.flags.contains(PyMethodFlags::STATIC) {
169 self.build_staticmethod(ctx, class).into()
170 } else {
171 unreachable!()
172 }
173 }
174
175 #[must_use]
176 pub const fn to_function(&'static self) -> PyNativeFunction {
177 PyNativeFunction {
178 zelf: None,
179 value: self,
180 module_object: None,
181 module: crate::object::PyAtomicRef::new_empty(),
182 _method_def_owner: None,
183 }
184 }
185
186 pub fn to_method(
187 &'static self,
188 class: &'static Py<PyType>,
189 ctx: &Context,
190 ) -> PyMethodDescriptor {
191 PyMethodDescriptor::new(self, class, ctx)
192 }
193
194 pub const fn to_bound_method(
195 &'static self,
196 obj: PyObjectRef,
197 class: &'static Py<PyType>,
198 ) -> PyNativeMethod {
199 PyNativeMethod {
200 func: PyNativeFunction {
201 zelf: Some(obj),
202 value: self,
203 module_object: None,
204 module: crate::object::PyAtomicRef::new_empty(),
205 _method_def_owner: None,
206 },
207 class,
208 }
209 }
210
211 pub fn build_function(&'static self, ctx: &Context) -> PyRef<PyNativeFunction> {
212 self.to_function().into_ref(ctx)
213 }
214
215 pub fn build_bound_function(
216 &'static self,
217 ctx: &Context,
218 obj: PyObjectRef,
219 ) -> PyRef<PyNativeFunction> {
220 let function = PyNativeFunction {
221 zelf: Some(obj),
222 value: self,
223 module_object: None,
224 module: crate::object::PyAtomicRef::new_empty(),
225 _method_def_owner: None,
226 };
227 PyRef::new_ref(
228 function,
229 ctx.types.builtin_function_or_method_type.to_owned(),
230 None,
231 )
232 }
233
234 pub fn build_method(
235 &'static self,
236 ctx: &Context,
237 class: &'static Py<PyType>,
238 ) -> PyRef<PyMethodDescriptor> {
239 debug_assert!(self.flags.contains(PyMethodFlags::METHOD));
240 let method = self.to_method(class, ctx);
241 PyRef::new_ref(method, ctx.types.method_descriptor_type.to_owned(), None)
242 }
243
244 pub fn build_bound_method(
245 &'static self,
246 ctx: &Context,
247 obj: PyObjectRef,
248 class: &'static Py<PyType>,
249 ) -> PyRef<PyNativeMethod> {
250 PyRef::new_ref(
251 self.to_bound_method(obj, class),
252 ctx.types.builtin_function_or_method_type.to_owned(),
253 None,
254 )
255 }
256
257 pub fn build_classmethod(
258 &'static self,
259 ctx: &Context,
260 class: &'static Py<PyType>,
261 ) -> PyRef<PyClassMethodDescriptor> {
262 debug_assert!(self.flags.contains(PyMethodFlags::CLASS));
263 PyClassMethodDescriptor::new(self, class, ctx).into_ref(ctx)
264 }
265
266 pub fn build_staticmethod(
267 &'static self,
268 ctx: &Context,
269 class: &'static Py<PyType>,
270 ) -> PyRef<PyNativeMethod> {
271 debug_assert!(self.flags.contains(PyMethodFlags::STATIC));
272 let func = PyNativeFunction {
275 zelf: Some(class.to_owned().into()),
276 value: self,
277 module_object: None,
278 module: crate::object::PyAtomicRef::new_empty(),
279 _method_def_owner: None,
280 };
281 PyNativeMethod { func, class }.into_ref(ctx)
282 }
283
284 #[cfg(feature = "doc")]
286 #[must_use]
287 pub const fn concat_with_attr_docs<const N: usize>(
288 method_groups: &[&[Self]],
289 docs: &[(&str, u32, u32)],
290 ) -> [Self; N] {
291 let combined = Self::__const_concat_arrays::<N>(method_groups);
292 let mut i = 0;
293 let mut out = combined;
294 while i < N {
295 if out[i].doc_body_pending {
296 if let Some((offset, len)) = crate::class::attr_doc(docs, out[i].name)
297 && len != 0
298 {
299 out[i].doc_off = offset;
300 out[i].doc_len = len;
301 }
302 out[i].doc_body_pending = false;
303 }
304 i += 1;
305 }
306 out
307 }
308
309 #[doc(hidden)]
310 #[must_use]
311 pub const fn __const_concat_arrays<const SUM_LEN: usize>(
312 method_groups: &[&[Self]],
313 ) -> [Self; SUM_LEN] {
314 const NULL_METHOD: PyMethodDef = PyMethodDef {
315 name: "",
316 func: &|_, _, _| unreachable!(),
317 flags: PyMethodFlags::empty(),
318 #[cfg(feature = "doc")]
319 doc_off: 0,
320 #[cfg(feature = "doc")]
321 doc_len: 0,
322 #[cfg(feature = "doc")]
323 doc_body_pending: false,
324 doc: None,
325 };
326 let mut all_methods = [NULL_METHOD; SUM_LEN];
327 let mut all_idx = 0;
328 let mut group_idx = 0;
329 while group_idx < method_groups.len() {
330 let group = method_groups[group_idx];
331 let mut method_idx = 0;
332 while method_idx < group.len() {
333 all_methods[all_idx] = group[method_idx].const_copy();
334 method_idx += 1;
335 all_idx += 1;
336 }
337 group_idx += 1;
338 }
339 all_methods
340 }
341
342 #[must_use]
343 const fn const_copy(&self) -> Self {
344 Self {
345 name: self.name,
346 func: self.func,
347 flags: self.flags,
348 #[cfg(feature = "doc")]
349 doc_off: self.doc_off,
350 #[cfg(feature = "doc")]
351 doc_len: self.doc_len,
352 #[cfg(feature = "doc")]
353 doc_body_pending: self.doc_body_pending,
354 doc: self.doc,
355 }
356 }
357}
358
359impl core::fmt::Debug for PyMethodDef {
360 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
361 f.debug_struct("PyMethodDef")
362 .field("name", &self.name)
363 .field(
364 "func",
365 &(unsafe {
366 core::mem::transmute::<&dyn PyNativeFn, [usize; 2]>(self.func)[1] as *const u8
367 }),
368 )
369 .field("flags", &self.flags)
370 .field("doc", &self.doc)
371 .finish()
372 }
373}
374
375#[pyclass(name, module = false, ctx = "method_def")]
378#[derive(Debug)]
379pub struct HeapMethodDef {
380 method: PyMethodDef,
381}
382
383impl HeapMethodDef {
384 #[must_use]
385 pub const fn new(method: PyMethodDef) -> Self {
386 Self { method }
387 }
388}
389
390impl Py<HeapMethodDef> {
391 pub(crate) unsafe fn method(&self) -> &'static PyMethodDef {
392 unsafe { &*(&self.method as *const _) }
393 }
394
395 pub fn build_function(
396 &self,
397 vm: &VirtualMachine,
398 zelf: Option<PyObjectRef>,
399 ) -> PyRef<PyNativeFunction> {
400 let mut function = unsafe { self.method() }.to_function();
401 function._method_def_owner = Some(self.to_owned().into());
402 function.zelf = zelf;
403 PyRef::new_ref(
404 function,
405 vm.ctx.types.builtin_function_or_method_type.to_owned(),
406 None,
407 )
408 }
409
410 pub fn build_method(
411 &self,
412 class: &'static Py<PyType>,
413 vm: &VirtualMachine,
414 ) -> PyRef<PyMethodDescriptor> {
415 let mut function = unsafe { self.method() }.to_method(class, &vm.ctx);
416 function._method_def_owner = Some(self.to_owned().into());
417 PyRef::new_ref(
418 function,
419 vm.ctx.types.method_descriptor_type.to_owned(),
420 None,
421 )
422 }
423}
424
425#[pyclass]
426impl HeapMethodDef {}
427
428pub(crate) fn init(ctx: &'static Context) {
429 let _ = ctx.intern_str(HeapMethodDef::NAME);
433}