#pragma once #include "obj.h" #include "error.h" namespace pkpy{ enum NameScope { NAME_LOCAL, NAME_GLOBAL, NAME_GLOBAL_UNKNOWN }; enum Opcode: uint8_t { #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{ uint8_t op; uint16_t arg; }; enum class CodeBlockType { NO_BLOCK, FOR_LOOP, WHILE_LOOP, CONTEXT_MANAGER, TRY_EXCEPT, }; inline const uint8_t BC_NOARG = 0; inline const int BC_KEEPLINE = -1; struct CodeBlock { CodeBlockType type; int parent; // parent index in blocks int base_stack_size; // 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 base_stack_size, int start): type(type), parent(parent), base_stack_size(base_stack_size), 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 CodeObject { std::shared_ptr src; Str name; bool is_generator = false; std::vector codes; std::vector iblocks; // block index for each bytecode std::vector lines; // line number for each bytecode List consts; std::vector varnames; // local variables NameDictInt varnames_inv; std::vector blocks = { CodeBlock(CodeBlockType::NO_BLOCK, -1, 0, 0) }; NameDictInt labels; std::vector func_decls; const CodeBlock& _get_block_codei(int codei) const{ return blocks[iblocks[codei]]; } CodeObject(std::shared_ptr src, const Str& name); void _gc_mark() const; }; struct FuncDecl { struct KwArg { int index; // index in co->varnames StrName key; // name of this argument PyObject* value; // default value }; CodeObject_ code; // code object of this function std::vector args; // indices in co->varnames std::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 bool is_simple; NameDictInt kw_to_index; void add_kwarg(int index, StrName key, PyObject* value){ kw_to_index.set(key, index); kwargs.push_back({index, key, value}); } void _gc_mark() const; }; struct UserData{ char data[12]; 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; // weak ref PyObject* _class; // weak ref NameDict_ _closure; explicit Function(FuncDecl_ decl, PyObject* _module, PyObject* _class, NameDict_ _closure): decl(decl), _module(_module), _class(_class), _closure(_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._closure != nullptr) gc_mark_namedict(*_value._closure); } void* _value_ptr() override { return &_value; } }; 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(); } } void* _value_ptr() override { return &_value; } }; 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