Merge pull request #1691 from youknowone/call-descriptor

call slot and PyBuiltinCallable
This commit is contained in:
Jeong YunWon
2020-01-27 13:38:05 +09:00
committed by GitHub
13 changed files with 94 additions and 82 deletions

View File

@@ -957,9 +957,8 @@ pub fn builtin_build_class_(
function.invoke_with_scope(vec![].into(), &scope, vm)?;
let class = vm.call_method(
let class = vm.invoke(
metaclass.as_object(),
"__call__",
vec![name_obj, bases, namespace.into_object()],
)?;
cells.set_item("__class__", class.clone(), vm)?;

View File

@@ -59,7 +59,6 @@ pub mod macros;
mod builtins;
pub mod cformat;
mod descriptor;
mod dictdatatype;
#[cfg(feature = "rustpython-compiler")]
pub mod eval;
@@ -75,6 +74,7 @@ mod pyhash;
pub mod pyobject;
pub mod scope;
mod sequence;
mod slots;
pub mod stdlib;
mod sysmodule;
pub mod types;

View File

@@ -169,7 +169,7 @@ macro_rules! py_namespace {
{
let namespace = $vm.ctx.new_namespace();
$(
$vm.set_attr(&namespace, $name, $value).unwrap();
$vm.__module_set_attr(&namespace, $name, $value).unwrap();
)*
namespace
}

View File

@@ -1,11 +1,11 @@
use std::fmt;
use crate::descriptor::PyBuiltinDescriptor;
use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc};
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
};
use crate::slots::{PyBuiltinCallable, PyBuiltinDescriptor};
use crate::vm::VirtualMachine;
#[pyclass]
@@ -35,14 +35,15 @@ impl PyBuiltinFunction {
}
}
#[pyimpl]
impl PyBuiltinFunction {
#[pymethod(name = "__call__")]
pub fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
impl PyBuiltinCallable for PyBuiltinFunction {
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
(self.value)(vm, args)
}
}
#[pyimpl(with(PyBuiltinCallable))]
impl PyBuiltinFunction {}
#[pyclass]
pub struct PyBuiltinMethod {
function: PyBuiltinFunction,
@@ -87,14 +88,15 @@ impl PyBuiltinDescriptor for PyBuiltinMethod {
}
}
#[pyimpl(with(PyBuiltinDescriptor))]
impl PyBuiltinMethod {
#[pymethod(name = "__call__")]
pub fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
self.function.call(args, vm)
impl PyBuiltinCallable for PyBuiltinMethod {
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
(self.function.value)(vm, args)
}
}
#[pyimpl(with(PyBuiltinDescriptor, PyBuiltinCallable))]
impl PyBuiltinMethod {}
pub fn init(context: &PyContext) {
PyBuiltinFunction::extend_class(context, &context.types.builtin_function_or_method_type);
PyBuiltinMethod::extend_class(context, &context.types.method_descriptor_type);

View File

@@ -1,9 +1,9 @@
use super::objtype::PyClassRef;
use crate::descriptor::PyBuiltinDescriptor;
use crate::function::OptionalArg;
use crate::pyobject::{
PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
};
use crate::slots::PyBuiltinDescriptor;
use crate::vm::VirtualMachine;
/// classmethod(function) -> method

View File

@@ -4,7 +4,6 @@ use super::objstr::PyStringRef;
use super::objtuple::PyTupleRef;
use super::objtype::PyClassRef;
use crate::bytecode;
use crate::descriptor::PyBuiltinDescriptor;
use crate::frame::Frame;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::obj::objcoroutine::PyCoroutine;
@@ -14,6 +13,7 @@ use crate::pyobject::{
TypeProtocol,
};
use crate::scope::Scope;
use crate::slots::{PyBuiltinCallable, PyBuiltinDescriptor};
use crate::vm::VirtualMachine;
pub type PyFunctionRef = PyRef<PyFunction>;
@@ -242,9 +242,10 @@ impl PyValue for PyFunction {
#[pyimpl(with(PyBuiltinDescriptor))]
impl PyFunction {
#[pyslot]
#[pymethod(magic)]
fn call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm.invoke(&zelf, args)
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
self.invoke(args, vm)
}
#[pyproperty(name = "__code__")]
@@ -263,6 +264,7 @@ impl PyFunction {
}
}
#[pyclass]
#[derive(Debug)]
pub struct PyBoundMethod {
// TODO: these shouldn't be public
@@ -270,11 +272,22 @@ pub struct PyBoundMethod {
pub function: PyObjectRef,
}
impl PyBuiltinCallable for PyBoundMethod {
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
let args = args.insert(self.object.clone());
vm.invoke(&self.function, args)
}
}
impl PyBoundMethod {
pub fn new(object: PyObjectRef, function: PyObjectRef) -> Self {
PyBoundMethod { object, function }
}
}
#[pyimpl(with(PyBuiltinCallable))]
impl PyBoundMethod {
#[pymethod(magic)]
fn getattribute(&self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
vm.get_attribute(self.function.clone(), name)
}
@@ -291,7 +304,5 @@ pub fn init(context: &PyContext) {
PyFunction::extend_class(context, function_type);
let method_type = &context.types.bound_method_type;
extend_class!(context, method_type, {
"__getattribute__" => context.new_method(PyBoundMethod::getattribute),
});
PyBoundMethod::extend_class(context, method_type);
}

View File

@@ -77,7 +77,7 @@ fn object_hash(zelf: PyObjectRef, _vm: &VirtualMachine) -> pyhash::PyHash {
zelf.get_id() as pyhash::PyHash
}
fn object_setattr(
pub(crate) fn object_setattr(
obj: PyObjectRef,
attr_name: PyStringRef,
value: PyObjectRef,

View File

@@ -4,12 +4,12 @@
use std::cell::RefCell;
use super::objtype::PyClassRef;
use crate::descriptor::PyBuiltinDescriptor;
use crate::function::{IntoPyNativeFunc, OptionalArg};
use crate::pyobject::{
IdProtocol, PyClassImpl, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue,
TypeProtocol,
};
use crate::slots::PyBuiltinDescriptor;
use crate::vm::VirtualMachine;
// Read-only property, doesn't have __set__ or __delete__

View File

@@ -1,7 +1,7 @@
use super::objtype::PyClassRef;
use crate::descriptor::PyBuiltinDescriptor;
use crate::function::OptionalArg;
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::slots::PyBuiltinDescriptor;
use crate::vm::VirtualMachine;
#[pyclass(name = "staticmethod")]

View File

@@ -9,11 +9,12 @@ use super::objproperty::PropertyBuilder;
use super::objstr::PyStringRef;
use super::objtuple::PyTuple;
use super::objweakref::PyWeak;
use crate::function::{PyFuncArgs, PyNativeFunc};
use crate::function::PyFuncArgs;
use crate::pyobject::{
IdProtocol, PyAttributes, PyContext, PyIterable, PyObject, PyObjectRef, PyRef, PyResult,
PyValue, TypeProtocol,
};
use crate::slots::PyClassSlots;
use crate::vm::VirtualMachine;
#[derive(Debug)]
@@ -26,18 +27,6 @@ pub struct PyClass {
pub slots: RefCell<PyClassSlots>,
}
#[derive(Default)]
pub struct PyClassSlots {
pub new: Option<PyNativeFunc>,
pub descr_get: Option<PyNativeFunc>,
}
impl fmt::Debug for PyClassSlots {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PyClassSlots")
}
}
impl fmt::Display for PyClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.name, f)
@@ -255,6 +244,7 @@ pub fn init(ctx: &PyContext) {
extend_class!(&ctx, &ctx.types.type_type, {
"mro" => ctx.new_method(type_mro),
"__call__" => ctx.new_method(type_call),
(slot call) => type_call,
"__dict__" =>
PropertyBuilder::new(ctx)
.add_getter(type_dict)

View File

@@ -1,12 +1,14 @@
use super::objtype::PyClassRef;
use crate::function::OptionalArg;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::pyobject::{
PyContext, PyObject, PyObjectPayload, PyObjectRef, PyRef, PyResult, PyValue,
PyClassImpl, PyContext, PyObject, PyObjectPayload, PyObjectRef, PyRef, PyResult, PyValue,
};
use crate::slots::PyBuiltinCallable;
use crate::vm::VirtualMachine;
use std::rc::{Rc, Weak};
#[pyclass]
#[derive(Debug)]
pub struct PyWeak {
referent: Weak<PyObject<dyn PyObjectPayload>>,
@@ -32,25 +34,27 @@ impl PyValue for PyWeak {
pub type PyWeakRef = PyRef<PyWeak>;
impl PyWeakRef {
impl PyBuiltinCallable for PyWeak {
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
args.bind::<()>(vm)?;
Ok(self.referent.upgrade().unwrap_or_else(|| vm.get_none()))
}
}
#[pyimpl(with(PyBuiltinCallable))]
impl PyWeak {
// TODO callbacks
fn create(
#[pyslot]
fn tp_new(
cls: PyClassRef,
referent: PyObjectRef,
_callback: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<Self> {
) -> PyResult<PyRef<Self>> {
PyWeak::downgrade(&referent).into_ref_with_type(vm, cls)
}
fn call(self, vm: &VirtualMachine) -> PyObjectRef {
self.referent.upgrade().unwrap_or_else(|| vm.get_none())
}
}
pub fn init(context: &PyContext) {
extend_class!(context, &context.types.weakref_type, {
(slot new) => PyWeakRef::create,
"__call__" => context.new_method(PyWeakRef::call)
});
PyWeak::extend_class(context, &context.types.weakref_type);
}

View File

@@ -1,10 +1,30 @@
use crate::function::OptionalArg;
use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc};
use crate::pyobject::{IdProtocol, PyObjectRef, PyRef, PyResult, PyValue};
use crate::VirtualMachine;
#[derive(Default)]
pub struct PyClassSlots {
pub new: Option<PyNativeFunc>,
pub call: Option<PyNativeFunc>,
pub descr_get: Option<PyNativeFunc>,
}
impl std::fmt::Debug for PyClassSlots {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("PyClassSlots")
}
}
#[pyimpl]
pub trait PyBuiltinCallable: PyValue {
#[pymethod(magic)]
#[pyslot]
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult;
}
#[pyimpl]
pub trait PyBuiltinDescriptor: PyValue {
#[pymethod(name = "__get__")]
#[pymethod(magic)]
#[pyslot(descr_get)]
fn get(
zelf: PyRef<Self>,

View File

@@ -26,14 +26,13 @@ use crate::frozen;
use crate::function::PyFuncArgs;
use crate::import;
use crate::obj::objbool;
use crate::obj::objbuiltinfunc::{PyBuiltinFunction, PyBuiltinMethod};
use crate::obj::objcode::{PyCode, PyCodeRef};
use crate::obj::objdict::PyDictRef;
use crate::obj::objfunction::{PyBoundMethod, PyFunction};
use crate::obj::objint::PyInt;
use crate::obj::objiter;
use crate::obj::objlist::PyList;
use crate::obj::objmodule::{self, PyModule};
use crate::obj::objobject;
use crate::obj::objstr::{PyString, PyStringRef};
use crate::obj::objtuple::PyTuple;
use crate::obj::objtype::{self, PyClassRef};
@@ -656,31 +655,23 @@ impl VirtualMachine {
}
}
fn _invoke(&self, func_ref: &PyObjectRef, args: PyFuncArgs) -> PyResult {
vm_trace!("Invoke: {:?} {:?}", func_ref, args);
if let Some(py_func) = func_ref.payload::<PyFunction>() {
fn _invoke(&self, callable: &PyObjectRef, args: PyFuncArgs) -> PyResult {
vm_trace!("Invoke: {:?} {:?}", callable, args);
let class = callable.class();
let slots = class.slots.borrow();
if let Some(slot_call) = slots.borrow().call.as_ref() {
self.trace_event(TraceEvent::Call)?;
let res = py_func.invoke(args, self);
let args = args.insert(callable.clone());
let result = slot_call(self, args);
self.trace_event(TraceEvent::Return)?;
res
} else if let Some(PyBoundMethod {
ref function,
ref object,
}) = func_ref.payload()
{
let args = args.insert(object.clone());
self.invoke(&function, args)
} else if let Some(builtin_func) = func_ref.payload::<PyBuiltinFunction>() {
builtin_func.as_func()(self, args)
} else if let Some(method) = func_ref.payload::<PyBuiltinMethod>() {
method.as_func()(self, args)
} else if self.is_callable(&func_ref) {
self.call_method(&func_ref, "__call__", args)
result
} else if objtype::class_has_attr(&class, "__call__") {
let result = self.call_method(&callable, "__call__", args);
result
} else {
Err(self.new_type_error(format!(
"'{}' object is not callable",
func_ref.class().name
callable.class().name
)))
}
}
@@ -889,12 +880,8 @@ impl VirtualMachine {
}
pub fn is_callable(&self, obj: &PyObjectRef) -> bool {
match_class!(match obj {
PyFunction => true,
PyBoundMethod => true,
PyBuiltinFunction => true,
obj => objtype::class_has_attr(&obj.class(), "__call__"),
})
obj.class().slots.borrow().call.is_some()
|| objtype::class_has_attr(&obj.class(), "__call__")
}
#[inline]
@@ -1311,8 +1298,7 @@ impl VirtualMachine {
attr_value: impl Into<PyObjectRef>,
) -> PyResult<()> {
let val = attr_value.into();
self.set_attr(module, attr_name, val)?;
Ok(())
objobject::object_setattr(module.clone(), attr_name.try_into_ref(self)?, val, self)
}
}