blueloveTH 1 anno fa
parent
commit
8b8552d54b

+ 1 - 1
build_g.sh

@@ -5,7 +5,7 @@ python prebuild.py
 SRC_C=$(find src/ -name "*.c")
 SRC_CPP=$(find src/ -name "*.cpp")
 
-COMMON_FLAGS="-Iinclude -O0 -Wfatal-errors -g -DDEBUG -DPK_ENABLE_OS=1"
+COMMON_FLAGS="-Iinclude -O0 -Wfatal-errors -g -DDEBUG -DPK_ENABLE_OS=1" # -fsanitize=address,leak,undefined"
 
 FLAGS_C="-std=c11 $COMMON_FLAGS"
 FLAGS_CPP="-std=c++17 -stdlib=libc++ -frtti $COMMON_FLAGS"

+ 49 - 46
include/pocketpy/common/sstream.h

@@ -10,11 +10,11 @@
 extern "C" {
 #endif
 
-typedef struct pkpy_SStream {
+typedef struct pk_SStream {
     c11_vector data;
-} pkpy_SStream;
+} pk_SStream;
 
-typedef struct pkpy_AnyStr {
+typedef struct pk_AnyStr {
     int type;
     union {
         int _int;
@@ -27,59 +27,62 @@ typedef struct pkpy_AnyStr {
         const char* _cstr;
         void* _ptr;
     };
-} pkpy_AnyStr;
+} pk_AnyStr;
 
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__int(int x) { pkpy_AnyStr s; s.type = 1; s._int = x; return s; }
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__i64(int64_t x) { pkpy_AnyStr s; s.type = 2; s._i64 = x; return s; }
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__float(float x) { pkpy_AnyStr s; s.type = 3; s._float = x; return s; }
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__double(double x) { pkpy_AnyStr s; s.type = 4; s._double = x; return s; }
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__char(char x) { pkpy_AnyStr s; s.type = 5; s._char = x; return s; }
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__str(const pkpy_Str* x) { pkpy_AnyStr s; s.type = 6; s._str = x; return s; }
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__sv(c11_string x) { pkpy_AnyStr s; s.type = 7; s._sv = x; return s; }
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__cstr(const char* x) { pkpy_AnyStr s; s.type = 8; s._cstr = x; return s; }
-PK_INLINE pkpy_AnyStr pkpy_AnyStr__ptr(void* x) { pkpy_AnyStr s; s.type = 9; s._ptr = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__int(int x) { pk_AnyStr s; s.type = 1; s._int = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__i64(int64_t x) { pk_AnyStr s; s.type = 2; s._i64 = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__float(float x) { pk_AnyStr s; s.type = 3; s._float = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__double(double x) { pk_AnyStr s; s.type = 4; s._double = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__char(char x) { pk_AnyStr s; s.type = 5; s._char = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__str(const pkpy_Str* x) { pk_AnyStr s; s.type = 6; s._str = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__sv(c11_string x) { pk_AnyStr s; s.type = 7; s._sv = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__cstr(const char* x) { pk_AnyStr s; s.type = 8; s._cstr = x; return s; }
+PK_INLINE pk_AnyStr pk_AnyStr__ptr(void* x) { pk_AnyStr s; s.type = 9; s._ptr = x; return s; }
 
-void pkpy_SStream__ctor(pkpy_SStream* self);
-void pkpy_SStream__ctor2(pkpy_SStream* self, int capacity);
-void pkpy_SStream__dtor(pkpy_SStream* self);
+void pk_SStream__ctor(pk_SStream* self);
+void pk_SStream__ctor2(pk_SStream* self, int capacity);
+void pk_SStream__dtor(pk_SStream* self);
 
-void pkpy_SStream__write_int(pkpy_SStream* self, int);
-void pkpy_SStream__write_i64(pkpy_SStream* self, int64_t);
-void pkpy_SStream__write_float(pkpy_SStream* self, float, int precision);
-void pkpy_SStream__write_double(pkpy_SStream* self, double, int precision);
-void pkpy_SStream__write_char(pkpy_SStream* self, char);
-void pkpy_SStream__write_Str(pkpy_SStream* self, const pkpy_Str*);
-void pkpy_SStream__write_sv(pkpy_SStream* self, c11_string);
-void pkpy_SStream__write_cstr(pkpy_SStream* self, const char*);
-void pkpy_SStream__write_cstrn(pkpy_SStream* self, const char*, int);
-void pkpy_SStream__write_hex(pkpy_SStream* self, unsigned char, bool non_zero);
-void pkpy_SStream__write_ptr(pkpy_SStream* self, void*);
-void pkpy_SStream__write_any(pkpy_SStream* self, const char* fmt, const pkpy_AnyStr* args, int n);
+void pk_SStream__write_int(pk_SStream* self, int);
+void pk_SStream__write_i64(pk_SStream* self, int64_t);
+void pk_SStream__write_float(pk_SStream* self, float, int precision);
+void pk_SStream__write_double(pk_SStream* self, double, int precision);
+void pk_SStream__write_char(pk_SStream* self, char);
+void pk_SStream__write_Str(pk_SStream* self, const pkpy_Str*);
+void pk_SStream__write_sv(pk_SStream* self, c11_string);
+void pk_SStream__write_cstr(pk_SStream* self, const char*);
+void pk_SStream__write_cstrn(pk_SStream* self, const char*, int);
+void pk_SStream__write_hex(pk_SStream* self, unsigned char, bool non_zero);
+void pk_SStream__write_ptr(pk_SStream* self, void*);
+
+void pk_SStream__write_any(pk_SStream* self, const char* fmt, const pk_AnyStr* args, int n);
+const char* pk_format_any(const char* fmt, const pk_AnyStr* args, int n);
 
 // Submit the stream and return the final string. The stream becomes invalid after this call
-pkpy_Str pkpy_SStream__submit(pkpy_SStream* self);
+pkpy_Str pk_SStream__submit(pk_SStream* self);
 
-#define pkpy__anystr(x) _Generic((x), \
-    int: pkpy_AnyStr__int, \
-    int64_t: pkpy_AnyStr__i64, \
-    float: pkpy_AnyStr__float, \
-    double: pkpy_AnyStr__double, \
-    char: pkpy_AnyStr__char, \
-    const pkpy_Str*: pkpy_AnyStr__str, \
-    c11_string: pkpy_AnyStr__sv, \
-    const char*: pkpy_AnyStr__cstr, \
-    void*: pkpy_AnyStr__ptr \
+#define pk__anystr(x) _Generic((x), \
+    int: pk_AnyStr__int, \
+    int64_t: pk_AnyStr__i64, \
+    float: pk_AnyStr__float, \
+    double: pk_AnyStr__double, \
+    char: pk_AnyStr__char, \
+    const pkpy_Str*: pk_AnyStr__str, \
+    c11_string: pk_AnyStr__sv, \
+    const char*: pk_AnyStr__cstr, \
+    void*: pk_AnyStr__ptr \
 )(x)
 
-#define pkpy__anystr_list_1(a) (pkpy_AnyStr[]){pkpy__anystr(a)}, 1
-#define pkpy__anystr_list_2(a, b) (pkpy_AnyStr[]){pkpy__anystr(a), pkpy__anystr(b)}, 2
-#define pkpy__anystr_list_3(a, b, c) (pkpy_AnyStr[]){pkpy__anystr(a), pkpy__anystr(b), pkpy__anystr(c)}, 3
-#define pkpy__anystr_list_4(a, b, c, d) (pkpy_AnyStr[]){pkpy__anystr(a), pkpy__anystr(b), pkpy__anystr(c), pkpy__anystr(d)}, 4
+#define pk__anystr_list_1(a) (pk_AnyStr[]){pk__anystr(a)}, 1
+#define pk__anystr_list_2(a, b) (pk_AnyStr[]){pk__anystr(a), pk__anystr(b)}, 2
+#define pk__anystr_list_3(a, b, c) (pk_AnyStr[]){pk__anystr(a), pk__anystr(b), pk__anystr(c)}, 3
+#define pk__anystr_list_4(a, b, c, d) (pk_AnyStr[]){pk__anystr(a), pk__anystr(b), pk__anystr(c), pk__anystr(d)}, 4
 
-#define pkpy__anystr_list_dispatcher(...) PK_NARGS_SEQ(__VA_ARGS__, pkpy__anystr_list_4, pkpy__anystr_list_3, pkpy__anystr_list_2, pkpy__anystr_list_1, 0)
-#define pkpy__anystr_list(...) pkpy__anystr_list_dispatcher(__VA_ARGS__)(__VA_ARGS__) 
+#define pk__anystr_list_dispatcher(...) PK_NARGS_SEQ(__VA_ARGS__, pk__anystr_list_4, pk__anystr_list_3, pk__anystr_list_2, pk__anystr_list_1, 0)
+#define pk__anystr_list(...) pk__anystr_list_dispatcher(__VA_ARGS__)(__VA_ARGS__) 
 
-#define pkpy_SStream__write(self, fmt, ...) pkpy_SStream__write_any(self, fmt, pkpy__anystr_list(__VA_ARGS__))
+#define pk_SStream__write(self, fmt, ...) pk_SStream__write_any(self, fmt, pk__anystr_list(__VA_ARGS__))
+#define pk_format(fmt, ...) pk_format_any(fmt, pk__anystr_list(__VA_ARGS__))
 
 #ifdef __cplusplus
 }

+ 17 - 17
include/pocketpy/common/str.hpp

@@ -288,7 +288,7 @@ struct StrName {
     }
 };
 
-struct SStream: pkpy_SStream {
+struct SStream: pk_SStream {
     PK_ALWAYS_PASS_BY_POINTER(SStream)
 
     int _precision = -1;
@@ -298,85 +298,85 @@ struct SStream: pkpy_SStream {
     void setprecision(int precision) { _precision = precision; }
 
     SStream() {
-        pkpy_SStream__ctor(this);
+        pk_SStream__ctor(this);
     }
 
     SStream(int guess_size) { c11_vector__reserve(&data, guess_size); }
 
     ~SStream() {
         // in case of error
-        if(!_submited) pkpy_SStream__dtor(this);
+        if(!_submited) pk_SStream__dtor(this);
     }
 
     Str str(){
         assert(!_submited);
         _submited = true;
-        return pkpy_SStream__submit(this);
+        return pk_SStream__submit(this);
     }
 
     SStream& operator<< (const Str& val){
-        pkpy_SStream__write_Str(this, &val);
+        pk_SStream__write_Str(this, &val);
         return *this;
     }
 
     SStream& operator<< (const char* val){
-        pkpy_SStream__write_cstr(this, val);
+        pk_SStream__write_cstr(this, val);
         return *this;
     }
 
     SStream& operator<< (int val){
-        pkpy_SStream__write_int(this, val);
+        pk_SStream__write_int(this, val);
         return *this;
     }
 
     SStream& operator<< (size_t val){
         // size_t could overflow int64, but nevermind...
-        pkpy_SStream__write_i64(this, val);
+        pk_SStream__write_i64(this, val);
         return *this;
     }
 
     SStream& operator<< (i64 val){
-        pkpy_SStream__write_i64(this, val);
+        pk_SStream__write_i64(this, val);
         return *this;
     }
 
     SStream& operator<< (f64 val){
-        pkpy_SStream__write_double(this, val, _precision);
+        pk_SStream__write_double(this, val, _precision);
         return *this;
     }
 
     SStream& operator<< (const std::string& val){
-        pkpy_SStream__write_cstrn(this, val.data(), val.size());
+        pk_SStream__write_cstrn(this, val.data(), val.size());
         return *this;
     }
 
     SStream& operator<< (std::string_view val){
-        pkpy_SStream__write_cstrn(this, val.data(), val.size());
+        pk_SStream__write_cstrn(this, val.data(), val.size());
         return *this;
     }
 
     SStream& operator<< (c11_string val){
-        pkpy_SStream__write_cstrn(this, val.data, val.size);
+        pk_SStream__write_cstrn(this, val.data, val.size);
         return *this;
     }
 
     SStream& operator<< (char val){
-        pkpy_SStream__write_char(this, val);
+        pk_SStream__write_char(this, val);
         return *this;
     }
 
     SStream& operator<< (StrName name){
         std::string_view sv = name.sv();
-        pkpy_SStream__write_cstrn(this, sv.data(), sv.size());
+        pk_SStream__write_cstrn(this, sv.data(), sv.size());
         return *this;
     }
 
     void write_hex(unsigned char val, bool non_zero = false){
-        pkpy_SStream__write_hex(this, val, non_zero);
+        pk_SStream__write_hex(this, val, non_zero);
     }
 
     void write_ptr(void* p){
-        pkpy_SStream__write_ptr(this, p);
+        pk_SStream__write_ptr(this, p);
     }
 };
 

+ 2 - 2
include/pocketpy/compiler/compiler.hpp

@@ -55,7 +55,7 @@ struct Compiler {
 
     NameScope name_scope() const noexcept;
     CodeObject* push_global_context() noexcept;
-    FuncDecl_ push_f_context(Str name) noexcept;
+    FuncDecl_ push_f_context(c11_string name, int* out_index) noexcept;
 
     static void init_pratt_rules() noexcept;
 
@@ -118,7 +118,7 @@ struct Compiler {
     [[nodiscard]] Error* try_compile_assignment(bool* is_assign) noexcept;
     [[nodiscard]] Error* compile_stmt() noexcept;
     [[nodiscard]] Error* consume_type_hints() noexcept;
-    [[nodiscard]] Error* _compile_f_args(FuncDecl_ decl, bool enable_type_hints) noexcept;
+    [[nodiscard]] Error* _compile_f_args(FuncDecl* decl, bool enable_type_hints) noexcept;
     [[nodiscard]] Error* compile_function(int decorators = 0) noexcept;
     [[nodiscard]] Error* compile_class(int decorators = 0) noexcept;
 

+ 4 - 5
include/pocketpy/compiler/expr.hpp

@@ -47,13 +47,14 @@ inline void delete_expr(Expr* p) noexcept{
 
 struct CodeEmitContext{
     VM* vm;
-    FuncDecl_ func;  // optional
+    FuncDecl* func;  // optional, weakref
     CodeObject* co;  // 1 CodeEmitContext <=> 1 CodeObject*
     vector<Expr*> _s_expr;
     int level;
     vector<StrName> global_names;
 
     CodeEmitContext(VM* vm, CodeObject* co, int level) : vm(vm), co(co), level(level) {
+        func = NULL;
         c11_smallmap_s2n__ctor(&_co_consts_string_dedup_map);
     }
 
@@ -73,7 +74,6 @@ struct CodeEmitContext{
     int add_varname(StrName name) noexcept;
     int add_const(PyVar) noexcept;
     int add_const_string(std::string_view) noexcept;
-    int add_func_decl(FuncDecl_ decl) noexcept;
     void emit_store_name(NameScope scope, StrName name, int line) noexcept;
     void try_merge_for_iter_store(int) noexcept;
     // emit top -> pop -> delete
@@ -366,12 +366,11 @@ struct CompExpr : Expr {
 };
 
 struct LambdaExpr : Expr {
-    FuncDecl_ decl;
+    int index;
 
-    LambdaExpr(FuncDecl_ decl) : decl(decl) {}
+    LambdaExpr(int index) : index(index) {}
 
     void emit_(CodeEmitContext* ctx) override {
-        int index = ctx->add_func_decl(decl);
         ctx->emit_(OP_LOAD_FUNCTION, index, line);
     }
 };

+ 10 - 6
include/pocketpy/interpreter/frame.hpp

@@ -1,5 +1,6 @@
 #pragma once
 
+#include "pocketpy/objects/codeobject.h"
 #include "pocketpy/objects/codeobject.hpp"
 
 namespace pkpy {
@@ -117,21 +118,21 @@ struct Frame {
 
     PyVar* f_closure_try_get(StrName name);
 
-    int ip() const { return _ip - co->codes.data(); }
+    int ip() const { return _ip - (Bytecode*)co->codes.data; }
 
     // function scope
     Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, PyVar* _locals_base) :
-        _ip(co->codes.data() - 1), _sp_base(p0), co(co), _module(_module), _callable(_callable),
+        _ip((Bytecode*)co->codes.data - 1), _sp_base(p0), co(co), _module(_module), _callable(_callable),
         _locals(co, _locals_base), _uw_list(nullptr) {}
 
     // exec/eval
     Frame(PyVar* p0, const CodeObject* co, PyObject* _module, PyObject* _callable, FastLocals _locals) :
-        _ip(co->codes.data() - 1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals),
+        _ip((Bytecode*)co->codes.data - 1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals),
         _uw_list(nullptr) {}
 
     // global scope
     Frame(PyVar* p0, const CodeObject* co, PyObject* _module) :
-        _ip(co->codes.data() - 1), _sp_base(p0), co(co), _module(_module), _callable(nullptr),
+        _ip((Bytecode*)co->codes.data - 1), _sp_base(p0), co(co), _module(_module), _callable(nullptr),
         _locals(co, p0), _uw_list(nullptr) {}
 
     PyVar* actual_sp_base() const { return _locals.a; }
@@ -143,12 +144,15 @@ struct Frame {
     int _exit_block(ValueStack*, int);
 
     [[nodiscard]] int prepare_loop_break(ValueStack* s_data) {
-        int target = co->blocks[co->lines[ip()].iblock].end;
+        int iblock = c11__getitem(BytecodeEx, &co->codes_ex, ip()).iblock;
+        int target = c11__getitem(CodeBlock, &co->blocks, iblock).end;
         prepare_jump_break(s_data, target);
         return target;
     }
 
-    int curr_lineno() const { return co->lines[ip()].lineno; }
+    int curr_lineno() const {
+        return c11__getitem(BytecodeEx, &co->codes_ex, ip()).lineno;
+    }
 
     void set_unwind_target(PyVar* _sp);
     UnwindTarget* find_unwind_target(int iblock);

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

@@ -501,7 +501,7 @@ public:
     void __pop_frame();
     PyVar __py_generator(LinkedFrame* frame, ArgsView buffer);
     void __op_unpack_sequence(uint16_t arg);
-    void __prepare_py_call(PyVar*, ArgsView, ArgsView, const FuncDecl_&);
+    void __prepare_py_call(PyVar*, ArgsView, ArgsView, const FuncDecl*);
     void __unpack_as_list(ArgsView args, List& list);
     void __unpack_as_dict(ArgsView args, Dict& dict);
     [[noreturn]] void __raise_exc(bool re_raise = false);

+ 67 - 1
include/pocketpy/objects/codeobject.h

@@ -3,6 +3,12 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+#include "pocketpy/common/vector.h"
+#include "pocketpy/common/smallmap.h"
+#include "pocketpy/objects/base.h"
+#include "pocketpy/objects/sourcedata.h"
+#include "pocketpy/common/refcount.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -52,7 +58,6 @@ typedef struct Bytecode {
 void Bytecode__set_signed_arg(Bytecode* self, int arg);
 bool Bytecode__is_forward_jump(const Bytecode* self);
 
-
 typedef struct CodeBlock {
     CodeBlockType type;
     int parent;  // parent index in blocks
@@ -61,6 +66,67 @@ typedef struct CodeBlock {
     int end2;    // ...
 } CodeBlock;
 
+typedef struct BytecodeEx {
+    int lineno;       // line number for each bytecode
+    bool is_virtual;  // whether this bytecode is virtual (not in source code)
+    int iblock;       // block index
+} BytecodeEx;
+
+typedef struct CodeObject {
+    pkpy_SourceData_ src;
+    pkpy_Str name;
+
+    c11_vector/*T=Bytecode*/                codes;
+    c11_vector/*T=CodeObjectByteCodeEx*/    codes_ex;
+
+    c11_vector/*T=PyVar*/   consts;     // constants
+    c11_vector/*T=StrName*/ varnames;   // local variables
+    int nlocals;                        // cached varnames.size()
+
+    c11_smallmap_n2i varnames_inv;
+    c11_smallmap_n2i labels;
+
+    c11_vector/*T=CodeBlock*/ blocks;
+    c11_vector/*T=FuncDecl_*/ func_decls;
+
+    int start_line;
+    int end_line;
+} CodeObject;
+
+CodeObject* CodeObject__new(pkpy_SourceData_ src, c11_string name);
+void CodeObject__delete(CodeObject* self);
+void CodeObject__gc_mark(const CodeObject* self);
+
+typedef struct FuncDeclKwArg{
+    int index;    // index in co->varnames
+    uint16_t key;  // name of this argument
+    PyVar value;  // default value
+} FuncDeclKwArg;
+
+typedef struct FuncDecl {
+    RefCounted rc;
+    CodeObject* code;  // strong ref
+
+    c11_vector/*T=int*/     args;   // indices in co->varnames
+    c11_vector/*T=KwArg*/ kwargs;   // indices in co->varnames
+
+    int starred_arg;    // index in co->varnames, -1 if no *arg
+    int starred_kwarg;  // index in co->varnames, -1 if no **kwarg
+    bool nested;        // whether this function is nested
+
+    const char* docstring;  // docstring of this function (weak ref)
+
+    FuncType type;
+    c11_smallmap_n2i kw_to_index;
+} FuncDecl;
+
+typedef FuncDecl* FuncDecl_;
+
+FuncDecl_ FuncDecl__rcnew(pkpy_SourceData_ src, c11_string name);
+void FuncDecl__dtor(FuncDecl* self);
+void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const PyVar* value);
+void FuncDecl__gc_mark(const FuncDecl* self);
+
 #ifdef __cplusplus
 }
 #endif

+ 8 - 91
include/pocketpy/objects/codeobject.hpp

@@ -12,96 +12,6 @@ namespace pkpy {
 
 typedef PyVar (*NativeFuncC)(VM*, ArgsView);
 
-struct CodeObject;
-struct FuncDecl;
-using FuncDecl_ = std::shared_ptr<FuncDecl>;
-
-struct CodeObject {
-    PK_ALWAYS_PASS_BY_POINTER(CodeObject)
-    
-    struct LineInfo {
-        int lineno;       // line number for each bytecode
-        bool is_virtual;  // whether this bytecode is virtual (not in source code)
-        int iblock;       // block index
-    };
-
-    pkpy_SourceData_ src;
-    Str name;
-
-    vector<Bytecode> codes;
-    vector<LineInfo> lines;
-
-    small_vector_2<PyVar, 8> consts;      // constants
-    small_vector_2<StrName, 8> varnames;  // local variables
-    int nlocals;                          // varnames.size()
-
-    c11_smallmap_n2i varnames_inv;
-    vector<CodeBlock> blocks;
-    c11_smallmap_n2i labels;
-    vector<FuncDecl_> func_decls;
-
-    int start_line;
-    int end_line;
-
-    void _gc_mark(VM*) const;
-
-    CodeObject(pkpy_SourceData_ src, const Str& name) :
-        src(src), name(name), nlocals(0), start_line(-1), end_line(-1) {
-        blocks.push_back(CodeBlock{CodeBlockType_NO_BLOCK, -1, 0, -1, -1});
-        c11_smallmap_n2i__ctor(&varnames_inv);
-        c11_smallmap_n2i__ctor(&labels);
-        PK_INCREF(src);
-    }
-
-    ~CodeObject() {
-        c11_smallmap_n2i__dtor(&varnames_inv);
-        c11_smallmap_n2i__dtor(&labels);
-        PK_DECREF(src);
-    }
-};
-
-struct FuncDecl {
-    PK_ALWAYS_PASS_BY_POINTER(FuncDecl)
-    struct KwArg {
-        int index;    // index in co->varnames
-        StrName key;  // name of this argument
-        PyVar value;  // default value
-    };
-
-    CodeObject* code;  // strong ref
-
-    small_vector_2<int, 8> args;    // indices in co->varnames
-    c11_vector/*T=KwArg*/ kwargs;   // indices in co->varnames
-
-    int starred_arg = -1;    // index in co->varnames, -1 if no *arg
-    int starred_kwarg = -1;  // index in co->varnames, -1 if no **kwarg
-    bool nested = false;     // whether this function is nested
-
-    const char* docstring;  // docstring of this function (weak ref)
-
-    FuncType type = FuncType_UNSET;
-    c11_smallmap_n2i kw_to_index;
-
-    void add_kwarg(int index, StrName key, PyVar value) {
-        c11_smallmap_n2i__set(&kw_to_index, key.index, index);
-        c11_vector__push(KwArg, &kwargs, (KwArg{index, key.index, value}));
-    }
-
-    void _gc_mark(VM*) const;
-
-    FuncDecl(CodeObject* code){
-        this->code = code;
-        c11_vector__ctor(&kwargs, sizeof(KwArg));
-        c11_smallmap_n2i__ctor(&kw_to_index);
-    }
-
-    ~FuncDecl(){
-        delete code;
-        c11_vector__dtor(&kwargs);
-        c11_smallmap_n2i__dtor(&kw_to_index);
-    }
-};
-
 struct NativeFunc {
     NativeFuncC f;
     int argc;        // old style argc-based call
@@ -117,6 +27,10 @@ struct NativeFunc {
     PyVar call(VM* vm, ArgsView args) const { return f(vm, args); }
 
     void _gc_mark(VM*) const;
+
+    ~NativeFunc() {
+        if(decl) PK_DECREF(decl);
+    }
 };
 
 struct Function {
@@ -128,11 +42,14 @@ struct Function {
     NameDict* _closure; // strong ref
 
     Function(FuncDecl_ decl, PyObject* _module, PyObject* _class, NameDict* _closure) :
-        decl(decl), _module(_module), _class(_class), _closure(_closure) {}
+        decl(decl), _module(_module), _class(_class), _closure(_closure) {
+            PK_INCREF(decl);
+        }
 
     void _gc_mark(VM*) const;
 
     ~Function() {
+        PK_DECREF(decl);
         delete _closure;
     }
 };

+ 21 - 21
src/common/sourcedata.c

@@ -25,14 +25,14 @@ void pkpy_SourceData__ctor(struct pkpy_SourceData* self,
     // Skip utf8 BOM if there is any.
     if (source.size >= 3 && strncmp(source.data, "\xEF\xBB\xBF", 3) == 0) index += 3;
     // Drop all '\r'
-    pkpy_SStream ss;
-    pkpy_SStream__ctor2(&ss, source.size + 1);
+    pk_SStream ss;
+    pk_SStream__ctor2(&ss, source.size + 1);
     while(index < source.size){
         char c = source.data[index];
-        if(c != '\r') pkpy_SStream__write_char(&ss, c);
+        if(c != '\r') pk_SStream__write_char(&ss, c);
         index++;
     }
-    self->source = pkpy_SStream__submit(&ss);
+    self->source = pk_SStream__submit(&ss);
     self->is_precompiled = (strncmp(pkpy_Str__data(&self->source), "pkpy:", 5) == 0);
     c11_vector__push(const char*, &self->line_starts, pkpy_Str__data(&self->source));
 }
@@ -63,46 +63,46 @@ bool pkpy_SourceData__get_line(const struct pkpy_SourceData* self, int lineno, c
 }
 
 pkpy_Str pkpy_SourceData__snapshot(const struct pkpy_SourceData* self, int lineno, const char* cursor, const char* name) {
-    pkpy_SStream ss;
-    pkpy_SStream__ctor(&ss);
+    pk_SStream ss;
+    pk_SStream__ctor(&ss);
 
-    // pkpy_SStream__write_cstr(&ss, "  File \"");
-    // pkpy_SStream__write_Str(&ss, &self->filename);
-    // pkpy_SStream__write_cstr(&ss, "\", line ");
-    // pkpy_SStream__write_int(&ss, lineno);
+    // pk_SStream__write_cstr(&ss, "  File \"");
+    // pk_SStream__write_Str(&ss, &self->filename);
+    // pk_SStream__write_cstr(&ss, "\", line ");
+    // pk_SStream__write_int(&ss, lineno);
 
-    pkpy_SStream__write(&ss,
+    pk_SStream__write(&ss,
         "  File \"{}\", line {}",
         &self->filename,
         lineno
     );
 
     if(name && *name) {
-        pkpy_SStream__write_cstr(&ss, ", in ");
-        pkpy_SStream__write_cstr(&ss, name);
+        pk_SStream__write_cstr(&ss, ", in ");
+        pk_SStream__write_cstr(&ss, name);
     }
 
     if(!self->is_precompiled) {
-        pkpy_SStream__write_char(&ss, '\n');
+        pk_SStream__write_char(&ss, '\n');
         const char *st = NULL, *ed;
         if(pkpy_SourceData__get_line(self, lineno, &st, &ed)) {
             while(st < ed && isblank(*st))
                 ++st;
             if(st < ed) {
-                pkpy_SStream__write_cstr(&ss, "    ");
-                pkpy_SStream__write_cstrn(&ss, st, ed - st);
+                pk_SStream__write_cstr(&ss, "    ");
+                pk_SStream__write_cstrn(&ss, st, ed - st);
                 if(cursor && st <= cursor && cursor <= ed) {
-                    pkpy_SStream__write_cstr(&ss, "\n    ");
+                    pk_SStream__write_cstr(&ss, "\n    ");
                     for(int i = 0; i < (cursor - st); ++i)
-                        pkpy_SStream__write_char(&ss, ' ');
-                    pkpy_SStream__write_cstr(&ss, "^");
+                        pk_SStream__write_char(&ss, ' ');
+                    pk_SStream__write_cstr(&ss, "^");
                 }
             } else {
                 st = NULL;
             }
         }
 
-        if(!st) { pkpy_SStream__write_cstr(&ss, "    <?>"); }
+        if(!st) { pk_SStream__write_cstr(&ss, "    <?>"); }
     }
-    return pkpy_SStream__submit(&ss);
+    return pk_SStream__submit(&ss);
 }

+ 58 - 45
src/common/sstream.c

@@ -1,4 +1,5 @@
 #include "pocketpy/common/sstream.h"
+#include "pocketpy/common/config.h"
 #include "pocketpy/common/utils.h"
 
 #include <stdio.h>
@@ -6,39 +7,39 @@
 #include <ctype.h>
 #include <math.h>
 
-void pkpy_SStream__ctor(pkpy_SStream* self) {
+void pk_SStream__ctor(pk_SStream* self) {
     c11_vector__ctor(&self->data, sizeof(char));
 }
 
-void pkpy_SStream__ctor2(pkpy_SStream* self, int capacity) {
+void pk_SStream__ctor2(pk_SStream* self, int capacity) {
     c11_vector__ctor(&self->data, sizeof(char));
     c11_vector__reserve(&self->data, capacity);
 }
 
-void pkpy_SStream__dtor(pkpy_SStream* self) {
+void pk_SStream__dtor(pk_SStream* self) {
     c11_vector__dtor(&self->data);
 }
 
-void pkpy_SStream__write_char(pkpy_SStream* self, char c) {
+void pk_SStream__write_char(pk_SStream* self, char c) {
     c11_vector__push(char, &self->data, c);
 }
 
-void pkpy_SStream__write_int(pkpy_SStream* self, int i) {
+void pk_SStream__write_int(pk_SStream* self, int i) {
     char buf[12]; // sign + 10 digits + null terminator
     snprintf(buf, sizeof(buf), "%d", i);
-    pkpy_SStream__write_cstr(self, buf);
+    pk_SStream__write_cstr(self, buf);
 }
 
-void pkpy_SStream__write_i64(pkpy_SStream* self, int64_t val) {
+void pk_SStream__write_i64(pk_SStream* self, int64_t val) {
     // sign + 21 digits + null terminator
     // str(-2**64).__len__() == 21
     c11_vector__reserve(&self->data, self->data.count + 23);
     if(val == 0){
-        pkpy_SStream__write_char(self, '0');
+        pk_SStream__write_char(self, '0');
         return;
     }
     if(val < 0){
-        pkpy_SStream__write_char(self, '-');
+        pk_SStream__write_char(self, '-');
         val = -val;
     }
     int start = self->data.count;
@@ -50,17 +51,17 @@ void pkpy_SStream__write_i64(pkpy_SStream* self, int64_t val) {
     c11_vector__reverse(char, &self->data, start, end);
 }
 
-void pkpy_SStream__write_float(pkpy_SStream* self, float val, int precision){
-    pkpy_SStream__write_double(self, val, precision);
+void pk_SStream__write_float(pk_SStream* self, float val, int precision){
+    pk_SStream__write_double(self, val, precision);
 }
 
-void pkpy_SStream__write_double(pkpy_SStream* self, double val, int precision){
+void pk_SStream__write_double(pk_SStream* self, double val, int precision){
     if(isinf(val)) {
-        pkpy_SStream__write_cstr(self, val > 0 ? "inf" : "-inf");
+        pk_SStream__write_cstr(self, val > 0 ? "inf" : "-inf");
         return;
     }
     if(isnan(val)) {
-        pkpy_SStream__write_cstr(self, "nan");
+        pk_SStream__write_cstr(self, "nan");
         return;
     }
     char b[32];
@@ -72,90 +73,102 @@ void pkpy_SStream__write_double(pkpy_SStream* self, double val, int precision){
         int prec = precision;
         size = snprintf(b, sizeof(b), "%.*f", prec, val);
     }
-    pkpy_SStream__write_cstr(self, b);
+    pk_SStream__write_cstr(self, b);
     bool all_is_digit = true;
     for(int i = 1; i < size; i++){
         if(!isdigit(b[i])){ all_is_digit = false; break; }
     }
-    if(all_is_digit) pkpy_SStream__write_cstr(self, ".0");
+    if(all_is_digit) pk_SStream__write_cstr(self, ".0");
 }
 
-void pkpy_SStream__write_Str(pkpy_SStream* self, const pkpy_Str* str) {
-    pkpy_SStream__write_cstrn(self, pkpy_Str__data(str), str->size);
+void pk_SStream__write_Str(pk_SStream* self, const pkpy_Str* str) {
+    pk_SStream__write_cstrn(self, pkpy_Str__data(str), str->size);
 }
 
-void pkpy_SStream__write_sv(pkpy_SStream* self, c11_string sv) {
-    pkpy_SStream__write_cstrn(self, sv.data, sv.size);
+void pk_SStream__write_sv(pk_SStream* self, c11_string sv) {
+    pk_SStream__write_cstrn(self, sv.data, sv.size);
 }
 
-void pkpy_SStream__write_cstr(pkpy_SStream* self, const char* str) {
-    pkpy_SStream__write_cstrn(self, str, strlen(str));
+void pk_SStream__write_cstr(pk_SStream* self, const char* str) {
+    pk_SStream__write_cstrn(self, str, strlen(str));
 }
 
-void pkpy_SStream__write_cstrn(pkpy_SStream* self, const char* str, int n) {
+void pk_SStream__write_cstrn(pk_SStream* self, const char* str, int n) {
     c11_vector__extend(char, &self->data, str, n);
 }
 
-void pkpy_SStream__write_hex(pkpy_SStream* self, unsigned char c, bool non_zero) {
+void pk_SStream__write_hex(pk_SStream* self, unsigned char c, bool non_zero) {
     unsigned char high = c >> 4;
     unsigned char low = c & 0xf;
     if(non_zero) {
-        if(high) pkpy_SStream__write_char(self, PK_HEX_TABLE[high]);
-        if(high || low) pkpy_SStream__write_char(self, PK_HEX_TABLE[low]);
+        if(high) pk_SStream__write_char(self, PK_HEX_TABLE[high]);
+        if(high || low) pk_SStream__write_char(self, PK_HEX_TABLE[low]);
     } else {
-        pkpy_SStream__write_char(self, PK_HEX_TABLE[high]);
-        pkpy_SStream__write_char(self, PK_HEX_TABLE[low]);
+        pk_SStream__write_char(self, PK_HEX_TABLE[high]);
+        pk_SStream__write_char(self, PK_HEX_TABLE[low]);
     }
 }
 
-void pkpy_SStream__write_ptr(pkpy_SStream* self, void* p) {
+void pk_SStream__write_ptr(pk_SStream* self, void* p) {
     if(p == NULL) {
-        pkpy_SStream__write_cstr(self, "0x0");
+        pk_SStream__write_cstr(self, "0x0");
         return;
     }
-    pkpy_SStream__write_cstr(self, "0x");
+    pk_SStream__write_cstr(self, "0x");
     uintptr_t p_t = (uintptr_t)(p);
     bool non_zero = true;
     for(int i = sizeof(void*) - 1; i >= 0; i--) {
         unsigned char cpnt = (p_t >> (i * 8)) & 0xff;
-        pkpy_SStream__write_hex(self, cpnt, non_zero);
+        pk_SStream__write_hex(self, cpnt, non_zero);
         if(cpnt != 0) non_zero = false;
     }
 }
 
-void pkpy_SStream__write_any(pkpy_SStream* self, const char* fmt, const pkpy_AnyStr* args, int n){
+void pk_SStream__write_any(pk_SStream* self, const char* fmt, const pk_AnyStr* args, int n){
     int i = 0;
     while(*fmt){
         if(*fmt == '{' && fmt[1] == '}'){
             assert(i < n);
             switch(args[i].type){
-                case 1: pkpy_SStream__write_int(self, args[i]._int); break;
-                case 2: pkpy_SStream__write_i64(self, args[i]._i64); break;
-                case 3: pkpy_SStream__write_float(self, args[i]._float, -1); break;
-                case 4: pkpy_SStream__write_double(self, args[i]._double, -1); break;
-                case 5: pkpy_SStream__write_char(self, args[i]._char); break;
-                case 6: pkpy_SStream__write_Str(self, args[i]._str); break;
-                case 7: pkpy_SStream__write_sv(self, args[i]._sv); break;
-                case 8: pkpy_SStream__write_cstr(self, args[i]._cstr); break;
-                case 9: assert(0); break;
+                case 1: pk_SStream__write_int(self, args[i]._int); break;
+                case 2: pk_SStream__write_i64(self, args[i]._i64); break;
+                case 3: pk_SStream__write_float(self, args[i]._float, -1); break;
+                case 4: pk_SStream__write_double(self, args[i]._double, -1); break;
+                case 5: pk_SStream__write_char(self, args[i]._char); break;
+                case 6: pk_SStream__write_Str(self, args[i]._str); break;
+                case 7: pk_SStream__write_sv(self, args[i]._sv); break;
+                case 8: pk_SStream__write_cstr(self, args[i]._cstr); break;
+                case 9: pk_SStream__write_ptr(self, args[i]._ptr); break;
                 default: assert(0); break;
             }
             fmt += 2;
             i++;
         }else{
-            pkpy_SStream__write_char(self, *fmt);
+            pk_SStream__write_char(self, *fmt);
             fmt++;
         }
     }
 }
 
-pkpy_Str pkpy_SStream__submit(pkpy_SStream* self) {
+pkpy_Str pk_SStream__submit(pk_SStream* self) {
     c11_vector__push(char, &self->data, '\0');
     pkpy_Str retval = {
         .size = self->data.count - 1,
-        .is_ascii = false,  // need to check
+        .is_ascii = c11__isascii((char*)self->data.data, self->data.count),
         .is_sso = false,
         ._ptr = (char*)self->data.data
     };
     return retval;
 }
+
+const char* pk_format_any(const char* fmt, const pk_AnyStr* args, int n){
+    PK_THREAD_LOCAL pk_SStream ss;
+    if(ss.data.elem_size == 0){
+        pk_SStream__ctor2(&ss, 128);
+    }else{
+        c11_vector__clear(&ss.data);
+    }
+    pk_SStream__write_any(&ss, fmt, args, n);
+    pk_SStream__write_char(&ss, '\0');
+    return (const char*)ss.data.data;
+}

+ 70 - 49
src/compiler/compiler.cpp

@@ -1,5 +1,6 @@
 #include "pocketpy/compiler/compiler.hpp"
 #include "pocketpy/common/config.h"
+#include "pocketpy/compiler/expr.hpp"
 #include "pocketpy/interpreter/vm.hpp"
 #include "pocketpy/objects/codeobject.hpp"
 
@@ -21,17 +22,21 @@ NameScope Compiler::name_scope() const noexcept{
 }
 
 CodeObject* Compiler::push_global_context() noexcept{
-    CodeObject* co = new CodeObject(lexer.src, static_cast<const Str&>(lexer.src->filename));
+    CodeObject* co = CodeObject__new(lexer.src, pkpy_Str__sv(&lexer.src->filename));
     co->start_line = __i == 0 ? 1 : prev().line;
     contexts.push_back(CodeEmitContext(vm, co, contexts.size()));
     return co;
 }
 
-FuncDecl_ Compiler::push_f_context(Str name) noexcept{
-    CodeObject* co = new CodeObject(lexer.src, name);
-    FuncDecl_ decl = std::make_shared<FuncDecl>(co);
+FuncDecl_ Compiler::push_f_context(c11_string name, int* out_index) noexcept{
+    FuncDecl_ decl = FuncDecl__rcnew(lexer.src, name);
     decl->code->start_line = __i == 0 ? 1 : prev().line;
     decl->nested = name_scope() == NAME_LOCAL;
+    // add_func_decl
+    CodeEmitContext* ctx = &contexts.back();
+    c11_vector__push(FuncDecl_, &ctx->co->func_decls, decl);
+    *out_index = ctx->co->func_decls.count - 1;
+    // push new context
     contexts.push_back(CodeEmitContext(vm, decl->code, contexts.size()));
     contexts.back().func = decl;
     return decl;
@@ -54,29 +59,29 @@ Error* Compiler::pop_context() noexcept{
     if(ctx()->co->nlocals > PK_MAX_CO_VARNAMES) {
         return SyntaxError("maximum number of local variables exceeded");
     }
-    if(ctx()->co->consts.size() > 65530) {
+    if(ctx()->co->consts.count > 65530) {
         return SyntaxError("maximum number of constants exceeded");
     }
     // pre-compute LOOP_BREAK and LOOP_CONTINUE
-    for(int i = 0; i < codes.size(); i++) {
-        Bytecode& bc = codes[i];
+    for(int i = 0; i < codes.count; i++) {
+        Bytecode bc = c11__getitem(Bytecode, &codes, i);
         if(bc.op == OP_LOOP_CONTINUE) {
-            CodeBlock* block = &ctx()->co->blocks[bc.arg];
+            CodeBlock* block = c11__at(CodeBlock, &ctx()->co->blocks, bc.arg);
             Bytecode__set_signed_arg(&bc, block->start - i);
         } else if(bc.op == OP_LOOP_BREAK) {
-            CodeBlock* block = &ctx()->co->blocks[bc.arg];
+            CodeBlock* block = c11__at(CodeBlock, &ctx()->co->blocks, bc.arg);
             Bytecode__set_signed_arg(&bc, (block->end2 != -1 ? block->end2 : block->end) - i);
         }
     }
     // pre-compute func->is_simple
-    FuncDecl_ func = contexts.back().func;
+    FuncDecl* func = contexts.back().func;
     if(func) {
         // check generator
-        for(Bytecode bc: func->code->codes) {
-            if(bc.op == OP_YIELD_VALUE || bc.op == OP_FOR_ITER_YIELD_VALUE) {
+        c11_vector__foreach(Bytecode, &func->code->codes, bc) {
+            if(bc->op == OP_YIELD_VALUE || bc->op == OP_FOR_ITER_YIELD_VALUE) {
                 func->type = FuncType_GENERATOR;
-                for(Bytecode bc: func->code->codes) {
-                    if(bc.op == OP_RETURN_VALUE && bc.arg == BC_NOARG) {
+                c11_vector__foreach(Bytecode, &func->code->codes, bc) {
+                    if(bc->op == OP_RETURN_VALUE && bc->arg == BC_NOARG) {
                         return SyntaxError("'return' with argument inside generator function");
                     }
                 }
@@ -93,9 +98,11 @@ Error* Compiler::pop_context() noexcept{
                 func->type = FuncType_SIMPLE;
 
                 bool is_empty = false;
-                if(func->code->codes.size() == 1) {
-                    Bytecode bc = func->code->codes[0];
-                    if(bc.op == OP_RETURN_VALUE && bc.arg == 1) { is_empty = true; }
+                if(func->code->codes.count == 1) {
+                    Bytecode bc = c11__getitem(Bytecode, &func->code->codes, 0);
+                    if(bc.op == OP_RETURN_VALUE && bc.arg == 1) {
+                        is_empty = true;
+                    }
                 }
                 if(is_empty) func->type = FuncType_EMPTY;
             } else
@@ -261,7 +268,8 @@ Error* Compiler::exprFString() noexcept{
 
 Error* Compiler::exprLambda() noexcept{
     Error* err;
-    FuncDecl_ decl = push_f_context("<lambda>");
+    int decl_index;
+    FuncDecl_ decl = push_f_context({"<lambda>", 8}, &decl_index);
     int line = prev().line;     // backup line
     if(!match(TK_COLON)) {
         check(_compile_f_args(decl, false));
@@ -272,7 +280,7 @@ Error* Compiler::exprLambda() noexcept{
     ctx()->s_emit_top();
     ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
     check(pop_context());
-    LambdaExpr* e = make_expr<LambdaExpr>(decl);
+    LambdaExpr* e = make_expr<LambdaExpr>(decl_index);
     e->line = line;
     ctx()->s_push(e);
     return NULL;
@@ -744,7 +752,7 @@ Error* Compiler::compile_while_loop() noexcept{
     // optional else clause
     if(match(TK_ELSE)) {
         check(compile_block_body());
-        block->end2 = ctx()->co->codes.size();
+        block->end2 = ctx()->co->codes.count;
     }
     return NULL;
 }
@@ -769,7 +777,7 @@ Error* Compiler::compile_for_loop() noexcept{
     // optional else clause
     if(match(TK_ELSE)) {
         check(compile_block_body());
-        block->end2 = ctx()->co->codes.size();
+        block->end2 = ctx()->co->codes.count;
     }
     return NULL;
 }
@@ -815,17 +823,18 @@ Error* Compiler::compile_try_except() noexcept{
 
     if(match(TK_FINALLY)) {
         int patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
-        finally_entry = ctx()->co->codes.size();
+        finally_entry = ctx()->co->codes.count;
         check(compile_block_body());
         ctx()->emit_(OP_JUMP_ABSOLUTE_TOP, BC_NOARG, BC_KEEPLINE);
         ctx()->patch_jump(patch);
     }
     // no match, re-raise
     if(finally_entry != -1) {
-        i64 target = ctx()->co->codes.size() + 2;
+        i64 target = ctx()->co->codes.count + 2;
         ctx()->emit_(OP_LOAD_CONST, ctx()->add_const(VAR(target)), BC_KEEPLINE);
         int i = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
-        Bytecode__set_signed_arg(&ctx()->co->codes[i], finally_entry - i);
+        Bytecode* bc = c11__at(Bytecode, &ctx()->co->codes, i);
+        Bytecode__set_signed_arg(bc, finally_entry - i);
     }
     ctx()->emit_(OP_RE_RAISE, BC_NOARG, BC_KEEPLINE);
 
@@ -833,10 +842,11 @@ Error* Compiler::compile_try_except() noexcept{
     for(int patch: patches)
         ctx()->patch_jump(patch);
     if(finally_entry != -1) {
-        i64 target = ctx()->co->codes.size() + 2;
+        i64 target = ctx()->co->codes.count + 2;
         ctx()->emit_(OP_LOAD_CONST, ctx()->add_const(VAR(target)), BC_KEEPLINE);
         int i = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
-        Bytecode__set_signed_arg(&ctx()->co->codes[i], finally_entry - i);
+        Bytecode* bc = c11__at(Bytecode, &ctx()->co->codes, i);
+        Bytecode__set_signed_arg(bc, finally_entry - i);
     }
     return NULL;
 }
@@ -1144,7 +1154,7 @@ Error* Compiler::compile_class(int decorators) noexcept{
     return NULL;
 }
 
-Error* Compiler::_compile_f_args(FuncDecl_ decl, bool enable_type_hints) noexcept{
+Error* Compiler::_compile_f_args(FuncDecl* decl, bool enable_type_hints) noexcept{
     int state = 0;  // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs
     Error* err;
     do {
@@ -1163,17 +1173,23 @@ Error* Compiler::_compile_f_args(FuncDecl_ decl, bool enable_type_hints) noexcep
         StrName name(prev().sv());
 
         // check duplicate argument name
-        for(int j: decl->args) {
-            if(decl->code->varnames[j] == name) return SyntaxError("duplicate argument name");
+        uint16_t tmp_name;
+        c11_vector__foreach(int, &decl->args, j) {
+            tmp_name = c11__getitem(uint16_t, &decl->args, *j);
+            if(tmp_name == name.index) return SyntaxError("duplicate argument name");
         }
-        c11_vector__foreach(FuncDecl::KwArg, &decl->kwargs, kv) {
-            if(decl->code->varnames[kv->index] == name) return SyntaxError("duplicate argument name");
+        c11_vector__foreach(FuncDeclKwArg, &decl->kwargs, kv) {
+            tmp_name = c11__getitem(uint16_t, &decl->code->varnames, kv->index);
+            if(tmp_name == name.index) return SyntaxError("duplicate argument name");
         }
-        if(decl->starred_arg != -1 && decl->code->varnames[decl->starred_arg] == name) {
-            return SyntaxError("duplicate argument name");
+        
+        if(decl->starred_arg != -1) {
+            tmp_name = c11__getitem(uint16_t, &decl->code->varnames, decl->starred_arg);
+            if(tmp_name == name.index) return SyntaxError("duplicate argument name");
         }
-        if(decl->starred_kwarg != -1 && decl->code->varnames[decl->starred_kwarg] == name) {
-            return SyntaxError("duplicate argument name");
+        if(decl->starred_kwarg != -1) {
+            tmp_name = c11__getitem(uint16_t, &decl->code->varnames, decl->starred_kwarg);
+            if(tmp_name == name.index) return SyntaxError("duplicate argument name");
         }
 
         // eat type hints
@@ -1181,7 +1197,9 @@ Error* Compiler::_compile_f_args(FuncDecl_ decl, bool enable_type_hints) noexcep
         if(state == 0 && curr().type == TK_ASSIGN) state = 2;
         int index = ctx()->add_varname(name);
         switch(state) {
-            case 0: decl->args.push_back(index); break;
+            case 0:
+                c11_vector__push(int, &decl->args, index);
+                break;
             case 1:
                 decl->starred_arg = index;
                 state += 1;
@@ -1191,7 +1209,7 @@ Error* Compiler::_compile_f_args(FuncDecl_ decl, bool enable_type_hints) noexcep
                 PyVar value;
                 check(read_literal(&value));
                 if(value == nullptr) return SyntaxError("default argument must be a literal");
-                decl->add_kwarg(index, name, value);
+                FuncDecl__add_kwarg(decl, index, name.index, (const ::PyVar*)&value);
             } break;
             case 3:
                 decl->starred_kwarg = index;
@@ -1205,8 +1223,9 @@ Error* Compiler::_compile_f_args(FuncDecl_ decl, bool enable_type_hints) noexcep
 Error* Compiler::compile_function(int decorators) noexcept{
     Error* err;
     consume(TK_ID);
-    Str decl_name = prev().str();
-    FuncDecl_ decl = push_f_context(decl_name);
+    std::string_view decl_name = prev().sv();
+    int decl_index;
+    FuncDecl_ decl = push_f_context({decl_name.data(), (int)decl_name.size()}, &decl_index);
     consume(TK_LPAREN);
     if(!match(TK_RPAREN)) {
         check(_compile_f_args(decl, true));
@@ -1216,22 +1235,24 @@ Error* Compiler::compile_function(int decorators) noexcept{
     check(compile_block_body());
     check(pop_context());
 
-    decl->docstring = nullptr;
-    if(decl->code->codes.size() >= 2 && decl->code->codes[0].op == OP_LOAD_CONST &&
-       decl->code->codes[1].op == OP_POP_TOP) {
-        PyVar c = decl->code->consts[decl->code->codes[0].arg];
-        if(is_type(c, vm->tp_str)) {
-            decl->code->codes[0].op = OP_NO_OP;
-            decl->code->codes[1].op = OP_NO_OP;
-            decl->docstring = PK_OBJ_GET(Str, c).c_str();
+    if(decl->code->codes.count >= 2) {
+        Bytecode* codes = (Bytecode*)decl->code->codes.data;
+        if(codes[0].op == OP_LOAD_CONST && codes[1].op == OP_POP_TOP) {
+            // handle optional docstring
+            PyVar* c = c11__at(PyVar, &decl->code->consts, codes[0].arg);
+            if(is_type(*c, vm->tp_str)) {
+                codes[0].op = OP_NO_OP;
+                codes[1].op = OP_NO_OP;
+                decl->docstring = PK_OBJ_GET(Str, *c).c_str();
+            }
         }
     }
-    ctx()->emit_(OP_LOAD_FUNCTION, ctx()->add_func_decl(decl), prev().line);
+    ctx()->emit_(OP_LOAD_FUNCTION, decl_index, prev().line);
 
     ctx()->s_emit_decorators(decorators);
 
     if(!ctx()->is_compiling_class) {
-        NameExpr* e = make_expr<NameExpr>(decl_name, name_scope());
+        NameExpr* e = make_expr<NameExpr>(StrName(decl_name), name_scope());
         e->emit_store(ctx());
         delete_expr(e);
     } else {

+ 50 - 50
src/compiler/expr.cpp

@@ -1,5 +1,7 @@
 #include "pocketpy/compiler/expr.hpp"
 #include "pocketpy/interpreter/vm.hpp"
+#include "pocketpy/objects/codeobject.h"
+#include "pocketpy/objects/public.h"
 
 namespace pkpy {
 
@@ -16,23 +18,26 @@ inline bool is_small_int(i64 value) { return value >= INT16_MIN && value <= INT1
 int CodeEmitContext::get_loop() const noexcept{
     int index = curr_iblock;
     while(index >= 0) {
-        if(co->blocks[index].type == CodeBlockType_FOR_LOOP) break;
-        if(co->blocks[index].type == CodeBlockType_WHILE_LOOP) break;
-        index = co->blocks[index].parent;
+        CodeBlock* block = c11__at(CodeBlock, &co->blocks, index);
+        if(block->type == CodeBlockType_FOR_LOOP) break;
+        if(block->type == CodeBlockType_WHILE_LOOP) break;
+        index = block->parent;
     }
     return index;
 }
 
 CodeBlock* CodeEmitContext::enter_block(CodeBlockType type) noexcept{
-    co->blocks.push_back(CodeBlock{type, curr_iblock, (int)co->codes.size(), -1, -1});
-    curr_iblock = co->blocks.size() - 1;
-    return &co->blocks[curr_iblock];
+    CodeBlock block = {type, curr_iblock, co->codes.count, -1, -1};
+    c11_vector__push(CodeBlock, &co->blocks, block);
+    curr_iblock = co->blocks.count - 1;
+    return c11__at(CodeBlock, &co->blocks, curr_iblock);
 }
 
 void CodeEmitContext::exit_block() noexcept{
-    auto curr_type = co->blocks[curr_iblock].type;
-    co->blocks[curr_iblock].end = co->codes.size();
-    curr_iblock = co->blocks[curr_iblock].parent;
+    CodeBlock* block = c11__at(CodeBlock, &co->blocks, curr_iblock);
+    CodeBlockType curr_type = block->type;
+    block->end = co->codes.count;
+    curr_iblock = block->parent;
     assert(curr_iblock >= 0);
     if(curr_type == CodeBlockType_FOR_LOOP) {
         // add a no op here to make block check work
@@ -55,38 +60,37 @@ void CodeEmitContext::s_emit_decorators(int count) noexcept{
 }
 
 int CodeEmitContext::emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual) noexcept{
-    co->codes.push_back(Bytecode{(uint8_t)opcode, arg});
-    co->lines.push_back(CodeObject::LineInfo{line, is_virtual, curr_iblock});
-    int i = co->codes.size() - 1;
+    c11_vector__push(Bytecode, &co->codes, (Bytecode{(uint8_t)opcode, arg}));
+    c11_vector__push(BytecodeEx, &co->codes_ex, (BytecodeEx{line, is_virtual, curr_iblock}));
+    int i = co->codes.count - 1;
+    BytecodeEx* codes_ex = (BytecodeEx*)co->codes_ex.data;
     if(line == BC_KEEPLINE) {
-        if(i >= 1)
-            co->lines[i].lineno = co->lines[i - 1].lineno;
-        else
-            co->lines[i].lineno = 1;
+        codes_ex[i].lineno = i>=1 ? codes_ex[i-1].lineno : 1;
     }
     return i;
 }
 
 void CodeEmitContext::revert_last_emit_() noexcept{
-    co->codes.pop_back();
-    co->lines.pop_back();
+    c11_vector__pop(Bytecode, &co->codes);
+    c11_vector__pop(BytecodeEx, &co->codes_ex);
 }
 
 void CodeEmitContext::try_merge_for_iter_store(int i) noexcept{
     // [FOR_ITER, STORE_?, ]
-    if(co->codes[i].op != OP_FOR_ITER) return;
-    if(co->codes.size() - i != 2) return;
-    uint16_t arg = co->codes[i + 1].arg;
-    if(co->codes[i + 1].op == OP_STORE_FAST) {
+    Bytecode* co_codes = (Bytecode*)co->codes.data;
+    if(co_codes[i].op != OP_FOR_ITER) return;
+    if(co->codes.count - i != 2) return;
+    uint16_t arg = co_codes[i + 1].arg;
+    if(co_codes[i + 1].op == OP_STORE_FAST) {
         revert_last_emit_();
-        co->codes[i].op = OP_FOR_ITER_STORE_FAST;
-        co->codes[i].arg = arg;
+        co_codes[i].op = OP_FOR_ITER_STORE_FAST;
+        co_codes[i].arg = arg;
         return;
     }
-    if(co->codes[i + 1].op == OP_STORE_GLOBAL) {
+    if(co_codes[i + 1].op == OP_STORE_GLOBAL) {
         revert_last_emit_();
-        co->codes[i].op = OP_FOR_ITER_STORE_GLOBAL;
-        co->codes[i].arg = arg;
+        co_codes[i].op = OP_FOR_ITER_STORE_GLOBAL;
+        co_codes[i].arg = arg;
         return;
     }
 }
@@ -100,14 +104,15 @@ int CodeEmitContext::emit_int(i64 value, int line) noexcept{
 }
 
 void CodeEmitContext::patch_jump(int index) noexcept{
-    int target = co->codes.size();
-    Bytecode__set_signed_arg(&co->codes[index], target - index);
+    Bytecode* co_codes = (Bytecode*)co->codes.data;
+    int target = co->codes.count;
+    Bytecode__set_signed_arg(&co_codes[index], target - index);
 }
 
 bool CodeEmitContext::add_label(StrName name) noexcept{
     bool ok = c11_smallmap_n2i__contains(&co->labels, name.index);
     if(ok) return false;
-    c11_smallmap_n2i__set(&co->labels, name.index, co->codes.size());
+    c11_smallmap_n2i__set(&co->labels, name.index, co->codes.count);
     return true;
 }
 
@@ -115,9 +120,9 @@ int CodeEmitContext::add_varname(StrName name) noexcept{
     // PK_MAX_CO_VARNAMES will be checked when pop_context(), not here
     int index = c11_smallmap_n2i__get(&co->varnames_inv, name.index, -1);
     if(index >= 0) return index;
-    co->varnames.push_back(name);
+    c11_vector__push(uint16_t, &co->varnames, name.index);
     co->nlocals++;
-    index = co->varnames.size() - 1;
+    index = co->varnames.count - 1;
     c11_smallmap_n2i__set(&co->varnames_inv, name.index, index);
     return index;
 }
@@ -127,9 +132,10 @@ int CodeEmitContext::add_const_string(std::string_view key) noexcept{
     if(val) {
         return *val;
     } else {
-        co->consts.push_back(VAR(key));
-        int index = co->consts.size() - 1;
-        key = co->consts.back().obj_get<Str>().sv();
+        // co->consts.push_back(VAR(key));
+        c11_vector__push(PyVar, &co->consts, VAR(key));
+        int index = co->consts.count - 1;
+        key = c11__getitem(PyVar, &co->consts, index).obj_get<Str>().sv();
         c11_smallmap_s2n__set(&_co_consts_string_dedup_map, {key.data(), (int)key.size()}, index);
         return index;
     }
@@ -137,14 +143,8 @@ int CodeEmitContext::add_const_string(std::string_view key) noexcept{
 
 int CodeEmitContext::add_const(PyVar v) noexcept{
     assert(!is_type(v, VM::tp_str));
-    co->consts.push_back(v);
-    int index = co->consts.size() - 1;
-    return index;
-}
-
-int CodeEmitContext::add_func_decl(FuncDecl_ decl) noexcept{
-    co->func_decls.push_back(decl);
-    return co->func_decls.size() - 1;
+    c11_vector__push(PyVar, &co->consts, v);
+    return co->consts.count - 1;
 }
 
 void CodeEmitContext::emit_store_name(NameScope scope, StrName name, int line) noexcept{
@@ -340,14 +340,14 @@ bool TupleExpr::emit_store(CodeEmitContext* ctx) {
     }
 
     if(starred_i == -1) {
-        Bytecode& prev = ctx->co->codes.back();
-        if(prev.op == OP_BUILD_TUPLE && prev.arg == items.size()) {
+        Bytecode* prev = c11__at(Bytecode, &ctx->co->codes, ctx->co->codes.count - 1);
+        if(prev->op == OP_BUILD_TUPLE && prev->arg == items.size()) {
             // build tuple and unpack it is meaningless
             ctx->revert_last_emit_();
         } else {
-            if(prev.op == OP_FOR_ITER) {
-                prev.op = OP_FOR_ITER_UNPACK;
-                prev.arg = items.size();
+            if(prev->op == OP_FOR_ITER) {
+                prev->op = OP_FOR_ITER_UNPACK;
+                prev->arg = items.size();
             } else {
                 ctx->emit_(OP_UNPACK_SEQUENCE, items.size(), line);
             }
@@ -543,7 +543,7 @@ void FStringExpr::emit_(CodeEmitContext* ctx) {
 void SubscrExpr::emit_(CodeEmitContext* ctx) {
     lhs->emit_(ctx);
     rhs->emit_(ctx);
-    Bytecode last_bc = ctx->co->codes.back();
+    Bytecode last_bc = c11__getitem(Bytecode, &ctx->co->codes, ctx->co->codes.count-1);
     if(rhs->is_name() && last_bc.op == OP_LOAD_FAST) {
         ctx->revert_last_emit_();
         ctx->emit_(OP_LOAD_SUBSCR_FAST, last_bc.arg, line);
@@ -558,7 +558,7 @@ void SubscrExpr::emit_(CodeEmitContext* ctx) {
 bool SubscrExpr::emit_store(CodeEmitContext* ctx) {
     lhs->emit_(ctx);
     rhs->emit_(ctx);
-    Bytecode last_bc = ctx->co->codes.back();
+    Bytecode last_bc = c11__getitem(Bytecode, &ctx->co->codes, ctx->co->codes.count-1);
     if(rhs->is_name() && last_bc.op == OP_LOAD_FAST) {
         ctx->revert_last_emit_();
         ctx->emit_(OP_STORE_SUBSCR_FAST, last_bc.arg, line);

+ 9 - 9
src/error.c

@@ -34,26 +34,26 @@ void pkpy_Exception__stpush(pkpy_Exception* self, pkpy_SourceData_ src, int line
 }
 
 pkpy_Str pkpy_Exception__summary(pkpy_Exception* self){
-    pkpy_SStream ss;
-    pkpy_SStream__ctor(&ss);
+    pk_SStream ss;
+    pk_SStream__ctor(&ss);
 
     if(self->is_re){
-        pkpy_SStream__write_cstr(&ss, "Traceback (most recent call last):\n");
+        pk_SStream__write_cstr(&ss, "Traceback (most recent call last):\n");
     }
     for(int i=self->stacktrace.count-1; i >= 0; i--) {
         pkpy_ExceptionFrame* frame = c11__at(pkpy_ExceptionFrame, &self->stacktrace, i);
         pkpy_Str s = pkpy_SourceData__snapshot(frame->src, frame->lineno, frame->cursor, pkpy_Str__data(&frame->name));
-        pkpy_SStream__write_Str(&ss, &s);
+        pk_SStream__write_Str(&ss, &s);
         pkpy_Str__dtor(&s);
-        pkpy_SStream__write_cstr(&ss, "\n");
+        pk_SStream__write_cstr(&ss, "\n");
     }
 
     const char* name = pkpy_StrName__rmap(self->type);
-    pkpy_SStream__write_cstr(&ss, name);
+    pk_SStream__write_cstr(&ss, name);
 
     if(self->msg.size > 0){
-        pkpy_SStream__write_cstr(&ss, ": ");
-        pkpy_SStream__write_Str(&ss, &self->msg);
+        pk_SStream__write_cstr(&ss, ": ");
+        pk_SStream__write_Str(&ss, &self->msg);
     }
-    return pkpy_SStream__submit(&ss);
+    return pk_SStream__submit(&ss);
 }

+ 16 - 13
src/interpreter/ceval.cpp

@@ -115,7 +115,7 @@ bool VM::py_ge(PyVar _0, PyVar _1) {
     }
 #define DISPATCH_JUMP_ABSOLUTE(__target)                                                                               \
     {                                                                                                                  \
-        frame->_ip = &frame->co->codes[__target];                                                                      \
+        frame->_ip = c11__at(Bytecode, &frame->co->codes, __target);                                                   \
         goto __NEXT_STEP;                                                                                              \
     }
 
@@ -136,7 +136,7 @@ PyVar VM::__run_top_frame() {
                     frame->_ip++;
                 } else if(__internal_exception.type == InternalExceptionType::Handled) {
                     // HandledException + continue
-                    frame->_ip = &frame->co->codes[__internal_exception.arg];
+                    frame->_ip = c11__at(Bytecode, &frame->co->codes, __internal_exception.arg);
                     __internal_exception = {};
                 } else {
                     // UnhandledException + continue (need_raise = true)
@@ -176,7 +176,9 @@ PyVar VM::__run_top_frame() {
                         POP();
                         DISPATCH()
                     /*****************************************/
-                    case OP_LOAD_CONST: PUSH(frame->co->consts[byte.arg]); DISPATCH()
+                    case OP_LOAD_CONST:
+                        PUSH(c11__getitem(PyVar, &frame->co->consts, byte.arg));
+                        DISPATCH()
                     case OP_LOAD_NONE: PUSH(None); DISPATCH()
                     case OP_LOAD_TRUE: PUSH(True); DISPATCH()
                     case OP_LOAD_FALSE: PUSH(False); DISPATCH()
@@ -185,12 +187,13 @@ PyVar VM::__run_top_frame() {
                     /*****************************************/
                     case OP_LOAD_ELLIPSIS: PUSH(Ellipsis); DISPATCH()
                     case OP_LOAD_FUNCTION: {
-                        const FuncDecl_& decl = frame->co->func_decls[byte.arg];
+                        FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg);
                         PyVar obj;
                         if(decl->nested) {
                             NameDict* captured = frame->_locals.to_namedict();
                             obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, captured);
-                            captured->set(decl->code->name, obj);
+                            uint16_t name = pkpy_StrName__map2(pkpy_Str__sv(&decl->code->name));
+                            captured->set(name, obj);
                         } else {
                             obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, nullptr);
                         }
@@ -201,7 +204,7 @@ PyVar VM::__run_top_frame() {
                     /*****************************************/
                     case OP_LOAD_FAST: {
                         PyVar _0 = frame->_locals[byte.arg];
-                        if(_0 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]);
+                        if(_0 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
                         PUSH(_0);
                     }
                         DISPATCH()
@@ -311,7 +314,7 @@ PyVar VM::__run_top_frame() {
                         DISPATCH()
                     case OP_LOAD_SUBSCR_FAST: {
                         PyVar _1 = frame->_locals[byte.arg];
-                        if(_1 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]);
+                        if(_1 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
                         PyVar _0 = TOP();  // a
                         auto _ti = _tp_info(_0);
                         if(_ti->m__getitem__) {
@@ -376,7 +379,7 @@ PyVar VM::__run_top_frame() {
                         DISPATCH()
                     case OP_STORE_SUBSCR_FAST: {
                         PyVar _2 = frame->_locals[byte.arg];  // b
-                        if(_2 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]);
+                        if(_2 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
                         PyVar _1 = POPX();  // a
                         PyVar _0 = POPX();  // val
                         auto _ti = _tp_info(_1);
@@ -389,7 +392,7 @@ PyVar VM::__run_top_frame() {
                         DISPATCH()
                     case OP_DELETE_FAST: {
                         PyVar _0 = frame->_locals[byte.arg];
-                        if(_0 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]);
+                        if(_0 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
                         frame->_locals[byte.arg].set_null();
                     }
                         DISPATCH()
@@ -792,12 +795,12 @@ PyVar VM::__run_top_frame() {
                     }
                     /*****************************************/
                     case OP_FSTRING_EVAL: {
-                        PyVar _0 = frame->co->consts[byte.arg];
+                        PyVar _0 = c11__getitem(PyVar, &frame->co->consts, byte.arg);
                         std::string_view string = CAST(Str&, _0).sv();
                         // TODO: optimize this
                         CodeObject* code = vm->compile(string, "<eval>", EVAL_MODE, true);
                         _0 = vm->_exec(code, frame->_module, frame->_callable, frame->_locals);
-                        delete code;    // leak on error
+                        CodeObject__delete(code);
                         PUSH(_0);
                     }
                         DISPATCH()
@@ -968,7 +971,7 @@ PyVar VM::__run_top_frame() {
                         DISPATCH()
                     /*****************************************/
                     case OP_IMPORT_PATH: {
-                        PyVar _0 = frame->co->consts[byte.arg];
+                        PyVar _0 = c11__getitem(PyVar, &frame->co->consts, byte.arg);
                         PUSH(py_import(CAST(Str&, _0)));
                     }
                         DISPATCH()
@@ -1103,7 +1106,7 @@ PyVar VM::__run_top_frame() {
                     /*****************************************/
                     case OP_FORMAT_STRING: {
                         PyVar _0 = POPX();
-                        const Str& spec = CAST(Str&, frame->co->consts[byte.arg]);
+                        const Str& spec = CAST(Str&, c11__getitem(PyVar, &frame->co->consts, byte.arg));
                         PUSH(__format_object(_0, spec));
                     }
                         DISPATCH()

+ 13 - 12
src/interpreter/frame.cpp

@@ -27,32 +27,33 @@ PyVar* Frame::f_closure_try_get(StrName name) {
 
 int Frame::prepare_jump_exception_handler(ValueStack* _s) {
     // try to find a parent try block
-    int i = co->lines[ip()].iblock;
+    int i = c11__at(BytecodeEx, &co->codes_ex, ip())->iblock;
     while(i >= 0) {
-        if(co->blocks[i].type == CodeBlockType_TRY_EXCEPT) break;
-        i = co->blocks[i].parent;
+        CodeBlock* block = c11__at(CodeBlock, &co->blocks, i);
+        if(block->type == CodeBlockType_TRY_EXCEPT) break;
+        i = block->parent;
     }
     if(i < 0) return -1;
     PyVar obj = _s->popx();  // pop exception object
     UnwindTarget* uw = find_unwind_target(i);
     _s->reset(actual_sp_base() + uw->offset);  // unwind the stack
     _s->push(obj);                             // push it back
-    return co->blocks[i].end;
+    return c11__at(CodeBlock, &co->blocks, i)->end;
 }
 
 int Frame::_exit_block(ValueStack* _s, int i) {
-    auto type = co->blocks[i].type;
-    if(type == CodeBlockType_FOR_LOOP) {
+    CodeBlock* block = c11__at(CodeBlock, &co->blocks, i);
+    if(block->type == CodeBlockType_FOR_LOOP) {
         _s->pop();  // pop the iterator
-    } else if(type == CodeBlockType_CONTEXT_MANAGER) {
+    } else if(block->type == CodeBlockType_CONTEXT_MANAGER) {
         _s->pop();
     }
-    return co->blocks[i].parent;
+    return block->parent;
 }
 
 void Frame::prepare_jump_break(ValueStack* _s, int target) {
-    int i = co->lines[ip()].iblock;
-    if(target >= co->codes.size()) {
+    int i = c11__at(BytecodeEx, &co->codes_ex, ip())->iblock;
+    if(target >= co->codes.count) {
         while(i >= 0)
             i = _exit_block(_s, i);
     } else {
@@ -61,7 +62,7 @@ void Frame::prepare_jump_break(ValueStack* _s, int target) {
         //     _ = 0
         // # if there is no op here, the block check will fail
         // while i: --i
-        int next_block = co->lines[target].iblock;
+        int next_block = c11__at(BytecodeEx, &co->codes_ex, target)->iblock;
         while(i >= 0 && i != next_block)
             i = _exit_block(_s, i);
         assert(i == next_block);
@@ -69,7 +70,7 @@ void Frame::prepare_jump_break(ValueStack* _s, int target) {
 }
 
 void Frame::set_unwind_target(PyVar* _sp) {
-    int iblock = co->lines[ip()].iblock;
+    int iblock = c11__at(BytecodeEx, &co->codes_ex, ip())->iblock;
     UnwindTarget* existing = find_unwind_target(iblock);
     if(existing) {
         existing->offset = _sp - actual_sp_base();

+ 101 - 69
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/codeobject.h"
 #include "pocketpy/objects/public.h"
 
 #include <cstddef>
@@ -209,7 +210,7 @@ PyVar VM::exec(std::string_view source, Str filename, CompileMode mode, PyObject
 #endif
         code = compile(source, filename, mode);
         PyVar retval = _exec(code, _module);
-        delete code;    // leak if exception occurs
+        CodeObject__delete(code);
         return retval;
     } catch(TopLevelException e) {
         stderr_write(e.summary() + "\n");
@@ -221,7 +222,7 @@ PyVar VM::exec(std::string_view source, Str filename, CompileMode mode, PyObject
         Str msg = "An unknown exception occurred! It could be a bug. Please report it to @blueloveTH on GitHub.\n";
         stderr_write(msg);
     }
-    delete code;
+    CodeObject__delete(code);
     callstack.clear();
     s_data.clear();
     return nullptr;
@@ -419,11 +420,12 @@ PyObject* VM::py_import(Str path, bool throw_err) {
     PyObject* new_mod = new_module(name_cpnt, f_join(path_cpnts));
     CodeObject* code = compile(source, filename, EXEC_MODE);
     _exec(code, new_mod);
-    delete code;    // leak if exception occurs
+    CodeObject__delete(code);
     return new_mod;
 }
 
 VM::~VM() {
+    PK_DECREF(__dynamic_func_decl);
     // destroy all objects
     pk_ManagedHeap__dtor(&heap);
     // clear everything
@@ -628,11 +630,14 @@ PyVar VM::__py_exec_internal(const CodeObject* code, PyVar globals, PyVar locals
 void VM::py_exec(std::string_view source, PyVar globals, PyVar locals) {
     CodeObject* code = vm->compile(source, "<exec>", EXEC_MODE, true);
     __py_exec_internal(code, globals, locals);
+    CodeObject__delete(code);
 }
 
 PyVar VM::py_eval(std::string_view source, PyVar globals, PyVar locals) {
     CodeObject* code = vm->compile(source, "<eval>", EVAL_MODE, true);
-    return __py_exec_internal(code, globals, locals);
+    PyVar retval = __py_exec_internal(code, globals, locals);
+    CodeObject__delete(code);
+    return retval;
 }
 
 PyVar VM::__format_object(PyVar obj, Str spec) {
@@ -752,9 +757,11 @@ static std::string _opcode_argstr(VM* vm, int i, Bytecode byte, const CodeObject
     switch(byte.op) {
         case OP_LOAD_CONST:
         case OP_FORMAT_STRING:
-        case OP_IMPORT_PATH:
-            if(vm != nullptr) ss << " (" << vm->py_repr(co->consts[byte.arg]) << ")";
+        case OP_IMPORT_PATH: {
+            PyVar obj = c11__getitem(PyVar, &co->consts, byte.arg);
+            if(vm != nullptr) ss << " (" << vm->py_repr(obj) << ")";
             break;
+        }
         case OP_LOAD_NAME:
         case OP_LOAD_GLOBAL:
         case OP_LOAD_NONLOCAL:
@@ -773,8 +780,16 @@ static std::string _opcode_argstr(VM* vm, int i, Bytecode byte, const CodeObject
         case OP_DELETE_FAST:
         case OP_FOR_ITER_STORE_FAST:
         case OP_LOAD_SUBSCR_FAST:
-        case OP_STORE_SUBSCR_FAST: ss << " (" << co->varnames[byte.arg].sv() << ")"; break;
-        case OP_LOAD_FUNCTION: ss << " (" << co->func_decls[byte.arg]->code->name << ")"; break;
+        case OP_STORE_SUBSCR_FAST:{
+            StrName name = c11__getitem(StrName, &co->varnames, byte.arg);
+            ss << " (" << name.sv() << ")";
+            break;
+        }
+        case OP_LOAD_FUNCTION: {
+            const FuncDecl* decl = c11__getitem(FuncDecl*, &co->func_decls, byte.arg);
+            ss << " (" << pkpy_Str__data(&decl->code->name) << ")";
+            break;
+        }
     }
     return ss.str().str();
 }
@@ -786,22 +801,23 @@ Str VM::disassemble(CodeObject* co) {
     };
 
     vector<int> jumpTargets;
-    for(int i = 0; i < co->codes.size(); i++) {
-        Bytecode byte = co->codes[i];
-        if(Bytecode__is_forward_jump(&byte)) {
-            jumpTargets.push_back((int16_t)byte.arg + i);
+    for(int i=0; i<co->codes.count; i++) {
+        Bytecode* bc = c11__at(Bytecode, &co->codes, i);
+        if(Bytecode__is_forward_jump(bc)) {
+            jumpTargets.push_back((int16_t)bc->arg + i);
         }
     }
     SStream ss;
     int prev_line = -1;
-    for(int i = 0; i < co->codes.size(); i++) {
-        const Bytecode& byte = co->codes[i];
-        Str line = std::to_string(co->lines[i].lineno);
-        if(co->lines[i].lineno == prev_line)
+    for(int i = 0; i < co->codes.count; i++) {
+        Bytecode byte = c11__getitem(Bytecode, &co->codes, i);
+        BytecodeEx ex = c11__getitem(BytecodeEx, &co->codes_ex, i);
+        Str line = std::to_string(ex.lineno);
+        if(ex.lineno == prev_line)
             line = "";
         else {
             if(prev_line != -1) ss << "\n";
-            prev_line = co->lines[i].lineno;
+            prev_line = ex.lineno;
         }
 
         std::string pointer;
@@ -812,16 +828,17 @@ Str VM::disassemble(CodeObject* co) {
         }
         ss << pad(line, 8) << pointer << pad(std::to_string(i), 3);
         std::string bc_name(OP_NAMES[byte.op]);
-        if(co->lines[i].is_virtual) bc_name += '*';
+        if(ex.is_virtual) bc_name += '*';
         ss << " " << pad(bc_name, 25) << " ";
         std::string argStr = _opcode_argstr(this, i, byte, co);
         ss << argStr;
-        if(i != co->codes.size() - 1) ss << '\n';
+        if(i != co->codes.count - 1) ss << '\n';
     }
 
-    for(auto& decl: co->func_decls) {
+    c11_vector__foreach(FuncDecl*, &co->func_decls, it) {
+        FuncDecl* decl = *it;
         ss << "\n\n"
-           << "Disassembly of " << decl->code->name << ":\n";
+           << "Disassembly of " << pkpy_Str__data(&decl->code->name) << ":\n";
         ss << disassemble(decl->code);
     }
     ss << "\n";
@@ -986,21 +1003,22 @@ void VM::__unpack_as_dict(ArgsView args, Dict& dict) {
     }
 }
 
-void VM::__prepare_py_call(PyVar* buffer, ArgsView args, ArgsView kwargs, const FuncDecl_& decl) {
+void VM::__prepare_py_call(PyVar* buffer, ArgsView args, ArgsView kwargs, const FuncDecl* decl) {
     const CodeObject* co = decl->code;
-    int decl_argc = decl->args.size();
+    int decl_argc = decl->args.count;
 
     if(args.size() < decl_argc) {
-        vm->TypeError(_S(co->name, "() takes ", decl_argc, " positional arguments but ", args.size(), " were given"));
+        vm->TypeError(_S(pkpy_Str__data(&co->name), "() takes ", decl_argc, " positional arguments but ", args.size(), " were given"));
     }
 
     int i = 0;
     // prepare args
     std::memset(buffer, 0, co->nlocals * sizeof(PyVar));
-    for(int index: decl->args)
-        buffer[index] = args[i++];
+    c11_vector__foreach(int, &decl->args, index) {
+        buffer[*index] = args[i++];
+    }
     // prepare kwdefaults
-    c11_vector__foreach(FuncDecl::KwArg, &decl->kwargs, kv) {
+    c11_vector__foreach(FuncDeclKwArg, &decl->kwargs, kv) {
         buffer[kv->index] = kv->value;
     }
 
@@ -1011,11 +1029,11 @@ void VM::__prepare_py_call(PyVar* buffer, ArgsView args, ArgsView kwargs, const
         i += vargs.size();
     } else {
         // kwdefaults override
-        c11_vector__foreach(FuncDecl::KwArg, &decl->kwargs, kv) {
+        c11_vector__foreach(FuncDeclKwArg, &decl->kwargs, kv) {
             if(i >= args.size()) break;
             buffer[kv->index] = args[i++];
         }
-        if(i < args.size()) TypeError(_S("too many arguments", " (", decl->code->name, ')'));
+        if(i < args.size()) TypeError(_S("too many arguments", " (", pkpy_Str__data(&decl->code->name), ')'));
     }
 
     PyVar vkwargs;
@@ -1035,7 +1053,7 @@ void VM::__prepare_py_call(PyVar* buffer, ArgsView args, ArgsView kwargs, const
         } else {
             // otherwise, set as **kwargs if possible
             if(!vkwargs) {
-                TypeError(_S(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
+                TypeError(_S(key.escape(), " is an invalid keyword argument for ", pkpy_Str__data(&co->name), "()"));
             } else {
                 Dict& dict = _CAST(Dict&, vkwargs);
                 dict.set(this, VAR(key.sv()), kwargs[j + 1]);
@@ -1085,14 +1103,15 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
                     _base[j] = __vectorcall_buffer[j];
                 break;
             case FuncType_SIMPLE:
-                if(args.size() != fn.decl->args.size())
-                    TypeError(_S(co->name,
-                                 "() takes ",
-                                 fn.decl->args.size(),
-                                 " positional arguments but ",
-                                 args.size(),
-                                 " were given"));
-                if(!kwargs.empty()) TypeError(_S(co->name, "() takes no keyword arguments"));
+                if(args.size() != fn.decl->args.count){
+                    TypeError(pk_format(
+                        "{} takes {} positional arguments but {} were given",
+                        &co->name, fn.decl->args.count, args.size()
+                    ));
+                }
+                if(!kwargs.empty()){
+                    TypeError(pk_format("{} takes no keyword arguments", &co->name));
+                }
                 // [callable, <self>, args..., local_vars...]
                 //      ^p0                    ^p1      ^_sp
                 s_data.reset(_base + co->nlocals);
@@ -1100,14 +1119,15 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call) {
                 std::memset(p1, 0, (char*)s_data._sp - (char*)p1);
                 break;
             case FuncType_EMPTY:
-                if(args.size() != fn.decl->args.size())
-                    TypeError(_S(co->name,
-                                 "() takes ",
-                                 fn.decl->args.size(),
-                                 " positional arguments but ",
-                                 args.size(),
-                                 " were given"));
-                if(!kwargs.empty()) TypeError(_S(co->name, "() takes no keyword arguments"));
+                if(args.size() != fn.decl->args.count){
+                    TypeError(pk_format(
+                        "{} takes {} positional arguments but {} were given",
+                        &co->name, fn.decl->args.count, args.size()
+                    ));
+                }
+                if(!kwargs.empty()){
+                    TypeError(pk_format("{} takes no keyword arguments", &co->name));
+                }
                 s_data.reset(p0);
                 return None;
             case FuncType_GENERATOR:
@@ -1385,9 +1405,10 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native
     std::string_view source(buffer, length);
     // fn(a, b, *c, d=1) -> None
     CodeObject* code = compile(source, "<bind>", EXEC_MODE);
-    assert(code->func_decls.size() == 1);
-    FuncDecl_ decl = code->func_decls[0];
-    delete code;  // may leak if exception occurs
+    assert(code->func_decls.count == 1);
+    FuncDecl_ decl = c11__getitem(FuncDecl_, &code->func_decls, 0);
+    c11_vector__clear(&code->func_decls);   // move decl
+    CodeObject__delete(code);   // may leak if exception occurs
     decl->docstring = docstring;
     PyObject* f_obj = new_object<NativeFunc>(tp_native_func, fn, decl, std::move(userdata)).get();
 
@@ -1396,7 +1417,10 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native
         case BindType_CLASSMETHOD: f_obj = new_object<ClassMethod>(tp_classmethod, f_obj).get(); break;
         case BindType_FUNCTION: break;
     }
-    if(obj != nullptr) obj->attr().set(decl->code->name, f_obj);
+    if(obj != nullptr){
+        StrName name = pkpy_Str__data(&decl->code->name);
+        obj->attr().set(name, f_obj);
+    }
     return f_obj;
 }
 
@@ -1459,9 +1483,9 @@ void VM::__raise_exc(bool re_raise) {
 
     int actual_ip = frame->ip();
     if(e._ip_on_error >= 0 && e._code_on_error == (void*)frame->co) actual_ip = e._ip_on_error;
-    int current_line = frame->co->lines[actual_ip].lineno;  // current line
-    const char* current_f_name = frame->co->name.c_str();   // current function name
-    if(frame->_callable == nullptr) current_f_name = "";    // not in a function
+    int current_line = c11__at(BytecodeEx, &frame->co->codes_ex, actual_ip)->lineno;
+    const char* current_f_name = pkpy_Str__data(&frame->co->name);  // current function name
+    if(frame->_callable == nullptr) current_f_name = "";            // not in a function
     e.stpush(frame->co->src, current_line, nullptr, current_f_name);
 
     if(next_ip >= 0) {
@@ -1802,13 +1826,13 @@ void VM::__breakpoint() {
             if(cmd == "p" || cmd == "print") {
                 CodeObject* code = compile(arg, "<stdin>", EVAL_MODE, true);
                 PyVar retval = vm->_exec(code, frame_0->_module, frame_0->_callable, frame_0->_locals);
-                delete code;
+                CodeObject__delete(code);
                 stdout_write(vm->py_repr(retval));
                 stdout_write("\n");
             } else if(cmd == "!") {
                 CodeObject* code = compile(arg, "<stdin>", EXEC_MODE, true);
                 vm->_exec(code, frame_0->_module, frame_0->_callable, frame_0->_locals);
-                delete code;
+                CodeObject__delete(code);
             }
             continue;
         }
@@ -1822,7 +1846,7 @@ PyVar PyObject::attr(StrName name) const{
 
 /**************************************************************************/
 void Function::_gc_mark(VM* vm) const {
-    decl->_gc_mark(vm);
+    FuncDecl__gc_mark(decl);
     if(_closure) {
         _closure->apply([](StrName _, PyVar obj, void* userdata) {
             VM* vm = (VM*)userdata;
@@ -1832,13 +1856,28 @@ void Function::_gc_mark(VM* vm) const {
 }
 
 void NativeFunc::_gc_mark(VM* vm) const {
-    if(decl) decl->_gc_mark(vm);
+    if(decl){
+        FuncDecl__gc_mark(decl);
+    }
 }
 
-void FuncDecl::_gc_mark(VM* vm) const {
-    code->_gc_mark(vm);
-    c11_vector__foreach(FuncDecl::KwArg, &kwargs, kv) {
-        vm->obj_gc_mark(kv->value);
+extern "C"{
+    void FuncDecl__gc_mark(const FuncDecl *self){
+        VM* vm = (VM*)pkpy_g.vm;
+        CodeObject__gc_mark(self->code);
+        c11_vector__foreach(FuncDeclKwArg, &self->kwargs, kv) {
+            vm->obj_gc_mark(kv->value);
+        }
+    }
+
+    void CodeObject__gc_mark(const CodeObject* self) {
+        VM* vm = (VM*)pkpy_g.vm;
+        c11_vector__foreach(PyVar, &self->consts, v) {
+            vm->obj_gc_mark(*v);
+        }
+        c11_vector__foreach(FuncDecl*, &self->func_decls, decl) {
+            FuncDecl__gc_mark(*decl);
+        }
     }
 }
 
@@ -1880,7 +1919,7 @@ void Super::_gc_mark(VM* vm) const { vm->obj_gc_mark(first); }
 
 void Frame::_gc_mark(VM* vm) const {
     vm->obj_gc_mark(_module);
-    co->_gc_mark(vm);
+    CodeObject__gc_mark(co);
     // Frame could be stored in a generator, so mark _callable for safety
     vm->obj_gc_mark(_callable);
 }
@@ -1924,11 +1963,4 @@ void Dict::_gc_mark(VM* vm) const {
     });
 }
 
-void CodeObject::_gc_mark(VM* vm) const {
-    for(PyVar v: consts)
-        vm->obj_gc_mark(v);
-    for(auto& decl: func_decls)
-        decl->_gc_mark(vm);
-}
-
 }  // namespace pkpy

+ 3 - 3
src/modules/modules.cpp

@@ -108,7 +108,7 @@ void add_module_json(VM* vm) {
         }
         CodeObject* code = vm->compile(sv, "<json>", JSON_MODE);
         PyVar retval = vm->_exec(code, vm->callstack.top()._module);
-        delete code;    // leak on error
+        CodeObject__delete(code);
         return retval;
     });
 
@@ -238,7 +238,7 @@ void add_module_dis(VM* vm) {
         if(is_type(f, vm->tp_bound_method)) f = CAST(BoundMethod, obj).func;
         code = CAST(Function&, f).decl->code;
         vm->stdout_write(vm->disassemble(code));
-        if(need_delete) delete code;
+        if(need_delete) CodeObject__delete(code);
         return vm->None;
     });
 }
@@ -252,7 +252,7 @@ void add_module_enum(VM* vm) {
     PyObject* mod = vm->new_module("enum");
     CodeObject* code = vm->compile(kPythonLibs__enum, "enum.py", EXEC_MODE);
     vm->_exec(code, mod);
-    delete code;    // leak on error
+    CodeObject__delete(code);
     PyVar Enum = mod->attr("Enum");
     vm->_all_types[PK_OBJ_GET(Type, Enum)].on_end_subclass = [](VM* vm, PyTypeInfo* new_ti) {
         new_ti->subclass_enabled = false;  // Enum class cannot be subclassed twice

+ 84 - 0
src/objects/codeobject.c

@@ -1,5 +1,6 @@
 #include "pocketpy/objects/codeobject.h"
 #include "pocketpy/common/utils.h"
+#include <stdint.h>
 
 void Bytecode__set_signed_arg(Bytecode* self, int arg) {
     if(arg < INT16_MIN || arg > INT16_MAX) {
@@ -11,3 +12,86 @@ void Bytecode__set_signed_arg(Bytecode* self, int arg) {
 bool Bytecode__is_forward_jump(const Bytecode* self) {
     return self->op >= OP_JUMP_FORWARD && self->op <= OP_LOOP_BREAK;
 }
+
+FuncDecl_ FuncDecl__rcnew(pkpy_SourceData_ src, c11_string name){
+    FuncDecl* self = malloc(sizeof(FuncDecl));
+    self->rc.count = 1;
+    self->rc.dtor = (void (*)(void*))FuncDecl__dtor;
+    self->code = CodeObject__new(src, name);
+
+    c11_vector__ctor(&self->args, sizeof(int));
+    c11_vector__ctor(&self->kwargs, sizeof(FuncDeclKwArg));
+
+    self->starred_arg = -1;
+    self->starred_kwarg = -1;
+    self->nested = false;
+
+    self->docstring = NULL;
+    self->type = FuncType_UNSET;
+
+    c11_smallmap_n2i__ctor(&self->kw_to_index);
+    return self;
+}
+
+void FuncDecl__dtor(FuncDecl* self){
+    CodeObject__delete(self->code);
+    c11_vector__dtor(&self->args);
+    c11_vector__dtor(&self->kwargs);
+    c11_smallmap_n2i__dtor(&self->kw_to_index);
+}
+
+void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const PyVar* value){
+    c11_smallmap_n2i__set(&self->kw_to_index, key, index);
+    FuncDeclKwArg item = {index, key, *value};
+    c11_vector__push(FuncDeclKwArg, &self->kwargs, item);
+}
+
+CodeObject* CodeObject__new(pkpy_SourceData_ src, c11_string name){
+    CodeObject* self = malloc(sizeof(CodeObject));
+    self->src = src; PK_INCREF(src);
+    pkpy_Str__ctor2(&self->name, name.data, name.size);
+
+    c11_vector__ctor(&self->codes, sizeof(Bytecode));
+    c11_vector__ctor(&self->codes_ex, sizeof(BytecodeEx));
+
+    c11_vector__ctor(&self->consts, sizeof(PyVar));
+    c11_vector__ctor(&self->varnames, sizeof(uint16_t));
+    self->nlocals = 0;
+
+    c11_smallmap_n2i__ctor(&self->varnames_inv);
+    c11_smallmap_n2i__ctor(&self->labels);
+
+    c11_vector__ctor(&self->blocks, sizeof(CodeBlock));
+    c11_vector__ctor(&self->func_decls, sizeof(FuncDecl_));
+
+    self->start_line = -1;
+    self->end_line = -1;
+
+    CodeBlock root_block = {CodeBlockType_NO_BLOCK, -1, 0, -1, -1};
+    c11_vector__push(CodeBlock, &self->blocks, root_block);
+    return self;
+}
+
+void CodeObject__delete(CodeObject* self){
+    PK_DECREF(self->src);
+    pkpy_Str__dtor(&self->name);
+    
+    c11_vector__dtor(&self->codes);
+    c11_vector__dtor(&self->codes_ex);
+
+    c11_vector__dtor(&self->consts);
+    c11_vector__dtor(&self->varnames);
+
+    c11_smallmap_n2i__dtor(&self->varnames_inv);
+    c11_smallmap_n2i__dtor(&self->labels);
+
+    c11_vector__dtor(&self->blocks);
+
+    for(int i=0; i<self->func_decls.count; i++){
+        FuncDecl_ decl = c11__getitem(FuncDecl_, &self->func_decls, i);
+        PK_DECREF(decl);
+    }
+    c11_vector__dtor(&self->func_decls);
+
+    free(self);
+}

+ 7 - 4
src/pocketpy.cpp

@@ -2,6 +2,7 @@
 
 #include "pocketpy/common/_generated.h"
 
+#include "pocketpy/common/refcount.h"
 #include "pocketpy/modules/array2d.hpp"
 #include "pocketpy/modules/base64.hpp"
 #include "pocketpy/modules/csv.hpp"
@@ -12,6 +13,7 @@
 #include "pocketpy/modules/random.hpp"
 #include "pocketpy/modules/modules.hpp"
 #include "pocketpy/objects/base.h"
+#include "pocketpy/objects/codeobject.h"
 
 #include <iostream>
 #include <algorithm>
@@ -1692,15 +1694,16 @@ void VM::__post_init_builtin_types() {
     try {
         // initialize dummy func_decl for exec/eval
         CodeObject* code = compile("def _(): pass", "<dynamic>", EXEC_MODE);
-        __dynamic_func_decl = code->func_decls[0];
-        delete code;    // may leak on error
+        __dynamic_func_decl = c11__getitem(FuncDecl_, &code->func_decls, 0);
+        PK_INCREF(__dynamic_func_decl);
+        CodeObject__delete(code);
         // initialize builtins
         code = compile(kPythonLibs_builtins, "<builtins>", EXEC_MODE);
         this->_exec(code, this->builtins);
-        delete code;    // may leak on error
+        CodeObject__delete(code);
         code = compile(kPythonLibs__set, "<set>", EXEC_MODE);
         this->_exec(code, this->builtins);
-        delete code;    // may leak on error
+        CodeObject__delete(code);
     } catch(TopLevelException e) {
         std::cerr << e.summary() << std::endl;
         std::cerr << "failed to load builtins module!!" << std::endl;

+ 7 - 6
src/pocketpy_c.cpp

@@ -1,3 +1,4 @@
+#include "pocketpy/objects/codeobject.h"
 #ifndef PK_NO_EXPORT_C_API
 
 #include "pocketpy/pocketpy.hpp"
@@ -62,7 +63,7 @@ bool pkpy_exec(pkpy_vm* vm_handle, const char* source) {
     PK_PROTECTED(
         CodeObject* code = vm->compile(source, "main.py", EXEC_MODE);
         res = vm->_exec(code, vm->_main);
-        delete code;    // TODO: _exec may raise, so code may leak
+        CodeObject__delete(code);
     )
     return res != nullptr;
 }
@@ -78,8 +79,8 @@ bool pkpy_exec_2(pkpy_vm* vm_handle, const char* source, const char* filename, i
                         mod = vm->_modules[module].get();  // may raise
         }
         CodeObject* code = vm->compile(source, filename, (CompileMode)mode);
-        res = vm->_exec(code, mod);
-        delete code;    // TODO: _exec may raise, so code may leak
+        res = vm->_exec(code, mod); 
+        CodeObject__delete(code);   // TODO: _exec may raise, so code may leak
     )
     return res != nullptr;
 }
@@ -419,10 +420,10 @@ bool pkpy_eval(pkpy_vm* vm_handle, const char* source) {
     VM* vm = (VM*)vm_handle;
     PK_ASSERT_NO_ERROR()
     PK_PROTECTED(
-        CodeObject* co = vm->compile(source, "<eval>", EVAL_MODE);
-        PyVar ret = vm->_exec(co, vm->_main);
+        CodeObject* code = vm->compile(source, "<eval>", EVAL_MODE);
+        PyVar ret = vm->_exec(code, vm->_main);
         vm->s_data.push(ret);
-        delete co;  // TODO: _exec may raise, so code may leak
+        CodeObject__delete(code);
     )
     return true;
 }