|
Expand Up
|
@@ -10,6 +10,7 @@ use crate::{ |
|
|
use alloc::fmt; |
|
|
|
|
|
// PyCFunctionObject in CPython |
|
|
#[repr(C)] |
|
|
#[pyclass(name = "builtin_function_or_method", module = false)] |
|
|
pub struct PyNativeFunction { |
|
|
pub(crate) value: &'static PyMethodDef, |
|
Expand Down
Expand Up
|
@@ -68,13 +69,65 @@ impl Callable for PyNativeFunction { |
|
|
#[inline] |
|
|
fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { |
|
|
if let Some(z) = &zelf.zelf { |
|
|
args.prepend_arg(z.clone()); |
|
|
// STATIC methods store the class in zelf for qualname/repr purposes, |
|
|
// but should not prepend it to args (the Rust function doesn't expect it). |
|
|
if !zelf.value.flags.contains(PyMethodFlags::STATIC) { |
|
|
args.prepend_arg(z.clone()); |
|
|
} |
|
|
} |
|
|
(zelf.value.func)(vm, args) |
|
|
} |
|
|
} |
|
|
|
|
|
#[pyclass(with(Callable), flags(HAS_DICT, DISALLOW_INSTANTIATION))] |
|
|
// meth_richcompare in CPython |
|
|
impl Comparable for PyNativeFunction { |
|
|
fn cmp( |
|
|
zelf: &Py<Self>, |
|
|
other: &PyObject, |
|
|
op: PyComparisonOp, |
|
|
_vm: &VirtualMachine, |
|
|
) -> PyResult<PyComparisonValue> { |
|
|
op.eq_only(|| { |
|
|
if let Some(other) = other.downcast_ref::<Self>() { |
|
|
let eq = match (zelf.zelf.as_ref(), other.zelf.as_ref()) { |
|
|
(Some(z), Some(o)) => z.is(o), |
|
|
(None, None) => true, |
|
|
_ => false, |
|
|
}; |
|
|
let eq = eq && core::ptr::eq(zelf.value, other.value); |
|
|
Ok(eq.into()) |
|
|
} else { |
|
|
Ok(PyComparisonValue::NotImplemented) |
|
|
} |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
// meth_repr in CPython |
|
|
impl Representable for PyNativeFunction { |
|
|
#[inline] |
|
|
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> { |
|
|
if let Some(bound) = zelf |
|
|
.zelf |
|
|
.as_ref() |
|
|
.filter(|b| !b.class().is(vm.ctx.types.module_type)) |
|
|
{ |
|
|
Ok(format!( |
|
|
"<built-in method {} of {} object at {:#x}>", |
|
|
zelf.value.name, |
|
|
bound.class().name(), |
|
|
bound.get_id() |
|
|
)) |
|
|
} else { |
|
|
Ok(format!("<built-in function {}>", zelf.value.name)) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
#[pyclass( |
|
|
with(Callable, Comparable, Representable), |
|
|
flags(HAS_DICT, DISALLOW_INSTANTIATION) |
|
|
)] |
|
|
impl PyNativeFunction { |
|
|
#[pygetset] |
|
|
fn __module__(zelf: NativeFunctionOrMethod) -> Option<&'static PyStrInterned> { |
|
Expand All
|
@@ -86,20 +139,19 @@ impl PyNativeFunction { |
|
|
zelf.0.value.name |
|
|
} |
|
|
|
|
|
// meth_get__qualname__ in CPython |
|
|
#[pygetset] |
|
|
fn __qualname__(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult<PyStrRef> { |
|
|
let zelf = zelf.0; |
|
|
let flags = zelf.value.flags; |
|
|
// if flags.contains(PyMethodFlags::CLASS) || flags.contains(PyMethodFlags::STATIC) { |
|
|
let qualname = if let Some(bound) = &zelf.zelf { |
|
|
let prefix = if flags.contains(PyMethodFlags::CLASS) { |
|
|
bound |
|
|
.get_attr("__qualname__", vm) |
|
|
.unwrap() |
|
|
.str(vm) |
|
|
.unwrap() |
|
|
.to_string() |
|
|
if bound.class().is(vm.ctx.types.module_type) { |
|
|
return Ok(vm.ctx.intern_str(zelf.value.name).to_owned()); |
|
|
} |
|
|
let prefix = if bound.class().is(vm.ctx.types.type_type) { |
|
|
// m_self is a type: use PyType_GetQualName(m_self) |
|
|
bound.get_attr("__qualname__", vm)?.str(vm)?.to_string() |
|
|
} else { |
|
|
// m_self is an instance: use Py_TYPE(m_self).__qualname__ |
|
|
bound.class().name().to_string() |
|
|
}; |
|
|
vm.ctx.new_str(format!("{}.{}", prefix, &zelf.value.name)) |
|
Expand All
|
@@ -114,15 +166,23 @@ impl PyNativeFunction { |
|
|
zelf.0.value.doc |
|
|
} |
|
|
|
|
|
// meth_get__self__ in CPython |
|
|
#[pygetset] |
|
|
fn __self__(_zelf: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { |
|
|
vm.ctx.none() |
|
|
fn __self__(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyObjectRef { |
|
|
zelf.0.zelf.clone().unwrap_or_else(|| vm.ctx.none()) |
|
|
} |
|
|
|
|
|
// meth_reduce in CPython |
|
|
#[pymethod] |
|
|
const fn __reduce__(&self) -> &'static str { |
|
|
// TODO: return (getattr, (self.object, self.name)) if this is a method |
|
|
self.value.name |
|
|
fn __reduce__(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult { |
|
|
let zelf = zelf.0; |
|
|
if zelf.zelf.is_none() || zelf.module.is_some() { |
|
|
Ok(vm.ctx.new_str(zelf.value.name).into()) |
|
|
} else { |
|
|
let getattr = vm.builtins.get_attr("getattr", vm)?; |
|
|
let target = zelf.zelf.clone().unwrap(); |
|
|
Ok(vm.new_tuple((getattr, (target, zelf.value.name))).into()) |
|
|
} |
|
|
} |
|
|
|
|
|
#[pymethod] |
|
Expand All
|
@@ -138,54 +198,20 @@ impl PyNativeFunction { |
|
|
} |
|
|
} |
|
|
|
|
|
impl Representable for PyNativeFunction { |
|
|
#[inline] |
|
|
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> { |
|
|
Ok(format!("<built-in function {}>", zelf.value.name)) |
|
|
} |
|
|
} |
|
|
|
|
|
// `PyCMethodObject` in CPython |
|
|
#[pyclass(name = "builtin_method", module = false, base = PyNativeFunction, ctx = "builtin_method_type")] |
|
|
// PyCMethodObject in CPython |
|
|
// repr(C) ensures `func` is at offset 0, allowing safe cast from PyNativeMethod to PyNativeFunction |
|
|
#[repr(C)] |
|
|
#[pyclass(name = "builtin_function_or_method", module = false, base = PyNativeFunction, ctx = "builtin_function_or_method_type")] |
|
|
pub struct PyNativeMethod { |
|
|
pub(crate) func: PyNativeFunction, |
|
|
pub(crate) class: &'static Py<PyType>, // TODO: the actual life is &'self |
|
|
} |
|
|
|
|
|
#[pyclass( |
|
|
with(Callable, Comparable, Representable), |
|
|
flags(HAS_DICT, DISALLOW_INSTANTIATION) |
|
|
)] |
|
|
impl PyNativeMethod { |
|
|
#[pygetset] |
|
|
fn __qualname__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> { |
|
|
let prefix = zelf.class.name().to_string(); |
|
|
Ok(vm |
|
|
.ctx |
|
|
.new_str(format!("{}.{}", prefix, &zelf.func.value.name))) |
|
|
} |
|
|
|
|
|
#[pymethod] |
|
|
fn __reduce__( |
|
|
&self, |
|
|
vm: &VirtualMachine, |
|
|
) -> PyResult<(PyObjectRef, (PyObjectRef, &'static str))> { |
|
|
// TODO: return (getattr, (self.object, self.name)) if this is a method |
|
|
let getattr = vm.builtins.get_attr("getattr", vm)?; |
|
|
let target = self |
|
|
.func |
|
|
.zelf |
|
|
.clone() |
|
|
.unwrap_or_else(|| self.class.to_owned().into()); |
|
|
let name = self.func.value.name; |
|
|
Ok((getattr, (target, name))) |
|
|
} |
|
|
|
|
|
#[pygetset] |
|
|
fn __self__(zelf: PyRef<Self>, _vm: &VirtualMachine) -> Option<PyObjectRef> { |
|
|
zelf.func.zelf.clone() |
|
|
} |
|
|
} |
|
|
// All Python-visible behavior (getters, slots) is registered by PyNativeFunction::extend_class. |
|
|
// PyNativeMethod only extends the Rust-side struct with the defining class reference. |
|
|
// The func field at offset 0 (#[repr(C)]) allows NativeFunctionOrMethod to read it safely. |
|
|
#[pyclass(flags(HAS_DICT, DISALLOW_INSTANTIATION))] |
|
|
impl PyNativeMethod {} |
|
|
|
|
|
impl fmt::Debug for PyNativeMethod { |
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
Expand All
|
@@ -198,63 +224,21 @@ impl fmt::Debug for PyNativeMethod { |
|
|
} |
|
|
} |
|
|
|
|
|
impl Comparable for PyNativeMethod { |
|
|
fn cmp( |
|
|
zelf: &Py<Self>, |
|
|
other: &PyObject, |
|
|
op: PyComparisonOp, |
|
|
_vm: &VirtualMachine, |
|
|
) -> PyResult<PyComparisonValue> { |
|
|
op.eq_only(|| { |
|
|
if let Some(other) = other.downcast_ref::<Self>() { |
|
|
let eq = match (zelf.func.zelf.as_ref(), other.func.zelf.as_ref()) { |
|
|
(Some(z), Some(o)) => z.is(o), |
|
|
(None, None) => true, |
|
|
_ => false, |
|
|
}; |
|
|
let eq = eq && core::ptr::eq(zelf.func.value, other.func.value); |
|
|
Ok(eq.into()) |
|
|
} else { |
|
|
Ok(PyComparisonValue::NotImplemented) |
|
|
} |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
impl Callable for PyNativeMethod { |
|
|
type Args = FuncArgs; |
|
|
|
|
|
#[inline] |
|
|
fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { |
|
|
if let Some(zelf) = &zelf.func.zelf { |
|
|
args.prepend_arg(zelf.clone()); |
|
|
} |
|
|
(zelf.func.value.func)(vm, args) |
|
|
} |
|
|
} |
|
|
|
|
|
impl Representable for PyNativeMethod { |
|
|
#[inline] |
|
|
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> { |
|
|
Ok(format!( |
|
|
"<built-in method {} of {} object at ...>", |
|
|
&zelf.func.value.name, |
|
|
zelf.class.name() |
|
|
)) |
|
|
} |
|
|
} |
|
|
|
|
|
pub fn init(context: &Context) { |
|
|
PyNativeFunction::extend_class(context, context.types.builtin_function_or_method_type); |
|
|
PyNativeMethod::extend_class(context, context.types.builtin_method_type); |
|
|
} |
|
|
|
|
|
/// Wrapper that provides access to the common PyNativeFunction data |
|
|
/// for both PyNativeFunction and PyNativeMethod (which has func as its first field). |
|
|
struct NativeFunctionOrMethod(PyRef<PyNativeFunction>); |
|
|
|
|
|
impl TryFromObject for NativeFunctionOrMethod { |
|
|
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> { |
|
|
let class = vm.ctx.types.builtin_function_or_method_type; |
|
|
if obj.fast_isinstance(class) { |
|
|
// Both PyNativeFunction and PyNativeMethod share the same type now. |
|
|
// PyNativeMethod has `func: PyNativeFunction` as its first field, |
|
|
// so we can safely treat the data pointer as PyNativeFunction for reading. |
|
|
Ok(Self(unsafe { obj.downcast_unchecked() })) |
|
|
} else { |
|
|
Err(vm.new_downcast_type_error(class, &obj)) |
|
Expand Down
|
|