Add PyBuiltinCallable and call slot

This commit is contained in:
Jeong YunWon
2020-01-16 02:16:30 +09:00
parent d7085db17d
commit e2e5c7ab00
9 changed files with 62 additions and 47 deletions

10
vm/src/callable.rs Normal file
View 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;
}

View File

@@ -58,6 +58,7 @@ macro_rules! py_compile_bytecode {
pub mod macros;
mod builtins;
mod callable;
pub mod cformat;
mod descriptor;
mod dictdatatype;

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,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);

View File

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

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

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

View File

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

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