#pragma once #include "obj.h" #include "error.h" namespace pkpy{ enum NameScope { NAME_LOCAL, NAME_GLOBAL, NAME_GLOBAL_UNKNOWN }; enum Opcode { #define OPCODE(name) OP_##name, #include "opcodes.h" #undef OPCODE }; inline const char* OP_NAMES[] = { #define OPCODE(name) #name, #include "opcodes.h" #undef OPCODE }; struct Bytecode{ uint16_t op; uint16_t block; int arg; }; enum CodeBlockType { NO_BLOCK, FOR_LOOP, WHILE_LOOP, CONTEXT_MANAGER, TRY_EXCEPT, }; inline const int BC_NOARG = -1; inline const int BC_KEEPLINE = -1; struct CodeBlock { CodeBlockType type; int parent; // parent index in blocks int for_loop_depth; // this is used for exception handling int start; // start index of this block in codes, inclusive int end; // end index of this block in codes, exclusive int end2; // ... CodeBlock(CodeBlockType type, int parent, int for_loop_depth, int start): type(type), parent(parent), for_loop_depth(for_loop_depth), start(start), end(-1), end2(-1) {} int get_break_end() const{ if(end2 != -1) return end2; return end; } }; struct CodeObject; struct FuncDecl; using CodeObject_ = std::shared_ptr; using FuncDecl_ = std::shared_ptr; struct CodeObjectSerializer{ std::string buffer; int depth = 0; std::set names; static const char END = '\n'; CodeObjectSerializer(); void write_int(i64 v); void write_float(f64 v); void write_str(const Str& v); void write_none(); void write_ellipsis(); void write_bool(bool v); void write_begin_mark(); void write_name(StrName name); void write_end_mark(); template void write_bytes(T v){ static_assert(std::is_trivially_copyable::value); buffer += 'x'; char* p = (char*)&v; for(int i=0; i> 4) & 0xf]; buffer += "0123456789abcdef"[c & 0xf]; } buffer += END; } void write_object(VM* vm, PyObject* obj); void write_code(VM* vm, const CodeObject* co); std::string str(); }; struct CodeObject { std::shared_ptr src; Str name; bool is_generator = false; std::vector codes; std::vector lines; // line number for each bytecode List consts; std::vector varnames; // local variables NameDictInt varnames_inv; std::vector blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) }; NameDictInt labels; std::vector func_decls; CodeObject(std::shared_ptr src, const Str& name); void _gc_mark() const; void write(VM* vm, CodeObjectSerializer& ss) const; Str serialize(VM* vm) const; }; struct FuncDecl { struct KwArg { int key; // index in co->varnames PyObject* value; // default value }; CodeObject_ code; // code object of this function pod_vector args; // indices in co->varnames pod_vector 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 Str signature; // signature of this function Str docstring; // docstring of this function void _gc_mark() const; }; struct UserData{ char data[15]; bool empty; UserData(): empty(true) {} template UserData(T t): empty(false){ static_assert(std::is_trivially_copyable_v); static_assert(sizeof(T) <= sizeof(data)); memcpy(data, &t, sizeof(T)); } template T get() const{ static_assert(std::is_trivially_copyable_v); static_assert(sizeof(T) <= sizeof(data)); #if PK_DEBUG_EXTRA_CHECK PK_ASSERT(!empty); #endif return reinterpret_cast(data); } }; struct NativeFunc { NativeFuncC f; // old style argc-based call int argc; // new style decl-based call FuncDecl_ decl; UserData _userdata; void set_userdata(UserData data) { if(!_userdata.empty && !data.empty){ // override is not supported throw std::runtime_error("userdata already set"); } _userdata = data; } NativeFunc(NativeFuncC f, int argc, bool method); NativeFunc(NativeFuncC f, FuncDecl_ decl); void check_size(VM* vm, ArgsView args) const; PyObject* call(VM* vm, ArgsView args) const; }; struct Function{ FuncDecl_ decl; PyObject* _module; NameDict_ _closure; }; template<> struct Py_ final: PyObject { Function _value; template Py_(Type type, Args&&... args): PyObject(type), _value(std::forward(args)...) { enable_instance_dict(); } void _obj_gc_mark() override { _value.decl->_gc_mark(); if(_value._module != nullptr) PK_OBJ_MARK(_value._module); if(_value._closure != nullptr) gc_mark_namedict(*_value._closure); } }; template<> struct Py_ final: PyObject { NativeFunc _value; template Py_(Type type, Args&&... args): PyObject(type), _value(std::forward(args)...) { enable_instance_dict(); } void _obj_gc_mark() override { if(_value.decl != nullptr){ _value.decl->_gc_mark(); } } }; template T lambda_get_userdata(PyObject** p){ if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1])._userdata.get(); else return PK_OBJ_GET(NativeFunc, p[-2])._userdata.get(); } } // namespace pkpy