From a6b46717110bff4f29e7b1ecd3261b208d685a9b Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 2 Apr 2023 20:24:27 +0800 Subject: [PATCH] up --- src/ceval.h | 210 ++++++++++++++++++++++++-------------------------- src/expr.h | 2 +- src/frame.h | 21 ++++- src/obj.h | 3 +- src/opcodes.h | 35 ++++----- src/str.h | 1 + src/vm.h | 3 +- 7 files changed, 143 insertions(+), 132 deletions(-) diff --git a/src/ceval.h b/src/ceval.h index 139c31ae..a8a17795 100644 --- a/src/ceval.h +++ b/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; ipop_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"); } } diff --git a/src/expr.h b/src/expr.h index fdf8c555..c8ef18f7 100644 --- a/src/expr.h +++ b/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++; diff --git a/src/frame.h b/src/frame.h index 1440b2d1..072719b8 100644 --- a/src/frame.h +++ b/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 void push(T&& obj){ _data.push_back(std::forward(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; diff --git a/src/opcodes.h b/src/opcodes.h index 98c0e425..07325b0c 100644 --- a/src/opcodes.h +++ b/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 \ No newline at end of file diff --git a/src/str.h b/src/str.h index 9cb4a001..53d71297 100644 --- a/src/str.h +++ b/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__"); diff --git a/src/vm.h b/src/vm.h index cf868ba3..12b3be44 100644 --- a/src/vm.h +++ b/src/vm.h @@ -566,7 +566,7 @@ inline Str VM::disassemble(CodeObject_ co){ std::vector 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__);