Skip to content

Commit e2e5c7a

Browse files
committed
Add PyBuiltinCallable and call slot
1 parent d7085db commit e2e5c7a

9 files changed

Lines changed: 62 additions & 47 deletions

File tree

vm/src/callable.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use crate::function::PyFuncArgs;
2+
use crate::pyobject::{PyResult, PyValue};
3+
use crate::VirtualMachine;
4+
5+
#[pyimpl]
6+
pub trait PyBuiltinCallable: PyValue {
7+
#[pymethod(magic)]
8+
#[pyslot]
9+
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult;
10+
}

vm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ macro_rules! py_compile_bytecode {
5858
pub mod macros;
5959

6060
mod builtins;
61+
mod callable;
6162
pub mod cformat;
6263
mod descriptor;
6364
mod dictdatatype;

vm/src/macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ macro_rules! py_namespace {
169169
{
170170
let namespace = $vm.ctx.new_namespace();
171171
$(
172-
$vm.set_attr(&namespace, $name, $value).unwrap();
172+
$vm.__module_set_attr(&namespace, $name, $value).unwrap();
173173
)*
174174
namespace
175175
}

vm/src/obj/objbuiltinfunc.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt;
22

3+
use crate::callable::PyBuiltinCallable;
34
use crate::descriptor::PyBuiltinDescriptor;
45
use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc};
56
use crate::obj::objtype::PyClassRef;
@@ -35,14 +36,15 @@ impl PyBuiltinFunction {
3536
}
3637
}
3738

38-
#[pyimpl]
39-
impl PyBuiltinFunction {
40-
#[pymethod(name = "__call__")]
41-
pub fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
39+
impl PyBuiltinCallable for PyBuiltinFunction {
40+
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
4241
(self.value)(vm, args)
4342
}
4443
}
4544

45+
#[pyimpl(with(PyBuiltinCallable))]
46+
impl PyBuiltinFunction {}
47+
4648
#[pyclass]
4749
pub struct PyBuiltinMethod {
4850
function: PyBuiltinFunction,
@@ -87,14 +89,15 @@ impl PyBuiltinDescriptor for PyBuiltinMethod {
8789
}
8890
}
8991

90-
#[pyimpl(with(PyBuiltinDescriptor))]
91-
impl PyBuiltinMethod {
92-
#[pymethod(name = "__call__")]
93-
pub fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
94-
self.function.call(args, vm)
92+
impl PyBuiltinCallable for PyBuiltinMethod {
93+
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
94+
(self.function.value)(vm, args)
9595
}
9696
}
9797

98+
#[pyimpl(with(PyBuiltinDescriptor, PyBuiltinCallable))]
99+
impl PyBuiltinMethod {}
100+
98101
pub fn init(context: &PyContext) {
99102
PyBuiltinFunction::extend_class(context, &context.types.builtin_function_or_method_type);
100103
PyBuiltinMethod::extend_class(context, &context.types.method_descriptor_type);

vm/src/obj/objfunction.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::objstr::PyStringRef;
44
use super::objtuple::PyTupleRef;
55
use super::objtype::PyClassRef;
66
use crate::bytecode;
7+
use crate::callable::PyBuiltinCallable;
78
use crate::descriptor::PyBuiltinDescriptor;
89
use crate::frame::Frame;
910
use crate::function::{OptionalArg, PyFuncArgs};
@@ -242,9 +243,10 @@ impl PyValue for PyFunction {
242243

243244
#[pyimpl(with(PyBuiltinDescriptor))]
244245
impl PyFunction {
246+
#[pyslot]
245247
#[pymethod(magic)]
246-
fn call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
247-
vm.invoke(&zelf, args)
248+
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
249+
self.invoke(args, vm)
248250
}
249251

250252
#[pyproperty(name = "__code__")]
@@ -263,18 +265,30 @@ impl PyFunction {
263265
}
264266
}
265267

268+
#[pyclass]
266269
#[derive(Debug)]
267270
pub struct PyBoundMethod {
268271
// TODO: these shouldn't be public
269272
pub object: PyObjectRef,
270273
pub function: PyObjectRef,
271274
}
272275

276+
impl PyBuiltinCallable for PyBoundMethod {
277+
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
278+
let args = args.insert(self.object.clone());
279+
vm.invoke(&self.function, args)
280+
}
281+
}
282+
273283
impl PyBoundMethod {
274284
pub fn new(object: PyObjectRef, function: PyObjectRef) -> Self {
275285
PyBoundMethod { object, function }
276286
}
287+
}
277288

