blueloveTH 2 éve
szülő
commit
c403252aec
12 módosított fájl, 225 hozzáadás és 155 törlés
  1. 0 9
      .github/workflows/main.yml
  2. 26 18
      src/ceval.h
  3. 5 8
      src/codeobject.h
  4. 1 3
      src/common.h
  5. 58 29
      src/compiler.h
  6. 26 16
      src/expr.h
  7. 30 5
      src/frame.h
  8. 1 1
      src/gc.h
  9. 9 15
      src/obj.h
  10. 3 2
      src/opcodes.h
  11. 17 3
      src/pocketpy.h
  12. 49 46
      src/vm.h

+ 0 - 9
.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

+ 26 - 18
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<StrName>& co_names = co->names;
-    const List& co_consts = co->consts;
-    const std::vector<CodeBlock>& 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();

+ 5 - 8
src/codeobject.h

@@ -52,23 +52,20 @@ struct CodeObject {
     Str name;
     bool is_generator = false;
 
-    CodeObject(shared_ptr<SourceData> src, Str name) {
-        this->src = src;
-        this->name = name;
-    }
+    CodeObject(shared_ptr<SourceData> src, const Str& name):
+        src(src), name(name) {}
 
     std::vector<Bytecode> codes;
     std::vector<int> lines; // line number for each bytecode
     List consts;
-    std::vector<StrName> names;
+    std::vector<StrName> names;         // other names
+    std::vector<StrName> varnames;      // local variables
+    std::map<StrName, int> varnames_inv;
     std::set<Str> global_names;
     std::vector<CodeBlock> blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) };
     std::map<StrName, int> labels;
     std::vector<FuncDecl_> func_decls;
 
-    uint32_t perfect_locals_capacity = NameDict::__Capacity;
-    uint32_t perfect_hash_seed = kHashSeeds[0];
-
     void optimize(VM* vm);
 
     void _gc_mark() const {

+ 1 - 3
src/common.h

@@ -29,13 +29,12 @@
 #include <variant>
 #include <type_traits>
 
-#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;
 

+ 58 - 29
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<typename... Args>
-    CodeObject_ push_context(Args&&... args){
-        CodeObject_ co = make_sp<CodeObject>(std::forward<Args>(args)...);
+    CodeObject_ push_global_context(){
+        CodeObject_ co = make_sp<CodeObject>(lexer->src, lexer->src->filename);
         contexts.push(CodeEmitContext(vm, co));
         return co;
     }
 
+    FuncDecl_ push_f_context(Str name){
+        FuncDecl_ decl = make_sp<FuncDecl>();
+        decl->code = make_sp<CodeObject>(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<LambdaExpr>(name_scope());
+        FuncDecl_ decl = push_f_context("<lambda>");
+        auto e = make_expr<LambdaExpr>(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<Expr_>& decorators={}){
-        FuncDecl_ decl = make_sp<FuncDecl>();
-        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<NameExpr>(decl->name, name_scope());
+                auto e = make_expr<NameExpr>(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'

+ 26 - 16
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<FuncDecl>();
-        this->decl->name = "<lambda>";
-        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();

+ 30 - 5
src/frame.h

@@ -7,6 +7,7 @@
 namespace pkpy{
 
 using ValueStack = pod_vector<PyObject*>;
+using FastLocals = pod_vector<PyObject*>;
 
 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<NameDict>();
+        for(int i=0; i<co->varnames.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();
     }

+ 1 - 1
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<kwargs.size(); i++) OBJ_MARK(kwargs[i].value);
 }
 
 template<> inline void _gc_mark<List>(List& t){

+ 9 - 15
src/obj.h

@@ -8,7 +8,6 @@ namespace pkpy {
     
 struct CodeObject;
 struct Frame;
-struct BaseRef;
 class VM;
 
 typedef std::function<PyObject*(VM*, Args&)> NativeFuncRaw;
@@ -25,20 +24,15 @@ struct NativeFunc {
 };
 
 struct FuncDecl {
-    StrName name;
-    CodeObject_ code;
-    std::vector<StrName> args;
-    StrName starred_arg;                // empty if no *arg
-    NameDict kwargs;              // empty if no k=v
-    std::vector<StrName> 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<int> args;       // indices in co->varnames
+    int starred_arg = -1;       // index in co->varnames, -1 if no *arg
+    pod_vector<KwArg> kwargs;   // indices in co->varnames
+    bool nested = false;        // whether this function is nested
     void _gc_mark() const;
 };
 

+ 3 - 2
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)

+ 17 - 3
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>", 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>", 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>", 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]})));

+ 49 - 46
src/vm.h

@@ -348,7 +348,7 @@ public:
     template<int ARGC>
     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; i<co->names.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; i<co->names.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<NameDict>(
-            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; i<kwargs.size(); i+=2){
-            const Str& key = CAST(Str&, kwargs[i]);
-            if(!fn.decl->kwargs.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();
     }