diff --git a/derive-impl/src/pyclass.rs b/derive-impl/src/pyclass.rs index 2b8689669d..8b1759dee9 100644 --- a/derive-impl/src/pyclass.rs +++ b/derive-impl/src/pyclass.rs @@ -738,7 +738,7 @@ where quote!(.with_doc(#doc.to_owned(), ctx)) }); let build_func = match self.inner.attr_name { - AttrName::Method => quote!(.build_method(ctx, class)), + AttrName::Method => quote!(.build_method(ctx, class, false)), AttrName::ClassMethod => quote!(.build_classmethod(ctx, class)), AttrName::StaticMethod => quote!(.build_staticmethod(ctx, class)), other => unreachable!( diff --git a/extra_tests/snippets/builtin_type.py b/extra_tests/snippets/builtin_type.py index 68ef65efd1..de7596a94c 100644 --- a/extra_tests/snippets/builtin_type.py +++ b/extra_tests/snippets/builtin_type.py @@ -560,3 +560,8 @@ def my_repr_func(): pass assert repr(my_repr_func).startswith(', } impl PyNativeFuncDef { - pub fn new(func: PyNativeFunc, name: &'static PyStrInterned) -> Self { + pub fn new(func: &'static PyNativeFunc, name: &'static PyStrInterned) -> Self { Self { func, name, @@ -29,14 +29,36 @@ impl PyNativeFuncDef { } pub fn into_function(self) -> PyBuiltinFunction { - self.into() + PyBuiltinFunction { + zelf: None, + value: self, + module: None, + is_classmethod: false, + } + } + pub fn into_method(self, obj: PyObjectRef, is_classmethod: bool) -> PyBuiltinFunction { + PyBuiltinFunction { + zelf: Some(obj), + value: self, + module: None, + is_classmethod, + } } pub fn build_function(self, ctx: &Context) -> PyRef { self.into_function().into_ref(ctx) } - pub fn build_method(self, ctx: &Context, class: &'static Py) -> PyRef { + pub fn build_method( + self, + ctx: &Context, + class: &'static Py, + is_classmethod: bool, + ) -> PyRef { PyRef::new_ref( - PyBuiltinMethod { value: self, class }, + PyBuiltinMethod { + value: self, + class, + is_classmethod, + }, ctx.types.method_descriptor_type.to_owned(), None, ) @@ -47,7 +69,7 @@ impl PyNativeFuncDef { class: &'static Py, ) -> PyRef { // TODO: classmethod_descriptor - let callable = self.build_method(ctx, class).into(); + let callable = self.build_method(ctx, class, true).into(); PyClassMethod::new_ref(callable, ctx) } pub fn build_staticmethod( @@ -55,15 +77,18 @@ impl PyNativeFuncDef { ctx: &Context, class: &'static Py, ) -> PyRef { - let callable = self.build_method(ctx, class).into(); + // TODO + let callable = self.build_method(ctx, class, true).into(); PyStaticMethod::new_ref(callable, ctx) } } #[pyclass(name = "builtin_function_or_method", module = false)] pub struct PyBuiltinFunction { + zelf: Option, value: PyNativeFuncDef, module: Option, + is_classmethod: bool, } impl PyPayload for PyBuiltinFunction { @@ -78,15 +103,6 @@ impl fmt::Debug for PyBuiltinFunction { } } -impl From for PyBuiltinFunction { - fn from(value: PyNativeFuncDef) -> Self { - Self { - value, - module: None, - } - } -} - impl PyBuiltinFunction { pub fn with_module(mut self, module: PyObjectRef) -> Self { self.module = Some(module); @@ -101,15 +117,18 @@ impl PyBuiltinFunction { ) } - pub fn as_func(&self) -> &PyNativeFunc { - &self.value.func + pub fn as_func(&self) -> &'static PyNativeFunc { + self.value.func } } impl Callable for PyBuiltinFunction { type Args = FuncArgs; #[inline] - fn call(zelf: &Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + fn call(zelf: &Py, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { + if let Some(z) = &zelf.zelf { + args.prepend_arg(z.clone()); + } (zelf.value.func)(vm, args) } } @@ -125,8 +144,22 @@ impl PyBuiltinFunction { self.value.name.to_owned() } #[pygetset(magic)] - fn qualname(&self) -> PyStrRef { - self.name() + fn qualname(&self, vm: &VirtualMachine) -> PyStrRef { + if let Some(zelf) = &self.zelf { + // TODO: is_classmethod 이면 zelf 의 이름을 알 방법이 없나? + let prefix = if self.is_classmethod { + zelf.get_attr("__qualname__", vm) + .unwrap() + .str(vm) + .unwrap() + .to_string() + } else { + zelf.class().name().to_string() + }; + PyStr::from(format!("{}.{}", prefix, &self.value.name)).into_ref(&vm.ctx) + } else { + self.name() + } } #[pygetset(magic)] fn doc(&self) -> Option { @@ -173,6 +206,7 @@ impl Unconstructible for PyBuiltinFunction {} pub struct PyBuiltinMethod { value: PyNativeFuncDef, class: &'static Py, + is_classmethod: bool, } impl PyPayload for PyBuiltinMethod { @@ -200,8 +234,20 @@ impl GetDescriptor for PyBuiltinMethod { }; let r = if vm.is_none(&obj) && !Self::_cls_is(&cls, obj.class()) { zelf + } else if _zelf.is_classmethod { + _zelf + .value + .clone() + .into_method(cls.unwrap(), _zelf.is_classmethod) + .into_ref(&vm.ctx) + .into() } else { - PyBoundMethod::new_ref(obj, zelf, &vm.ctx).into() + _zelf + .value + .clone() + .into_method(obj, _zelf.is_classmethod) + .into_ref(&vm.ctx) + .into() }; Ok(r) } @@ -225,7 +271,7 @@ impl PyBuiltinMethod { where F: IntoPyNativeFunc, { - ctx.make_func_def(name, f).build_method(ctx, class) + ctx.make_func_def(name, f).build_method(ctx, class, false) } } diff --git a/vm/src/function/builtin.rs b/vm/src/function/builtin.rs index 3a1591cb45..2bc058824d 100644 --- a/vm/src/function/builtin.rs +++ b/vm/src/function/builtin.rs @@ -6,7 +6,7 @@ use crate::{ use std::marker::PhantomData; /// A built-in Python function. -pub type PyNativeFunc = Box PyResult)>; +pub type PyNativeFunc = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult); /// Implemented by types that are or can generate built-in functions. /// @@ -32,8 +32,10 @@ pub trait IntoPyNativeFunc: Sized + PyThreadingConstraint + 'static { /// `IntoPyNativeFunc::into_func()` generates a PyNativeFunc that performs the /// appropriate type and arity checking, any requested conversions, and then if /// successful calls the function with the extracted parameters. - fn into_func(self) -> PyNativeFunc { - Box::new(move |vm: &VirtualMachine, args| self.call(vm, args)) + fn into_func(self) -> &'static PyNativeFunc { + Box::leak(Box::new(move |vm: &VirtualMachine, args| { + self.call(vm, args) + })) } } @@ -177,7 +179,7 @@ mod tests { #[test] fn test_intonativefunc_noalloc() { - let check_zst = |f: PyNativeFunc| assert_eq!(std::mem::size_of_val(f.as_ref()), 0); + let check_zst = |f: &'static PyNativeFunc| assert_eq!(std::mem::size_of_val(f), 0); fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 { 1 }