From c403252aecf87ce81bce3437ff6d5b4500b72b01 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Tue, 11 Apr 2023 17:45:40 +0800 Subject: [PATCH] impl `LOAD_FAST` --- .github/workflows/main.yml | 9 ---- src/ceval.h | 44 ++++++++++-------- src/codeobject.h | 13 ++---- src/common.h | 4 +- src/compiler.h | 87 ++++++++++++++++++++++------------ src/expr.h | 42 ++++++++++------- src/frame.h | 35 ++++++++++++-- src/gc.h | 2 +- src/obj.h | 24 ++++------ src/opcodes.h | 5 +- src/pocketpy.h | 20 ++++++-- src/vm.h | 95 ++++++++++++++++++++------------------ 12 files changed, 225 insertions(+), 155 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6c261a15..3f310555 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,15 +32,6 @@ jobs: platform: x64 - name: Install libc++ run: sudo apt install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15 - # libclang-rt-15-dev ...some bugs here - # - name: Coverage Test - # run: | - # python3 preprocess.py - # bash run_tests.sh - # - uses: actions/upload-artifact@v3 - # with: - # name: coverage - # path: .coverage - name: Compile run: | python3 build.py linux diff --git a/src/ceval.h b/src/ceval.h index 2785b164..277859a9 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -1,11 +1,12 @@ #pragma once #include "common.h" +#include "namedict.h" #include "vm.h" namespace pkpy{ -inline PyObject* VM::_run_top_frame(){ +inline PyObject* VM::_run_top_frame(bool force_no_pop){ FrameId frame = top_frame(); const int base_id = frame.index; bool need_raise = false; @@ -28,9 +29,9 @@ inline PyObject* VM::_run_top_frame(){ Bytecode byte = frame->next_bytecode(); // cache const CodeObject* co = frame->co; - const std::vector& co_names = co->names; - const List& co_consts = co->consts; - const std::vector& co_blocks = co->blocks; + const auto& co_names = co->names; + const auto& co_consts = co->consts; + const auto& co_blocks = co->blocks; #if PK_ENABLE_COMPUTED_GOTO static void* OP_LABELS[] = { @@ -76,16 +77,27 @@ __NEXT_STEP:; TARGET(LOAD_BUILTIN_EVAL) frame->push(builtins->attr(m_eval)); DISPATCH(); TARGET(LOAD_FUNCTION) { FuncDecl_ decl = co->func_decls[byte.arg]; - PyObject* obj = VAR(Function({decl, frame->_module, frame->_locals})); + PyObject* obj; + if(decl->nested){ + obj = VAR(Function({decl, frame->_module, frame->locals_to_namedict()})); + }else{ + obj = VAR(Function({decl, frame->_module, nullptr})); + } frame->push(obj); } DISPATCH(); TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH(); /*****************************************/ + TARGET(LOAD_FAST) { + heap._auto_collect(); + PyObject* val = frame->_locals[byte.arg]; + if(val == nullptr) vm->NameError(co->varnames[byte.arg]); + frame->push(val); + } DISPATCH(); TARGET(LOAD_NAME) { heap._auto_collect(); StrName name = co_names[byte.arg]; PyObject* val; - val = frame->f_locals().try_get(name); + val = frame->f_locals_try_get(name); if(val != nullptr) { frame->push(val); DISPATCH(); } val = frame->f_closure_try_get(name); if(val != nullptr) { frame->push(val); DISPATCH(); } @@ -122,10 +134,9 @@ __NEXT_STEP:; args[0] = frame->top(); // a frame->top() = fast_call(__getitem__, std::move(args)); } DISPATCH(); - TARGET(STORE_LOCAL) { - StrName name = co_names[byte.arg]; - frame->f_locals().set(name, frame->popx()); - } DISPATCH(); + TARGET(STORE_FAST) + frame->_locals[byte.arg] = frame->popx(); + DISPATCH(); TARGET(STORE_GLOBAL) { StrName name = co_names[byte.arg]; frame->f_globals().set(name, frame->popx()); @@ -144,13 +155,10 @@ __NEXT_STEP:; args[2] = frame->popx(); // val fast_call(__setitem__, std::move(args)); } DISPATCH(); - TARGET(DELETE_LOCAL) { - StrName name = co_names[byte.arg]; - if(frame->f_locals().contains(name)){ - frame->f_locals().erase(name); - }else{ - NameError(name); - } + TARGET(DELETE_FAST) { + PyObject* val = frame->_locals[byte.arg]; + if(val == nullptr) vm->NameError(co->varnames[byte.arg]); + frame->_locals[byte.arg] = nullptr; } DISPATCH(); TARGET(DELETE_GLOBAL) { StrName name = co_names[byte.arg]; @@ -515,7 +523,7 @@ __NEXT_STEP:; /**********************************************************************/ __PY_SIMPLE_RETURN: if(frame.index == base_id){ // [ frameBase<- ] - callstack.pop(); + if(!force_no_pop) callstack.pop(); return __ret; }else{ callstack.pop(); diff --git a/src/codeobject.h b/src/codeobject.h index 54c7807d..8520b70f 100644 --- a/src/codeobject.h +++ b/src/codeobject.h @@ -52,23 +52,20 @@ struct CodeObject { Str name; bool is_generator = false; - CodeObject(shared_ptr src, Str name) { - this->src = src; - this->name = name; - } + CodeObject(shared_ptr src, const Str& name): + src(src), name(name) {} std::vector codes; std::vector lines; // line number for each bytecode List consts; - std::vector names; + std::vector names; // other names + std::vector varnames; // local variables + std::map varnames_inv; std::set global_names; std::vector blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) }; std::map labels; std::vector func_decls; - uint32_t perfect_locals_capacity = NameDict::__Capacity; - uint32_t perfect_hash_seed = kHashSeeds[0]; - void optimize(VM* vm); void _gc_mark() const { diff --git a/src/common.h b/src/common.h index 0745f216..ce726b1a 100644 --- a/src/common.h +++ b/src/common.h @@ -29,13 +29,12 @@ #include #include -#define PK_VERSION "0.9.7" +#define PK_VERSION "0.9.8" // debug macros #define DEBUG_NO_BUILTIN_MODULES 0 #define DEBUG_EXTRA_CHECK 0 #define DEBUG_DIS_EXEC 0 -#define DEBUG_DIS_EXEC_MIN 1 #define DEBUG_CEVAL_STEP 0 #define DEBUG_FULL_EXCEPTION 0 #define DEBUG_MEMORY_POOL 0 @@ -96,7 +95,6 @@ struct Type { #define FATAL_ERROR() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " FATAL_ERROR()!"); #endif -inline const float kLocalsLoadFactor = 0.67f; inline const float kInstAttrLoadFactor = 0.67f; inline const float kTypeAttrLoadFactor = 0.5f; diff --git a/src/compiler.h b/src/compiler.h index beaed8f2..0a3b773b 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -3,6 +3,7 @@ #include "codeobject.h" #include "common.h" #include "expr.h" +#include "obj.h" namespace pkpy{ @@ -38,13 +39,20 @@ class Compiler { CompileMode mode() const{ return lexer->src->mode; } NameScope name_scope() const { return contexts.size()>1 ? NAME_LOCAL : NAME_GLOBAL; } - template - CodeObject_ push_context(Args&&... args){ - CodeObject_ co = make_sp(std::forward(args)...); + CodeObject_ push_global_context(){ + CodeObject_ co = make_sp(lexer->src, lexer->src->filename); contexts.push(CodeEmitContext(vm, co)); return co; } + FuncDecl_ push_f_context(Str name){ + FuncDecl_ decl = make_sp(); + decl->code = make_sp(lexer->src, name); + decl->nested = name_scope() == NAME_LOCAL; + contexts.push(CodeEmitContext(vm, decl->code)); + return decl; + } + void pop_context(){ if(!ctx()->s_expr.empty()){ throw std::runtime_error("!ctx()->s_expr.empty()\n" + ctx()->_log_s_expr()); @@ -190,12 +198,12 @@ class Compiler { void exprLambda(){ - auto e = make_expr(name_scope()); + FuncDecl_ decl = push_f_context(""); + auto e = make_expr(decl); if(!match(TK(":"))){ _compile_f_args(e->decl, false); consume(TK(":")); } - e->decl->code = push_context(lexer->src, e->decl->name.sv()); // https://github.com/blueloveTH/pocketpy/issues/37 parse_expression(PREC_LAMBDA + 1, false); ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); @@ -528,9 +536,11 @@ __SUBSCR_END: consume(TK("@id")); name = prev().str(); } - int index = ctx()->add_name(name); - auto op = name_scope()==NAME_LOCAL ? OP_STORE_LOCAL : OP_STORE_GLOBAL; - ctx()->emit(op, index, prev().line); + if(name_scope() == NAME_LOCAL){ + ctx()->emit(OP_STORE_FAST, ctx()->add_varname(name), prev().line); + }else{ + ctx()->emit(OP_STORE_GLOBAL, ctx()->add_name(name), prev().line); + } } while (match(TK(","))); consume_end_stmt(); } @@ -555,9 +565,11 @@ __SUBSCR_END: consume(TK("@id")); name = prev().str(); } - index = ctx()->add_name(name); - auto op = name_scope()==NAME_LOCAL ? OP_STORE_LOCAL : OP_STORE_GLOBAL; - ctx()->emit(op, index, prev().line); + if(name_scope() == NAME_LOCAL){ + ctx()->emit(OP_STORE_FAST, ctx()->add_varname(name), prev().line); + }else{ + ctx()->emit(OP_STORE_GLOBAL, ctx()->add_name(name), prev().line); + } } while (match(TK(","))); ctx()->emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); consume_end_stmt(); @@ -849,27 +861,44 @@ __SUBSCR_END: else if(match(TK("**"))){ state = 3; } - consume(TK("@id")); - const Str& name = prev().str(); - if(decl->has_name(name)) SyntaxError("duplicate argument name"); + StrName name = prev().str(); + + // check duplicate argument name + for(int i: decl->args){ + if(decl->code->varnames[i] == name) { + SyntaxError("duplicate argument name"); + } + } + if(decl->starred_arg!=-1 && decl->code->varnames[decl->starred_arg] == name){ + SyntaxError("duplicate argument name"); + } + for(auto& kv: decl->kwargs){ + if(decl->code->varnames[kv.key] == name){ + SyntaxError("duplicate argument name"); + } + } // eat type hints if(enable_type_hints && match(TK(":"))) consume(TK("@id")); - if(state == 0 && curr().type == TK("=")) state = 2; + int index = ctx()->add_varname(name); switch (state) { - case 0: decl->args.push_back(name); break; - case 1: decl->starred_arg = name; state+=1; break; + case 0: + decl->args.push_back(index); + break; + case 1: + decl->starred_arg = index; + state+=1; + break; case 2: { consume(TK("=")); PyObject* value = read_literal(); if(value == nullptr){ SyntaxError(Str("expect a literal, not ") + TK_STR(curr().type)); } - decl->kwargs.set(name, value); - decl->kwargs_order.push_back(name); + decl->kwargs.push_back(FuncDecl::KwArg{index, value}); } break; case 3: SyntaxError("**kwargs is not supported yet"); break; } @@ -877,15 +906,16 @@ __SUBSCR_END: } void compile_function(const std::vector& decorators={}){ - FuncDecl_ decl = make_sp(); - StrName obj_name; + Str obj_name; + Str decl_name; consume(TK("@id")); - decl->name = prev().str(); + decl_name = prev().str(); if(!ctx()->is_compiling_class && match(TK("::"))){ consume(TK("@id")); - obj_name = decl->name; - decl->name = prev().str(); + obj_name = decl_name; + decl_name = prev().str(); } + FuncDecl_ decl = push_f_context(decl_name); consume(TK("(")); if (!match(TK(")"))) { _compile_f_args(decl, true); @@ -894,7 +924,6 @@ __SUBSCR_END: if(match(TK("->"))){ if(!match(TK("None"))) consume(TK("@id")); } - decl->code = push_context(lexer->src, decl->name.sv()); compile_block_body(); pop_context(); ctx()->emit(OP_LOAD_FUNCTION, ctx()->add_func_decl(decl), prev().line); @@ -908,15 +937,15 @@ __SUBSCR_END: } if(!ctx()->is_compiling_class){ if(obj_name.empty()){ - auto e = make_expr(decl->name, name_scope()); + auto e = make_expr(decl_name, name_scope()); e->emit_store(ctx()); } else { ctx()->emit(OP_LOAD_GLOBAL, ctx()->add_name(obj_name), prev().line); - int index = ctx()->add_name(decl->name); + int index = ctx()->add_name(decl_name); ctx()->emit(OP_STORE_ATTR, index, prev().line); } }else{ - int index = ctx()->add_name(decl->name); + int index = ctx()->add_name(decl_name); ctx()->emit(OP_STORE_CLASS_ATTR, index, prev().line); } } @@ -963,7 +992,7 @@ public: // for(auto& t: tokens) std::cout << t.info() << std::endl; // } - CodeObject_ code = push_context(lexer->src, lexer->src->filename); + CodeObject_ code = push_global_context(); advance(); // skip @sof, so prev() is always valid match_newlines(); // skip possible leading '\n' diff --git a/src/expr.h b/src/expr.h index d0a53745..39f4d02d 100644 --- a/src/expr.h +++ b/src/expr.h @@ -105,6 +105,15 @@ struct CodeEmitContext{ return co->names.size() - 1; } + int add_varname(StrName name){ + auto it = co->varnames_inv.find(name); + if(it != co->varnames_inv.end()) return it->second; + co->varnames.push_back(name); + int index = co->varnames.size() - 1; + co->varnames_inv[name] = index; + return index; + } + int add_const(PyObject* v){ co->consts.push_back(v); return co->consts.size() - 1; @@ -124,18 +133,24 @@ struct NameExpr: Expr{ std::string str() const override { return fmt("Name(", name.escape(), ")"); } void emit(CodeEmitContext* ctx) override { - int index = ctx->add_name(name); - ctx->emit(OP_LOAD_NAME, index, line); + switch(scope){ + case NAME_LOCAL: + ctx->emit(OP_LOAD_FAST, ctx->add_varname(name), line); + break; + case NAME_GLOBAL: + ctx->emit(OP_LOAD_GLOBAL, ctx->add_name(name), line); + break; + default: FATAL_ERROR(); break; + } } bool emit_del(CodeEmitContext* ctx) override { - int index = ctx->add_name(name); switch(scope){ case NAME_LOCAL: - ctx->emit(OP_DELETE_LOCAL, index, line); + ctx->emit(OP_DELETE_FAST, ctx->add_varname(name), line); break; case NAME_GLOBAL: - ctx->emit(OP_DELETE_GLOBAL, index, line); + ctx->emit(OP_DELETE_GLOBAL, ctx->add_name(name), line); break; default: FATAL_ERROR(); break; } @@ -143,17 +158,17 @@ struct NameExpr: Expr{ } bool emit_store(CodeEmitContext* ctx) override { - int index = ctx->add_name(name); if(ctx->is_compiling_class){ + int index = ctx->add_name(name); ctx->emit(OP_STORE_CLASS_ATTR, index, line); return true; } switch(scope){ case NAME_LOCAL: - ctx->emit(OP_STORE_LOCAL, index, line); + ctx->emit(OP_STORE_FAST, ctx->add_varname(name), line); break; case NAME_GLOBAL: - ctx->emit(OP_STORE_GLOBAL, index, line); + ctx->emit(OP_STORE_GLOBAL, ctx->add_name(name), line); break; default: FATAL_ERROR(); break; } @@ -484,14 +499,9 @@ struct SetCompExpr: CompExpr{ struct LambdaExpr: Expr{ FuncDecl_ decl; - NameScope scope; std::string str() const override { return "Lambda()"; } - LambdaExpr(NameScope scope){ - this->decl = make_sp(); - this->decl->name = ""; - this->scope = scope; - } + LambdaExpr(FuncDecl_ decl): decl(decl) {} void emit(CodeEmitContext* ctx) override { int index = ctx->add_func_decl(decl); @@ -624,8 +634,8 @@ struct CallExpr: Expr{ for(auto& item: args) item->emit(ctx); // emit kwargs for(auto& item: kwargs){ - // TODO: optimize this - ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(item.first)), line); + int index = ctx->add_varname(item.first); + ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(index)), line); item.second->emit(ctx); } int KWARGC = (int)kwargs.size(); diff --git a/src/frame.h b/src/frame.h index 0599ce2d..f7ac487f 100644 --- a/src/frame.h +++ b/src/frame.h @@ -7,6 +7,7 @@ namespace pkpy{ using ValueStack = pod_vector; +using FastLocals = pod_vector; struct Frame { ValueStack _data; @@ -15,18 +16,39 @@ struct Frame { const CodeObject* co; PyObject* _module; - NameDict_ _locals; + FastLocals _locals; NameDict_ _closure; - NameDict& f_locals() noexcept { return _locals!=nullptr ? *_locals : _module->attr(); } + PyObject* f_locals_try_get(StrName name){ + auto it = co->varnames_inv.find(name); + if(it == co->varnames_inv.end()) return nullptr; + return _locals[it->second]; + } + NameDict& f_globals() noexcept { return _module->attr(); } PyObject* f_closure_try_get(StrName name){ if(_closure == nullptr) return nullptr; return _closure->try_get(name); } - Frame(const CodeObject_& co, PyObject* _module, const NameDict_& _locals=nullptr, const NameDict_& _closure=nullptr) - : co(co.get()), _module(_module), _locals(_locals), _closure(_closure) { + NameDict_ locals_to_namedict() const{ + NameDict_ dict = make_sp(); + for(int i=0; ivarnames.size(); i++){ + if(_locals[i] != nullptr) dict->set(co->varnames[i], _locals[i]); + } + return dict; + } + + Frame(const CodeObject* co, PyObject* _module, FastLocals&& _locals, const NameDict_& _closure) + : co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) { + } + + Frame(const CodeObject* co, PyObject* _module) + : co(co), _module(_module), _locals(), _closure(nullptr) { + } + + Frame(const CodeObject_& co, PyObject* _module) + : co(co.get()), _module(_module), _locals(), _closure(nullptr) { } Frame(const Frame& other) = delete; @@ -155,7 +177,10 @@ struct Frame { if(_data._data == nullptr) return; for(PyObject* obj : _data) OBJ_MARK(obj); OBJ_MARK(_module); - if(_locals != nullptr) _locals->_gc_mark(); + // _locals may be move for `eval/exec` + if(_locals._data != nullptr){ + for(PyObject* obj: _locals) OBJ_MARK(obj); + } if(_closure != nullptr) _closure->_gc_mark(); co->_gc_mark(); } diff --git a/src/gc.h b/src/gc.h index b1c5808d..98a8b4ef 100644 --- a/src/gc.h +++ b/src/gc.h @@ -120,7 +120,7 @@ inline void NameDict::_gc_mark() const{ inline void FuncDecl::_gc_mark() const{ code->_gc_mark(); - kwargs._gc_mark(); + for(int i=0; i inline void _gc_mark(List& t){ diff --git a/src/obj.h b/src/obj.h index c8c11d7a..21f19992 100644 --- a/src/obj.h +++ b/src/obj.h @@ -8,7 +8,6 @@ namespace pkpy { struct CodeObject; struct Frame; -struct BaseRef; class VM; typedef std::function NativeFuncRaw; @@ -25,20 +24,15 @@ struct NativeFunc { }; struct FuncDecl { - StrName name; - CodeObject_ code; - std::vector args; - StrName starred_arg; // empty if no *arg - NameDict kwargs; // empty if no k=v - std::vector kwargs_order; - - bool has_name(StrName val) const { - bool _0 = std::find(args.begin(), args.end(), val) != args.end(); - bool _1 = starred_arg == val; - bool _2 = kwargs.contains(val); - return _0 || _1 || _2; - } - + 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 + int starred_arg = -1; // index in co->varnames, -1 if no *arg + pod_vector kwargs; // indices in co->varnames + bool nested = false; // whether this function is nested void _gc_mark() const; }; diff --git a/src/opcodes.h b/src/opcodes.h index eb5c8aab..22c26b59 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -17,18 +17,19 @@ OPCODE(LOAD_BUILTIN_EVAL) OPCODE(LOAD_FUNCTION) OPCODE(LOAD_NULL) /**************************/ +OPCODE(LOAD_FAST) OPCODE(LOAD_NAME) OPCODE(LOAD_GLOBAL) OPCODE(LOAD_ATTR) OPCODE(LOAD_METHOD) OPCODE(LOAD_SUBSCR) -OPCODE(STORE_LOCAL) +OPCODE(STORE_FAST) OPCODE(STORE_GLOBAL) OPCODE(STORE_ATTR) OPCODE(STORE_SUBSCR) -OPCODE(DELETE_LOCAL) +OPCODE(DELETE_FAST) OPCODE(DELETE_GLOBAL) OPCODE(DELETE_ATTR) OPCODE(DELETE_SUBSCR) diff --git a/src/pocketpy.h b/src/pocketpy.h index 6251147d..4ae67c7e 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -98,12 +98,26 @@ inline void init_builtins(VM* _vm) { _vm->bind_builtin_func<1>("eval", [](VM* vm, Args& args) { CodeObject_ code = vm->compile(CAST(Str&, args[0]), "", EVAL_MODE); - return vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals); + FrameId frame = vm->top_frame(); + vm->_push_new_frame(code.get(), frame->_module, std::move(frame->_locals), nullptr); + PyObject* ret = vm->_run_top_frame(true); + frame->_locals = std::move(vm->top_frame()->_locals); + vm->callstack.pop(); + return ret; }); _vm->bind_builtin_func<1>("exec", [](VM* vm, Args& args) { CodeObject_ code = vm->compile(CAST(Str&, args[0]), "", EXEC_MODE); - vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals); + FrameId frame = vm->top_frame(); + // TODO: implementation is not correct + // ... + // moving _locals is dangerous since OP_LOAD_FAST's arg is index of _locals + // the new opcode may not generate the same index, or even not a OP_LOAD_FAST call + // we should do some special handling here + vm->_push_new_frame(code.get(), frame->_module, std::move(frame->_locals), nullptr); + vm->_run_top_frame(true); + frame->_locals = std::move(vm->top_frame()->_locals); + vm->callstack.pop(); return vm->None; }); @@ -598,7 +612,7 @@ inline void add_module_json(VM* vm){ vm->bind_func<1>(mod, "loads", [](VM* vm, Args& args) { const Str& expr = CAST(Str&, args[0]); CodeObject_ code = vm->compile(expr, "", JSON_MODE); - return vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals); + return vm->_exec(code, vm->top_frame()->_module); }); vm->bind_func<1>(mod, "dumps", CPP_LAMBDA(vm->fast_call(__json__, Args{args[0]}))); diff --git a/src/vm.h b/src/vm.h index b0316c0f..7f2b29cc 100644 --- a/src/vm.h +++ b/src/vm.h @@ -348,7 +348,7 @@ public: template void bind_func(PyObject*, Str, NativeFuncRaw); void _error(Exception); - PyObject* _run_top_frame(); + PyObject* _run_top_frame(bool force_no_pop=false); void post_init(); }; @@ -361,11 +361,9 @@ inline PyObject* NativeFunc::operator()(VM* vm, Args& args) const{ } inline void CodeObject::optimize(VM* vm){ - // here we simple pass all names, but only some of them are NAME_LOCAL - // TODO: ... - uint32_t base_n = (uint32_t)(names.size() / kLocalsLoadFactor + 0.5); - perfect_locals_capacity = std::max(find_next_capacity(base_n), NameDict::__Capacity); - perfect_hash_seed = find_perfect_hash_seed(perfect_locals_capacity, names); + // uint32_t base_n = (uint32_t)(names.size() / kLocalsLoadFactor + 0.5); + // perfect_locals_capacity = std::max(find_next_capacity(base_n), NameDict::__Capacity); + // perfect_hash_seed = find_perfect_hash_seed(perfect_locals_capacity, names); } DEF_NATIVE_2(Str, tp_str) @@ -596,12 +594,15 @@ inline Str VM::disassemble(CodeObject_ co){ argStr += fmt(" (", CAST(Str, asRepr(co->consts[byte.arg])), ")"); break; case OP_LOAD_NAME: case OP_LOAD_GLOBAL: - case OP_STORE_LOCAL: case OP_STORE_GLOBAL: + case OP_STORE_GLOBAL: case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR: case OP_IMPORT_NAME: case OP_BEGIN_CLASS: - case OP_DELETE_LOCAL: case OP_DELETE_GLOBAL: + case OP_DELETE_GLOBAL: argStr += fmt(" (", co->names[byte.arg].sv(), ")"); break; + case OP_LOAD_FAST: case OP_STORE_FAST: case OP_DELETE_FAST: + argStr += fmt(" (", co->varnames[byte.arg].sv(), ")"); + break; case OP_BINARY_OP: argStr += fmt(" (", BINARY_SPECIAL_METHODS[byte.arg], ")"); break; @@ -611,22 +612,21 @@ inline Str VM::disassemble(CodeObject_ co){ if(i != co->codes.size() - 1) ss << '\n'; } -#if !DEBUG_DIS_EXEC_MIN - std::stringstream consts; - consts << "co_consts: "; - consts << CAST(Str&, asRepr(VAR(co->consts))); + // std::stringstream consts; + // consts << "co_consts: "; + // consts << CAST(Str&, asRepr(VAR(co->consts))); + + // std::stringstream names; + // names << "co_names: "; + // List list; + // for(int i=0; inames.size(); i++){ + // list.push_back(VAR(co->names[i].sv())); + // } + // names << CAST(Str, asRepr(VAR(list))); + // ss << '\n' << consts.str() << '\n' << names.str(); - std::stringstream names; - names << "co_names: "; - List list; - for(int i=0; inames.size(); i++){ - list.push_back(VAR(co->names[i].sv())); - } - names << CAST(Str, asRepr(VAR(list))); - ss << '\n' << consts.str() << '\n' << names.str(); -#endif for(auto& decl: co->func_decls){ - ss << "\n\n" << "Disassembly of " << decl->name << ":\n"; + ss << "\n\n" << "Disassembly of " << decl->code->name << ":\n"; ss << disassemble(decl->code); } return Str(ss.str()); @@ -699,33 +699,35 @@ inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, boo return f(this, args); } else if(is_type(callable, tp_function)){ const Function& fn = CAST(Function&, callable); - NameDict_ locals = make_sp( - kLocalsLoadFactor, - fn.decl->code->perfect_locals_capacity, - fn.decl->code->perfect_hash_seed - ); + const CodeObject* co = fn.decl->code.get(); + // create a FastLocals with the same size as co->varnames + FastLocals locals(co->varnames.size()); + // zero init + for(auto& v: locals) v = nullptr; int i = 0; - for(StrName name : fn.decl->args){ + for(int index: fn.decl->args){ if(i < args.size()){ - locals->set(name, args[i++]); - continue; + locals[index] = args[i++]; + }else{ + StrName name = co->varnames[index]; + TypeError(fmt("missing positional argument ", name.escape())); } - TypeError(fmt("missing positional argument ", name.escape())); } - // NameDict.update is of O(capacity) complexity - // so we try not to call it if possible - if(fn.decl->kwargs.size()!=0) locals->update(fn.decl->kwargs); - - if(!fn.decl->starred_arg.empty()){ + // prepare kwdefaults + for(auto& kv: fn.decl->kwargs) locals[kv.key] = kv.value; + + // handle *args + if(fn.decl->starred_arg != -1){ List vargs; // handle *args while(i < args.size()) vargs.push_back(args[i++]); - locals->set(fn.decl->starred_arg, VAR(Tuple(std::move(vargs)))); + locals[fn.decl->starred_arg] = VAR(Tuple(std::move(vargs))); }else{ - for(StrName key : fn.decl->kwargs_order){ + // kwdefaults override + for(auto& kv: fn.decl->kwargs){ if(i < args.size()){ - locals->set(key, args[i++]); + locals[kv.key] = args[i++]; }else{ break; } @@ -734,17 +736,18 @@ inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, boo } for(int i=0; ikwargs.contains(key)){ - TypeError(fmt(key.escape(), " is an invalid keyword argument for ", fn.decl->name, "()")); + StrName key = CAST(int, kwargs[i]); + auto it = co->varnames_inv.find(key); + if(it == co->varnames_inv.end()){ + TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()")); } - locals->set(key, kwargs[i+1]); + locals[it->second] = kwargs[i+1]; } PyObject* _module = fn._module != nullptr ? fn._module : top_frame()->_module; - if(fn.decl->code->is_generator){ - return PyIter(Generator(this, Frame(fn.decl->code, _module, locals, fn._closure))); + if(co->is_generator){ + return PyIter(Generator(this, Frame(co, _module, std::move(locals), fn._closure))); } - _push_new_frame(fn.decl->code, _module, locals, fn._closure); + _push_new_frame(co, _module, std::move(locals), fn._closure); if(opCall) return _py_op_call; return _run_top_frame(); }