mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
move gc.h
This commit is contained in:
parent
f06f7e21c9
commit
e1e3e208cb
@ -9,7 +9,7 @@ order: 0
|
||||
Sometimes you need to use the following code to prevent the gc from collecting objects.
|
||||
|
||||
```cpp
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
auto _lock = vm->gc_scope_lock();
|
||||
```
|
||||
|
||||
The scope lock is required if you create a PyVar and then try to run python-level bytecodes.
|
||||
@ -34,7 +34,7 @@ The scope lock prevents this from happening.
|
||||
void some_func(VM* vm){
|
||||
PyVar obj = VAR(List(5));
|
||||
// safe
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
auto _lock = vm->gc_scope_lock();
|
||||
PyVar iter = vm->py_iter(obj);
|
||||
PyVar next = vm->py_next(iter);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ PyObject* VM::bind_field(PyObject* obj, const char* name, F T::*field) {
|
||||
};
|
||||
_1 = new_object<NativeFunc>(tp_native_func, fset, 2, field);
|
||||
}
|
||||
PyObject* prop = heap.gcnew<Property>(tp_property, _0, _1);
|
||||
PyObject* prop = new_object<Property>(tp_property, _0, _1).get();
|
||||
obj->attr().set(StrName(name_sv), prop);
|
||||
return prop;
|
||||
}
|
||||
|
40
include/pocketpy/interpreter/gc.h
Normal file
40
include/pocketpy/interpreter/gc.h
Normal file
@ -0,0 +1,40 @@
|
||||
#include "pocketpy/objects/object.h"
|
||||
#include "pocketpy/objects/public.h"
|
||||
#include "pocketpy/common/config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pk_ManagedHeap{
|
||||
c11_vector no_gc;
|
||||
c11_vector gen;
|
||||
|
||||
int gc_threshold;
|
||||
int gc_counter;
|
||||
int gc_lock_counter;
|
||||
pkpy_VM* vm;
|
||||
|
||||
void (*_gc_on_delete)(pkpy_VM*, PyObject*);
|
||||
void (*_gc_marker_ex)(pkpy_VM*);
|
||||
} pk_ManagedHeap;
|
||||
|
||||
void pk_ManagedHeap__ctor(pk_ManagedHeap* self, pkpy_VM* vm);
|
||||
void pk_ManagedHeap__dtor(pk_ManagedHeap* self);
|
||||
|
||||
void pk_ManagedHeap__push_lock(pk_ManagedHeap* self);
|
||||
void pk_ManagedHeap__pop_lock(pk_ManagedHeap* self);
|
||||
|
||||
void pk_ManagedHeap__collect_if_needed(pk_ManagedHeap* self);
|
||||
int pk_ManagedHeap__collect(pk_ManagedHeap* self);
|
||||
int pk_ManagedHeap__sweep(pk_ManagedHeap* self);
|
||||
|
||||
PyObject* pk_ManagedHeap__new(pk_ManagedHeap* self, pkpy_Type type, int size, bool gc);
|
||||
|
||||
// external implementation
|
||||
void pk_ManagedHeap__mark(pk_ManagedHeap* self);
|
||||
void pk_ManagedHeap__delete_obj(pk_ManagedHeap* self, PyObject* obj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,92 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/common/config.h"
|
||||
#include "pocketpy/common/vector.hpp"
|
||||
#include "pocketpy/common/utils.h"
|
||||
#include "pocketpy/objects/object.hpp"
|
||||
#include "pocketpy/objects/namedict.hpp"
|
||||
|
||||
namespace pkpy {
|
||||
struct ManagedHeap {
|
||||
vector<PyObject*> _no_gc;
|
||||
vector<PyObject*> gen;
|
||||
VM* vm;
|
||||
void (*_gc_on_delete)(VM*, PyObject*) = nullptr;
|
||||
void (*_gc_marker_ex)(VM*) = nullptr;
|
||||
|
||||
ManagedHeap(VM* vm) : vm(vm) {}
|
||||
|
||||
int gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||
int gc_counter = 0;
|
||||
|
||||
/********************/
|
||||
int _gc_lock_counter = 0;
|
||||
|
||||
struct ScopeLock {
|
||||
PK_ALWAYS_PASS_BY_POINTER(ScopeLock)
|
||||
|
||||
ManagedHeap* heap;
|
||||
|
||||
ScopeLock(ManagedHeap* heap) : heap(heap) { heap->_gc_lock_counter++; }
|
||||
|
||||
~ScopeLock() { heap->_gc_lock_counter--; }
|
||||
};
|
||||
|
||||
ScopeLock gc_scope_lock() { return ScopeLock(this); }
|
||||
|
||||
/********************/
|
||||
template <typename T, typename... Args>
|
||||
PyObject* _basic_new(Type type, Args&&... args) {
|
||||
using __T = std::decay_t<T>;
|
||||
static_assert(!is_sso_v<__T>, "gcnew cannot be used with SSO types");
|
||||
// https://github.com/pocketpy/pocketpy/issues/94#issuecomment-1594784476
|
||||
PyObject* p;
|
||||
if constexpr(py_sizeof<__T> <= kPoolObjectBlockSize){
|
||||
p = new (PoolObject_alloc()) PyObject(type, false);
|
||||
}else{
|
||||
p = new (std::malloc(py_sizeof<__T>)) PyObject(type, true);
|
||||
}
|
||||
new (p->_value_ptr()) T(std::forward<Args>(args)...);
|
||||
|
||||
// backdoor for important builtin types
|
||||
if constexpr(std::is_same_v<__T, DummyInstance>) {
|
||||
p->_attr = new NameDict();
|
||||
} else if constexpr(std::is_same_v<__T, Type>) {
|
||||
p->_attr = new NameDict();
|
||||
} else if constexpr(std::is_same_v<__T, DummyModule>) {
|
||||
p->_attr = new NameDict();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
PyObject* gcnew(Type type, Args&&... args) {
|
||||
PyObject* p = _basic_new<T>(type, std::forward<Args>(args)...);
|
||||
gen.push_back(p);
|
||||
gc_counter++;
|
||||
return p;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
PyObject* _new(Type type, Args&&... args) {
|
||||
PyObject* p = _basic_new<T>(type, std::forward<Args>(args)...);
|
||||
_no_gc.push_back(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void _delete(PyObject*);
|
||||
|
||||
#if PK_DEBUG_GC_STATS
|
||||
inline static std::map<Type, int> deleted;
|
||||
#endif
|
||||
|
||||
int sweep();
|
||||
void _auto_collect();
|
||||
|
||||
bool _should_auto_collect() const { return gc_counter >= gc_threshold; }
|
||||
|
||||
int collect();
|
||||
void mark();
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
14
include/pocketpy/interpreter/vm.h
Normal file
14
include/pocketpy/interpreter/vm.h
Normal file
@ -0,0 +1,14 @@
|
||||
#include "pocketpy/objects/object.h"
|
||||
|
||||
typedef struct pkpy_VM{
|
||||
PyVar True;
|
||||
PyVar False;
|
||||
PyVar None;
|
||||
PyVar NotImplemented;
|
||||
PyVar Ellipsis;
|
||||
} pkpy_VM;
|
||||
|
||||
void pkpy_VM__ctor(pkpy_VM* self);
|
||||
void pkpy_VM__dtor(pkpy_VM* self);
|
||||
|
||||
PyObject* pkpy_VM__gcnew(pkpy_VM* self, pkpy_Type type);
|
@ -4,7 +4,7 @@
|
||||
#include "pocketpy/objects/dict.hpp"
|
||||
#include "pocketpy/objects/error.hpp"
|
||||
#include "pocketpy/objects/builtins.hpp"
|
||||
#include "pocketpy/interpreter/gc.hpp"
|
||||
#include "pocketpy/interpreter/gc.h"
|
||||
#include "pocketpy/interpreter/frame.hpp"
|
||||
#include "pocketpy/interpreter/profiler.hpp"
|
||||
|
||||
@ -162,7 +162,7 @@ class VM {
|
||||
VM* vm; // self reference to simplify code
|
||||
|
||||
public:
|
||||
ManagedHeap heap;
|
||||
pk_ManagedHeap heap;
|
||||
ValueStack s_data;
|
||||
CallStack callstack;
|
||||
vector<PyTypeInfo> _all_types;
|
||||
@ -446,7 +446,31 @@ public:
|
||||
template<typename T, typename ...Args>
|
||||
PyVar new_object(Type type, Args&&... args){
|
||||
static_assert(!is_sso_v<T>);
|
||||
return heap.gcnew<T>(type, std::forward<Args>(args)...);
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
PyObject* p = (PyObject*)pk_ManagedHeap__new(&heap, type, py_sizeof<T>, true);
|
||||
new (p->_value_ptr()) T(std::forward<Args>(args)...);
|
||||
// backdoor for important builtin types
|
||||
if constexpr(std::is_same_v<T, DummyInstance>
|
||||
|| std::is_same_v<T, Type>
|
||||
|| std::is_same_v<T, DummyModule>) {
|
||||
p->_attr = new NameDict();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
PyVar new_object_no_gc(Type type, Args&&... args){
|
||||
static_assert(!is_sso_v<T>);
|
||||
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||
PyObject* p = (PyObject*)pk_ManagedHeap__new(&heap, type, py_sizeof<T>, false);
|
||||
new (p->_value_ptr()) T(std::forward<Args>(args)...);
|
||||
// backdoor for important builtin types
|
||||
if constexpr(std::is_same_v<T, DummyInstance>
|
||||
|| std::is_same_v<T, Type>
|
||||
|| std::is_same_v<T, DummyModule>) {
|
||||
p->_attr = new NameDict();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -456,7 +480,14 @@ public:
|
||||
if(it == nullptr) PK_FATAL_ERROR("T not found in cxx_typeid_map\n")
|
||||
return *it;
|
||||
}
|
||||
|
||||
/********** old heap op **********/
|
||||
struct HeapScopeLock {
|
||||
PK_ALWAYS_PASS_BY_POINTER(HeapScopeLock)
|
||||
pk_ManagedHeap* heap;
|
||||
HeapScopeLock(pk_ManagedHeap* heap) : heap(heap) { pk_ManagedHeap__push_lock(heap);}
|
||||
~HeapScopeLock() { pk_ManagedHeap__pop_lock(heap); }
|
||||
};
|
||||
HeapScopeLock gc_scope_lock(){ return {&heap}; }
|
||||
/********** private **********/
|
||||
virtual ~VM();
|
||||
|
||||
@ -569,13 +600,13 @@ PyVar py_var(VM* vm, __T&& value) {
|
||||
if constexpr(is_sso_v<T>)
|
||||
return PyVar(const_type, value);
|
||||
else
|
||||
return vm->heap.gcnew<T>(const_type, std::forward<__T>(value));
|
||||
return vm->new_object<T>(const_type, std::forward<__T>(value));
|
||||
} else {
|
||||
Type type = vm->_find_type_in_cxx_typeid_map<T>();
|
||||
if constexpr(is_sso_v<T>)
|
||||
return PyVar(type, value);
|
||||
else
|
||||
return vm->heap.gcnew<T>(type, std::forward<__T>(value));
|
||||
return vm->new_object<T>(type, std::forward<__T>(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stdbool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -15,10 +16,24 @@ struct pkpy_G {
|
||||
pkpy_VM* vm;
|
||||
} extern pkpy_g;
|
||||
|
||||
void py_initialize();
|
||||
void py_switch_vm(const char* name);
|
||||
void py_finalize();
|
||||
|
||||
bool py_eq(const PyVar*, const PyVar*);
|
||||
bool py_le(const PyVar*, const PyVar*);
|
||||
int64_t py_hash(const PyVar*);
|
||||
|
||||
|
||||
/* py_var */
|
||||
void py_newint(PyVar*, int64_t);
|
||||
void py_newfloat(PyVar*, double);
|
||||
void py_newbool(PyVar*, bool);
|
||||
void py_newstr(PyVar*, const char*);
|
||||
void py_newstr2(PyVar*, const char*, int);
|
||||
void py_newbytes(PyVar*, const uint8_t*, int);
|
||||
void py_newnone(PyVar*);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -52,7 +52,7 @@ void VM::__op_unpack_sequence(uint16_t arg) {
|
||||
ValueError(_S("expected ", (int)arg, " values to unpack, got ", (int)tuple.size()));
|
||||
}
|
||||
} else {
|
||||
auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!!
|
||||
auto _lock = gc_scope_lock(); // lock the gc via RAII!!
|
||||
_0 = py_iter(_0);
|
||||
const PyTypeInfo* ti = _tp_info(_0);
|
||||
for(int i = 0; i < arg; i++) {
|
||||
@ -802,7 +802,7 @@ PyVar VM::__run_top_frame() {
|
||||
DISPATCH()
|
||||
case OP_REPR: TOP() = VAR(py_repr(TOP())); DISPATCH()
|
||||
case OP_CALL: {
|
||||
if(heap._should_auto_collect()) heap._auto_collect();
|
||||
pk_ManagedHeap__collect_if_needed(&heap);
|
||||
PyVar _0 = vectorcall(byte.arg & 0xFF, // ARGC
|
||||
(byte.arg >> 8) & 0xFF, // KWARGC
|
||||
true);
|
||||
@ -814,7 +814,7 @@ PyVar VM::__run_top_frame() {
|
||||
}
|
||||
DISPATCH()
|
||||
case OP_CALL_TP: {
|
||||
if(heap._should_auto_collect()) heap._auto_collect();
|
||||
pk_ManagedHeap__collect_if_needed(&heap);
|
||||
PyVar _0;
|
||||
PyVar _1;
|
||||
PyVar _2;
|
||||
@ -1000,7 +1000,7 @@ PyVar VM::__run_top_frame() {
|
||||
}
|
||||
DISPATCH()
|
||||
case OP_UNPACK_EX: {
|
||||
auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!!
|
||||
auto _lock = gc_scope_lock(); // lock the gc via RAII!!
|
||||
PyVar _0 = py_iter(POPX());
|
||||
const PyTypeInfo* _ti = _tp_info(_0);
|
||||
PyVar _1;
|
||||
|
109
src/interpreter/gc.c
Normal file
109
src/interpreter/gc.c
Normal file
@ -0,0 +1,109 @@
|
||||
#include "pocketpy/interpreter/gc.h"
|
||||
#include "pocketpy/common/memorypool.h"
|
||||
|
||||
void pk_ManagedHeap__ctor(pk_ManagedHeap *self, pkpy_VM *vm){
|
||||
c11_vector__ctor(&self->no_gc, sizeof(PyObject*));
|
||||
c11_vector__ctor(&self->gen, sizeof(PyObject*));
|
||||
|
||||
self->gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||
self->gc_counter = 0;
|
||||
self->gc_lock_counter = 0;
|
||||
self->vm = vm;
|
||||
|
||||
self->_gc_on_delete = NULL;
|
||||
self->_gc_marker_ex = NULL;
|
||||
}
|
||||
|
||||
void pk_ManagedHeap__dtor(pk_ManagedHeap *self){
|
||||
for(int i = 0; i < self->gen.count; i++){
|
||||
PyObject* obj = c11__getitem(PyObject*, &self->gen, i);
|
||||
pk_ManagedHeap__delete_obj(self, obj);
|
||||
}
|
||||
for(int i = 0; i < self->no_gc.count; i++){
|
||||
PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i);
|
||||
pk_ManagedHeap__delete_obj(self, obj);
|
||||
}
|
||||
c11_vector__dtor(&self->no_gc);
|
||||
c11_vector__dtor(&self->gen);
|
||||
}
|
||||
|
||||
void pk_ManagedHeap__push_lock(pk_ManagedHeap *self){
|
||||
self->gc_lock_counter++;
|
||||
}
|
||||
|
||||
void pk_ManagedHeap__pop_lock(pk_ManagedHeap *self){
|
||||
self->gc_lock_counter--;
|
||||
}
|
||||
|
||||
void pk_ManagedHeap__collect_if_needed(pk_ManagedHeap *self){
|
||||
if(self->gc_counter < self->gc_threshold) return;
|
||||
if(self->gc_lock_counter > 0) return;
|
||||
self->gc_counter = 0;
|
||||
pk_ManagedHeap__collect(self);
|
||||
self->gc_threshold = self->gen.count * 2;
|
||||
if(self->gc_threshold < PK_GC_MIN_THRESHOLD){
|
||||
self->gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||
}
|
||||
}
|
||||
|
||||
int pk_ManagedHeap__collect(pk_ManagedHeap *self){
|
||||
assert(self->gc_lock_counter == 0);
|
||||
pk_ManagedHeap__mark(self);
|
||||
int freed = pk_ManagedHeap__sweep(self);
|
||||
return freed;
|
||||
}
|
||||
|
||||
int pk_ManagedHeap__sweep(pk_ManagedHeap *self){
|
||||
c11_vector alive;
|
||||
c11_vector__ctor(&alive, sizeof(PyObject*));
|
||||
c11_vector__reserve(&alive, self->gen.count / 2);
|
||||
|
||||
for(int i = 0; i < self->gen.count; i++){
|
||||
PyObject* obj = c11__getitem(PyObject*, &self->gen, i);
|
||||
if(obj->gc_marked) {
|
||||
obj->gc_marked = false;
|
||||
c11_vector__push(PyObject*, &alive, obj);
|
||||
} else {
|
||||
if(self->_gc_on_delete){
|
||||
self->_gc_on_delete(self->vm, obj);
|
||||
}
|
||||
pk_ManagedHeap__delete_obj(self, obj);
|
||||
}
|
||||
}
|
||||
|
||||
// clear _no_gc marked flag
|
||||
for(int i=0; i<self->no_gc.count; i++){
|
||||
PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i);
|
||||
obj->gc_marked = false;
|
||||
}
|
||||
|
||||
int freed = self->gen.count - alive.count;
|
||||
|
||||
// destroy old gen
|
||||
c11_vector__dtor(&self->gen);
|
||||
// move alive to gen
|
||||
self->gen = alive;
|
||||
|
||||
PoolObject_shrink_to_fit();
|
||||
return freed;
|
||||
}
|
||||
|
||||
PyObject* pk_ManagedHeap__new(pk_ManagedHeap *self, pkpy_Type type, int size, bool gc){
|
||||
PyObject* obj;
|
||||
// TODO: can we use compile time check?
|
||||
if(size <= kPoolObjectBlockSize){
|
||||
obj = PoolObject_alloc();
|
||||
PyObject__ctor(obj, type, false);
|
||||
}else{
|
||||
obj = malloc(size);
|
||||
PyObject__ctor(obj, type, true);
|
||||
}
|
||||
// TODO: can we use compile time check?
|
||||
if(gc){
|
||||
c11_vector__push(PyObject*, &self->gen, obj);
|
||||
self->gc_counter++;
|
||||
}else{
|
||||
c11_vector__push(PyObject*, &self->no_gc, obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
#include "pocketpy/interpreter/gc.hpp"
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
int ManagedHeap::sweep() {
|
||||
vector<PyObject*> alive;
|
||||
alive.reserve(gen.size() / 2);
|
||||
for(PyObject* obj: gen) {
|
||||
if(obj->gc_marked) {
|
||||
obj->gc_marked = false;
|
||||
alive.push_back(obj);
|
||||
} else {
|
||||
#if PK_DEBUG_GC_STATS
|
||||
deleted[obj->type] += 1;
|
||||
#endif
|
||||
if(_gc_on_delete) _gc_on_delete(vm, obj);
|
||||
_delete(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// clear _no_gc marked flag
|
||||
for(PyObject* obj: _no_gc)
|
||||
obj->gc_marked = false;
|
||||
|
||||
int freed = gen.size() - alive.size();
|
||||
|
||||
#if PK_DEBUG_GC_STATS
|
||||
for(auto& [type, count]: deleted) {
|
||||
std::cout << "GC: " << _type_name(vm, type).sv() << "=" << count << std::endl;
|
||||
}
|
||||
std::cout << "GC: " << alive.size() << "/" << gen.size() << " (" << freed << " freed)" << std::endl;
|
||||
deleted.clear();
|
||||
#endif
|
||||
gen.clear();
|
||||
gen.swap(alive);
|
||||
PoolObject_shrink_to_fit();
|
||||
return freed;
|
||||
}
|
||||
|
||||
void ManagedHeap::_auto_collect() {
|
||||
#if !PK_DEBUG_NO_AUTO_GC
|
||||
if(_gc_lock_counter > 0) return;
|
||||
gc_counter = 0;
|
||||
collect();
|
||||
gc_threshold = gen.size() * 2;
|
||||
if(gc_threshold < PK_GC_MIN_THRESHOLD) gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ManagedHeap::collect() {
|
||||
assert(_gc_lock_counter == 0);
|
||||
mark();
|
||||
int freed = sweep();
|
||||
return freed;
|
||||
}
|
||||
} // namespace pkpy
|
40
src/interpreter/vm.c
Normal file
40
src/interpreter/vm.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
#include "pocketpy/objects/base.h"
|
||||
|
||||
void pkpy_VM__ctor(pkpy_VM* self){
|
||||
self->True = (PyVar){
|
||||
.type=tp_bool,
|
||||
.is_ptr=true,
|
||||
.extra=1,
|
||||
._obj=pkpy_VM__gcnew(self, tp_bool)
|
||||
};
|
||||
|
||||
self->False = (PyVar){
|
||||
.type=tp_bool,
|
||||
.is_ptr=true,
|
||||
.extra=0,
|
||||
._obj=pkpy_VM__gcnew(self, tp_bool)
|
||||
};
|
||||
|
||||
self->None = (PyVar){
|
||||
.type=tp_none_type,
|
||||
.is_ptr=true,
|
||||
._obj=pkpy_VM__gcnew(self, tp_none_type)
|
||||
};
|
||||
|
||||
self->NotImplemented = (PyVar){
|
||||
.type=tp_not_implemented_type,
|
||||
.is_ptr=true,
|
||||
._obj=pkpy_VM__gcnew(self, tp_not_implemented_type)
|
||||
};
|
||||
|
||||
self->Ellipsis = (PyVar){
|
||||
.type=tp_ellipsis,
|
||||
.is_ptr=true,
|
||||
._obj=pkpy_VM__gcnew(self, tp_ellipsis)
|
||||
};
|
||||
}
|
||||
|
||||
void pkpy_VM__dtor(pkpy_VM* self){
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#include "pocketpy/interpreter/vm.hpp"
|
||||
#include "pocketpy/common/memorypool.h"
|
||||
#include "pocketpy/objects/base.h"
|
||||
#include "pocketpy/objects/public.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
@ -77,16 +78,17 @@ struct JsonSerializer {
|
||||
}
|
||||
|
||||
Str serialize() {
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
auto _lock = vm->gc_scope_lock();
|
||||
write_object(root);
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
VM::VM(bool enable_os) : heap(this), enable_os(enable_os) {
|
||||
VM::VM(bool enable_os) : enable_os(enable_os) {
|
||||
pkpy_g.vm = (pkpy_VM*)this; // setup the current VM
|
||||
Pools_initialize();
|
||||
pkpy_StrName__initialize();
|
||||
pkpy_g.vm = (pkpy_VM*)this; // setup the current VM
|
||||
pk_ManagedHeap__ctor(&heap, (pkpy_VM*)this);
|
||||
|
||||
static ::PyObject __true_obj = {tp_bool, false, false, NULL};
|
||||
static ::PyObject __false_obj = {tp_bool, false, false, NULL};
|
||||
@ -226,7 +228,7 @@ PyVar VM::exec(std::string_view source) { return exec(source, "main.py", EXEC_MO
|
||||
PyVar VM::eval(std::string_view source) { return exec(source, "<eval>", EVAL_MODE); }
|
||||
|
||||
PyObject* VM::new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled, PyTypeInfo::Vt vt) {
|
||||
PyObject* obj = heap._new<Type>(tp_type, Type(_all_types.size()));
|
||||
PyObject* obj = new_object_no_gc<Type>(tp_type, Type(_all_types.size())).get();
|
||||
const PyTypeInfo& base_info = _all_types[base];
|
||||
if(!base_info.subclass_enabled) {
|
||||
Str error = _S("type ", base_info.name.escape(), " is not `subclass_enabled`");
|
||||
@ -302,7 +304,7 @@ bool VM::py_callable(PyVar obj) {
|
||||
}
|
||||
|
||||
PyVar VM::__minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key) {
|
||||
auto _lock = heap.gc_scope_lock();
|
||||
auto _lock = gc_scope_lock();
|
||||
const Tuple& args_tuple = PK_OBJ_GET(Tuple, args); // from *args, it must be a tuple
|
||||
if(is_none(key) && args_tuple.size() == 2) {
|
||||
// fast path
|
||||
@ -328,7 +330,7 @@ PyVar VM::__minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key) {
|
||||
if((this->*op)(view[i], res)) res = view[i];
|
||||
}
|
||||
} else {
|
||||
auto _lock = heap.gc_scope_lock();
|
||||
auto _lock = gc_scope_lock();
|
||||
for(int i = 1; i < view.size(); i++) {
|
||||
PyVar a = call(key, view[i]);
|
||||
PyVar b = call(key, res);
|
||||
@ -418,11 +420,8 @@ PyObject* VM::py_import(Str path, bool throw_err) {
|
||||
}
|
||||
|
||||
VM::~VM() {
|
||||
// clear managed heap
|
||||
for(PyObject* obj: heap.gen)
|
||||
heap._delete(obj);
|
||||
for(PyObject* obj: heap._no_gc)
|
||||
heap._delete(obj);
|
||||
// destroy all objects
|
||||
pk_ManagedHeap__dtor(&heap);
|
||||
// clear everything
|
||||
callstack.clear();
|
||||
s_data.clear();
|
||||
@ -472,7 +471,7 @@ void VM::__stack_gc_mark(PyVar* begin, PyVar* end) {
|
||||
}
|
||||
|
||||
List VM::py_list(PyVar it) {
|
||||
auto _lock = heap.gc_scope_lock();
|
||||
auto _lock = gc_scope_lock();
|
||||
it = py_iter(it);
|
||||
List list;
|
||||
const PyTypeInfo* info = _tp_info(it);
|
||||
@ -566,7 +565,7 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local
|
||||
return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
|
||||
}
|
||||
|
||||
auto _lock = heap.gc_scope_lock(); // for safety
|
||||
auto _lock = gc_scope_lock(); // for safety
|
||||
|
||||
PyObject* globals_obj = nullptr;
|
||||
Dict* globals_dict = nullptr;
|
||||
@ -602,7 +601,7 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local
|
||||
locals_closure->set(CAST(Str&, k), v);
|
||||
});
|
||||
PyObject* _callable =
|
||||
heap.gcnew<Function>(tp_function, __dynamic_func_decl, globals_obj, nullptr, locals_closure);
|
||||
new_object<Function>(tp_function, __dynamic_func_decl, globals_obj, nullptr, locals_closure).get();
|
||||
retval = vm->_exec(code.get(), globals_obj, _callable, vm->s_data._sp);
|
||||
}
|
||||
|
||||
@ -722,7 +721,7 @@ PyVar VM::__format_object(PyVar obj, Str spec) {
|
||||
}
|
||||
|
||||
PyObject* VM::new_module(Str name, Str package) {
|
||||
PyObject* obj = heap._new<DummyModule>(tp_module);
|
||||
PyObject* obj = new_object_no_gc<DummyModule>(tp_module).get();
|
||||
obj->attr().set(__name__, VAR(name));
|
||||
obj->attr().set(__package__, VAR(package));
|
||||
// convert to fullname
|
||||
@ -872,8 +871,8 @@ void VM::__log_s_data(const char* title) {
|
||||
|
||||
void VM::__init_builtin_types() {
|
||||
_all_types.emplace_back(nullptr, Type(), nullptr, "", false); // 0 is not used
|
||||
_all_types.emplace_back(heap._new<Type>(tp_type, tp_object), Type(), nullptr, "object", true);
|
||||
_all_types.emplace_back(heap._new<Type>(tp_type, tp_type), tp_object, nullptr, "type", false);
|
||||
_all_types.emplace_back(new_object_no_gc<Type>(tp_type, tp_object).get(), Type(), nullptr, "object", true);
|
||||
_all_types.emplace_back(new_object_no_gc<Type>(tp_type, tp_type).get(), tp_object, nullptr, "type", false);
|
||||
|
||||
auto validate = [](Type type, PyObject* ret) {
|
||||
Type ret_t = ret->as<Type>();
|
||||
@ -945,7 +944,7 @@ void VM::__init_builtin_types() {
|
||||
}
|
||||
|
||||
void VM::__unpack_as_list(ArgsView args, List& list) {
|
||||
auto _lock = heap.gc_scope_lock();
|
||||
auto _lock = gc_scope_lock();
|
||||
for(PyVar obj: args) {
|
||||
if(is_type(obj, tp_star_wrapper)) {
|
||||
const StarWrapper& w = _CAST(StarWrapper&, obj);
|
||||
@ -965,7 +964,7 @@ void VM::__unpack_as_list(ArgsView args, List& list) {
|
||||
}
|
||||
|
||||
void VM::__unpack_as_dict(ArgsView args, Dict& dict) {
|
||||
auto _lock = heap.gc_scope_lock();
|
||||
auto _lock = gc_scope_lock();
|
||||
for(PyVar obj: args) {
|
||||
if(is_type(obj, tp_star_wrapper)) {
|
||||
const StarWrapper& w = _CAST(StarWrapper&, obj);
|
||||
@ -1359,11 +1358,11 @@ void VM::setattr(PyVar obj, StrName name, PyVar value) {
|
||||
}
|
||||
|
||||
PyObject* VM::bind_func(PyObject* obj, StrName name, int argc, NativeFuncC fn, any userdata, BindType bt) {
|
||||
PyObject* nf = heap.gcnew<NativeFunc>(tp_native_func, fn, argc, std::move(userdata));
|
||||
PyObject* nf = new_object<NativeFunc>(tp_native_func, fn, argc, std::move(userdata)).get();
|
||||
switch(bt) {
|
||||
case BindType::DEFAULT: break;
|
||||
case BindType::STATICMETHOD: nf = heap.gcnew<StaticMethod>(tp_staticmethod, nf); break;
|
||||
case BindType::CLASSMETHOD: nf = heap.gcnew<ClassMethod>(tp_classmethod, nf); break;
|
||||
case BindType::STATICMETHOD: nf = new_object<StaticMethod>(tp_staticmethod, nf).get(); break;
|
||||
case BindType::CLASSMETHOD: nf = new_object<ClassMethod>(tp_classmethod, nf).get(); break;
|
||||
}
|
||||
if(obj != nullptr) obj->attr().set(name, nf);
|
||||
return nf;
|
||||
@ -1383,11 +1382,11 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native
|
||||
|
||||
FuncDecl_ decl = co->func_decls[0];
|
||||
decl->docstring = docstring;
|
||||
PyObject* f_obj = heap.gcnew<NativeFunc>(tp_native_func, fn, decl, std::move(userdata));
|
||||
PyObject* f_obj = new_object<NativeFunc>(tp_native_func, fn, decl, std::move(userdata)).get();
|
||||
|
||||
switch(bt) {
|
||||
case BindType::STATICMETHOD: f_obj = heap.gcnew<StaticMethod>(tp_staticmethod, f_obj); break;
|
||||
case BindType::CLASSMETHOD: f_obj = heap.gcnew<ClassMethod>(tp_classmethod, f_obj); break;
|
||||
case BindType::STATICMETHOD: f_obj = new_object<StaticMethod>(tp_staticmethod, f_obj).get(); break;
|
||||
case BindType::CLASSMETHOD: f_obj = new_object<ClassMethod>(tp_classmethod, f_obj).get(); break;
|
||||
case BindType::DEFAULT: break;
|
||||
}
|
||||
if(obj != nullptr) obj->attr().set(decl->code->name, f_obj);
|
||||
@ -1402,7 +1401,7 @@ PyObject* VM::bind_property(PyObject* obj, const char* name, NativeFuncC fget, N
|
||||
PyVar _0 = new_object<NativeFunc>(tp_native_func, fget, 1);
|
||||
PyVar _1 = vm->None;
|
||||
if(fset != nullptr) _1 = new_object<NativeFunc>(tp_native_func, fset, 2);
|
||||
PyObject* prop = heap.gcnew<Property>(tp_property, _0, _1);
|
||||
PyObject* prop = new_object<Property>(tp_property, _0, _1).get();
|
||||
obj->attr().set(StrName(name_sv), prop);
|
||||
return prop;
|
||||
}
|
||||
@ -1876,29 +1875,35 @@ void Frame::_gc_mark(VM* vm) const {
|
||||
vm->obj_gc_mark(_callable);
|
||||
}
|
||||
|
||||
void ManagedHeap::mark() {
|
||||
for(PyObject* obj: _no_gc)
|
||||
vm->__obj_gc_mark(obj);
|
||||
vm->callstack.apply([this](Frame& frame) {
|
||||
frame._gc_mark(vm);
|
||||
});
|
||||
vm->obj_gc_mark(vm->__last_exception);
|
||||
vm->obj_gc_mark(vm->__curr_class);
|
||||
vm->obj_gc_mark(vm->__c.error);
|
||||
vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end());
|
||||
if(_gc_marker_ex) _gc_marker_ex(vm);
|
||||
}
|
||||
extern "C"{
|
||||
void pk_ManagedHeap__mark(pk_ManagedHeap* self){
|
||||
VM* vm = (VM*)self->vm;
|
||||
for(int i=0; i<self->no_gc.count; i++){
|
||||
PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i);
|
||||
vm->__obj_gc_mark(obj);
|
||||
}
|
||||
vm->callstack.apply([vm](Frame& frame) {
|
||||
frame._gc_mark(vm);
|
||||
});
|
||||
vm->obj_gc_mark(vm->__last_exception);
|
||||
vm->obj_gc_mark(vm->__curr_class);
|
||||
vm->obj_gc_mark(vm->__c.error);
|
||||
vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end());
|
||||
if(self->_gc_marker_ex) self->_gc_marker_ex((pkpy_VM*)vm);
|
||||
}
|
||||
|
||||
void ManagedHeap::_delete(PyObject* obj) {
|
||||
const PyTypeInfo* ti = vm->_tp_info(obj->type);
|
||||
if(ti->vt._dtor) ti->vt._dtor(obj->_value_ptr());
|
||||
if (obj->_attr)
|
||||
c11_vector__dtor(obj->_attr);
|
||||
delete obj->_attr; // delete __dict__ if exists
|
||||
if(obj->gc_is_large){
|
||||
std::free(obj);
|
||||
}else{
|
||||
PoolObject_dealloc(obj);
|
||||
void pk_ManagedHeap__delete_obj(pk_ManagedHeap* self, ::PyObject* __obj){
|
||||
PyObject* obj = (PyObject*)__obj;
|
||||
const PyTypeInfo* ti = ((VM*)(self->vm))->_tp_info(obj->type);
|
||||
if(ti->vt._dtor) ti->vt._dtor(obj->_value_ptr());
|
||||
if (obj->_attr)
|
||||
c11_vector__dtor(obj->_attr);
|
||||
delete obj->_attr; // delete __dict__ if exists
|
||||
if(obj->gc_is_large){
|
||||
std::free(obj);
|
||||
}else{
|
||||
PoolObject_dealloc(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ void add_module_io(VM* vm) {
|
||||
|
||||
void add_module_os(VM* vm) {
|
||||
PyObject* mod = vm->new_module("os");
|
||||
PyObject* path_obj = vm->heap.gcnew<DummyInstance>(VM::tp_object);
|
||||
PyObject* path_obj = vm->new_object<DummyInstance>(VM::tp_object).get();
|
||||
mod->attr().set("path", path_obj);
|
||||
|
||||
// Working directory is shared by all VMs!!
|
||||
|
@ -78,8 +78,8 @@ void add_module_sys(VM* vm) {
|
||||
vm->setattr(mod, "version", VAR(PK_VERSION));
|
||||
vm->setattr(mod, "platform", VAR(kPlatformStrings[PK_SYS_PLATFORM]));
|
||||
|
||||
PyObject* stdout_ = vm->heap.gcnew<DummyInstance>(vm->tp_object);
|
||||
PyObject* stderr_ = vm->heap.gcnew<DummyInstance>(vm->tp_object);
|
||||
PyObject* stdout_ = vm->new_object<DummyInstance>(vm->tp_object).get();
|
||||
PyObject* stderr_ = vm->new_object<DummyInstance>(vm->tp_object).get();
|
||||
vm->setattr(mod, "stdout", stdout_);
|
||||
vm->setattr(mod, "stderr", stderr_);
|
||||
|
||||
@ -240,7 +240,7 @@ void add_module_dis(VM* vm) {
|
||||
|
||||
void add_module_gc(VM* vm) {
|
||||
PyObject* mod = vm->new_module("gc");
|
||||
vm->bind_func(mod, "collect", 0, PK_LAMBDA(VAR(vm->heap.collect())));
|
||||
vm->bind_func(mod, "collect", 0, PK_LAMBDA(VAR(pk_ManagedHeap__collect(&vm->heap))));
|
||||
}
|
||||
|
||||
void add_module_enum(VM* vm) {
|
||||
|
@ -94,7 +94,7 @@ void __init_builtins(VM* _vm) {
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
_vm->bind_func(_vm->builtins, "super", -1, [](VM* vm, ArgsView args) {
|
||||
_vm->bind_func(_vm->builtins, "super", -1, [](VM* vm, ArgsView args)->PyVar {
|
||||
PyObject* class_arg = nullptr;
|
||||
PyVar self_arg = nullptr;
|
||||
if(args.size() == 2) {
|
||||
@ -122,13 +122,13 @@ void __init_builtins(VM* _vm) {
|
||||
return vm->new_object<Super>(vm->tp_super, self_arg, vm->_all_types[type].base);
|
||||
});
|
||||
|
||||
_vm->bind_func(_vm->builtins, "staticmethod", 1, [](VM* vm, ArgsView args) {
|
||||
_vm->bind_func(_vm->builtins, "staticmethod", 1, [](VM* vm, ArgsView args)->PyVar {
|
||||
PyVar func = args[0];
|
||||
vm->check_type(func, vm->tp_function);
|
||||
return vm->new_object<StaticMethod>(vm->tp_staticmethod, args[0]);
|
||||
});
|
||||
|
||||
_vm->bind_func(_vm->builtins, "classmethod", 1, [](VM* vm, ArgsView args) {
|
||||
_vm->bind_func(_vm->builtins, "classmethod", 1, [](VM* vm, ArgsView args)->PyVar {
|
||||
PyVar func = args[0];
|
||||
vm->check_type(func, vm->tp_function);
|
||||
return vm->new_object<ClassMethod>(vm->tp_classmethod, args[0]);
|
||||
@ -380,7 +380,7 @@ void __init_builtins(VM* _vm) {
|
||||
return _0._obj == _1._obj ? vm->True : vm->False;
|
||||
});
|
||||
|
||||
_vm->__cached_object_new = _vm->bind_func(VM::tp_object, __new__, 1, [](VM* vm, ArgsView args) {
|
||||
_vm->__cached_object_new = _vm->bind_func(VM::tp_object, __new__, 1, [](VM* vm, ArgsView args) ->PyVar {
|
||||
vm->check_type(args[0], vm->tp_type);
|
||||
Type t = PK_OBJ_GET(Type, args[0]);
|
||||
return vm->new_object<DummyInstance>(t);
|
||||
@ -734,7 +734,7 @@ void __init_builtins(VM* _vm) {
|
||||
});
|
||||
|
||||
_vm->bind_func(VM::tp_str, "join", 2, [](VM* vm, ArgsView args) {
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
auto _lock = vm->gc_scope_lock();
|
||||
const Str& self = _CAST(Str&, args[0]);
|
||||
SStream ss;
|
||||
PyVar it = vm->py_iter(args[1]); // strong ref
|
||||
@ -964,7 +964,7 @@ void __init_builtins(VM* _vm) {
|
||||
});
|
||||
|
||||
_vm->bind_func(VM::tp_list, "extend", 2, [](VM* vm, ArgsView args) {
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
auto _lock = vm->gc_scope_lock();
|
||||
List& self = _CAST(List&, args[0]);
|
||||
PyVar it = vm->py_iter(args[1]); // strong ref
|
||||
const PyTypeInfo* info = vm->_tp_info(args[1]);
|
||||
@ -1342,7 +1342,7 @@ void __init_builtins(VM* _vm) {
|
||||
});
|
||||
|
||||
// tp_dict
|
||||
_vm->bind_func(VM::tp_dict, __new__, -1, [](VM* vm, ArgsView args) {
|
||||
_vm->bind_func(VM::tp_dict, __new__, -1, [](VM* vm, ArgsView args)->PyVar {
|
||||
Type cls_t = PK_OBJ_GET(Type, args[0]);
|
||||
return vm->new_object<Dict>(cls_t);
|
||||
});
|
||||
@ -1350,7 +1350,7 @@ void __init_builtins(VM* _vm) {
|
||||
_vm->bind_func(VM::tp_dict, __init__, -1, [](VM* vm, ArgsView args) {
|
||||
if(args.size() == 1 + 0) return vm->None;
|
||||
if(args.size() == 1 + 1) {
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
auto _lock = vm->gc_scope_lock();
|
||||
Dict& self = PK_OBJ_GET(Dict, args[0]);
|
||||
if(is_type(args[1], vm->tp_dict)) {
|
||||
Dict& other = CAST(Dict&, args[1]);
|
||||
@ -1539,7 +1539,7 @@ void __init_builtins(VM* _vm) {
|
||||
_vm->bind_func(VM::tp_exception, __new__, -1, [](VM* vm, ArgsView args) -> PyVar {
|
||||
Type cls = PK_OBJ_GET(Type, args[0]);
|
||||
StrName cls_name = _type_name(vm, cls);
|
||||
PyObject* e_obj = vm->heap.gcnew<Exception>(cls, cls_name.index);
|
||||
PyObject* e_obj = vm->new_object<Exception>(cls, cls_name.index).get();
|
||||
e_obj->_attr = new NameDict();
|
||||
e_obj->as<Exception>().self = e_obj;
|
||||
return e_obj;
|
||||
|
@ -428,7 +428,7 @@ bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) {
|
||||
VM* vm = (VM*)vm_handle;
|
||||
PK_ASSERT_NO_ERROR()
|
||||
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
auto _lock = vm->gc_scope_lock();
|
||||
PK_PROTECTED(
|
||||
PyVar _0 = vm->py_iter(vm->s_data.popx());
|
||||
for(int i=0; i<n; i++){
|
||||
|
36
src/public.c
Normal file
36
src/public.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "pocketpy/objects/public.h"
|
||||
#include "pocketpy/objects/object.h"
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
|
||||
void py_initialize(){
|
||||
// initialize the global VM
|
||||
}
|
||||
|
||||
void py_newint(PyVar* self, int64_t val){
|
||||
self->type = tp_int;
|
||||
self->is_ptr = false;
|
||||
self->_i64 = val;
|
||||
}
|
||||
|
||||
void py_newfloat(PyVar* self, double val){
|
||||
self->type = tp_float;
|
||||
self->is_ptr = false;
|
||||
self->_f64 = val;
|
||||
}
|
||||
|
||||
void py_newbool(PyVar* self, bool val){
|
||||
// return a global singleton
|
||||
}
|
||||
|
||||
void py_newnone(PyVar* self){
|
||||
// return a heap object
|
||||
}
|
||||
|
||||
void py_newstr(PyVar* self, const char* val){
|
||||
// return a heap object
|
||||
}
|
||||
|
||||
void py_newstr2(PyVar*, const char*, int);
|
||||
|
||||
void py_newbytes(PyVar*, const uint8_t*, int);
|
||||
|
Loading…
x
Reference in New Issue
Block a user