mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
WIP getmethod
This commit is contained in:
@@ -137,7 +137,7 @@ impl PyValue for PyBuiltinMethod {
|
||||
|
||||
impl fmt::Debug for PyBuiltinMethod {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "method descriptor")
|
||||
write!(f, "method descriptor for '{}'", self.value.name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ impl Callable for PyBuiltinMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(SlotDescriptor, Callable))]
|
||||
#[pyimpl(with(SlotDescriptor, Callable), flags(METHOD_DESCR))]
|
||||
impl PyBuiltinMethod {
|
||||
#[pyproperty(magic)]
|
||||
fn name(&self) -> PyStrRef {
|
||||
|
||||
@@ -302,7 +302,7 @@ impl PyValue for PyFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(SlotDescriptor, Callable), flags(HAS_DICT))]
|
||||
#[pyimpl(with(SlotDescriptor, Callable), flags(HAS_DICT, METHOD_DESCR))]
|
||||
impl PyFunction {
|
||||
#[pyproperty(magic)]
|
||||
fn code(&self) -> PyCodeRef {
|
||||
|
||||
@@ -240,7 +240,7 @@ impl PyBaseObject {
|
||||
|
||||
#[pymethod(name = "__getattribute__")]
|
||||
#[pyslot]
|
||||
fn getattro(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
|
||||
pub(crate) fn getattro(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm_trace!("object.__getattribute__({:?}, {:?})", obj, name);
|
||||
vm.generic_getattribute(obj, name)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,13 @@ use result_like::impl_option_like;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
pub trait IntoFuncArgs {
|
||||
pub trait IntoFuncArgs: Sized {
|
||||
fn into_args(self, vm: &VirtualMachine) -> FuncArgs;
|
||||
fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs {
|
||||
let mut args = self.into_args(vm);
|
||||
args.prepend_arg(obj);
|
||||
args
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoFuncArgs for T
|
||||
@@ -26,11 +31,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! count {
|
||||
() => (0usize);
|
||||
( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
|
||||
}
|
||||
|
||||
// A tuple of values that each implement `IntoPyObject` represents a sequence of
|
||||
// arguments that can be bound and passed to a built-in function.
|
||||
macro_rules! into_func_args_from_tuple {
|
||||
@@ -42,9 +42,13 @@ macro_rules! into_func_args_from_tuple {
|
||||
#[inline]
|
||||
fn into_args(self, vm: &VirtualMachine) -> FuncArgs {
|
||||
let ($($n,)*) = self;
|
||||
let mut result = Vec::with_capacity(count!($($n,)*) + 1);
|
||||
$(result.push($n.into_pyobject(vm), );)*
|
||||
result.into()
|
||||
vec![$($n.into_pyobject(vm),)*].into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs {
|
||||
let ($($n,)*) = self;
|
||||
vec![obj, $($n.into_pyobject(vm),)*].into()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1251,3 +1251,97 @@ pub fn hash_iter_unordered<'a, I: IntoIterator<Item = &'a PyObjectRef>>(
|
||||
) -> PyResult<rustpython_common::hash::PyHash> {
|
||||
rustpython_common::hash::hash_iter_unordered(iter, |obj| vm._hash(obj))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PyMethod {
|
||||
Function {
|
||||
target: PyObjectRef,
|
||||
func: PyObjectRef,
|
||||
},
|
||||
Attribute(PyObjectRef),
|
||||
}
|
||||
|
||||
impl PyMethod {
|
||||
pub fn get(obj: PyObjectRef, name: pystr::PyStrRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let cls = obj.class();
|
||||
let getattro = cls.mro_find_map(|cls| cls.slots.getattro.load()).unwrap();
|
||||
if getattro as usize != object::PyBaseObject::getattro as usize {
|
||||
drop(cls);
|
||||
return getattro(obj, name, vm).map(Self::Attribute);
|
||||
}
|
||||
|
||||
let mut is_method = false;
|
||||
|
||||
let cls_attr = match cls.get_attr(name.borrow_value()) {
|
||||
Some(descr) => {
|
||||
let descr_cls = descr.class();
|
||||
let descr_get = if descr_cls.slots.flags.has_feature(PyTpFlags::METHOD_DESCR) {
|
||||
is_method = true;
|
||||
None
|
||||
} else {
|
||||
let descr_get = descr_cls.mro_find_map(|cls| cls.slots.descr_get.load());
|
||||
if let Some(descr_get) = descr_get {
|
||||
if descr_cls.has_attr("__set__") {
|
||||
drop(descr_cls);
|
||||
let cls = PyLease::into_pyref(cls).into_object();
|
||||
return descr_get(descr, Some(obj), Some(cls), vm).map(Self::Attribute);
|
||||
}
|
||||
}
|
||||
descr_get
|
||||
};
|
||||
drop(descr_cls);
|
||||
Some((descr, descr_get))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
if let Some(dict) = obj.dict() {
|
||||
if let Some(attr) = dict.get_item_option(name.clone(), vm)? {
|
||||
return Ok(Self::Attribute(attr));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((attr, descr_get)) = cls_attr {
|
||||
match descr_get {
|
||||
None if is_method => {
|
||||
drop(cls);
|
||||
Ok(Self::Function {
|
||||
target: obj,
|
||||
func: attr,
|
||||
})
|
||||
}
|
||||
Some(descr_get) => {
|
||||
let cls = PyLease::into_pyref(cls).into_object();
|
||||
descr_get(attr, Some(obj), Some(cls), vm).map(Self::Attribute)
|
||||
}
|
||||
None => Ok(Self::Attribute(attr)),
|
||||
}
|
||||
} else if let Some(getter) = cls.get_attr("__getattr__") {
|
||||
drop(cls);
|
||||
vm.invoke(&getter, (obj, name)).map(Self::Attribute)
|
||||
} else {
|
||||
Err(vm.new_attribute_error(format!(
|
||||
"'{}' object has not attribute '{}'",
|
||||
cls.name, name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke(self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let (func, args) = match self {
|
||||
PyMethod::Function { target, func } => (func, args.into_method_args(target, vm)),
|
||||
PyMethod::Attribute(func) => (func, args.into_args(vm)),
|
||||
};
|
||||
vm.invoke(&func, args)
|
||||
}
|
||||
|
||||
pub fn invoke_ref(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let (func, args) = match self {
|
||||
PyMethod::Function { target, func } => {
|
||||
(func, args.into_method_args(target.clone(), vm))
|
||||
}
|
||||
PyMethod::Attribute(func) => (func, args.into_args(vm)),
|
||||
};
|
||||
vm.invoke(func, args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ bitflags! {
|
||||
pub struct PyTpFlags: u64 {
|
||||
const HEAPTYPE = 1 << 9;
|
||||
const BASETYPE = 1 << 10;
|
||||
const METHOD_DESCR = 1 << 17;
|
||||
const HAS_DICT = 1 << 40;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
||||
16
vm/src/vm.rs
16
vm/src/vm.rs
@@ -29,8 +29,8 @@ use crate::frame::{ExecutionResult, Frame, FrameRef};
|
||||
use crate::function::{FuncArgs, IntoFuncArgs};
|
||||
use crate::pyobject::{
|
||||
BorrowValue, Either, IdProtocol, IntoPyObject, ItemProtocol, PyArithmaticValue, PyContext,
|
||||
PyObject, PyObjectRef, PyRef, PyRefExact, PyResult, PyValue, TryFromObject, TryIntoRef,
|
||||
TypeProtocol,
|
||||
PyLease, PyMethod, PyObject, PyObjectRef, PyRef, PyRefExact, PyResult, PyValue, TryFromObject,
|
||||
TryIntoRef, TypeProtocol,
|
||||
};
|
||||
use crate::scope::Scope;
|
||||
use crate::slots::PyComparisonOp;
|
||||
@@ -965,21 +965,15 @@ impl VirtualMachine {
|
||||
self.call_get_descriptor(attr, obj).unwrap_or_else(Ok)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn call_method<T>(&self, obj: &PyObjectRef, method_name: &str, args: T) -> PyResult
|
||||
where
|
||||
T: IntoFuncArgs,
|
||||
{
|
||||
flame_guard!(format!("call_method({:?})", method_name));
|
||||
|
||||
// This is only used in the vm for magic methods, which use a greatly simplified attribute lookup.
|
||||
match obj.get_class_attr(method_name) {
|
||||
Some(func) => {
|
||||
vm_trace!("vm.call_method {:?} {:?} -> {:?}", obj, method_name, func);
|
||||
let wrapped = self.call_if_get_descriptor(func, obj.clone())?;
|
||||
self.invoke(&wrapped, args)
|
||||
}
|
||||
None => Err(self.new_type_error(format!("Unsupported method: {}", method_name))),
|
||||
}
|
||||
PyMethod::get(obj.clone(), PyStr::from(method_name).into_ref(self), self)?
|
||||
.invoke(args, self)
|
||||
}
|
||||
|
||||
fn _invoke(&self, callable: &PyObjectRef, args: FuncArgs) -> PyResult {
|
||||
|
||||
Reference in New Issue
Block a user