rustpython_vm/function/
method.rs

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    // METH_XXX flags in CPython
13    #[derive(Copy, Clone, Debug, PartialEq)]
14    pub struct PyMethodFlags: u32 {
15        // const VARARGS = 0x0001;
16        // const KEYWORDS = 0x0002;
17        // METH_NOARGS and METH_O must not be combined with the flags above.
18        // const NOARGS = 0x0004;
19        // const O = 0x0008;
20
21        // METH_CLASS and METH_STATIC are a little different; these control
22        // the construction of methods for a class.  These cannot be used for
23        // functions in modules.
24        const CLASS = 0x0010;
25        const STATIC = 0x0020;
26
27        // METH_COEXIST allows a method to be entered even though a slot has
28        // already filled the entry.  When defined, the flag allows a separate
29        // method, "__contains__" for example, to coexist with a defined
30        // slot like sq_contains.
31        // const COEXIST = 0x0040;
32
33        // if not Py_LIMITED_API
34        // const FASTCALL = 0x0080;
35
36        // This bit is preserved for Stackless Python
37        // const STACKLESS = 0x0100;
38
39        // METH_METHOD means the function stores an
40        // additional reference to the class that defines it;
41        // both self and class are passed to it.
42        // It uses PyCMethodObject instead of PyCFunctionObject.
43        // May not be combined with METH_NOARGS, METH_O, METH_CLASS or METH_STATIC.
44        const METHOD = 0x0200;
45    }
46}
47
48impl PyMethodFlags {
49    // FIXME: macro temp
50    pub const EMPTY: Self = Self::empty();
51}
52
53#[macro_export]
54macro_rules! define_methods {
55    // TODO: more flexible syntax
56    ($($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, // TODO: interned
69    pub func: &'static dyn PyNativeFn,
70    pub flags: PyMethodFlags,
71    pub doc: Option<&'static str>, // TODO: interned
72}
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// This is not a part of CPython API.
262// But useful to support dynamically generated methods
263#[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 {}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.