mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
Add PyBuiltinCallable and call slot
This commit is contained in:
10
vm/src/callable.rs
Normal file
10
vm/src/callable.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use crate::function::PyFuncArgs;
|
||||
use crate::pyobject::{PyResult, PyValue};
|
||||
use crate::VirtualMachine;
|
||||
|
||||
#[pyimpl]
|
||||
pub trait PyBuiltinCallable: PyValue {
|
||||
#[pymethod(magic)]
|
||||
#[pyslot]
|
||||
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult;
|
||||
}
|
||||
@@ -58,6 +58,7 @@ macro_rules! py_compile_bytecode {
|
||||
pub mod macros;
|
||||
|
||||
mod builtins;
|
||||
mod callable;
|
||||
pub mod cformat;
|
||||
mod descriptor;
|
||||
mod dictdatatype;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::callable::PyBuiltinCallable;
|
||||
use crate::descriptor::PyBuiltinDescriptor;
|
||||
use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc};
|
||||
use crate::obj::objtype::PyClassRef;
|
||||
@@ -35,14 +36,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 +89,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);
|
||||
|
||||
@@ -4,6 +4,7 @@ use super::objstr::PyStringRef;
|
||||
use super::objtuple::PyTupleRef;
|
||||
use super::objtype::PyClassRef;
|
||||
use crate::bytecode;
|
||||
use crate::callable::PyBuiltinCallable;
|
||||
use crate::descriptor::PyBuiltinDescriptor;
|
||||
use crate::frame::Frame;
|
||||
use crate::function::{OptionalArg, PyFuncArgs};
|
||||
@@ -242,9 +243,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 +265,7 @@ impl PyFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[derive(Debug)]
|
||||
pub struct PyBoundMethod {
|
||||
// TODO: these shouldn't be public
|
||||
@@ -270,11 +273,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 +305,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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -29,6 +29,7 @@ pub struct PyClass {
|
||||
#[derive(Default)]
|
||||
pub struct PyClassSlots {
|
||||
pub new: Option<PyNativeFunc>,
|
||||
pub call: Option<PyNativeFunc>,
|
||||
pub descr_get: Option<PyNativeFunc>,
|
||||
}
|
||||
|
||||
@@ -255,6 +256,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)
|
||||
|
||||
@@ -51,6 +51,7 @@ impl PyWeakRef {
|
||||
pub fn init(context: &PyContext) {
|
||||
extend_class!(context, &context.types.weakref_type, {
|
||||
(slot new) => PyWeakRef::create,
|
||||
"__call__" => context.new_method(PyWeakRef::call)
|
||||
"__call__" => context.new_method(PyWeakRef::call),
|
||||
(slot call) => PyWeakRef::call,
|
||||
});
|
||||
}
|
||||
|
||||
46
vm/src/vm.rs
46
vm/src/vm.rs
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user