use crate::{
Context, Py, PyObjectRef, PyPayload, PyRef, VirtualMachine,
builtins::{
PyType,
builtin_func::{PyNativeFunction, PyNativeMethod},
descriptor::PyMethodDescriptor,
},
function::{IntoPyNativeFn, PyNativeFn},
};
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PyMethodFlags: u32 {
const VARARGS = 0x0001;
const KEYWORDS = 0x0002;
const NOARGS = 0x0004;
const O = 0x0008;
const CLASS = 0x0010;
const STATIC = 0x0020;
const FASTCALL = 0x0080;
const METHOD = 0x0200;
}
}
impl PyMethodFlags {
pub const EMPTY: Self = Self::empty();
}
#[macro_export]
macro_rules! define_methods {
($($name:literal => $func:ident as $flags:ident),+) => {
vec![ $( $crate::function::PyMethodDef {
name: $name,
func: $crate::function::static_func($func),
flags: $crate::function::PyMethodFlags::$flags,
doc: None,
}),+ ]
};
}
#[derive(Clone)]
pub struct PyMethodDef {
pub name: &'static str, pub func: &'static dyn PyNativeFn,
pub flags: PyMethodFlags,
pub doc: Option<&'static str>, }
impl PyMethodDef {
#[inline]
pub const fn new_const<Kind>(
name: &'static str,
func: impl IntoPyNativeFn<Kind>,
flags: PyMethodFlags,
doc: Option<&'static str>,
) -> Self {
Self {
name,
func: super::static_func(func),
flags,
doc,
}
}
#[inline]
pub const fn new_raw_const(
name: &'static str,
func: impl PyNativeFn,
flags: PyMethodFlags,
doc: Option<&'static str>,
) -> Self {
Self {
name,
func: super::static_raw_func(func),
flags,
doc,
}
}
pub fn to_proper_method(
&'static self,
class: &'static Py<PyType>,
ctx: &Context,
) -> PyObjectRef {
if self.flags.contains(PyMethodFlags::METHOD) {
self.build_method(ctx, class).into()
} else if self.flags.contains(PyMethodFlags::CLASS) {
self.build_classmethod(ctx, class).into()
} else if self.flags.contains(PyMethodFlags::STATIC) {
self.build_staticmethod(ctx, class).into()
} else {
unreachable!()
}
}
pub const fn to_function(&'static self) -> PyNativeFunction {
PyNativeFunction {
zelf: None,
value: self,
module: None,
_method_def_owner: None,
}
}
pub fn to_method(
&'static self,
class: &'static Py<PyType>,
ctx: &Context,
) -> PyMethodDescriptor {
PyMethodDescriptor::new(self, class, ctx)
}
pub const fn to_bound_method(
&'static self,
obj: PyObjectRef,
class: &'static Py<PyType>,
) -> PyNativeMethod {
PyNativeMethod {
func: PyNativeFunction {
zelf: Some(obj),
value: self,
module: None,
_method_def_owner: None,
},
class,
}
}
pub fn build_function(&'static self, ctx: &Context) -> PyRef<PyNativeFunction> {
self.to_function().into_ref(ctx)
}
pub fn build_bound_function(
&'static self,
ctx: &Context,
obj: PyObjectRef,
) -> PyRef<PyNativeFunction> {
let function = PyNativeFunction {
zelf: Some(obj),
value: self,
module: None,
_method_def_owner: None,
};
PyRef::new_ref(
function,
ctx.types.builtin_function_or_method_type.to_owned(),
None,
)
}
pub fn build_method(
&'static self,
ctx: &Context,
class: &'static Py<PyType>,
) -> PyRef<PyMethodDescriptor> {
debug_assert!(self.flags.contains(PyMethodFlags::METHOD));
let method = self.to_method(class, ctx);
PyRef::new_ref(method, ctx.types.method_descriptor_type.to_owned(), None)
}
pub fn build_bound_method(
&'static self,
ctx: &Context,
obj: PyObjectRef,
class: &'static Py<PyType>,
) -> PyRef<PyNativeMethod> {
PyRef::new_ref(
self.to_bound_method(obj, class),
ctx.types.builtin_function_or_method_type.to_owned(),
None,
)
}
pub fn build_classmethod(
&'static self,
ctx: &Context,
class: &'static Py<PyType>,
) -> PyRef<PyMethodDescriptor> {
PyRef::new_ref(
self.to_method(class, ctx),
ctx.types.method_descriptor_type.to_owned(),
None,
)
}
pub fn build_staticmethod(
&'static self,
ctx: &Context,
class: &'static Py<PyType>,
) -> PyRef<PyNativeMethod> {
debug_assert!(self.flags.contains(PyMethodFlags::STATIC));
let func = PyNativeFunction {
zelf: Some(class.to_owned().into()),
value: self,
module: None,
_method_def_owner: None,
};
PyNativeMethod { func, class }.into_ref(ctx)
}
#[doc(hidden)]
pub const fn __const_concat_arrays<const SUM_LEN: usize>(
method_groups: &[&[Self]],
) -> [Self; SUM_LEN] {
const NULL_METHOD: PyMethodDef = PyMethodDef {
name: "",
func: &|_, _| unreachable!(),
flags: PyMethodFlags::empty(),
doc: None,
};
let mut all_methods = [NULL_METHOD; SUM_LEN];
let mut all_idx = 0;
let mut group_idx = 0;
while group_idx < method_groups.len() {
let group = method_groups[group_idx];
let mut method_idx = 0;
while method_idx < group.len() {
all_methods[all_idx] = group[method_idx].const_copy();
method_idx += 1;
all_idx += 1;
}
group_idx += 1;
}
all_methods
}
const fn const_copy(&self) -> Self {
Self {
name: self.name,
func: self.func,
flags: self.flags,
doc: self.doc,
}
}
}
impl core::fmt::Debug for PyMethodDef {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PyMethodDef")
.field("name", &self.name)
.field(
"func",
&(unsafe {
core::mem::transmute::<&dyn PyNativeFn, [usize; 2]>(self.func)[1] as *const u8
}),
)
.field("flags", &self.flags)
.field("doc", &self.doc)
.finish()
}
}
#[pyclass(name, module = false, ctx = "method_def")]
#[derive(Debug)]
pub struct HeapMethodDef {
method: PyMethodDef,
}
impl HeapMethodDef {
pub const fn new(method: PyMethodDef) -> Self {
Self { method }
}
}
impl Py<HeapMethodDef> {
pub(crate) unsafe fn method(&self) -> &'static PyMethodDef {
unsafe { &*(&self.method as *const _) }
}
pub fn build_function(&self, vm: &VirtualMachine) -> PyRef<PyNativeFunction> {
let mut function = unsafe { self.method() }.to_function();
function._method_def_owner = Some(self.to_owned().into());
PyRef::new_ref(
function,
vm.ctx.types.builtin_function_or_method_type.to_owned(),
None,
)
}
pub fn build_method(
&self,
class: &'static Py<PyType>,
vm: &VirtualMachine,
) -> PyRef<PyMethodDescriptor> {
let mut function = unsafe { self.method() }.to_method(class, &vm.ctx);
function._method_def_owner = Some(self.to_owned().into());
PyRef::new_ref(
function,
vm.ctx.types.method_descriptor_type.to_owned(),
None,
)
}
}
#[pyclass]
impl HeapMethodDef {}