diff --git a/build_g.sh b/build_g.sh index 5744fb07..b0048cd5 100644 --- a/build_g.sh +++ b/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" diff --git a/include/pocketpy/common/sstream.h b/include/pocketpy/common/sstream.h index 7525246b..99d4a094 100644 --- a/include/pocketpy/common/sstream.h +++ b/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 } diff --git a/include/pocketpy/common/str.hpp b/include/pocketpy/common/str.hpp index ff7547f6..6e356746 100644 --- a/include/pocketpy/common/str.hpp +++ b/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); } }; diff --git a/include/pocketpy/compiler/compiler.hpp b/include/pocketpy/compiler/compiler.hpp index c4e7fe01..1f28ebde 100644 --- a/include/pocketpy/compiler/compiler.hpp +++ b/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; diff --git a/include/pocketpy/compiler/expr.hpp b/include/pocketpy/compiler/expr.hpp index b3d3cd1c..b5fe06a9 100644 --- a/include/pocketpy/compiler/expr.hpp +++ b/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 _s_expr; int level; vector 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); } }; diff --git a/include/pocketpy/interpreter/frame.hpp b/include/pocketpy/interpreter/frame.hpp index 568d0343..e016595f 100644 --- a/include/pocketpy/interpreter/frame.hpp +++ b/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); diff --git a/include/pocketpy/interpreter/vm.hpp b/include/pocketpy/interpreter/vm.hpp index 56f8dee2..cc0c7304 100644 --- a/include/pocketpy/interpreter/vm.hpp +++ b/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); diff --git a/include/pocketpy/objects/codeobject.h b/include/pocketpy/objects/codeobject.h index 08712e4d..eab24093 100644 --- a/include/pocketpy/objects/codeobject.h +++ b/include/pocketpy/objects/codeobject.h @@ -3,6 +3,12 @@ #include #include +#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 \ No newline at end of file diff --git a/include/pocketpy/objects/codeobject.hpp b/include/pocketpy/objects/codeobject.hpp index a2a7fdd8..37cc4371 100644 --- a/include/pocketpy/objects/codeobject.hpp +++ b/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; - -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 codes; - vector lines; - - small_vector_2 consts; // constants - small_vector_2 varnames; // local variables - int nlocals; // varnames.size() - - c11_smallmap_n2i varnames_inv; - vector blocks; - c11_smallmap_n2i labels; - vector 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 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; } }; diff --git a/src/common/sourcedata.c b/src/common/sourcedata.c index f291548d..263acea3 100644 --- a/src/common/sourcedata.c +++ b/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); } diff --git a/src/common/sstream.c b/src/common/sstream.c index 727d766f..d29df09b 100644 --- a/src/common/sstream.c +++ b/src/common/sstream.c @@ -1,4 +1,5 @@ #include "pocketpy/common/sstream.h" +#include "pocketpy/common/config.h" #include "pocketpy/common/utils.h" #include @@ -6,39 +7,39 @@ #include #include -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; +} diff --git a/src/compiler/compiler.cpp b/src/compiler/compiler.cpp index 71dbe579..83465042 100644 --- a/src/compiler/compiler.cpp +++ b/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(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(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(""); + int decl_index; + FuncDecl_ decl = push_f_context({"", 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(decl); + LambdaExpr* e = make_expr(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(decl_name, name_scope()); + NameExpr* e = make_expr(StrName(decl_name), name_scope()); e->emit_store(ctx()); delete_expr(e); } else { diff --git a/src/compiler/expr.cpp b/src/compiler/expr.cpp index 44f7c19e..9bcd7d8f 100644 --- a/src/compiler/expr.cpp +++ b/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().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().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); diff --git a/src/error.c b/src/error.c index 5916af52..96af79ee 100644 --- a/src/error.c +++ b/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); } \ No newline at end of file diff --git a/src/interpreter/ceval.cpp b/src/interpreter/ceval.cpp index 475d5600..9c48e4ca 100644 --- a/src/interpreter/ceval.cpp +++ b/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(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(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_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() diff --git a/src/interpreter/frame.cpp b/src/interpreter/frame.cpp index afc80080..650519e9 100644 --- a/src/interpreter/frame.cpp +++ b/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(); diff --git a/src/interpreter/vm.cpp b/src/interpreter/vm.cpp index 54daedf8..ed21e7ea 100644 --- a/src/interpreter/vm.cpp +++ b/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 @@ -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_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_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 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; icodes.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, , 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, "", 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(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(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, "", 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, "", 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 diff --git a/src/modules/modules.cpp b/src/modules/modules.cpp index 50c5cada..b5f5d682 100644 --- a/src/modules/modules.cpp +++ b/src/modules/modules.cpp @@ -108,7 +108,7 @@ void add_module_json(VM* vm) { } CodeObject* code = vm->compile(sv, "", 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 diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index d3406606..33b949c5 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 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; ifunc_decls.count; i++){ + FuncDecl_ decl = c11__getitem(FuncDecl_, &self->func_decls, i); + PK_DECREF(decl); + } + c11_vector__dtor(&self->func_decls); + + free(self); +} \ No newline at end of file diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index 56fea6f8..c763e374 100644 --- a/src/pocketpy.cpp +++ b/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 #include @@ -1692,15 +1694,16 @@ void VM::__post_init_builtin_types() { try { // initialize dummy func_decl for exec/eval CodeObject* code = compile("def _(): pass", "", 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, "", EXEC_MODE); this->_exec(code, this->builtins); - delete code; // may leak on error + CodeObject__delete(code); code = compile(kPythonLibs__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; diff --git a/src/pocketpy_c.cpp b/src/pocketpy_c.cpp index 5a487cec..8df1fa80 100644 --- a/src/pocketpy_c.cpp +++ b/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_MODE); - PyVar ret = vm->_exec(co, vm->_main); + CodeObject* code = vm->compile(source, "", 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; }