WIP getmethod

This commit is contained in:
Noah
2021-03-14 12:46:47 -05:00
parent 2d179f0765
commit 79fa1b104f
7 changed files with 117 additions and 24 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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()
}
}
};

View File

@@ -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)
}
}

View File

@@ -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)]

View File

@@ -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 {