289+
#[pyimpl(with(PyBuiltinCallable))]
290+
impl PyBoundMethod {
291+
#[pymethod(magic)]
278292
fn getattribute(&self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
279293
vm.get_attribute(self.function.clone(), name)
280294
}
@@ -291,7 +305,5 @@ pub fn init(context: &PyContext) {
291305
PyFunction::extend_class(context, function_type);
292306

293307
let method_type = &context.types.bound_method_type;
294-
extend_class!(context, method_type, {
295-
"__getattribute__" => context.new_method(PyBoundMethod::getattribute),
296-
});
308+
PyBoundMethod::extend_class(context, method_type);
297309
}

vm/src/obj/objobject.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ fn object_hash(zelf: PyObjectRef, _vm: &VirtualMachine) -> pyhash::PyHash {
7777
zelf.get_id() as pyhash::PyHash
7878
}
7979

80-
fn object_setattr(
80+
pub(crate) fn object_setattr(
8181
obj: PyObjectRef,
8282
attr_name: PyStringRef,
8383
value: PyObjectRef,

vm/src/obj/objtype.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub struct PyClass {
2929
#[derive(Default)]
3030
pub struct PyClassSlots {
3131
pub new: Option<PyNativeFunc>,
32+
pub call: Option<PyNativeFunc>,
3233
pub descr_get: Option<PyNativeFunc>,
3334
}
3435

@@ -255,6 +256,7 @@ pub fn init(ctx: &PyContext) {
255256
extend_class!(&ctx, &ctx.types.type_type, {
256257
"mro" => ctx.new_method(type_mro),
257258
"__call__" => ctx.new_method(type_call),
259+
(slot call) => type_call,
258260
"__dict__" =>
259261
PropertyBuilder::new(ctx)
260262
.add_getter(type_dict)

vm/src/obj/objweakref.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ impl PyWeakRef {
5151
pub fn init(context: &PyContext) {
5252
extend_class!(context, &context.types.weakref_type, {
5353
(slot new) => PyWeakRef::create,
54-
"__call__" => context.new_method(PyWeakRef::call)
54+
"__call__" => context.new_method(PyWeakRef::call),
55+
(slot call) => PyWeakRef::call,
5556
});
5657
}

vm/src/vm.rs

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,13 @@ use crate::frozen;
2626
use crate::function::PyFuncArgs;
2727
use crate::import;
2828
use crate::obj::objbool;
29-
use crate::obj::objbuiltinfunc::{PyBuiltinFunction, PyBuiltinMethod};
3029
use crate::obj::objcode::{PyCode, PyCodeRef};
3130
use crate::obj::objdict::PyDictRef;
32-
use crate::obj::objfunction::{PyBoundMethod, PyFunction};
3331
use crate::obj::objint::PyInt;
3432
use crate::obj::objiter;
3533
use crate::obj::objlist::PyList;
3634
use crate::obj::objmodule::{self, PyModule};
35+
use crate::obj::objobject;
3736
use crate::obj::objstr::{PyString, PyStringRef};
3837
use crate::obj::objtuple::PyTuple;
3938
use crate::obj::objtype::{self, PyClassRef};
@@ -656,31 +655,23 @@ impl VirtualMachine {
656655
}
657656
}
658657

659-
fn _invoke(&self, func_ref: &PyObjectRef, args: PyFuncArgs) -> PyResult {
660-
vm_trace!("Invoke: {:?} {:?}", func_ref, args);
661-
662-
if let Some(py_func) = func_ref.payload::<PyFunction>() {
658+
fn _invoke(&self, callable: &PyObjectRef, args: PyFuncArgs) -> PyResult {
659+
vm_trace!("Invoke: {:?} {:?}", callable, args);
660+
let class = callable.class();
661+
let slots = class.slots.borrow();
662+
if let Some(slot_call) = slots.borrow().call.as_ref() {
663663
self.trace_event(TraceEvent::Call)?;
664-
let res = py_func.invoke(args, self);
664+
let args = args.insert(callable.clone());
665+
let result = slot_call(self, args);
665666
self.trace_event(TraceEvent::Return)?;
666-
res
667-
} else if let Some(PyBoundMethod {
668-
ref function,
669-
ref object,
670-
}) = func_ref.payload()
671-
{
672-
let args = args.insert(object.clone());
673-
self.invoke(&function, args)
674-
} else if let Some(builtin_func) = func_ref.payload::<PyBuiltinFunction>() {
675-
builtin_func.as_func()(self, args)
676-
} else if let Some(method) = func_ref.payload::<PyBuiltinMethod>() {
677-
method.as_func()(self, args)
678-
} else if self.is_callable(&func_ref) {
679-
self.call_method(&func_ref, "__call__", args)
667+
result
668+
} else if objtype::class_has_attr(&class, "__call__") {
669+
let result = self.call_method(&callable, "__call__", args);
670+
result
680671
} else {
681672
Err(self.new_type_error(format!(
682673
"'{}' object is not callable",
683-
func_ref.class().name
674+
callable.class().name
684675
)))
685676
}
686677
}
@@ -889,12 +880,8 @@ impl VirtualMachine {
889880
}
890881

891882
pub fn is_callable(&self, obj: &PyObjectRef) -> bool {
892-
match_class!(match obj {
893-
PyFunction => true,
894-
PyBoundMethod => true,
895-
PyBuiltinFunction => true,
896-
obj => objtype::class_has_attr(&obj.class(), "__call__"),
897-
})
883+
obj.class().slots.borrow().call.is_some()
884+
|| objtype::class_has_attr(&obj.class(), "__call__")
898885
}
899886

900887
#[inline]
@@ -1311,8 +1298,7 @@ impl VirtualMachine {
13111298
attr_value: impl Into<PyObjectRef>,
13121299
) -> PyResult<()> {
13131300
let val = attr_value.into();
1314-
self.set_attr(module, attr_name, val)?;
1315-
Ok(())
1301+
objobject::object_setattr(module.clone(), attr_name.try_into_ref(self)?, val, self)
13161302
}
13171303
}
13181304

0 commit comments

Comments
 (0)