Skip to content

Commit 4a099ab

Browse files
blue-pandaayouknowone
authored andcommitted
Make builtin method type same as that of builtin function
1 parent 9f58921 commit 4a099ab

File tree

3 files changed

+77
-29
lines changed

3 files changed

+77
-29
lines changed

derive-impl/src/pyclass.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ where
738738
quote!(.with_doc(#doc.to_owned(), ctx))
739739
});
740740
let build_func = match self.inner.attr_name {
741-
AttrName::Method => quote!(.build_method(ctx, class)),
741+
AttrName::Method => quote!(.build_method(ctx, class, false)),
742742
AttrName::ClassMethod => quote!(.build_classmethod(ctx, class)),
743743
AttrName::StaticMethod => quote!(.build_staticmethod(ctx, class)),
744744
other => unreachable!(

vm/src/builtins/builtin_func.rs

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
use super::{type_, PyClassMethod, PyStaticMethod, PyStr, PyStrInterned, PyStrRef, PyType};
22
use crate::{
3-
builtins::PyBoundMethod,
43
class::PyClassImpl,
54
function::{FuncArgs, IntoPyNativeFunc, PyNativeFunc},
65
types::{Callable, Constructor, GetDescriptor, Representable, Unconstructible},
76
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
87
};
98
use std::fmt;
109

10+
#[derive(Clone)]
1111
pub struct PyNativeFuncDef {
12-
pub func: PyNativeFunc,
12+
pub func: &'static PyNativeFunc,
1313
pub name: &'static PyStrInterned,
1414
pub doc: Option<PyStrRef>,
1515
}
1616

1717
impl PyNativeFuncDef {
18-
pub fn new(func: PyNativeFunc, name: &'static PyStrInterned) -> Self {
18+
pub fn new(func: &'static PyNativeFunc, name: &'static PyStrInterned) -> Self {
1919
Self {
2020
func,
2121
name,
@@ -29,14 +29,36 @@ impl PyNativeFuncDef {
2929
}
3030

3131
pub fn into_function(self) -> PyBuiltinFunction {
32-
self.into()
32+
PyBuiltinFunction {
33+
zelf: None,
34+
value: self,
35+
module: None,
36+
is_classmethod: false,
37+
}
38+
}
39+
pub fn into_method(self, obj: PyObjectRef, is_classmethod: bool) -> PyBuiltinFunction {
40+
PyBuiltinFunction {
41+
zelf: Some(obj),
42+
value: self,
43+
module: None,
44+
is_classmethod,
45+
}
3346
}
3447
pub fn build_function(self, ctx: &Context) -> PyRef<PyBuiltinFunction> {
3548
self.into_function().into_ref(ctx)
3649
}
37-
pub fn build_method(self, ctx: &Context, class: &'static Py<PyType>) -> PyRef<PyBuiltinMethod> {
50+
pub fn build_method(
51+
self,
52+
ctx: &Context,
53+
class: &'static Py<PyType>,
54+
is_classmethod: bool,
55+
) -> PyRef<PyBuiltinMethod> {
3856
PyRef::new_ref(
39-
PyBuiltinMethod { value: self, class },
57+
PyBuiltinMethod {
58+
value: self,
59+
class,
60+
is_classmethod,
61+
},
4062
ctx.types.method_descriptor_type.to_owned(),
4163
None,
4264
)
@@ -47,23 +69,26 @@ impl PyNativeFuncDef {
4769
class: &'static Py<PyType>,
4870
) -> PyRef<PyClassMethod> {
4971
// TODO: classmethod_descriptor
50-
let callable = self.build_method(ctx, class).into();
72+
let callable = self.build_method(ctx, class, true).into();
5173
PyClassMethod::new_ref(callable, ctx)
5274
}
5375
pub fn build_staticmethod(
5476
self,
5577
ctx: &Context,
5678
class: &'static Py<PyType>,
5779
) -> PyRef<PyStaticMethod> {
58-
let callable = self.build_method(ctx, class).into();
80+
// TODO
81+
let callable = self.build_method(ctx, class, true).into();
5982
PyStaticMethod::new_ref(callable, ctx)
6083
}
6184
}
6285

6386
#[pyclass(name = "builtin_function_or_method", module = false)]
6487
pub struct PyBuiltinFunction {
88+
zelf: Option<PyObjectRef>,
6589
value: PyNativeFuncDef,
6690
module: Option<PyObjectRef>,
91+
is_classmethod: bool,
6792
}
6893

6994
impl PyPayload for PyBuiltinFunction {
@@ -78,15 +103,6 @@ impl fmt::Debug for PyBuiltinFunction {
78103
}
79104
}
80105

81-
impl From<PyNativeFuncDef> for PyBuiltinFunction {
82-
fn from(value: PyNativeFuncDef) -> Self {
83-
Self {
84-
value,
85-
module: None,
86-
}
87-
}
88-
}
89-
90106
impl PyBuiltinFunction {
91107
pub fn with_module(mut self, module: PyObjectRef) -> Self {
92108
self.module = Some(module);
@@ -101,15 +117,18 @@ impl PyBuiltinFunction {
101117
)
102118
}
103119

104-
pub fn as_func(&self) -> &PyNativeFunc {
105-
&self.value.func
120+
pub fn as_func(&self) -> &'static PyNativeFunc {
121+
self.value.func
106122
}
107123
}
108124

109125
impl Callable for PyBuiltinFunction {
110126
type Args = FuncArgs;
111127
#[inline]
112-
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
128+
fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
129+
if let Some(z) = &zelf.zelf {
130+
args.prepend_arg(z.clone());
131+
}
113132
(zelf.value.func)(vm, args)
114133
}
115134
}
@@ -125,8 +144,22 @@ impl PyBuiltinFunction {
125144
self.value.name.to_owned()
126145
}
127146
#[pygetset(magic)]
128-
fn qualname(&self) -> PyStrRef {
129-
self.name()
147+
fn qualname(&self, vm: &VirtualMachine) -> PyStrRef {
148+
if let Some(zelf) = &self.zelf {
149+
// TODO: is_classmethod 이면 zelf 의 이름을 알 방법이 없나?
150+
let prefix = if self.is_classmethod {
151+
zelf.get_attr("__qualname__", vm)
152+
.unwrap()
153+
.str(vm)
154+
.unwrap()
155+
.to_string()
156+
} else {
157+
zelf.class().name().to_string()
158+
};
159+
PyStr::from(format!("{}.{}", prefix, &self.value.name)).into_ref(&vm.ctx)
160+
} else {
161+
self.name()
162+
}
130163
}
131164
#[pygetset(magic)]
132165
fn doc(&self) -> Option<PyStrRef> {
@@ -173,6 +206,7 @@ impl Unconstructible for PyBuiltinFunction {}
173206
pub struct PyBuiltinMethod {
174207
value: PyNativeFuncDef,
175208
class: &'static Py<PyType>,
209+
is_classmethod: bool,
176210
}
177211

178212
impl PyPayload for PyBuiltinMethod {
@@ -200,8 +234,20 @@ impl GetDescriptor for PyBuiltinMethod {
200234
};
201235
let r = if vm.is_none(&obj) && !Self::_cls_is(&cls, obj.class()) {
202236
zelf
237+
} else if _zelf.is_classmethod {
238+
_zelf
239+
.value
240+
.clone()
241+
.into_method(cls.unwrap(), _zelf.is_classmethod)
242+
.into_ref(&vm.ctx)
243+
.into()
203244
} else {
204-
PyBoundMethod::new_ref(obj, zelf, &vm.ctx).into()
245+
_zelf
246+
.value
247+
.clone()
248+
.into_method(obj, _zelf.is_classmethod)
249+
.into_ref(&vm.ctx)
250+
.into()
205251
};
206252
Ok(r)
207253
}
@@ -225,7 +271,7 @@ impl PyBuiltinMethod {
225271
where
226272
F: IntoPyNativeFunc<FKind>,
227273
{
228-
ctx.make_func_def(name, f).build_method(ctx, class)
274+
ctx.make_func_def(name, f).build_method(ctx, class, false)
229275
}
230276
}
231277

vm/src/function/builtin.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
use std::marker::PhantomData;
77

88
/// A built-in Python function.
9-
pub type PyNativeFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult)>;
9+
pub type PyNativeFunc = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult);
1010

1111
/// Implemented by types that are or can generate built-in functions.
1212
///
@@ -32,8 +32,10 @@ pub trait IntoPyNativeFunc<Kind>: Sized + PyThreadingConstraint + 'static {
3232
/// `IntoPyNativeFunc::into_func()` generates a PyNativeFunc that performs the
3333
/// appropriate type and arity checking, any requested conversions, and then if
3434
/// successful calls the function with the extracted parameters.
35-
fn into_func(self) -> PyNativeFunc {
36-
Box::new(move |vm: &VirtualMachine, args| self.call(vm, args))
35+
fn into_func(self) -> &'static PyNativeFunc {
36+
Box::leak(Box::new(move |vm: &VirtualMachine, args| {
37+
self.call(vm, args)
38+
}))
3739
}
3840
}
3941

@@ -177,7 +179,7 @@ mod tests {
177179

178180
#[test]
179181
fn test_intonativefunc_noalloc() {
180-
let check_zst = |f: PyNativeFunc| assert_eq!(std::mem::size_of_val(f.as_ref()), 0);
182+
let check_zst = |f: &'static PyNativeFunc| assert_eq!(std::mem::size_of_val(f), 0);
181183
fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 {
182184
1
183185
}

0 commit comments

Comments
 (0)