diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 2fc72534..b4697331 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -64,3 +64,5 @@ int Frame__exit_block(Frame* self, ValueStack*, int); UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock); void Frame__set_unwind_target(Frame* self, py_TValue* sp); + +void Frame__gc_mark(Frame* self); \ No newline at end of file diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 03057501..b5d0e91c 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -21,6 +21,7 @@ typedef struct py_TypeInfo { c11_vector /*T=py_Name*/ annotated_fields; void (*on_end_subclass)(struct py_TypeInfo*); // backdoor for enum module + void (*gc_mark)(void* ud); /* Magic Slots */ py_TValue magic[64]; @@ -61,8 +62,9 @@ void VM__pop_frame(VM* self); bool pk__parse_int_slice(py_Ref slice, int length, int* start, int* stop, int* step); bool pk__normalize_index(int* index, int length); -void pk_list__mark(void* ud, void (*marker)(py_TValue*)); -void pk_dict__mark(void* ud, void (*marker)(py_TValue*)); +void pk__mark_value(py_TValue*); +void pk__mark_namedict(NameDict*); +void pk__tp_set_marker(py_Type type, void (*gc_mark)(void*)); bool pk_wrapper__self(int argc, py_Ref argv); bool pk_wrapper__NotImplementedError(int argc, py_Ref argv); diff --git a/python/pickle.py b/python/pickle.py index 860dc4b3..bfd7ef4d 100644 --- a/python/pickle.py +++ b/python/pickle.py @@ -1,5 +1,4 @@ import json -from c import struct import builtins _BASIC_TYPES = [int, float, str, bool, type(None)] @@ -142,7 +141,8 @@ class _Unpickler: # generic object cls = _find_class(o[0]) - if getattr(cls, '__struct__', False): + # if getattr(cls, '__struct__', False): + if False: inst = cls.fromstruct(struct.fromhex(o[1])) self.tag(index, inst) return inst diff --git a/src/common/_generated.c b/src/common/_generated.c index 5cd31dec..012b05de 100644 --- a/src/common/_generated.c +++ b/src/common/_generated.c @@ -12,7 +12,7 @@ const char kPythonLibs_functools[] = "class cache:\n def __init__(self, f):\n const char kPythonLibs_heapq[] = "# Heap queue algorithm (a.k.a. priority queue)\ndef heappush(heap, item):\n \"\"\"Push item onto heap, maintaining the heap invariant.\"\"\"\n heap.append(item)\n _siftdown(heap, 0, len(heap)-1)\n\ndef heappop(heap):\n \"\"\"Pop the smallest item off the heap, maintaining the heap invariant.\"\"\"\n lastelt = heap.pop() # raises appropriate IndexError if heap is empty\n if heap:\n returnitem = heap[0]\n heap[0] = lastelt\n _siftup(heap, 0)\n return returnitem\n return lastelt\n\ndef heapreplace(heap, item):\n \"\"\"Pop and return the current smallest value, and add the new item.\n\n This is more efficient than heappop() followed by heappush(), and can be\n more appropriate when using a fixed-size heap. Note that the value\n returned may be larger than item! That constrains reasonable uses of\n this routine unless written as part of a conditional replacement:\n\n if item > heap[0]:\n item = heapreplace(heap, item)\n \"\"\"\n returnitem = heap[0] # raises appropriate IndexError if heap is empty\n heap[0] = item\n _siftup(heap, 0)\n return returnitem\n\ndef heappushpop(heap, item):\n \"\"\"Fast version of a heappush followed by a heappop.\"\"\"\n if heap and heap[0] < item:\n item, heap[0] = heap[0], item\n _siftup(heap, 0)\n return item\n\ndef heapify(x):\n \"\"\"Transform list into a heap, in-place, in O(len(x)) time.\"\"\"\n n = len(x)\n # Transform bottom-up. The largest index there's any point to looking at\n # is the largest with a child index in-range, so must have 2*i + 1 < n,\n # or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so\n # j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is\n # (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.\n for i in reversed(range(n//2)):\n _siftup(x, i)\n\n# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos\n# is the index of a leaf with a possibly out-of-order value. Restore the\n# heap invariant.\ndef _siftdown(heap, startpos, pos):\n newitem = heap[pos]\n # Follow the path to the root, moving parents down until finding a place\n # newitem fits.\n while pos > startpos:\n parentpos = (pos - 1) >> 1\n parent = heap[parentpos]\n if newitem < parent:\n heap[pos] = parent\n pos = parentpos\n continue\n break\n heap[pos] = newitem\n\ndef _siftup(heap, pos):\n endpos = len(heap)\n startpos = pos\n newitem = heap[pos]\n # Bubble up the smaller child until hitting a leaf.\n childpos = 2*pos + 1 # leftmost child position\n while childpos < endpos:\n # Set childpos to index of smaller child.\n rightpos = childpos + 1\n if rightpos < endpos and not heap[childpos] < heap[rightpos]:\n childpos = rightpos\n # Move the smaller child up.\n heap[pos] = heap[childpos]\n pos = childpos\n childpos = 2*pos + 1\n # The leaf at pos is empty now. Put newitem there, and bubble it up\n # to its final resting place (by sifting its parents down).\n heap[pos] = newitem\n _siftdown(heap, startpos, pos)"; const char kPythonLibs_itertools[] = "from pkpy import next\n\ndef zip_longest(a, b):\n a = iter(a)\n b = iter(b)\n while True:\n ai = next(a)\n bi = next(b)\n if ai is StopIteration and bi is StopIteration:\n break\n if ai is StopIteration:\n ai = None\n if bi is StopIteration:\n bi = None\n yield ai, bi\n"; const char kPythonLibs_operator[] = "# https://docs.python.org/3/library/operator.html#mapping-operators-to-functions\n\ndef le(a, b): return a <= b\ndef lt(a, b): return a < b\ndef ge(a, b): return a >= b\ndef gt(a, b): return a > b\ndef eq(a, b): return a == b\ndef ne(a, b): return a != b\n\ndef and_(a, b): return a & b\ndef or_(a, b): return a | b\ndef xor(a, b): return a ^ b\ndef invert(a): return ~a\ndef lshift(a, b): return a << b\ndef rshift(a, b): return a >> b\n\ndef is_(a, b): return a is b\ndef is_not(a, b): return a is not b\ndef not_(a): return not a\ndef truth(a): return bool(a)\ndef contains(a, b): return b in a\n\ndef add(a, b): return a + b\ndef sub(a, b): return a - b\ndef mul(a, b): return a * b\ndef truediv(a, b): return a / b\ndef floordiv(a, b): return a // b\ndef mod(a, b): return a % b\ndef pow(a, b): return a ** b\ndef neg(a): return -a\ndef matmul(a, b): return a @ b\n\ndef getitem(a, b): return a[b]\ndef setitem(a, b, c): a[b] = c\ndef delitem(a, b): del a[b]\n\ndef iadd(a, b): a += b; return a\ndef isub(a, b): a -= b; return a\ndef imul(a, b): a *= b; return a\ndef itruediv(a, b): a /= b; return a\ndef ifloordiv(a, b): a //= b; return a\ndef imod(a, b): a %= b; return a\n# def ipow(a, b): a **= b; return a\n# def imatmul(a, b): a @= b; return a\ndef iand(a, b): a &= b; return a\ndef ior(a, b): a |= b; return a\ndef ixor(a, b): a ^= b; return a\ndef ilshift(a, b): a <<= b; return a\ndef irshift(a, b): a >>= b; return a\n"; -const char kPythonLibs_pickle[] = "import json\nfrom c import struct\nimport builtins\n\n_BASIC_TYPES = [int, float, str, bool, type(None)]\n_MOD_T_SEP = \"@\"\n\ndef _find_class(path: str):\n if _MOD_T_SEP not in path:\n return builtins.__dict__[path]\n modpath, name = path.split(_MOD_T_SEP)\n return __import__(modpath).__dict__[name]\n\nclass _Pickler:\n def __init__(self, obj) -> None:\n self.obj = obj\n self.raw_memo = {} # id -> int\n self.memo = [] # int -> object\n\n @staticmethod\n def _type_id(t: type):\n assert type(t) is type\n name = t.__name__\n mod = t.__module__\n if mod is not None:\n name = mod.__path__ + _MOD_T_SEP + name\n return name\n\n def wrap(self, o):\n o_t = type(o)\n if o_t in _BASIC_TYPES:\n return o\n if o_t is type:\n return [\"type\", self._type_id(o)]\n\n index = self.raw_memo.get(id(o), None)\n if index is not None:\n return [index]\n \n ret = []\n index = len(self.memo)\n self.memo.append(ret)\n self.raw_memo[id(o)] = index\n\n if o_t is tuple:\n ret.append(\"tuple\")\n ret.append([self.wrap(i) for i in o])\n return [index]\n if o_t is bytes:\n ret.append(\"bytes\")\n ret.append([o[j] for j in range(len(o))])\n return [index]\n if o_t is list:\n ret.append(\"list\")\n ret.append([self.wrap(i) for i in o])\n return [index]\n if o_t is dict:\n ret.append(\"dict\")\n ret.append([[self.wrap(k), self.wrap(v)] for k,v in o.items()])\n return [index]\n \n _0 = self._type_id(o_t)\n\n if getattr(o_t, '__struct__', False):\n ret.append(_0)\n ret.append(o.tostruct().hex())\n return [index]\n\n if hasattr(o, \"__getnewargs__\"):\n _1 = o.__getnewargs__() # an iterable\n _1 = [self.wrap(i) for i in _1]\n else:\n _1 = None\n\n if o.__dict__ is None:\n _2 = None\n else:\n _2 = {k: self.wrap(v) for k,v in o.__dict__.items()}\n\n ret.append(_0) # type id\n ret.append(_1) # newargs\n ret.append(_2) # state\n return [index]\n \n def run_pipe(self):\n o = self.wrap(self.obj)\n return [o, self.memo]\n\n\n\nclass _Unpickler:\n def __init__(self, obj, memo: list) -> None:\n self.obj = obj\n self.memo = memo\n self._unwrapped = [None] * len(memo)\n\n def tag(self, index, o):\n assert self._unwrapped[index] is None\n self._unwrapped[index] = o\n\n def unwrap(self, o, index=None):\n if type(o) in _BASIC_TYPES:\n return o\n assert type(o) is list\n\n if o[0] == \"type\":\n return _find_class(o[1])\n\n # reference\n if type(o[0]) is int:\n assert index is None # index should be None\n index = o[0]\n if self._unwrapped[index] is None:\n o = self.memo[index]\n assert type(o) is list\n assert type(o[0]) is str\n self.unwrap(o, index)\n assert self._unwrapped[index] is not None\n return self._unwrapped[index]\n \n # concrete reference type\n if o[0] == \"tuple\":\n ret = tuple([self.unwrap(i) for i in o[1]])\n self.tag(index, ret)\n return ret\n if o[0] == \"bytes\":\n ret = bytes(o[1])\n self.tag(index, ret)\n return ret\n if o[0] == \"list\":\n ret = []\n self.tag(index, ret)\n for i in o[1]:\n ret.append(self.unwrap(i))\n return ret\n if o[0] == \"dict\":\n ret = {}\n self.tag(index, ret)\n for k,v in o[1]:\n ret[self.unwrap(k)] = self.unwrap(v)\n return ret\n \n # generic object\n cls = _find_class(o[0])\n if getattr(cls, '__struct__', False):\n inst = cls.fromstruct(struct.fromhex(o[1]))\n self.tag(index, inst)\n return inst\n else:\n _, newargs, state = o\n # create uninitialized instance\n new_f = getattr(cls, \"__new__\")\n if newargs is not None:\n newargs = [self.unwrap(i) for i in newargs]\n inst = new_f(cls, *newargs)\n else:\n inst = new_f(cls)\n self.tag(index, inst)\n # restore state\n if state is not None:\n for k,v in state.items():\n setattr(inst, k, self.unwrap(v))\n return inst\n\n def run_pipe(self):\n return self.unwrap(self.obj)\n\n\ndef _wrap(o):\n return _Pickler(o).run_pipe()\n\ndef _unwrap(packed: list):\n return _Unpickler(*packed).run_pipe()\n\ndef dumps(o) -> bytes:\n o = _wrap(o)\n return json.dumps(o).encode()\n\ndef loads(b) -> object:\n assert type(b) is bytes\n o = json.loads(b.decode())\n return _unwrap(o)"; +const char kPythonLibs_pickle[] = "import json\nimport builtins\n\n_BASIC_TYPES = [int, float, str, bool, type(None)]\n_MOD_T_SEP = \"@\"\n\ndef _find_class(path: str):\n if _MOD_T_SEP not in path:\n return builtins.__dict__[path]\n modpath, name = path.split(_MOD_T_SEP)\n return __import__(modpath).__dict__[name]\n\nclass _Pickler:\n def __init__(self, obj) -> None:\n self.obj = obj\n self.raw_memo = {} # id -> int\n self.memo = [] # int -> object\n\n @staticmethod\n def _type_id(t: type):\n assert type(t) is type\n name = t.__name__\n mod = t.__module__\n if mod is not None:\n name = mod.__path__ + _MOD_T_SEP + name\n return name\n\n def wrap(self, o):\n o_t = type(o)\n if o_t in _BASIC_TYPES:\n return o\n if o_t is type:\n return [\"type\", self._type_id(o)]\n\n index = self.raw_memo.get(id(o), None)\n if index is not None:\n return [index]\n \n ret = []\n index = len(self.memo)\n self.memo.append(ret)\n self.raw_memo[id(o)] = index\n\n if o_t is tuple:\n ret.append(\"tuple\")\n ret.append([self.wrap(i) for i in o])\n return [index]\n if o_t is bytes:\n ret.append(\"bytes\")\n ret.append([o[j] for j in range(len(o))])\n return [index]\n if o_t is list:\n ret.append(\"list\")\n ret.append([self.wrap(i) for i in o])\n return [index]\n if o_t is dict:\n ret.append(\"dict\")\n ret.append([[self.wrap(k), self.wrap(v)] for k,v in o.items()])\n return [index]\n \n _0 = self._type_id(o_t)\n\n if getattr(o_t, '__struct__', False):\n ret.append(_0)\n ret.append(o.tostruct().hex())\n return [index]\n\n if hasattr(o, \"__getnewargs__\"):\n _1 = o.__getnewargs__() # an iterable\n _1 = [self.wrap(i) for i in _1]\n else:\n _1 = None\n\n if o.__dict__ is None:\n _2 = None\n else:\n _2 = {k: self.wrap(v) for k,v in o.__dict__.items()}\n\n ret.append(_0) # type id\n ret.append(_1) # newargs\n ret.append(_2) # state\n return [index]\n \n def run_pipe(self):\n o = self.wrap(self.obj)\n return [o, self.memo]\n\n\n\nclass _Unpickler:\n def __init__(self, obj, memo: list) -> None:\n self.obj = obj\n self.memo = memo\n self._unwrapped = [None] * len(memo)\n\n def tag(self, index, o):\n assert self._unwrapped[index] is None\n self._unwrapped[index] = o\n\n def unwrap(self, o, index=None):\n if type(o) in _BASIC_TYPES:\n return o\n assert type(o) is list\n\n if o[0] == \"type\":\n return _find_class(o[1])\n\n # reference\n if type(o[0]) is int:\n assert index is None # index should be None\n index = o[0]\n if self._unwrapped[index] is None:\n o = self.memo[index]\n assert type(o) is list\n assert type(o[0]) is str\n self.unwrap(o, index)\n assert self._unwrapped[index] is not None\n return self._unwrapped[index]\n \n # concrete reference type\n if o[0] == \"tuple\":\n ret = tuple([self.unwrap(i) for i in o[1]])\n self.tag(index, ret)\n return ret\n if o[0] == \"bytes\":\n ret = bytes(o[1])\n self.tag(index, ret)\n return ret\n if o[0] == \"list\":\n ret = []\n self.tag(index, ret)\n for i in o[1]:\n ret.append(self.unwrap(i))\n return ret\n if o[0] == \"dict\":\n ret = {}\n self.tag(index, ret)\n for k,v in o[1]:\n ret[self.unwrap(k)] = self.unwrap(v)\n return ret\n \n # generic object\n cls = _find_class(o[0])\n # if getattr(cls, '__struct__', False):\n if False:\n inst = cls.fromstruct(struct.fromhex(o[1]))\n self.tag(index, inst)\n return inst\n else:\n _, newargs, state = o\n # create uninitialized instance\n new_f = getattr(cls, \"__new__\")\n if newargs is not None:\n newargs = [self.unwrap(i) for i in newargs]\n inst = new_f(cls, *newargs)\n else:\n inst = new_f(cls)\n self.tag(index, inst)\n # restore state\n if state is not None:\n for k,v in state.items():\n setattr(inst, k, self.unwrap(v))\n return inst\n\n def run_pipe(self):\n return self.unwrap(self.obj)\n\n\ndef _wrap(o):\n return _Pickler(o).run_pipe()\n\ndef _unwrap(packed: list):\n return _Unpickler(*packed).run_pipe()\n\ndef dumps(o) -> bytes:\n o = _wrap(o)\n return json.dumps(o).encode()\n\ndef loads(b) -> object:\n assert type(b) is bytes\n o = json.loads(b.decode())\n return _unwrap(o)"; const char kPythonLibs_this[] = "print(\"\"\"The Zen of Python, by Tim Peters\n\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren't special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is better than never.\nAlthough never is often better than *right* now.\nIf the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea -- let's do more of those!\"\"\")"; const char kPythonLibs_typing[] = "class _Placeholder:\n def __init__(self, *args, **kwargs):\n pass\n def __getitem__(self, *args):\n return self\n def __call__(self, *args, **kwargs):\n return self\n def __and__(self, other):\n return self\n def __or__(self, other):\n return self\n def __xor__(self, other):\n return self\n\n\n_PLACEHOLDER = _Placeholder()\n\nList = _PLACEHOLDER\nDict = _PLACEHOLDER\nTuple = _PLACEHOLDER\nSet = _PLACEHOLDER\nAny = _PLACEHOLDER\nUnion = _PLACEHOLDER\nOptional = _PLACEHOLDER\nCallable = _PLACEHOLDER\nType = _PLACEHOLDER\nProtocol = _PLACEHOLDER\n\nLiteral = _PLACEHOLDER\nLiteralString = _PLACEHOLDER\n\nIterable = _PLACEHOLDER\nGenerator = _PLACEHOLDER\n\nHashable = _PLACEHOLDER\n\nTypeVar = _PLACEHOLDER\nSelf = _PLACEHOLDER\n\nclass Generic:\n pass\n\nTYPE_CHECKING = False\n\n# decorators\noverload = lambda x: x\nfinal = lambda x: x\n"; diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 6b58a3b7..e65ba4d5 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -1,4 +1,5 @@ #include "pocketpy/interpreter/frame.h" +#include "pocketpy/interpreter/vm.h" #include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/object.h" #include "pocketpy/pocketpy.h" @@ -133,6 +134,11 @@ void Frame__set_unwind_target(Frame* self, py_TValue* sp) { } } +void Frame__gc_mark(Frame *self){ + pk__mark_value(self->module); + CodeObject__gc_mark(self->co); +} + py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name) { if(!self->has_function) return NULL; Function* ud = py_touserdata(self->p0); diff --git a/src/interpreter/generator.c b/src/interpreter/generator.c index 5b4d5009..39728d15 100644 --- a/src/interpreter/generator.c +++ b/src/interpreter/generator.c @@ -71,9 +71,16 @@ static bool generator__next__(int argc, py_Ref argv) { } } +static void generator__gc_mark(void* ud){ + Generator* gen = ud; + if(gen->frame) Frame__gc_mark(gen->frame); +} + py_Type pk_generator__register() { py_Type type = pk_newtype("generator", tp_object, NULL, (py_Dtor)Generator__dtor, false, true); + pk__tp_set_marker(type, generator__gc_mark); + py_bindmagic(type, __iter__, pk_wrapper__self); py_bindmagic(type, __next__, generator__next__); return type; diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 7bb420db..a5658766 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -534,10 +534,23 @@ void PyObject__delete(PyObject* self) { static void mark_object(PyObject* obj); -static void mark_value(py_TValue* val) { +void pk__mark_value(py_TValue* val) { if(val->is_ptr) mark_object(val->_obj); } +void pk__mark_namedict(NameDict* dict) { + for(int i = 0; i < dict->count; i++) { + NameDict_KV* kv = c11__at(NameDict_KV, dict, i); + pk__mark_value(&kv->value); + } +} + +void pk__tp_set_marker(py_Type type, void (*gc_mark)(void*)) { + py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + assert(ti->gc_mark == NULL); + ti->gc_mark = gc_mark; +} + static void mark_object(PyObject* obj) { if(obj->gc_marked) return; obj->gc_marked = true; @@ -545,24 +558,19 @@ static void mark_object(PyObject* obj) { if(obj->slots > 0) { py_TValue* p = PyObject__slots(obj); for(int i = 0; i < obj->slots; i++) - mark_value(p + i); - return; - } - - if(obj->slots == -1) { + pk__mark_value(p + i); + } else if(obj->slots == -1) { NameDict* dict = PyObject__dict(obj); - for(int j = 0; j < dict->count; j++) { - NameDict_KV* kv = c11__at(NameDict_KV, dict, j); - mark_value(&kv->value); - } - return; + pk__mark_namedict(dict); } - if(obj->type == tp_list) { - pk_list__mark(PyObject__userdata(obj), mark_value); - } else if(obj->type == tp_dict) { - pk_dict__mark(PyObject__userdata(obj), mark_value); - } + py_TypeInfo* types = c11__at(py_TypeInfo, &pk_current_vm->types, obj->type); + if(types->gc_mark) { types->gc_mark(PyObject__userdata(obj)); } +} + +void CodeObject__gc_mark(const CodeObject* self) { + c11__foreach(py_TValue, &self->consts, i) { pk__mark_value(i); } + c11__foreach(FuncDecl_, &self->func_decls, i) { CodeObject__gc_mark(&(*i)->code); } } void ManagedHeap__mark(ManagedHeap* self) { @@ -574,17 +582,17 @@ void ManagedHeap__mark(ManagedHeap* self) { } // mark value stack for(py_TValue* p = vm->stack.begin; p != vm->stack.end; p++) { - mark_value(p); + pk__mark_value(p); } // mark frame for(Frame* frame = vm->top_frame; frame; frame = frame->f_back) { - mark_value(frame->module); + Frame__gc_mark(frame); } // mark vm's registers - mark_value(&vm->last_retval); - mark_value(&vm->curr_exception); + pk__mark_value(&vm->last_retval); + pk__mark_value(&vm->curr_exception); for(int i = 0; i < c11__count_array(vm->reg); i++) { - mark_value(&vm->reg[i]); + pk__mark_value(&vm->reg[i]); } } diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index fcccb3d0..8e6634c1 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -1,5 +1,6 @@ #include "pocketpy/objects/codeobject.h" #include "pocketpy/common/utils.h" +#include "pocketpy/pocketpy.h" #include void Bytecode__set_signed_arg(Bytecode* self, int arg) { diff --git a/src/public/modules.c b/src/public/modules.c index 3dbfc7cc..6eb1d9e0 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -1,3 +1,4 @@ +#include "pocketpy/objects/codeobject.h" #include "pocketpy/pocketpy.h" #include "pocketpy/common/utils.h" @@ -501,7 +502,6 @@ static bool function__closure__getter(int argc, py_Ref argv) { Function* ud = py_touserdata(argv); if(!ud->closure) { py_newnone(py_retval()); - return true; } py_Ref r0 = py_pushtmp(); py_Ref retval = py_pushtmp(); @@ -520,10 +520,18 @@ static bool function__closure__getter(int argc, py_Ref argv) { return true; } +static void function__gc_mark(void* ud) { + Function* func = ud; + if(func->closure) pk__mark_namedict(func->closure); + CodeObject__gc_mark(&func->decl->code); +} + py_Type pk_function__register() { py_Type type = pk_newtype("function", tp_object, NULL, (void (*)(void*))Function__dtor, false, true); + pk__tp_set_marker(type, function__gc_mark); + py_bindproperty(type, "__closure__", function__closure__getter, NULL); return type; } diff --git a/src/public/py_dict.c b/src/public/py_dict.c index 6888eedd..db82c27a 100644 --- a/src/public/py_dict.c +++ b/src/public/py_dict.c @@ -433,9 +433,21 @@ static bool dict_values(int argc, py_Ref argv) { return true; } +static void dict__gc_mark(void* ud) { + Dict* self = ud; + for(int i = 0; i < self->entries.count; i++) { + DictEntry* entry = c11__at(DictEntry, &self->entries, i); + if(py_isnil(&entry->key)) continue; + pk__mark_value(&entry->key); + pk__mark_value(&entry->val); + } +} + py_Type pk_dict__register() { py_Type type = pk_newtype("dict", tp_object, NULL, (void (*)(void*))Dict__dtor, false, false); + pk__tp_set_marker(type, dict__gc_mark); + py_bindmagic(type, __new__, dict__new__); py_bindmagic(type, __init__, dict__init__); py_bindmagic(type, __getitem__, dict__getitem__); @@ -532,13 +544,3 @@ bool py_dict_apply(py_Ref self, bool (*f)(py_Ref, py_Ref, void*), void* ctx) { } return true; } - -void pk_dict__mark(void* ud, void (*marker)(py_TValue*)) { - Dict* self = ud; - for(int i = 0; i < self->entries.count; i++) { - DictEntry* entry = c11__at(DictEntry, &self->entries, i); - if(py_isnil(&entry->key)) continue; - marker(&entry->key); - marker(&entry->val); - } -} \ No newline at end of file diff --git a/src/public/py_list.c b/src/public/py_list.c index 26e75d3d..87f8d65b 100644 --- a/src/public/py_list.c +++ b/src/public/py_list.c @@ -44,7 +44,7 @@ int py_list_len(py_Ref self) { return userdata->count; } -void py_list_swap(py_Ref self, int i, int j){ +void py_list_swap(py_Ref self, int i, int j) { py_TValue* data = py_list_data(self); py_TValue tmp = data[i]; data[i] = data[j]; @@ -408,10 +408,19 @@ static bool list__contains__(int argc, py_Ref argv) { return pk_arraycontains(py_arg(0), py_arg(1)); } +static void list__gc_mark(void* ud) { + List* self = ud; + for(int i = 0; i < self->count; i++) { + pk__mark_value(c11__at(py_TValue, self, i)); + } +} + py_Type pk_list__register() { py_Type type = pk_newtype("list", tp_object, NULL, (void (*)(void*))c11_vector__dtor, false, true); + pk__tp_set_marker(type, list__gc_mark); + py_bindmagic(type, __len__, list__len__); py_bindmagic(type, __eq__, list__eq__); py_bindmagic(type, __ne__, list__ne__); @@ -443,10 +452,3 @@ py_Type pk_list__register() { py_setdict(py_tpobject(type), __hash__, py_None); return type; } - -void pk_list__mark(void* ud, void (*marker)(py_TValue*)) { - List* self = ud; - for(int i = 0; i < self->count; i++) { - marker(c11__at(py_TValue, self, i)); - } -} \ No newline at end of file