Skip to content

Commit 9733c52

Browse files
committed
Runtime: Implement weakref callable proxy type
1 parent 2ede9ec commit 9733c52

8 files changed

Lines changed: 333 additions & 18 deletions

File tree

src/memory/Heap.hpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
#include "GarbageCollector.hpp"
44
#include "utilities.hpp"
5+
#include "runtime/forward.hpp"
56

67
#include <bitset>
8+
#include <cstdint>
79
#include <memory>
810
#include <optional>
9-
#include <set>
11+
#include <unordered_map>
1012

1113
static constexpr size_t KB = 1024;
1214
static constexpr size_t MB = 1024 * KB;
@@ -207,11 +209,9 @@ class Slab
207209
if constexpr (sizeof(T) + sizeof(GarbageCollected) <= 2048) {
208210
return block2048->allocate();
209211
} else {
210-
[]<bool flag = false>()
211-
{
212+
[]<bool flag = false>() {
212213
static_assert(flag, "only object sizes <= 2048 bytes are currently supported");
213-
}
214-
();
214+
}();
215215
}
216216
}
217217

@@ -250,11 +250,9 @@ class Slab
250250

251251
template<typename T> uint8_t *allocate()
252252
{
253-
[]<bool flag = false>()
254-
{
253+
[]<bool flag = false>() {
255254
static_assert(flag, "only GC collected objects are currently supported");
256-
}
257-
();
255+
}();
258256
return nullptr;
259257

260258
// uint8_t *ptr{ nullptr };
@@ -315,7 +313,7 @@ class Heap
315313
size_t m_static_offset{ 8 };
316314
Slab m_slab;
317315
std::unique_ptr<GarbageCollector> m_gc;
318-
std::set<uint8_t *> m_weakrefs;
316+
std::unordered_map<uint8_t *, std::vector<py::PyObject *>> m_weakrefs;
319317
uintptr_t *m_bottom_stack_pointer;
320318
bool m_allocate_in_static{ false };
321319

@@ -360,7 +358,7 @@ class Heap
360358

361359
template<typename T, typename... Args> T *__attribute__((noinline)) allocate(Args &&...args)
362360
{
363-
if (m_allocate_in_static) { return allocate_static<T>(std::forward<Args>(args)...).get(); }
361+
if (m_allocate_in_static) { return allocate_static<T>(std::forward<Args>(args)...); }
364362
collect_garbage();
365363
auto *ptr = m_slab.allocate<T>();
366364

@@ -389,7 +387,7 @@ class Heap
389387
T *__attribute__((noinline)) allocate_weakref(TargetT &&target, Args &&...args)
390388
{
391389
T *obj = allocate<T>(std::forward<TargetT>(target), std::forward<Args>(args)...);
392-
if (obj) { m_weakrefs.insert(bit_cast<uint8_t *>(target)); }
390+
if (obj) { m_weakrefs[bit_cast<uint8_t *>(target)].push_back(obj); }
393391
return obj;
394392
}
395393

@@ -423,6 +421,16 @@ class Heap
423421
}
424422

425423
bool has_weakref_object(uint8_t *obj) const { return m_weakrefs.contains(obj); }
424+
size_t weakref_count(uint8_t *obj) const
425+
{
426+
if (auto it = m_weakrefs.find(obj); it != m_weakrefs.end()) { return it->second.size(); }
427+
return 0;
428+
}
429+
std::vector<py::PyObject *> get_weakrefs(uint8_t *obj) const
430+
{
431+
if (auto it = m_weakrefs.find(obj); it != m_weakrefs.end()) { return it->second; }
432+
return {};
433+
}
426434

427435
private:
428436
uint8_t *allocate_gc(uint8_t *ptr) const;
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#include "PyCallableProxyType.hpp"
2+
#include "runtime/MemoryError.hpp"
3+
#include "runtime/PyModule.hpp"
4+
#include "runtime/PyNone.hpp"
5+
#include "runtime/PyType.hpp"
6+
#include "runtime/ValueError.hpp"
7+
#include "runtime/types/api.hpp"
8+
#include "vm/VM.hpp"
9+
10+
namespace py {
11+
12+
namespace {
13+
static PyType *s_weak_callableproxy = nullptr;
14+
}
15+
16+
PyCallableProxyType::PyCallableProxyType(PyType *type) : PyBaseObject(type) {}
17+
18+
PyCallableProxyType::PyCallableProxyType(PyObject *object, PyObject *callback)
19+
: PyBaseObject(s_weak_callableproxy), m_object(object), m_callback(callback)
20+
{}
21+
22+
PyResult<PyCallableProxyType *> PyCallableProxyType::create(PyObject *object, PyObject *callback)
23+
{
24+
auto *result =
25+
VirtualMachine::the().heap().allocate_weakref<PyCallableProxyType>(object, callback);
26+
if (!result) { return Err(memory_error(sizeof(PyCallableProxyType))); }
27+
return Ok(result);
28+
}
29+
30+
void PyCallableProxyType::visit_graph(Visitor &visitor)
31+
{
32+
PyObject::visit_graph(visitor);
33+
if (m_callback) visitor.visit(*m_callback);
34+
}
35+
36+
std::string PyCallableProxyType::to_string() const
37+
{
38+
if (!m_object) {
39+
return fmt::format("<weakproxy at {} empty", static_cast<const void *>(this));
40+
}
41+
return fmt::format("<weakproxy at {} to {} at {}>",
42+
static_cast<const void *>(this),
43+
m_object->type()->name(),
44+
static_cast<const void *>(m_object));
45+
}
46+
47+
PyResult<PyObject *> PyCallableProxyType::__new__(const PyType *type, PyTuple *args, PyDict *kwargs)
48+
{
49+
ASSERT(type == s_weak_callableproxy)
50+
ASSERT(args && args->size() > 0)
51+
ASSERT(!kwargs || kwargs->size() == 0)
52+
53+
auto *obj = PyObject::from(args->elements()[0]).unwrap();
54+
auto *callback = [args]() -> PyObject * {
55+
if (args->size() > 1) { return PyObject::from(args->elements()[1]).unwrap(); }
56+
return nullptr;
57+
}();
58+
59+
return PyCallableProxyType::create(obj, callback);
60+
}
61+
62+
PyResult<PyObject *> PyCallableProxyType::__str__() const
63+
{
64+
if (!m_object) {
65+
// FIXME: should be a ReferenceError
66+
return Err(value_error("weakly-referenced object no longer exists"));
67+
}
68+
return m_object->str();
69+
}
70+
71+
PyResult<PyObject *> PyCallableProxyType::__repr__() const
72+
{
73+
if (!m_object) {
74+
// FIXME: should be a ReferenceError
75+
return Err(value_error("weakly-referenced object no longer exists"));
76+
}
77+
return PyString::create(to_string());
78+
}
79+
80+
PyResult<PyObject *> PyCallableProxyType::__getattribute__(PyObject *attribute) const
81+
{
82+
auto obj = [this]() -> PyResult<const PyObject *> {
83+
if (type() == s_weak_callableproxy) {
84+
if (!is_alive()) {
85+
// FIXME: should be a ReferenceError
86+
return Err(value_error("weakly-referenced object no longer exists"));
87+
} else {
88+
return Ok(m_object);
89+
}
90+
}
91+
return Ok(this);
92+
}();
93+
if (obj.is_err()) { return Err(obj.unwrap_err()); }
94+
95+
auto attr = [attribute]() -> PyResult<PyObject *> {
96+
if (attribute->type() == s_weak_callableproxy) {
97+
if (!static_cast<const PyCallableProxyType *>(attribute)->is_alive()) {
98+
// FIXME: should be a ReferenceError
99+
return Err(value_error("weakly-referenced object no longer exists"));
100+
} else {
101+
return Ok(static_cast<const PyCallableProxyType *>(attribute)->m_object);
102+
}
103+
}
104+
return Ok(attribute);
105+
}();
106+
if (attr.is_err()) { return attr; }
107+
108+
return obj.unwrap()->get_attribute(attr.unwrap());
109+
}
110+
111+
PyResult<PyObject *> PyCallableProxyType::__call__(PyTuple *args, PyDict *kwargs)
112+
{
113+
if (!m_object) {
114+
// FIXME: should be a ReferenceError
115+
return Err(value_error("weakly-referenced object no longer exists"));
116+
}
117+
return m_object->call(args, kwargs);
118+
}
119+
120+
PyType *PyCallableProxyType::register_type(PyModule *module, std::string_view name)
121+
{
122+
if (!s_weak_callableproxy) {
123+
s_weak_callableproxy = klass<PyCallableProxyType>(module, "weakcallableproxy")
124+
.attr("__callback__", &PyCallableProxyType::m_callback)
125+
.finalize();
126+
}
127+
module->add_symbol(PyString::create(std::string(name)).unwrap(), s_weak_callableproxy);
128+
return s_weak_callableproxy;
129+
}
130+
131+
bool PyCallableProxyType::is_alive() const
132+
{
133+
if (m_object
134+
&& !VirtualMachine::the().heap().has_weakref_object(bit_cast<uint8_t *>(m_object))) {
135+
m_object = nullptr;
136+
}
137+
return m_object != nullptr;
138+
}
139+
140+
}// namespace py
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include "runtime/PyObject.hpp"
4+
5+
namespace py {
6+
7+
class PyCallableProxyType : public PyBaseObject
8+
{
9+
friend class ::Heap;
10+
mutable PyObject *m_object{ nullptr };
11+
PyObject *m_callback{ nullptr };
12+
13+
protected:
14+
PyCallableProxyType(PyType *);
15+
16+
PyCallableProxyType(PyObject *object, PyObject *callback);
17+
18+
void visit_graph(Visitor &) override;
19+
20+
public:
21+
static PyResult<PyCallableProxyType *> create(PyObject *object, PyObject *callback);
22+
23+
std::string to_string() const override;
24+
25+
static PyResult<PyObject *> __new__(const PyType *type, PyTuple *args, PyDict *kwargs);
26+
PyResult<PyObject *> __repr__() const;
27+
PyResult<PyObject *> __str__() const;
28+
PyResult<PyObject *> __getattribute__(PyObject *attribute) const;
29+
PyResult<PyObject *> __call__(PyTuple *args, PyDict *kwargs);
30+
31+
static PyType *register_type(PyModule *module, std::string_view name);
32+
33+
private:
34+
bool is_alive() const;
35+
};
36+
37+
template<> PyCallableProxyType *as(PyObject *obj);
38+
template<> const PyCallableProxyType *as(const PyObject *obj);
39+
40+
}// namespace py

src/runtime/modules/weakref/PyWeakProxy.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ bool PyWeakProxy::is_alive() const
122122
{
123123
if (m_object
124124
&& !VirtualMachine::the().heap().has_weakref_object(bit_cast<uint8_t *>(m_object))) {
125-
m_object = nullptr;
125+
m_object = py_none();
126126
}
127-
return m_object != nullptr;
127+
return m_object != py_none();
128128
}
129129

130130
}// namespace py

src/runtime/modules/weakref/PyWeakProxy.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ class PyWeakProxy : public PyBaseObject
2929

3030
static PyType *register_type(PyModule *module, std::string_view name);
3131

32+
public:
33+
PyObject *get_object() const { return m_object; }
34+
3235
private:
3336
bool is_alive() const;
3437
};

src/runtime/modules/weakref/PyWeakRef.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ bool PyWeakRef::is_alive() const
8989
{
9090
if (m_object
9191
&& !VirtualMachine::the().heap().has_weakref_object(bit_cast<uint8_t *>(m_object))) {
92-
m_object = nullptr;
92+
m_object = py_none();
9393
}
94-
return m_object != nullptr;
94+
return m_object != py_none();
9595
}
9696

9797
}// namespace py

src/runtime/modules/weakref/PyWeakRef.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class PyWeakRef : public PyBaseObject
2828

2929
static PyType *register_type(PyModule *module, std::string_view name);
3030

31+
public:
32+
PyObject *get_object() const { return m_object; }
33+
3134
private:
3235
bool is_alive() const;
3336
};

0 commit comments

Comments
 (0)