blueloveTH 1 anno fa
parent
commit
e1e3e208cb

+ 2 - 2
docs/quick-start/misc.md

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

+ 1 - 1
include/pocketpy/interpreter/bindings.hpp

@@ -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 - 0
include/pocketpy/interpreter/gc.h

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

+ 0 - 92
include/pocketpy/interpreter/gc.hpp

@@ -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 - 0
include/pocketpy/interpreter/vm.h

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

+ 37 - 6
include/pocketpy/interpreter/vm.hpp

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

+ 15 - 0
include/pocketpy/objects/public.h

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

+ 4 - 4
src/interpreter/ceval.cpp

@@ -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 - 0
src/interpreter/gc.c

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

+ 0 - 56
src/interpreter/gc.cpp

@@ -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 - 0
src/interpreter/vm.c

@@ -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){
+
+}

+ 54 - 49
src/interpreter/vm.cpp

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

+ 1 - 1
src/modules/io.cpp

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

+ 3 - 3
src/modules/modules.cpp

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

+ 9 - 9
src/pocketpy.cpp

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

+ 1 - 1
src/pocketpy_c.cpp

@@ -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 - 0
src/public.c

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