Skip to content

Commit 592821c

Browse files
committed
function_or_method
1 parent 343e07c commit 592821c

2 files changed

Lines changed: 88 additions & 80 deletions

File tree

crates/vm/src/builtins/builtin_func.rs

Lines changed: 81 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,65 @@ impl Callable for PyNativeFunction {
6969
#[inline]
7070
fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
7171
if let Some(z) = &zelf.zelf {
72-
args.prepend_arg(z.clone());
72+
// STATIC methods store the class in zelf for qualname/repr purposes,
73+
// but should not prepend it to args (the Rust function doesn't expect it).
74+
if !zelf.value.flags.contains(PyMethodFlags::STATIC) {
75+
args.prepend_arg(z.clone());
76+
}
7377
}
7478
(zelf.value.func)(vm, args)
7579
}
7680
}
7781

78-
#[pyclass(with(Callable), flags(HAS_DICT, DISALLOW_INSTANTIATION))]
82+
// meth_richcompare in CPython
83+
impl Comparable for PyNativeFunction {
84+
fn cmp(
85+
zelf: &Py<Self>,
86+
other: &PyObject,
87+
op: PyComparisonOp,
88+
_vm: &VirtualMachine,
89+
) -> PyResult<PyComparisonValue> {
90+
op.eq_only(|| {
91+
if let Some(other) = other.downcast_ref::<Self>() {
92+
let eq = match (zelf.zelf.as_ref(), other.zelf.as_ref()) {
93+
(Some(z), Some(o)) => z.is(o),
94+
(None, None) => true,
95+
_ => false,
96+
};
97+
let eq = eq && core::ptr::eq(zelf.value, other.value);
98+
Ok(eq.into())
99+
} else {
100+
Ok(PyComparisonValue::NotImplemented)
101+
}
102+
})
103+
}
104+
}
105+
106+
// meth_repr in CPython
107+
impl Representable for PyNativeFunction {
108+
#[inline]
109+
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
110+
if let Some(bound) = zelf
111+
.zelf
112+
.as_ref()
113+
.filter(|b| !b.class().is(vm.ctx.types.module_type))
114+
{
115+
Ok(format!(
116+
"<built-in method {} of {} object at {:#x}>",
117+
zelf.value.name,
118+
bound.class().name(),
119+
bound.get_id()
120+
))
121+
} else {
122+
Ok(format!("<built-in function {}>", zelf.value.name))
123+
}
124+
}
125+
}
126+
127+
#[pyclass(
128+
with(Callable, Comparable, Representable),
129+
flags(HAS_DICT, DISALLOW_INSTANTIATION)
130+
)]
79131
impl PyNativeFunction {
80132
#[pygetset]
81133
fn __module__(zelf: NativeFunctionOrMethod) -> Option<&'static PyStrInterned> {
@@ -87,20 +139,19 @@ impl PyNativeFunction {
87139
zelf.0.value.name
88140
}
89141

142+
// meth_get__qualname__ in CPython
90143
#[pygetset]
91144
fn __qualname__(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult<PyStrRef> {
92145
let zelf = zelf.0;
93-
let flags = zelf.value.flags;
94-
// if flags.contains(PyMethodFlags::CLASS) || flags.contains(PyMethodFlags::STATIC) {
95146
let qualname = if let Some(bound) = &zelf.zelf {
96-
let prefix = if flags.contains(PyMethodFlags::CLASS) {
97-
bound
98-
.get_attr("__qualname__", vm)
99-
.unwrap()
100-
.str(vm)
101-
.unwrap()
102-
.to_string()
147+
if bound.class().is(vm.ctx.types.module_type) {
148+
return Ok(vm.ctx.intern_str(zelf.value.name).to_owned());
149+
}
150+
let prefix = if bound.class().is(vm.ctx.types.type_type) {
151+
// m_self is a type: use PyType_GetQualName(m_self)
152+
bound.get_attr("__qualname__", vm)?.str(vm)?.to_string()
103153
} else {
154+
// m_self is an instance: use Py_TYPE(m_self).__qualname__
104155
bound.class().name().to_string()
105156
};
106157
vm.ctx.new_str(format!("{}.{}", prefix, &zelf.value.name))
@@ -115,15 +166,23 @@ impl PyNativeFunction {
115166
zelf.0.value.doc
116167
}
117168

169+
// meth_get__self__ in CPython
118170
#[pygetset]
119-
fn __self__(_zelf: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
120-
vm.ctx.none()
171+
fn __self__(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyObjectRef {
172+
zelf.0.zelf.clone().unwrap_or_else(|| vm.ctx.none())
121173
}
122174

175+
// meth_reduce in CPython
123176
#[pymethod]
124-
const fn __reduce__(&self) -> &'static str {
125-
// TODO: return (getattr, (self.object, self.name)) if this is a method
126-
self.value.name
177+
fn __reduce__(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult {
178+
let zelf = zelf.0;
179+
if zelf.zelf.is_none() || zelf.module.is_some() {
180+
Ok(vm.ctx.new_str(zelf.value.name).into())
181+
} else {
182+
let getattr = vm.builtins.get_attr("getattr", vm)?;
183+
let target = zelf.zelf.clone().unwrap();
184+
Ok(vm.new_tuple((getattr, (target, zelf.value.name))).into())
185+
}
127186
}
128187

129188
#[pymethod]
@@ -139,14 +198,7 @@ impl PyNativeFunction {
139198
}
140199
}
141200

142-
impl Representable for PyNativeFunction {
143-
#[inline]
144-
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
145-
Ok(format!("<built-in function {}>", zelf.value.name))
146-
}
147-
}
148-
149-
// `PyCMethodObject` in CPython
201+
// PyCMethodObject in CPython
150202
// repr(C) ensures `func` is at offset 0, allowing safe cast from PyNativeMethod to PyNativeFunction
151203
#[repr(C)]
152204
#[pyclass(name = "builtin_function_or_method", module = false, base = PyNativeFunction, ctx = "builtin_function_or_method_type")]
@@ -155,14 +207,11 @@ pub struct PyNativeMethod {
155207
pub(crate) class: &'static Py<PyType>, // TODO: the actual life is &'self
156208
}
157209

158-
#[pyclass(
159-
with(Callable, Comparable, Representable),
160-
flags(HAS_DICT, DISALLOW_INSTANTIATION)
161-
)]
162-
impl PyNativeMethod {
163-
// __qualname__, __self__, and __reduce__ are inherited from PyNativeFunction
164-
// via NativeFunctionOrMethod wrapper since we share the same Python type.
165-
}
210+
// All Python-visible behavior (getters, slots) is registered by PyNativeFunction::extend_class.
211+
// PyNativeMethod only extends the Rust-side struct with the defining class reference.
212+
// The func field at offset 0 (#[repr(C)]) allows NativeFunctionOrMethod to read it safely.
213+
#[pyclass(flags(HAS_DICT, DISALLOW_INSTANTIATION))]
214+
impl PyNativeMethod {}
166215

167216
impl fmt::Debug for PyNativeMethod {
168217
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -175,55 +224,8 @@ impl fmt::Debug for PyNativeMethod {
175224
}
176225
}
177226

178-
impl Comparable for PyNativeMethod {
179-
fn cmp(
180-
zelf: &Py<Self>,
181-
other: &PyObject,
182-
op: PyComparisonOp,
183-
_vm: &VirtualMachine,
184-
) -> PyResult<PyComparisonValue> {
185-
op.eq_only(|| {
186-
if let Some(other) = other.downcast_ref::<Self>() {
187-
let eq = match (zelf.func.zelf.as_ref(), other.func.zelf.as_ref()) {
188-
(Some(z), Some(o)) => z.is(o),
189-
(None, None) => true,
190-
_ => false,
191-
};
192-
let eq = eq && core::ptr::eq(zelf.func.value, other.func.value);
193-
Ok(eq.into())
194-
} else {
195-
Ok(PyComparisonValue::NotImplemented)
196-
}
197-
})
198-
}
199-
}
200-
201-
impl Callable for PyNativeMethod {
202-
type Args = FuncArgs;
203-
204-
#[inline]
205-
fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
206-
if let Some(zelf) = &zelf.func.zelf {
207-
args.prepend_arg(zelf.clone());
208-
}
209-
(zelf.func.value.func)(vm, args)
210-
}
211-
}
212-
213-
impl Representable for PyNativeMethod {
214-
#[inline]
215-
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
216-
Ok(format!(
217-
"<built-in method {} of {} object at ...>",
218-
&zelf.func.value.name,
219-
zelf.class.name()
220-
))
221-
}
222-
}
223-
224227
pub fn init(context: &Context) {
225228
PyNativeFunction::extend_class(context, context.types.builtin_function_or_method_type);
226-
PyNativeMethod::extend_class(context, context.types.builtin_function_or_method_type);
227229
}
228230

229231
/// Wrapper that provides access to the common PyNativeFunction data

crates/vm/src/function/method.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,13 @@ impl PyMethodDef {
211211
class: &'static Py<PyType>,
212212
) -> PyRef<PyNativeMethod> {
213213
debug_assert!(self.flags.contains(PyMethodFlags::STATIC));
214-
let func = self.to_function();
214+
// Set zelf to the class, matching CPython's m_self = type for static methods.
215+
// Callable::call skips prepending when STATIC flag is set.
216+
let func = PyNativeFunction {
217+
zelf: Some(class.to_owned().into()),
218+
value: self,
219+
module: None,
220+
};
215221
PyNativeMethod { func, class }.into_ref(ctx)
216222
}
217223

0 commit comments

Comments
 (0)