blueloveTH пре 1 година
родитељ
комит
c416faf54d

+ 2 - 2
docs/bindings.md

@@ -155,7 +155,7 @@ struct Container{
 }
 ```
 
-Add a magic method `_gc_mark() const` to your custom type.
+Add a magic method `_gc_mark(VM*) const` to your custom type.
 
 ```cpp
 struct Container{
@@ -163,7 +163,7 @@ struct Container{
     std::vector<PyVar> b;
     // ...
 
-    void _gc_mark() const{
+    void _gc_mark(VM* vm) const{
         // mark a
         if(a) PK_OBJ_MARK(a);
 

+ 5 - 29
include/pocketpy/codeobject.h

@@ -81,7 +81,7 @@ struct CodeObject {
     }
 
     CodeObject(std::shared_ptr<SourceData> src, const Str& name);
-    void _gc_mark() const;
+    void _gc_mark(VM*) const;
 };
 
 enum class FuncType{
@@ -118,7 +118,7 @@ struct FuncDecl {
         kwargs.push_back(KwArg{index, key, value});
     }
     
-    void _gc_mark() const;
+    void _gc_mark(VM*) const;
 };
 
 struct NativeFunc {
@@ -130,8 +130,9 @@ struct NativeFunc {
     NativeFunc(NativeFuncC f, int argc, any userdata={}): f(f), argc(argc), decl(nullptr), _userdata(std::move(userdata)) {}
     NativeFunc(NativeFuncC f, FuncDecl_ decl, any userdata={}): f(f), argc(-1), decl(decl), _userdata(std::move(userdata)) {}
 
-    void check_size(VM* vm, ArgsView args) const;
     PyVar call(VM* vm, ArgsView args) const { return f(vm, args); }
+    void check_size(VM* vm, ArgsView args) const;
+    void _gc_mark(VM*) const;
 };
 
 struct Function{
@@ -142,33 +143,8 @@ struct Function{
 
     explicit Function(FuncDecl_ decl, PyVar _module, PyVar _class, NameDict_ _closure):
         decl(decl), _module(_module), _class(_class), _closure(_closure) {}
-};
 
-template<>
-struct Py_<Function> final: PyObject {
-    Function _value;
-    template<typename... Args>
-    Py_(Args&&... args): PyObject(), _value(std::forward<Args>(args)...) {
-        // _enable_instance_dict();
-    }
-    void _obj_gc_mark() override {
-        _value.decl->_gc_mark();
-        if(_value._closure != nullptr) _gc_mark_namedict(_value._closure.get());
-    }
-};
-
-template<>
-struct Py_<NativeFunc> final: PyObject {
-    NativeFunc _value;
-    template<typename... Args>
-    Py_(Args&&... args): PyObject(), _value(std::forward<Args>(args)...) {
-        // _enable_instance_dict();
-    }
-    void _obj_gc_mark() override {
-        if(_value.decl != nullptr){
-            _value.decl->_gc_mark();
-        }
-    }
+    void _gc_mark(VM*) const;
 };
 
 template<typename T>

+ 2 - 0
include/pocketpy/common.h

@@ -169,6 +169,8 @@ inline constexpr bool is_floating_point_v = std::is_same_v<T, float> || std::is_
 inline const char* PK_HEX_TABLE = "0123456789abcdef";
 
 struct PyObject;
+struct Frame;
+class VM;
 
 // by default, only `int` and `float` enable SSO
 // users can specialize this template to enable SSO for other types

+ 1 - 1
include/pocketpy/dict.h

@@ -66,7 +66,7 @@ struct Dict{
     void clear();
     ~Dict();
 
-    void _gc_mark() const;
+    void _gc_mark(VM*) const;
 };
 
 } // namespace pkpy

+ 1 - 6
include/pocketpy/frame.h

@@ -117,12 +117,7 @@ struct Frame {
 
     int curr_lineno() const { return co->lines[ip()].lineno; }
 
-    void _gc_mark() const {
-        PK_OBJ_MARK(_module);
-        co->_gc_mark();
-        // Frame could be stored in a generator, so mark _callable for safety
-        if(_callable != nullptr) PK_OBJ_MARK(_callable);
-    }
+    void _gc_mark(VM* vm) const;
 };
 
 struct LinkedFrame{

+ 2 - 1
include/pocketpy/gc.h

@@ -61,6 +61,8 @@ struct ManagedHeap{
         return obj;
     }
 
+    void _delete(PyVar);
+
 #if PK_DEBUG_GC_STATS
     inline static std::map<Type, int> deleted;
 #endif
@@ -70,7 +72,6 @@ struct ManagedHeap{
     bool _should_auto_collect() const { return gc_counter >= gc_threshold; }
     int collect();
     void mark();
-    ~ManagedHeap();
 };
 
 }   // namespace pkpy

+ 5 - 5
include/pocketpy/iter.h

@@ -23,7 +23,7 @@ struct ArrayIter{
     ArrayIter(PyVar ref, PyVar* begin, PyVar* end)
         : ref(ref), begin(begin), end(end), current(begin) {}
 
-    void _gc_mark() const{ PK_OBJ_MARK(ref); }
+    void _gc_mark(VM* vm) const{ PK_OBJ_MARK(ref); }
     static void _register(VM* vm, PyVar mod, PyVar type);
 };
 
@@ -31,7 +31,7 @@ struct StringIter{
     PyVar ref;
     int i;      // byte index
     StringIter(PyVar ref) : ref(ref), i(0) {}
-    void _gc_mark() const{ PK_OBJ_MARK(ref); }
+    void _gc_mark(VM* vm) const{ PK_OBJ_MARK(ref); }
     static void _register(VM* vm, PyVar mod, PyVar type);
 };
 
@@ -44,8 +44,8 @@ struct Generator{
         for(PyVar obj: buffer) s_backup.push_back(obj);
     }
 
-    void _gc_mark() const{
-        frame._gc_mark();
+    void _gc_mark(VM* vm) const{
+        frame._gc_mark(vm);
         for(PyVar obj: s_backup) PK_OBJ_MARK(obj);
     }
 
@@ -59,7 +59,7 @@ struct DictItemsIter{
     DictItemsIter(PyVar ref) : ref(ref) {
         i = PK_OBJ_GET(Dict, ref)._head_idx;
     }
-    void _gc_mark() const{ PK_OBJ_MARK(ref); }
+    void _gc_mark(VM* vm) const{ PK_OBJ_MARK(ref); }
     static void _register(VM* vm, PyVar mod, PyVar type);
 };
 

+ 22 - 120
include/pocketpy/obj.h

@@ -6,9 +6,6 @@
 
 namespace pkpy {
     
-struct Frame;
-class VM;
-
 #if PK_ENABLE_STD_FUNCTION
 using NativeFuncC = std::function<PyVar(VM*, ArgsView)>;
 #else
@@ -25,22 +22,26 @@ struct BoundMethod {
     PyVar self;
     PyVar func;
     BoundMethod(PyVar self, PyVar func) : self(self), func(func) {}
+    void _gc_mark(VM*) const;
 };
 
 struct StaticMethod{
     PyVar func;
     StaticMethod(PyVar func) : func(func) {}
+    void _gc_mark(VM*) const;
 };
 
 struct ClassMethod{
     PyVar func;
     ClassMethod(PyVar func) : func(func) {}
+    void _gc_mark(VM*) const;
 };
 
 struct Property{
     PyVar getter;
     PyVar setter;
     Property(PyVar getter, PyVar setter) : getter(getter), setter(setter) {}
+    void _gc_mark(VM*) const;
 };
 
 struct Range {
@@ -53,6 +54,7 @@ struct StarWrapper{
     int level;      // either 1 or 2
     PyVar obj;
     StarWrapper(int level, PyVar obj) : level(level), obj(obj) {}
+    void _gc_mark(VM*) const;
 };
 
 struct Bytes{
@@ -84,13 +86,19 @@ struct Bytes{
     ~Bytes(){ delete[] _data;}
 };
 
-using Super = std::pair<PyVar, Type>;
+struct Super{
+    PyVar first;
+    Type second;
+    Super(PyVar first, Type second) : first(first), second(second) {}
+    void _gc_mark(VM*) const;
+};
 
 struct Slice {
     PyVar start;
     PyVar stop;
     PyVar step;
     Slice(PyVar start, PyVar stop, PyVar step) : start(start), stop(stop), step(step) {}
+    void _gc_mark(VM*) const;
 };
 
 struct PyObject{
@@ -98,6 +106,8 @@ struct PyObject{
     bool gc_marked;     // whether this object is marked
     NameDict* _attr;
 
+    void* _value_ptr() noexcept { return 1 + &_attr; }
+
     bool is_attr_valid() const noexcept { return _attr != nullptr; }
 
     NameDict& attr() {
@@ -110,9 +120,6 @@ struct PyObject{
         return (*_attr)[name];
     }
 
-    virtual void _obj_gc_mark() = 0;
-    virtual ~PyObject();
-
     PyObject() : gc_enabled(true), gc_marked(false), _attr(nullptr) {}
 
     void _enable_instance_dict() {
@@ -124,6 +131,9 @@ struct PyObject{
     }
 };
 
+template<typename T>
+inline constexpr int py_sizeof = sizeof(PyObject) + sizeof(T);
+
 const int kTpIntIndex = 3;
 const int kTpFloatIndex = 4;
 
@@ -143,14 +153,7 @@ template <typename T>
 struct Py_ final: PyObject {
     static_assert(!std::is_reference_v<T>);
     static_assert(!is_sso_v<T>);
-
     T _value;
-    void _obj_gc_mark() override {
-        if constexpr (has_gc_marker<T>::value) {
-            _value._gc_mark();
-        }
-    }
-
     template <typename... Args>
     Py_(Args&&... args) : PyObject(), _value(std::forward<Args>(args)...) { }
 };
@@ -159,9 +162,9 @@ struct MappingProxy{
     PyVar obj;
     MappingProxy(PyVar obj) : obj(obj) {}
     NameDict& attr() { return obj->attr(); }
+    void _gc_mark(VM*) const;
 };
 
-void _gc_mark_namedict(NameDict*);
 StrName _type_name(VM* vm, Type type);
 template<typename T> T to_void_p(VM*, PyVar);
 PyVar from_void_p(VM*, void*);
@@ -181,9 +184,7 @@ obj_get_t<T> PyVar::obj_get(){
 
 #define PK_OBJ_MARK(obj) \
     if(!is_tagged(obj) && !(obj)->gc_marked) {                          \
-        (obj)->gc_marked = true;                                        \
-        (obj)->_obj_gc_mark();                                          \
-        if((obj)->is_attr_valid()) _gc_mark_namedict((obj)->_attr);     \
+        vm->__obj_gc_mark(obj);                                         \
     }
 
 #define VAR(x) py_var(vm, x)
@@ -202,109 +203,12 @@ inline bool try_cast_int(PyVar obj, i64* val) noexcept {
     return false;
 }
 
-template<>
-struct Py_<List> final: PyObject {
-    List _value;
-    Py_(List&& val): PyObject(), _value(std::move(val)) {}
-    Py_(const List& val): PyObject(), _value(val) {}
-
-    void _obj_gc_mark() override {
-        for(PyVar obj: _value) PK_OBJ_MARK(obj);
-    }
-};
-
-template<>
-struct Py_<Tuple> final: PyObject {
-    Tuple _value;
-    Py_(Tuple&& val): PyObject(), _value(std::move(val)) {}
-    Py_(const Tuple& val): PyObject(), _value(val) {}
-
-    void _obj_gc_mark() override {
-        for(PyVar obj: _value) PK_OBJ_MARK(obj);
-    }
-};
-
-template<>
-struct Py_<MappingProxy> final: PyObject {
-    MappingProxy _value;
-    Py_(MappingProxy val): PyObject(), _value(val) {}
-    void _obj_gc_mark() override {
-        PK_OBJ_MARK(_value.obj);
-    }
-};
-
-template<>
-struct Py_<BoundMethod> final: PyObject {
-    BoundMethod _value;
-    Py_(BoundMethod val): PyObject(), _value(val) {}
-    void _obj_gc_mark() override {
-        PK_OBJ_MARK(_value.self);
-        PK_OBJ_MARK(_value.func);
-    }
-};
-
-template<>
-struct Py_<StarWrapper> final: PyObject {
-    StarWrapper _value;
-    Py_(StarWrapper val): PyObject(), _value(val) {}
-    void _obj_gc_mark() override {
-        PK_OBJ_MARK(_value.obj);
-    }
-};
-
-template<>
-struct Py_<StaticMethod> final: PyObject {
-    StaticMethod _value;
-    Py_(StaticMethod val): PyObject(), _value(val) {}
-    void _obj_gc_mark() override {
-        PK_OBJ_MARK(_value.func);
-    }
-};
-
-template<>
-struct Py_<ClassMethod> final: PyObject {
-    ClassMethod _value;
-    Py_(ClassMethod val): PyObject(), _value(val) {}
-    void _obj_gc_mark() override {
-        PK_OBJ_MARK(_value.func);
-    }
-};
-
-template<>
-struct Py_<Property> final: PyObject {
-    Property _value;
-    Py_(Property val): PyObject(), _value(val) {}
-    void _obj_gc_mark() override {
-        PK_OBJ_MARK(_value.getter);
-        PK_OBJ_MARK(_value.setter);
-    }
-};
-
-template<>
-struct Py_<Slice> final: PyObject {
-    Slice _value;
-    Py_(Slice val): PyObject(), _value(val) {}
-    void _obj_gc_mark() override {
-        PK_OBJ_MARK(_value.start);
-        PK_OBJ_MARK(_value.stop);
-        PK_OBJ_MARK(_value.step);
-    }
-};
-
-template<>
-struct Py_<Super> final: PyObject {
-    Super _value;
-    template<typename... Args>
-    Py_(Args&&... args): PyObject(), _value(std::forward<Args>(args)...) {}
-    void _obj_gc_mark() override {
-        PK_OBJ_MARK(_value.first);
-    }
-};
 
 template<>
 struct Py_<DummyInstance> final: PyObject {
-    Py_(): PyObject() { _enable_instance_dict(); }
-    void _obj_gc_mark() override {}
+    Py_(): PyObject() {
+        _enable_instance_dict();
+    }
 };
 
 template<>
@@ -313,7 +217,6 @@ struct Py_<Type> final: PyObject {
     Py_(Type val): PyObject(), _value(val) {
         _enable_instance_dict(PK_TYPE_ATTR_LOAD_FACTOR);
     }
-    void _obj_gc_mark() override {}
 };
 
 template<>
@@ -321,7 +224,6 @@ struct Py_<DummyModule> final: PyObject {
     Py_(): PyObject() {
         _enable_instance_dict(PK_TYPE_ATTR_LOAD_FACTOR);
     }
-    void _obj_gc_mark() override {}
 };
 
 extern PyVar const PY_NULL;

+ 5 - 1
include/pocketpy/tuplelist.h

@@ -7,7 +7,10 @@
 
 namespace pkpy {
 
-using List = pod_vector<PyVar, 4>;
+struct List: pod_vector<PyVar, 4>{
+    using pod_vector<PyVar, 4>::pod_vector;
+    void _gc_mark(VM*) const;
+};
 
 struct Tuple {
     static const int INLINED_SIZE = 4;
@@ -35,6 +38,7 @@ struct Tuple {
     PyVar* begin() const { return _args; }
     PyVar* end() const { return _args + _size; }
     PyVar* data() const { return _args; }
+    void _gc_mark(VM*) const;
 };
 
 // a lightweight view for function args, it does not own the memory

+ 32 - 2
include/pocketpy/vm.h

@@ -42,12 +42,36 @@ struct NextBreakpoint{
 #endif
 
 struct PyTypeInfo{
+    struct Vt{
+        void (*_dtor)(void*);
+        void (*_gc_mark)(void*, VM*);
+
+        Vt(): _dtor(nullptr), _gc_mark(nullptr) {}
+
+        template<typename T>
+        inline static Vt get(){
+            static_assert(std::is_same_v<T, std::decay_t<T>>);
+            Vt vt;
+            if constexpr(!std::is_trivially_destructible_v<T>){
+                vt._dtor = [](void* p){ ((T*)p)->~T(); };
+            }
+            if constexpr(has_gc_marker<T>::value){
+                vt._gc_mark = [](void* p, VM* vm){ ((T*)p)->_gc_mark(vm); };
+            }
+            return vt;
+        }
+    };
+
     PyVar obj;      // never be garbage collected
     Type base;
     PyVar mod;      // never be garbage collected
     StrName name;
     bool subclass_enabled;
+    Vt vt;
 
+    PyTypeInfo(PyVar obj, Type base, PyVar mod, StrName name, bool subclass_enabled, Vt vt={}):
+        obj(obj), base(base), mod(mod), name(name), subclass_enabled(subclass_enabled), vt(vt) {}
+    
     std::vector<StrName> annotated_fields = {};
 
     // unary operators
@@ -386,7 +410,12 @@ public:
 
 #if PK_REGION("User Type Registration")
     PyVar new_module(Str name, Str package="");
-    PyVar new_type_object(PyVar mod, StrName name, Type base, bool subclass_enabled=true);
+    PyVar new_type_object(PyVar mod, StrName name, Type base, bool subclass_enabled, PyTypeInfo::Vt vt={});
+
+    template<typename T>
+    PyVar new_type_object(PyVar mod, StrName name, Type base, bool subclass_enabled){
+        return new_type_object(mod, name, base, subclass_enabled, PyTypeInfo::Vt::get<T>());
+    }
 
     template<typename T>
     Type _tp_user(){ return _find_type_in_cxx_typeid_map<T>(); }
@@ -455,6 +484,7 @@ public:
     PyVar __pack_next_retval(unsigned);
     PyVar __minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key);
     bool __py_bool_non_trivial(PyVar);
+    void __obj_gc_mark(PyVar);
 };
 
 
@@ -595,7 +625,7 @@ __T _py_cast(VM* vm, PyVar obj) { return _py_cast__internal<__T, false>(vm, obj)
 
 template<typename T>
 PyVar VM::register_user_class(PyVar mod, StrName name, RegisterFunc _register, Type base, bool subclass_enabled){
-    PyVar type = new_type_object(mod, name, base, subclass_enabled);
+    PyVar type = new_type_object(mod, name, base, subclass_enabled, PyTypeInfo::Vt::get<T>());
     mod->attr().set(name, type);
     _cxx_typeid_map[typeid(T)] = PK_OBJ_GET(Type, type);
     _register(this, mod, type);

+ 2 - 2
src/array2d.cpp

@@ -332,7 +332,7 @@ struct Array2d{
         });
     }
 
-    void _gc_mark() const{
+    void _gc_mark(VM* vm) const{
         for(int i = 0; i < numel; i++) PK_OBJ_MARK(data[i]);
     }
 
@@ -349,7 +349,7 @@ struct Array2dIter{
     int i;
     Array2dIter(PyVar ref) : ref(ref), i(0) {}
 
-    void _gc_mark() const{ PK_OBJ_MARK(ref); }
+    void _gc_mark(VM* vm) const{ PK_OBJ_MARK(ref); }
 
     static void _register(VM* vm, PyVar mod, PyVar type){
         vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM* vm, PyVar _0) { return _0; });

+ 1 - 1
src/ceval.cpp

@@ -924,7 +924,7 @@ __NEXT_STEP:
         PyVar _0 = POPX();   // super
         if(_0 == None) _0 = _t(tp_object);
         check_type(_0, tp_type);
-        __curr_class = new_type_object(frame->_module, _name, PK_OBJ_GET(Type, _0));
+        __curr_class = new_type_object(frame->_module, _name, PK_OBJ_GET(Type, _0), true);
     } DISPATCH()
     case OP_END_CLASS: {
         PK_ASSERT(__curr_class != nullptr);

+ 16 - 16
src/cffi.cpp

@@ -193,48 +193,48 @@ void add_module_c(VM* vm){
 #define BIND_PRIMITIVE(T, CNAME) \
     vm->bind_func(mod, CNAME "_", 1, [](VM* vm, ArgsView args){         \
         T val = CAST(T, args[0]);                                       \
-        return vm->new_user_object<Struct>(&val, sizeof(T));                       \
-    });                                                                 \
-    type = vm->new_type_object(mod, CNAME "_p", vm->_tp_user<VoidP>()); \
+        return vm->new_user_object<Struct>(&val, sizeof(T));                        \
+    });                                                                             \
+    type = vm->new_type_object(mod, CNAME "_p", vm->_tp_user<VoidP>(), true);       \
     mod->attr().set(CNAME "_p", type);                                  \
     type_t = PK_OBJ_GET(Type, type);                                    \
-    vm->bind_func(type, "read", 1, [](VM* vm, ArgsView args){         \
-        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]);                      \
+    vm->bind_func(type, "read", 1, [](VM* vm, ArgsView args){           \
+        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]);            \
         T* target = (T*)voidp.ptr;                                      \
         return VAR(*target);                                            \
     });                                                                 \
-    vm->bind_func(type, "write", 2, [](VM* vm, ArgsView args){        \
-        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]);                      \
+    vm->bind_func(type, "write", 2, [](VM* vm, ArgsView args){          \
+        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, args[0]);            \
         T val = CAST(T, args[1]);                                       \
         T* target = (T*)voidp.ptr;                                      \
         *target = val;                                                  \
         return vm->None;                                                \
     });                                                                 \
-    vm->bind__getitem__(type_t, [](VM* vm, PyVar obj, PyVar index){  \
-        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, obj);                               \
+    vm->bind__getitem__(type_t, [](VM* vm, PyVar obj, PyVar index){     \
+        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, obj);                \
         i64 offset = CAST(i64, index);                                  \
         T* target = (T*)voidp.ptr;                                      \
         return VAR(target[offset]);                                     \
     });                                                                 \
-    vm->bind__setitem__(type_t, [](VM* vm, PyVar obj, PyVar index, PyVar value){   \
-        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, obj);                          \
+    vm->bind__setitem__(type_t, [](VM* vm, PyVar obj, PyVar index, PyVar value){    \
+        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, obj);                            \
         i64 offset = CAST(i64, index);                                  \
         T* target = (T*)voidp.ptr;                                      \
         target[offset] = CAST(T, value);                                \
     });                                                                 \
-    vm->bind__add__(type_t, [](VM* vm, PyVar lhs, PyVar rhs){   \
-        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, lhs);                          \
+    vm->bind__add__(type_t, [](VM* vm, PyVar lhs, PyVar rhs){           \
+        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, lhs);                \
         i64 offset = CAST(i64, rhs);                                    \
         T* target = (T*)voidp.ptr;                                      \
         return vm->new_object<VoidP>(lhs.type, target + offset);        \
     });                                                                 \
-    vm->bind__sub__(type_t, [](VM* vm, PyVar lhs, PyVar rhs){   \
-        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, lhs);                          \
+    vm->bind__sub__(type_t, [](VM* vm, PyVar lhs, PyVar rhs){           \
+        obj_get_t<VoidP> voidp = PK_OBJ_GET(VoidP, lhs);                \
         i64 offset = CAST(i64, rhs);                                    \
         T* target = (T*)voidp.ptr;                                      \
         return vm->new_object<VoidP>(lhs.type, target - offset);        \
     });                                                                 \
-    vm->bind__repr__(type_t, [](VM* vm, PyVar obj) -> Str{          \
+    vm->bind__repr__(type_t, [](VM* vm, PyVar obj) -> Str{              \
         VoidP& self = _CAST(VoidP&, obj);                               \
         return _S("<", CNAME, "* at ", self.hex(), ">");                \
     });                                                                 \

+ 0 - 6
src/codeobject.cpp

@@ -7,14 +7,8 @@ namespace pkpy{
             blocks.push_back(CodeBlock(CodeBlockType::NO_BLOCK, -1, 0, 0));
         }
 
-    void CodeObject::_gc_mark() const {
-        for(PyVar v : consts) PK_OBJ_MARK(v);
-        for(auto& decl: func_decls) decl->_gc_mark();
-    }
-
     struct PySignalObject: PyObject {
         PySignalObject() : PyObject() { gc_enabled = false; }
-        void _obj_gc_mark() override {}
     };
 
     PyVar const PY_NULL(Type(), new PySignalObject());

+ 3 - 3
src/collections.cpp

@@ -18,7 +18,7 @@ namespace pkpy
         {
             this->is_reversed = true;
         }
-        void _gc_mark() const { PK_OBJ_MARK(ref); }
+        void _gc_mark(VM* vm) const { PK_OBJ_MARK(ref); }
         static void _register(VM *vm, PyVar mod, PyVar type);
     };
     void PyDequeIter::_register(VM *vm, PyVar mod, PyVar type)
@@ -53,7 +53,7 @@ namespace pkpy
         int findIndex(VM *vm, PyVar obj, int start, int stop);        // find the index of the given object in the deque
         // Special methods
         static void _register(VM *vm, PyVar mod, PyVar type); // register the type
-        void _gc_mark() const;                                        // needed for container types, mark all objects in the deque for gc
+        void _gc_mark(VM*) const;                                        // needed for container types, mark all objects in the deque for gc
     };
     void PyDeque::_register(VM *vm, PyVar mod, PyVar type)
     {
@@ -532,7 +532,7 @@ namespace pkpy
         }
     }
     /// @brief marks the deque items for garbage collection
-    void PyDeque::_gc_mark() const
+    void PyDeque::_gc_mark(VM* vm) const
     {
         for (PyVar obj : this->dequeItems)
             PK_OBJ_MARK(obj);

+ 0 - 8
src/dict.cpp

@@ -169,12 +169,4 @@ namespace pkpy{
         pool128_dealloc(_items);
         pool64_dealloc(_nodes);
     }
-
-    void Dict::_gc_mark() const{
-        apply([](PyVar k, PyVar v){
-            PK_OBJ_MARK(k);
-            PK_OBJ_MARK(v);
-        });
-    }
-
 }   // namespace pkpy

+ 1 - 14
src/gc.cpp

@@ -14,8 +14,7 @@ namespace pkpy{
                 deleted[obj.type] += 1;
 #endif
                 if(_gc_on_delete) _gc_on_delete(vm, obj);
-                obj->~PyObject();
-                pool128_dealloc(obj.get());
+                _delete(obj);
             }
         }
 
@@ -54,16 +53,4 @@ namespace pkpy{
         int freed = sweep();
         return freed;
     }
-
-    ManagedHeap::~ManagedHeap(){
-        for(PyVar obj: _no_gc) { obj->~PyObject(); pool128_dealloc(obj.get()); }
-        for(PyVar obj: gen) { obj->~PyObject(); pool128_dealloc(obj.get()); }
-    }
-
-
-void FuncDecl::_gc_mark() const{
-    code->_gc_mark();
-    for(int i=0; i<kwargs.size(); i++) PK_OBJ_MARK(kwargs[i].value);
-}
-
 }   // namespace pkpy

+ 0 - 7
src/obj.cpp

@@ -46,11 +46,4 @@ namespace pkpy{
         _size = 0;
         return {p, size};
     }
-
-    PyObject::~PyObject(){
-        if(_attr == nullptr) return;
-        _attr->~NameDict();
-        pool128_dealloc(_attr);
-    }
-
 }   // namespace pkpy

+ 156 - 59
src/vm.cpp

@@ -205,21 +205,14 @@ namespace pkpy{
         return exec(source, "<eval>", EVAL_MODE);
     }
 
-    PyVar VM::new_type_object(PyVar mod, StrName name, Type base, bool subclass_enabled){
+    PyVar VM::new_type_object(PyVar mod, StrName name, Type base, bool subclass_enabled, PyTypeInfo::Vt vt){
         PyVar obj = heap._new<Type>(tp_type, Type(_all_types.size()));
         const PyTypeInfo& base_info = _all_types[base];
         if(!base_info.subclass_enabled){
             Str error = _S("type ", base_info.name.escape(), " is not `subclass_enabled`");
             throw std::runtime_error(error.c_str());
         }
-        PyTypeInfo info{
-            obj,
-            base,
-            mod,
-            name,
-            subclass_enabled,
-        };
-        _all_types.push_back(info);
+        _all_types.emplace_back(obj, base, mod, name, subclass_enabled, vt);
         return obj;
     }
 
@@ -404,6 +397,10 @@ namespace pkpy{
     }
 
     VM::~VM() {
+        // clear managed heap
+        for(PyVar obj: heap.gen) heap._delete(obj);
+        for(PyVar obj: heap._no_gc) heap._delete(obj);
+        // clear everything
         callstack.clear();
         s_data.clear();
         _all_types.clear();
@@ -430,6 +427,17 @@ bool VM::__py_bool_non_trivial(PyVar obj){
     return true;
 }
 
+void VM::__obj_gc_mark(PyVar obj){
+    obj->gc_marked = true;
+    const PyTypeInfo* ti = _tp_info(obj);
+    if(ti->vt._gc_mark) ti->vt._gc_mark(obj->_value_ptr(), this);
+    if(obj->is_attr_valid()){
+        obj->attr().apply([this](StrName _, PyVar obj){
+            PK_OBJ_MARK(obj);
+        });
+    }
+}
+
 List VM::py_list(PyVar it){
     auto _lock = heap.gc_scope_lock();
     it = py_iter(it);
@@ -823,49 +831,49 @@ void VM::__log_s_data(const char* title) {
 #endif
 
 void VM::__init_builtin_types(){
-    _all_types.push_back({nullptr, Type(), nullptr, "", false});    // 0 is not used
-    _all_types.push_back({heap._new<Type>(tp_type, tp_object), Type(), nullptr, "object", true});
-    _all_types.push_back({heap._new<Type>(tp_type, tp_type), tp_object, nullptr, "type", false});
+    _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);
 
-    auto _new_type = [this](const char* name, Type base=tp_object, bool subclass_enabled=false){
-        PyVar obj = new_type_object(nullptr, name, base, subclass_enabled);
-        return PK_OBJ_GET(Type, obj);
+    auto validate = [](Type type, PyVar ret){
+        Type ret_t = PK_OBJ_GET(Type, ret);
+        if(ret_t != type) exit(-3);
     };
 
-    if(tp_int != _new_type("int")) exit(-3);
-    if((tp_float != _new_type("float"))) exit(-3);
+    validate(tp_int, new_type_object(nullptr, "int", tp_object, false));
+    validate(tp_float, new_type_object(nullptr, "float", tp_object, false));
+    validate(tp_bool, new_type_object(nullptr, "bool", tp_object, false));
 
-    if(tp_bool != _new_type("bool")) exit(-3);
-    if(tp_str != _new_type("str")) exit(-3);
-    if(tp_list != _new_type("list")) exit(-3);
-    if(tp_tuple != _new_type("tuple")) exit(-3);
+    validate(tp_str, new_type_object<Str>(nullptr, "str", tp_object, false));
+    validate(tp_list, new_type_object<List>(nullptr, "list", tp_object, false));
+    validate(tp_tuple, new_type_object<Tuple>(nullptr, "tuple", tp_object, false));
 
-    if(tp_slice != _new_type("slice")) exit(-3);
-    if(tp_range != _new_type("range")) exit(-3);
-    if(tp_module != _new_type("module")) exit(-3);
-    if(tp_function != _new_type("function")) exit(-3);
-    if(tp_native_func != _new_type("native_func")) exit(-3);
-    if(tp_bound_method != _new_type("bound_method")) exit(-3);
+    validate(tp_slice, new_type_object<Slice>(nullptr, "slice", tp_object, false));
+    validate(tp_range, new_type_object<Range>(nullptr, "range", tp_object, false));
+    validate(tp_module, new_type_object<DummyModule>(nullptr, "module", tp_object, false));
+    validate(tp_function, new_type_object<Function>(nullptr, "function", tp_object, false));
+    validate(tp_native_func, new_type_object<NativeFunc>(nullptr, "native_func", tp_object, false));
+    validate(tp_bound_method, new_type_object<BoundMethod>(nullptr, "bound_method", tp_object, false));
 
-    if(tp_super != _new_type("super")) exit(-3);
-    if(tp_exception != _new_type("Exception", tp_object, true)) exit(-3);
-    if(tp_bytes != _new_type("bytes")) exit(-3);
-    if(tp_mappingproxy != _new_type("mappingproxy")) exit(-3);
-    if(tp_dict != _new_type("dict", tp_object, true)) exit(-3);  // dict can be subclassed
-    if(tp_property != _new_type("property")) exit(-3);
-    if(tp_star_wrapper != _new_type("_star_wrapper")) exit(-3);
+    validate(tp_super, new_type_object<Super>(nullptr, "super", tp_object, false));
+    validate(tp_exception, new_type_object<Exception>(nullptr, "Exception", tp_object, true));
+    validate(tp_bytes, new_type_object<Bytes>(nullptr, "bytes", tp_object, false));
+    validate(tp_mappingproxy, new_type_object<MappingProxy>(nullptr, "mappingproxy", tp_object, false));
+    validate(tp_dict, new_type_object<Dict>(nullptr, "dict", tp_object, true));
+    validate(tp_property, new_type_object<Property>(nullptr, "property", tp_object, false));
+    validate(tp_star_wrapper, new_type_object<StarWrapper>(nullptr, "_star_wrapper", tp_object, false));
 
-    if(tp_staticmethod != _new_type("staticmethod")) exit(-3);
-    if(tp_classmethod != _new_type("classmethod")) exit(-3);
+    validate(tp_staticmethod, new_type_object<StaticMethod>(nullptr, "staticmethod", tp_object, false));
+    validate(tp_classmethod, new_type_object<ClassMethod>(nullptr, "classmethod", tp_object, false));
 
-    if(tp_none != _new_type("NoneType")) exit(-3);
-    if(tp_not_implemented != _new_type("NotImplementedType")) exit(-3);
-    if(tp_ellipsis != _new_type("ellipsis")) exit(-3);
+    validate(tp_none, new_type_object(nullptr, "NoneType", tp_object, false));
+    validate(tp_not_implemented, new_type_object(nullptr, "NotImplementedType", tp_object, false));
+    validate(tp_ellipsis, new_type_object(nullptr, "ellipsis", tp_object, false));
 
     // SyntaxError and IndentationError must be created here
-    Type tp_syntax_error = _new_type("SyntaxError", tp_exception, true);
-    Type tp_indentation_error = _new_type("IndentationError", tp_syntax_error, true);
-    this->StopIteration = _all_types[_new_type("StopIteration", tp_exception)].obj;
+    PyVar SyntaxError = new_type_object(nullptr, "SyntaxError", tp_exception, true);
+    PyVar IndentationError = new_type_object(nullptr, "IndentationError", PK_OBJ_GET(Type, SyntaxError), true);
+    this->StopIteration = new_type_object(nullptr, "StopIteration", tp_exception, true);
 
     this->builtins = new_module("builtins");
     
@@ -886,8 +894,8 @@ void VM::__init_builtin_types(){
     builtins->attr().set("NotImplemented", NotImplemented);
     builtins->attr().set("slice", _t(tp_slice));
     builtins->attr().set("Exception", _t(tp_exception));
-    builtins->attr().set("SyntaxError", _t(tp_syntax_error));
-    builtins->attr().set("IndentationError", _t(tp_indentation_error));
+    builtins->attr().set("SyntaxError", SyntaxError);
+    builtins->attr().set("IndentationError", IndentationError);
 
     __post_init_builtin_types();
     this->_main = new_module("__main__");
@@ -1414,26 +1422,11 @@ void VM::__raise_exc(bool re_raise){
     }
 }
 
-void ManagedHeap::mark() {
-    for(PyVar obj: _no_gc) PK_OBJ_MARK(obj);
-    vm->callstack.apply([](Frame& frame){ frame._gc_mark(); });
-    for(PyVar obj: vm->s_data) PK_OBJ_MARK(obj);
-    for(auto [_, co]: vm->__cached_codes) co->_gc_mark();
-    if(vm->__last_exception) PK_OBJ_MARK(vm->__last_exception);
-    if(vm->__curr_class) PK_OBJ_MARK(vm->__curr_class);
-    if(vm->__c.error != nullptr) PK_OBJ_MARK(vm->__c.error);
-    if(_gc_marker_ex) _gc_marker_ex(vm);
-}
 
 StrName _type_name(VM *vm, Type type){
     return vm->_all_types[type].name;
 }
 
-void _gc_mark_namedict(NameDict* t){
-    t->apply([](StrName name, PyVar obj){
-        PK_OBJ_MARK(obj);
-    });
-}
 
 void VM::bind__getitem__(Type type, PyVar (*f)(VM*, PyVar, PyVar)){
     _all_types[type].m__getitem__ = f;
@@ -1757,4 +1750,108 @@ void VM::__breakpoint(){
 #endif
 }
 
+/**************************************************************************/
+void Function::_gc_mark(VM* vm) const{
+    decl->_gc_mark(vm);
+    if(_closure){
+        _closure->apply([=](StrName _, PyVar obj){
+            PK_OBJ_MARK(obj);
+        });
+    }
+}
+
+void NativeFunc::_gc_mark(VM* vm) const{
+    if(decl) decl->_gc_mark(vm);
+}
+
+void FuncDecl::_gc_mark(VM* vm) const{
+    code->_gc_mark(vm);
+    for(int i=0; i<kwargs.size(); i++) PK_OBJ_MARK(kwargs[i].value);
+}
+
+void List::_gc_mark(VM* vm) const{
+    for(PyVar obj: *this) PK_OBJ_MARK(obj);
+}
+
+void Tuple::_gc_mark(VM* vm) const{
+    for(PyVar obj: *this) PK_OBJ_MARK(obj);
+}
+
+void MappingProxy::_gc_mark(VM* vm) const{
+    PK_OBJ_MARK(obj);
+}
+
+void BoundMethod::_gc_mark(VM* vm) const{
+    PK_OBJ_MARK(func);
+    PK_OBJ_MARK(self);
+}
+
+void StarWrapper::_gc_mark(VM* vm) const{
+    PK_OBJ_MARK(obj);
+}
+
+void StaticMethod::_gc_mark(VM* vm) const{
+    PK_OBJ_MARK(func);
+}
+
+void ClassMethod::_gc_mark(VM* vm) const{
+    PK_OBJ_MARK(func);
+}
+
+void Property::_gc_mark(VM* vm) const{
+    PK_OBJ_MARK(getter);
+    PK_OBJ_MARK(setter);
+}
+
+void Slice::_gc_mark(VM* vm) const{
+    PK_OBJ_MARK(start);
+    PK_OBJ_MARK(stop);
+    PK_OBJ_MARK(step);
+}
+
+void Super::_gc_mark(VM* vm) const{
+    PK_OBJ_MARK(first);
+}
+
+void Frame::_gc_mark(VM* vm) const {
+    PK_OBJ_MARK(_module);
+    co->_gc_mark(vm);
+    // Frame could be stored in a generator, so mark _callable for safety
+    if(_callable != nullptr) PK_OBJ_MARK(_callable);
+}
+
+void ManagedHeap::mark() {
+    for(PyVar obj: _no_gc) PK_OBJ_MARK(obj);
+    vm->callstack.apply([this](Frame& frame){ frame._gc_mark(vm); });
+    for(PyVar obj: vm->s_data) PK_OBJ_MARK(obj);
+    for(auto [_, co]: vm->__cached_codes) co->_gc_mark(vm);
+    if(vm->__last_exception) PK_OBJ_MARK(vm->__last_exception);
+    if(vm->__curr_class) PK_OBJ_MARK(vm->__curr_class);
+    if(vm->__c.error != nullptr) PK_OBJ_MARK(vm->__c.error);
+    if(_gc_marker_ex) _gc_marker_ex(vm);
+}
+
+void ManagedHeap::_delete(PyVar obj){
+    PK_DEBUG_ASSERT(!obj.is_sso)
+    const PyTypeInfo* ti = vm->_tp_info(obj);
+    if(ti->vt._dtor) ti->vt._dtor(obj->_value_ptr());
+    if(obj->_attr){
+        obj->_attr->~NameDict();
+        pool128_dealloc(obj->_attr);
+    }
+    pool128_dealloc(obj.get());
+}
+
+void Dict::_gc_mark(VM* vm) const{
+    apply([vm](PyVar k, PyVar v){
+        PK_OBJ_MARK(k);
+        PK_OBJ_MARK(v);
+    });
+}
+
+void CodeObject::_gc_mark(VM* vm) const {
+    for(PyVar v : consts) PK_OBJ_MARK(v);
+    for(auto& decl: func_decls) decl->_gc_mark(vm);
+}
+
 }   // namespace pkpy