blueloveTH 2 лет назад
Родитель
Сommit
a6b4671711
7 измененных файлов с 143 добавлено и 132 удалено
  1. 102 108
      src/ceval.h
  2. 1 1
      src/expr.h
  3. 19 2
      src/frame.h
  4. 1 2
      src/obj.h
  5. 17 18
      src/opcodes.h
  6. 1 0
      src/str.h
  7. 2 1
      src/vm.h

+ 102 - 108
src/ceval.h

@@ -7,7 +7,13 @@ namespace pkpy{
 
 inline PyObject* VM::run_frame(Frame* frame){
     while(true){
-        heap._auto_collect(this);   // gc
+        /* NOTE: 
+        * Be aware of accidental gc!
+        * DO NOT leave any strong reference of PyObject* in the C stack
+        * For example, frame->popx() returns a strong reference which may be dangerous
+        * `Args` containing strong references is safe if it is passed to `call` or `fast_call`
+        */
+        heap._auto_collect(this);
 
         const Bytecode& byte = frame->next_bytecode();
         switch (byte.op)
@@ -18,7 +24,7 @@ inline PyObject* VM::run_frame(Frame* frame){
         case OP_DUP_TOP: frame->push(frame->top()); continue;
         case OP_ROT_TWO: std::swap(frame->top(), frame->top_1()); continue;
         case OP_PRINT_EXPR: {
-            PyObject* obj = frame->top();  // use top() here to avoid accidental gc
+            PyObject* obj = frame->top();  // use top() to avoid accidental gc
             if(obj != None) *_stdout << CAST(Str, asRepr(obj)) << '\n';
             frame->pop();
         } continue;
@@ -28,7 +34,7 @@ inline PyObject* VM::run_frame(Frame* frame){
         case OP_LOAD_TRUE: frame->push(True); continue;
         case OP_LOAD_FALSE: frame->push(False); continue;
         case OP_LOAD_ELLIPSIS: frame->push(Ellipsis); continue;
-        case OP_LOAD_BUILTINS_EVAL: frame->push(builtins->attr(m_eval)); continue;
+        case OP_LOAD_BUILTIN_EVAL: frame->push(builtins->attr(m_eval)); continue;
         case OP_LOAD_FUNCTION: {
             PyObject* obj = frame->co->consts[byte.arg];
             Function f = CAST(Function, obj);   // copy it!
@@ -67,9 +73,10 @@ inline PyObject* VM::run_frame(Frame* frame){
         } continue;
         case OP_STORE_ATTR: {
             StrName name = frame->co->names[byte.arg];
-            PyObject* a = frame->popx();
-            PyObject* val = frame->popx();
+            PyObject* a = frame->top();
+            PyObject* val = frame->top_1();
             setattr(a, name, val);
+            frame->pop_n(2);
         } continue;
         case OP_STORE_SUBSCR: {
             Args args(3);
@@ -135,9 +142,12 @@ inline PyObject* VM::run_frame(Frame* frame){
             frame->push(VAR(std::move(items)));
         } continue;
         case OP_BUILD_STRING: {
-            Args items = frame->popx_n_reversed(byte.arg);
+            // asStr() may run extra bytecode
+            // so we use top_n_reversed() in order to avoid accidental gc
+            Args items = frame->top_n_reversed(byte.arg);
             StrStream ss;
             for(int i=0; i<items.size(); i++) ss << CAST(Str, asStr(items[i]));
+            frame->pop_n(byte.arg);
             frame->push(VAR(ss.str()));
         } continue;
         /*****************************************/
@@ -177,7 +187,6 @@ inline PyObject* VM::run_frame(Frame* frame){
         } continue;
         /*****************************************/
         case OP_JUMP_ABSOLUTE: frame->jump_abs(byte.arg); continue;
-        case OP_SAFE_JUMP_ABSOLUTE: frame->jump_abs_safe(byte.arg); continue;
         case OP_POP_JUMP_IF_FALSE:
             if(!asBool(frame->popx())) frame->jump_abs(byte.arg);
             continue;
@@ -195,13 +204,13 @@ inline PyObject* VM::run_frame(Frame* frame){
         } continue;
         case OP_LOOP_BREAK: {
             int target = frame->co->blocks[byte.block].end;
-            frame->jump_abs_safe(target);
+            frame->jump_abs_break(target);
         } continue;
         case OP_GOTO: {
             StrName label = frame->co->names[byte.arg];
             auto it = frame->co->labels.find(label);
             if(it == frame->co->labels.end()) _error("KeyError", "label " + label.str().escape(true) + " not found");
-            frame->jump_abs_safe(it->second);
+            frame->jump_abs_break(it->second);
         } continue;
         /*****************************************/
         // TODO: examine this later
@@ -226,139 +235,124 @@ inline PyObject* VM::run_frame(Frame* frame){
         } continue;
         case OP_RETURN_VALUE: return frame->popx();
         /*****************************************/
-
-        /*****************************************/
-        case OP_SETUP_DECORATOR: continue;
-
-        case OP_SETUP_CLOSURE: {
-            Function& f = CAST(Function&, frame->top());    // reference
-            f._closure = frame->_locals;
-        } continue;
-        case OP_BEGIN_CLASS: {
-            StrName name = frame->co->names[byte.arg];
-            PyObject* clsBase = frame->popx();
-            if(clsBase == None) clsBase = _t(tp_object);
-            check_type(clsBase, tp_type);
-            PyObject* cls = new_type_object(frame->_module, name, OBJ_GET(Type, clsBase));
-            frame->push(cls);
-        } continue;
-        case OP_END_CLASS: {
-            PyObject* cls = frame->popx();
-            cls->attr()._try_perfect_rehash();
-        }; continue;
-        case OP_STORE_CLASS_ATTR: {
-            StrName name = frame->co->names[byte.arg];
-            PyObject* obj = frame->popx();
-            PyObject* cls = frame->top();
-            cls->attr().set(name, obj);
-        } continue;
-        
-        case OP_UNARY_NEGATIVE:
-            frame->top() = num_negated(frame->top_value(this));
-            continue;
-        case OP_UNARY_NOT: {
-            PyObject* obj = frame->pop_value(this);
-            PyObject* obj_bool = asBool(obj);
-            frame->push(VAR(!_CAST(bool, obj_bool)));
-        } continue;
-
-        case OP_ASSERT: {
-            PyObject* _msg = frame->pop_value(this);
-            Str msg = CAST(Str, asStr(_msg));
-            PyObject* expr = frame->pop_value(this);
-            if(asBool(expr) != True) _error("AssertionError", msg);
-        } continue;
-        case OP_EXCEPTION_MATCH: {
-            const auto& e = CAST(Exception&, frame->top());
-            StrName name = frame->co->names[byte.arg].first;
-            frame->push(VAR(e.match_type(name)));
-        } continue;
-        case OP_RAISE: {
-            PyObject* obj = frame->pop_value(this);
-            Str msg = obj == None ? "" : CAST(Str, asStr(obj));
-            StrName type = frame->co->names[byte.arg].first;
-            _error(type, msg);
-        } continue;
-        case OP_RE_RAISE: _raise(); continue;
-
         case OP_LIST_APPEND: {
-            PyObject* obj = frame->pop_value(this);
+            PyObject* obj = frame->popx();
             List& list = CAST(List&, frame->top_1());
-            list.push_back(std::move(obj));
+            list.push_back(obj);
         } continue;
-        case OP_MAP_ADD: {
-            PyObject* value = frame->pop_value(this);
-            PyObject* key = frame->pop_value(this);
-            call(frame->top_1(), __setitem__, Args{key, value});
+        case OP_DICT_ADD: {
+            PyObject* kv = frame->popx();
+            // we do copy here to avoid accidental gc in `kv`
+            // TODO: optimize to avoid copy
+            call(frame->top_1(), __setitem__, CAST(Tuple, kv));
         } continue;
         case OP_SET_ADD: {
-            PyObject* obj = frame->pop_value(this);
-            call(frame->top_1(), "add", Args{obj});
-        } continue;
-        case OP_UNARY_STAR: {
-            if(byte.arg > 0){   // rvalue
-                frame->top() = VAR(StarWrapper(frame->top_value(this), true));
-            }else{
-                PyRef_AS_C(frame->top()); // check ref
-                frame->top() = VAR(StarWrapper(frame->top(), false));
-            }
-        } continue;
-        case OP_GET_ITER: {
-            PyObject* obj = frame->pop_value(this);
-            PyObject* iter = asIter(obj);
-            check_type(frame->top(), tp_ref);
-            PyIter_AS_C(iter)->loop_var = frame->pop();
-            frame->push(std::move(iter));
+            PyObject* obj = frame->popx();
+            call(frame->top_1(), m_add, Args{obj});
         } continue;
+        /*****************************************/
+        case OP_UNARY_NEGATIVE:
+            frame->top() = num_negated(frame->top());
+            continue;
+        case OP_UNARY_NOT:
+            frame->top() = VAR(!asBool(frame->top()));
+            continue;
+        case OP_UNARY_STAR:
+            frame->top() = VAR(StarWrapper(frame->top()));
+            continue;
+        /*****************************************/
+        case OP_GET_ITER:
+            frame->top() = asIter(frame->top());
+            continue;
         case OP_FOR_ITER: {
             BaseIter* it = PyIter_AS_C(frame->top());
             PyObject* obj = it->next();
             if(obj != nullptr){
-                PyRef_AS_C(it->loop_var)->set(this, frame, std::move(obj));
+                frame->push(obj);
             }else{
-                int blockEnd = frame->co->blocks[byte.block].end;
-                frame->jump_abs_safe(blockEnd);
+                int target = frame->co->blocks[byte.block].end;
+                frame->jump_abs_break(target);
             }
         } continue;
-
-
+        /*****************************************/
         case OP_IMPORT_NAME: {
-            StrName name = frame->co->names[byte.arg].first;
+            StrName name = frame->co->names[byte.arg];
             PyObject* ext_mod = _modules.try_get(name);
             if(ext_mod == nullptr){
                 Str source;
-                auto it2 = _lazy_modules.find(name);
-                if(it2 == _lazy_modules.end()){
+                auto it = _lazy_modules.find(name);
+                if(it == _lazy_modules.end()){
                     bool ok = false;
                     source = _read_file_cwd(name.str() + ".py", &ok);
                     if(!ok) _error("ImportError", "module " + name.str().escape(true) + " not found");
                 }else{
-                    source = it2->second;
-                    _lazy_modules.erase(it2);
+                    source = it->second;
+                    _lazy_modules.erase(it);
                 }
                 CodeObject_ code = compile(source, name.str(), EXEC_MODE);
                 PyObject* new_mod = new_module(name);
                 _exec(code, new_mod);
-                frame->push(new_mod);
                 new_mod->attr()._try_perfect_rehash();
-            }else{
-                frame->push(ext_mod);
             }
+            frame->push(ext_mod);
         } continue;
-        case OP_STORE_ALL_NAMES: {
-            PyObject* obj = frame->pop_value(this);
+        case OP_IMPORT_STAR: {
+            PyObject* obj = frame->popx();
             for(auto& [name, value]: obj->attr().items()){
                 Str s = name.str();
                 if(s.empty() || s[0] == '_') continue;
                 frame->f_globals().set(name, value);
             }
         }; continue;
-        case OP_YIELD_VALUE: return _py_op_yield;
-        // TODO: using "goto" inside with block may cause __exit__ not called
-        case OP_WITH_ENTER: call(frame->pop_value(this), __enter__, no_arg()); continue;
-        case OP_WITH_EXIT: call(frame->pop_value(this), __exit__, no_arg()); continue;
-        case OP_TRY_BLOCK_ENTER: frame->on_try_block_enter(); continue;
-        case OP_TRY_BLOCK_EXIT: frame->on_try_block_exit(); continue;
+        /*****************************************/
+        /*****************************************/
+        // case OP_SETUP_DECORATOR: continue;
+        // case OP_SETUP_CLOSURE: {
+        //     Function& f = CAST(Function&, frame->top());    // reference
+        //     f._closure = frame->_locals;
+        // } continue;
+        // case OP_BEGIN_CLASS: {
+        //     StrName name = frame->co->names[byte.arg];
+        //     PyObject* clsBase = frame->popx();
+        //     if(clsBase == None) clsBase = _t(tp_object);
+        //     check_type(clsBase, tp_type);
+        //     PyObject* cls = new_type_object(frame->_module, name, OBJ_GET(Type, clsBase));
+        //     frame->push(cls);
+        // } continue;
+        // case OP_END_CLASS: {
+        //     PyObject* cls = frame->popx();
+        //     cls->attr()._try_perfect_rehash();
+        // }; continue;
+        // case OP_STORE_CLASS_ATTR: {
+        //     StrName name = frame->co->names[byte.arg];
+        //     PyObject* obj = frame->popx();
+        //     PyObject* cls = frame->top();
+        //     cls->attr().set(name, obj);
+        // } continue;
+        // case OP_ASSERT: {
+        //     PyObject* _msg = frame->pop_value(this);
+        //     Str msg = CAST(Str, asStr(_msg));
+        //     PyObject* expr = frame->pop_value(this);
+        //     if(asBool(expr) != True) _error("AssertionError", msg);
+        // } continue;
+        // case OP_EXCEPTION_MATCH: {
+        //     const auto& e = CAST(Exception&, frame->top());
+        //     StrName name = frame->co->names[byte.arg].first;
+        //     frame->push(VAR(e.match_type(name)));
+        // } continue;
+        // case OP_RAISE: {
+        //     PyObject* obj = frame->pop_value(this);
+        //     Str msg = obj == None ? "" : CAST(Str, asStr(obj));
+        //     StrName type = frame->co->names[byte.arg].first;
+        //     _error(type, msg);
+        // } continue;
+        // case OP_RE_RAISE: _raise(); continue;
+        // case OP_YIELD_VALUE: return _py_op_yield;
+        // // TODO: using "goto" inside with block may cause __exit__ not called
+        // case OP_WITH_ENTER: call(frame->pop_value(this), __enter__, no_arg()); continue;
+        // case OP_WITH_EXIT: call(frame->pop_value(this), __exit__, no_arg()); continue;
+        // case OP_TRY_BLOCK_ENTER: frame->on_try_block_enter(); continue;
+        // case OP_TRY_BLOCK_EXIT: frame->on_try_block_exit(); continue;
         default: throw std::runtime_error(Str("opcode ") + OP_NAMES[byte.op] + " is not implemented");
         }
     }

+ 1 - 1
src/expr.h

@@ -425,7 +425,7 @@ struct FStringExpr: Expr{
                 ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
                 size++;
             }
-            ctx->emit(OP_LOAD_BUILTINS_EVAL, BC_NOARG, line);
+            ctx->emit(OP_LOAD_BUILTIN_EVAL, BC_NOARG, line);
             ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(m[1].str())), line);
             ctx->emit(OP_CALL, 1, line);
             size++;

+ 19 - 2
src/frame.h

@@ -82,6 +82,13 @@ struct Frame {
         return _data[_data.size()-2];
     }
 
+    PyObject*& top_2(){
+#if DEBUG_EXTRA_CHECK
+        if(_data.size() < 3) throw std::runtime_error("_data.size() < 3");
+#endif
+        return _data[_data.size()-3];
+    }
+
     template<typename T>
     void push(T&& obj){ _data.push_back(std::forward<T>(obj)); }
 
@@ -98,7 +105,7 @@ struct Frame {
 
     bool jump_to_exception_handler(){
         if(s_try_block.empty()) return false;
-        PyObject* obj = pop();
+        PyObject* obj = popx();
         auto& p = s_try_block.back();
         _data = std::move(p.second);
         _data.push_back(obj);
@@ -113,7 +120,7 @@ struct Frame {
         return co->blocks[i].parent;
     }
 
-    void jump_abs_safe(int target){
+    void jump_abs_break(int target){
         const Bytecode& prev = co->codes[_ip];
         int i = prev.block;
         _next_ip = target;
@@ -132,6 +139,16 @@ struct Frame {
         return v;
     }
 
+    Args top_n_reversed(int n){
+        Args v(n);
+        for(int i=0; i<n; i++) v[i] = _data[_data.size()-1-i];
+        return v;
+    }
+
+    void pop_n(int n){
+        _data.resize(_data.size()-n);
+    }
+
     void _mark() const {
         for(PyObject* obj : _data) OBJ_MARK(obj);
         OBJ_MARK(_module);

+ 1 - 2
src/obj.h

@@ -59,8 +59,7 @@ struct Range {
 
 struct StarWrapper {
     PyObject* obj;
-    bool rvalue;
-    StarWrapper(PyObject* obj, bool rvalue): obj(obj), rvalue(rvalue) {}
+    StarWrapper(PyObject* obj): obj(obj) {}
 };
 
 using Super = std::pair<PyObject*, Type>;

+ 17 - 18
src/opcodes.h

@@ -1,18 +1,6 @@
 #ifdef OPCODE
 
-OPCODE(UNARY_NEGATIVE)
-OPCODE(UNARY_NOT)
-OPCODE(UNARY_STAR)
-
-OPCODE(LIST_APPEND)
-OPCODE(MAP_ADD)
-OPCODE(SET_ADD)
-
-OPCODE(IMPORT_NAME)
-
-OPCODE(GET_ITER)
-OPCODE(FOR_ITER)
-
+/**************************/
 OPCODE(WITH_ENTER)
 OPCODE(WITH_EXIT)
 
@@ -30,13 +18,11 @@ OPCODE(YIELD_VALUE)
 
 OPCODE(SETUP_CLOSURE)
 OPCODE(SETUP_DECORATOR)
-OPCODE(STORE_ALL_NAMES)
 
 OPCODE(BEGIN_CLASS)
 OPCODE(END_CLASS)
 OPCODE(STORE_CLASS_ATTR)
 
-
 /**************************/
 OPCODE(NO_OP)
 /**************************/
@@ -50,7 +36,7 @@ OPCODE(LOAD_NONE)
 OPCODE(LOAD_TRUE)
 OPCODE(LOAD_FALSE)
 OPCODE(LOAD_ELLIPSIS)
-OPCODE(LOAD_BUILTINS_EVAL)
+OPCODE(LOAD_BUILTIN_EVAL)
 OPCODE(LOAD_FUNCTION)
 /**************************/
 OPCODE(LOAD_NAME)
@@ -81,7 +67,6 @@ OPCODE(IS_OP)
 OPCODE(CONTAINS_OP)
 /**************************/
 OPCODE(JUMP_ABSOLUTE)
-OPCODE(SAFE_JUMP_ABSOLUTE)
 OPCODE(POP_JUMP_IF_FALSE)
 OPCODE(JUMP_IF_TRUE_OR_POP)
 OPCODE(JUMP_IF_FALSE_OR_POP)
@@ -95,5 +80,19 @@ OPCODE(CALL_KWARGS)
 OPCODE(CALL_KWARGS_UNPACK)
 OPCODE(RETURN_VALUE)
 /**************************/
-
+OPCODE(LIST_APPEND)
+OPCODE(DICT_ADD)
+OPCODE(SET_ADD)
+/**************************/
+OPCODE(UNARY_NEGATIVE)
+OPCODE(UNARY_NOT)
+OPCODE(UNARY_STAR)
+/**************************/
+OPCODE(GET_ITER)
+OPCODE(FOR_ITER)
+/**************************/
+OPCODE(IMPORT_NAME)
+OPCODE(IMPORT_STAR)
+/**************************/
+/**************************/
 #endif

+ 1 - 0
src/str.h

@@ -211,6 +211,7 @@ const StrName m_eval = StrName::get("eval");
 const StrName m_self = StrName::get("self");
 const StrName m_dict = StrName::get("dict");
 const StrName m_set = StrName::get("set");
+const StrName m_add = StrName::get("add");
 const StrName __enter__ = StrName::get("__enter__");
 const StrName __exit__ = StrName::get("__exit__");
 

+ 2 - 1
src/vm.h

@@ -566,7 +566,7 @@ inline Str VM::disassemble(CodeObject_ co){
 
     std::vector<int> jumpTargets;
     for(auto byte : co->codes){
-        if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_SAFE_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE){
+        if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE){
             jumpTargets.push_back(byte.arg);
         }
     }
@@ -682,6 +682,7 @@ inline void VM::init_builtin_types(){
     for(auto [k, v]: _modules.items()) v->attr()._try_perfect_rehash();
 }
 
+// TODO: args here may be garbage collected accidentally
 inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, bool opCall){
     if(is_type(callable, tp_type)){
         PyObject* new_f = callable->attr().try_get(__new__);