diff --git a/include/pocketpy/error.h b/include/pocketpy/error.h index 0e66c9b1..c2be6a2e 100644 --- a/include/pocketpy/error.h +++ b/include/pocketpy/error.h @@ -11,9 +11,16 @@ struct NeedMoreLines { bool is_compiling_class; }; -struct HandledException {}; -struct UnhandledException {}; -struct ToBeRaisedException {}; +enum class InternalExceptionType: int{ + Null, Handled, Unhandled, ToBeRaised +}; + +struct InternalException final{ + InternalExceptionType type; + int arg; + InternalException(): type(InternalExceptionType::Null), arg(-1) {} + InternalException(InternalExceptionType type, int arg=-1): type(type), arg(arg) {} +}; enum CompileMode { EXEC_MODE, diff --git a/include/pocketpy/frame.h b/include/pocketpy/frame.h index 94c4d0c8..98a77821 100644 --- a/include/pocketpy/frame.h +++ b/include/pocketpy/frame.h @@ -70,7 +70,6 @@ struct ValueStack { struct Frame { int _ip; - int _next_ip; // This is for unwinding only, use `actual_sp_base()` for value stack access PyVar* _sp_base; @@ -84,28 +83,29 @@ struct Frame { // function scope Frame(PyVar* p0, const CodeObject* co, PyVar _module, PyVar _callable, PyVar* _locals_base) - : _ip(-1), _next_ip(0), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, _locals_base) { } + : _ip(-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, _locals_base) { } // exec/eval Frame(PyVar* p0, const CodeObject* co, PyVar _module, PyVar _callable, FastLocals _locals) - : _ip(-1), _next_ip(0), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals) { } + : _ip(-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals) { } // global scope Frame(PyVar* p0, const CodeObject_& co, PyVar _module) - : _ip(-1), _next_ip(0), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr), _locals(co.get(), p0) {} + : _ip(-1), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr), _locals(co.get(), p0) {} PyVar* actual_sp_base() const { return _locals.a; } int stack_size(ValueStack* _s) const { return _s->_sp - actual_sp_base(); } ArgsView stack_view(ValueStack* _s) const { return ArgsView(actual_sp_base(), _s->_sp); } - void jump_abs(int i){ _next_ip = i; } - bool jump_to_exception_handler(ValueStack*); + [[nodiscard]] int prepare_jump_exception_handler(ValueStack*); + void prepare_jump_break(ValueStack*, int); int _exit_block(ValueStack*, int); - void jump_abs_break(ValueStack*, int); - void loop_break(ValueStack* s_data, const CodeObject*){ - jump_abs_break(s_data, co->_get_block_codei(_ip).end); + [[nodiscard]] int prepare_loop_break(ValueStack* s_data){ + int target = co->_get_block_codei(_ip).end; + prepare_jump_break(s_data, target); + return target; } int curr_lineno() const { return co->lines[_ip].lineno; } diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index ed6d6d52..a13931ce 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -157,6 +157,7 @@ public: std::map __cached_codes; std::map __cached_op_funcs; FuncDecl_ __dynamic_func_decl; + InternalException __internal_exception; PyVar __vectorcall_buffer[PK_MAX_CO_VARNAMES]; #if PK_ENABLE_PROFILER diff --git a/src/ceval.cpp b/src/ceval.cpp index cec5d521..8505cb34 100644 --- a/src/ceval.cpp +++ b/src/ceval.cpp @@ -81,22 +81,6 @@ bool VM::py_ge(PyVar _0, PyVar _1){ #undef BINARY_F_COMPARE -PyVar VM::__run_top_frame(){ - Frame* frame = &callstack.top(); - const Frame* base_frame = frame; - bool need_raise = false; - - while(true){ - try{ - if(need_raise){ need_raise = false; __raise_exc(); } -/**********************************************************************/ -/* NOTE: - * Be aware of accidental gc! - * DO NOT leave any strong reference of PyVar in the C stack - */ -{ - - #if PK_ENABLE_PROFILER #define CEVAL_STEP_CALLBACK() \ if(_ceval_on_step) _ceval_on_step(this, frame, byte); \ @@ -109,17 +93,38 @@ PyVar VM::__run_top_frame(){ } #endif -#define DISPATCH() goto __NEXT_STEP; +#define DISPATCH() { frame->_ip++; goto __NEXT_STEP; } +#define DISPATCH_JUMP(__target) { frame->_ip = __target; goto __NEXT_STEP; } +#define RETURN_OP_YIELD() { frame->_ip++; return PY_OP_YIELD; } +PyVar VM::__run_top_frame(){ + Frame* frame = &callstack.top(); + const Frame* base_frame = frame; + + while(true){ + try{ +/**********************************************************************/ +{ __NEXT_FRAME: - const CodeObject* co = frame->co; + if(__internal_exception.type == InternalExceptionType::Null){ + // None + frame->_ip++; + }else if(__internal_exception.type == InternalExceptionType::Handled){ + // HandledException + continue + frame->_ip = __internal_exception.arg; + __internal_exception = {}; + }else{ + // UnhandledException + continue (need_raise = true) + // ToBeRaisedException + continue (need_raise = true) + __internal_exception = {}; + __raise_exc(); // no return + } + // TODO: when jit is enabled, co_codes may not be const - const Bytecode* co_codes = co->codes.data(); + const Bytecode* co_codes = frame->co->codes.data(); Bytecode byte; __NEXT_STEP: - frame->_ip = frame->_next_ip; - frame->_next_ip++; byte = co_codes[frame->_ip]; CEVAL_STEP_CALLBACK() @@ -139,13 +144,13 @@ __NEXT_STEP: SECOND() = THIRD(); THIRD() = _0; } DISPATCH() - case OP_PRINT_EXPR:{ + case OP_PRINT_EXPR: if(TOP() != None) stdout_write(py_repr(TOP()) + "\n"); POP(); - } DISPATCH() + DISPATCH() /*****************************************/ case OP_LOAD_CONST: - PUSH(co->consts[byte.arg]); + PUSH(frame->co->consts[byte.arg]); DISPATCH() case OP_LOAD_NONE: PUSH(None); DISPATCH() case OP_LOAD_TRUE: PUSH(True); DISPATCH() @@ -155,7 +160,7 @@ __NEXT_STEP: /*****************************************/ case OP_LOAD_ELLIPSIS: PUSH(Ellipsis); DISPATCH() case OP_LOAD_FUNCTION: { - const FuncDecl_& decl = co->func_decls[byte.arg]; + const FuncDecl_& decl = frame->co->func_decls[byte.arg]; PyVar obj; if(decl->nested){ NameDict_ captured = frame->_locals.to_namedict(); @@ -170,7 +175,7 @@ __NEXT_STEP: /*****************************************/ case OP_LOAD_FAST: { PyVar _0 = frame->_locals[byte.arg]; - if(_0 == PY_NULL) vm->UnboundLocalError(co->varnames[byte.arg]); + if(_0 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); PUSH(_0); } DISPATCH() case OP_LOAD_NAME: { @@ -239,7 +244,7 @@ __NEXT_STEP: } DISPATCH() case OP_LOAD_SUBSCR_FAST:{ PyVar _1 = frame->_locals[byte.arg]; - if(_1 == PY_NULL) vm->UnboundLocalError(co->varnames[byte.arg]); + if(_1 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); PyVar _0 = TOP(); // a auto _ti = _tp_info(_0); if(_ti->m__getitem__){ @@ -303,7 +308,7 @@ __NEXT_STEP: }DISPATCH() case OP_STORE_SUBSCR_FAST:{ PyVar _2 = frame->_locals[byte.arg]; // b - if(_2 == PY_NULL) vm->UnboundLocalError(co->varnames[byte.arg]); + if(_2 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); PyVar _1 = POPX(); // a PyVar _0 = POPX(); // val auto _ti = _tp_info(_1); @@ -315,7 +320,7 @@ __NEXT_STEP: }DISPATCH() case OP_DELETE_FAST:{ PyVar _0 = frame->_locals[byte.arg]; - if(_0 == PY_NULL) vm->UnboundLocalError(co->varnames[byte.arg]); + if(_0 == PY_NULL) vm->UnboundLocalError(frame->co->varnames[byte.arg]); frame->_locals[byte.arg] = PY_NULL; }DISPATCH() case OP_DELETE_NAME:{ @@ -626,49 +631,45 @@ __NEXT_STEP: } DISPATCH() /*****************************************/ case OP_JUMP_ABSOLUTE: - frame->jump_abs(byte.arg); - DISPATCH() + DISPATCH_JUMP(byte.arg) case OP_JUMP_ABSOLUTE_TOP: - frame->jump_abs(_CAST(int, POPX())); + DISPATCH_JUMP(_CAST(int, POPX())) + case OP_POP_JUMP_IF_FALSE: + if(!py_bool(POPX())) DISPATCH_JUMP(byte.arg) DISPATCH() - case OP_POP_JUMP_IF_FALSE:{ - if(!py_bool(TOP())) frame->jump_abs(byte.arg); - POP(); - } DISPATCH() - case OP_POP_JUMP_IF_TRUE:{ - if(py_bool(TOP())) frame->jump_abs(byte.arg); - POP(); - } DISPATCH() - case OP_JUMP_IF_TRUE_OR_POP:{ - if(py_bool(TOP())) frame->jump_abs(byte.arg); - else POP(); - } DISPATCH() - case OP_JUMP_IF_FALSE_OR_POP:{ - if(!py_bool(TOP())) frame->jump_abs(byte.arg); - else POP(); - } DISPATCH() - case OP_SHORTCUT_IF_FALSE_OR_POP:{ + case OP_POP_JUMP_IF_TRUE: + if(py_bool(POPX())) DISPATCH_JUMP(byte.arg) + DISPATCH() + case OP_JUMP_IF_TRUE_OR_POP: + if(py_bool(POPX())) DISPATCH_JUMP(byte.arg) + DISPATCH() + case OP_JUMP_IF_FALSE_OR_POP: + if(!py_bool(POPX())) DISPATCH_JUMP(byte.arg) + DISPATCH() + case OP_SHORTCUT_IF_FALSE_OR_POP: if(!py_bool(TOP())){ // [b, False] STACK_SHRINK(2); // [] PUSH(vm->False); // [False] - frame->jump_abs(byte.arg); - } else POP(); // [b] - } DISPATCH() + DISPATCH_JUMP(byte.arg) + } else{ + POP(); // [b] + DISPATCH() + } case OP_LOOP_CONTINUE: - frame->jump_abs(byte.arg); - DISPATCH() + DISPATCH_JUMP(byte.arg) case OP_LOOP_BREAK: - frame->jump_abs_break(&s_data, byte.arg); - DISPATCH() + frame->prepare_jump_break(&s_data, byte.arg); + DISPATCH_JUMP(byte.arg) case OP_GOTO: { StrName _name(byte.arg); - int index = co->labels.try_get_likely_found(_name); - if(index < 0) RuntimeError(_S("label ", _name.escape(), " not found")); - frame->jump_abs_break(&s_data, index); - } DISPATCH() + int target = frame->co->labels.try_get_likely_found(_name); + if(target < 0) RuntimeError(_S("label ", _name.escape(), " not found")); + frame->prepare_jump_break(&s_data, target); + DISPATCH_JUMP(target) + } /*****************************************/ case OP_FSTRING_EVAL:{ - PyVar _0 = co->consts[byte.arg]; + PyVar _0 = frame->co->consts[byte.arg]; std::string_view string = CAST(Str&, _0).sv(); auto it = __cached_codes.find(string); CodeObject_ code; @@ -743,8 +744,7 @@ __NEXT_STEP: goto __NEXT_FRAME; } } DISPATCH() - case OP_YIELD_VALUE: - return PY_OP_YIELD; + case OP_YIELD_VALUE: RETURN_OP_YIELD() /*****************************************/ case OP_LIST_APPEND:{ PyVar _0 = POPX(); @@ -785,34 +785,44 @@ __NEXT_STEP: DISPATCH() case OP_FOR_ITER:{ PyVar _0 = py_next(TOP()); - if(_0 == StopIteration) frame->loop_break(&s_data, co); - else PUSH(_0); - } DISPATCH() + if(_0 == StopIteration){ + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP(target) + } else{ + PUSH(_0); + DISPATCH() + } + } case OP_FOR_ITER_STORE_FAST:{ PyVar _0 = py_next(TOP()); if(_0 == StopIteration){ - frame->loop_break(&s_data, co); + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP(target) }else{ frame->_locals[byte.arg] = _0; + DISPATCH() } - } DISPATCH() + } case OP_FOR_ITER_STORE_GLOBAL:{ PyVar _0 = py_next(TOP()); if(_0 == StopIteration){ - frame->loop_break(&s_data, co); + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP(target) }else{ frame->f_globals().set(StrName(byte.arg), _0); + DISPATCH() } - } DISPATCH() + } case OP_FOR_ITER_YIELD_VALUE:{ PyVar _0 = py_next(TOP()); if(_0 == StopIteration){ - frame->loop_break(&s_data, co); + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP(target) }else{ PUSH(_0); - return PY_OP_YIELD; + RETURN_OP_YIELD() } - } DISPATCH() + } case OP_FOR_ITER_UNPACK:{ PyVar _0 = TOP(); const PyTypeInfo* _ti = _tp_info(_0); @@ -820,7 +830,8 @@ __NEXT_STEP: unsigned n = _ti->m__next__(this, _0); if(n == 0){ // StopIteration - frame->loop_break(&s_data, co); + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP(target) }else if(n == 1){ // UNPACK_SEQUENCE __op_unpack_sequence(byte.arg); @@ -837,13 +848,14 @@ __NEXT_STEP: // UNPACK_SEQUENCE __op_unpack_sequence(byte.arg); }else{ - frame->loop_break(&s_data, co); + int target = frame->prepare_loop_break(&s_data); + DISPATCH_JUMP(target) } } } DISPATCH() /*****************************************/ case OP_IMPORT_PATH:{ - PyVar _0 = co->consts[byte.arg]; + PyVar _0 = frame->co->consts[byte.arg]; PUSH(py_import(CAST(Str&, _0))); } DISPATCH() case OP_POP_IMPORT_STAR: { @@ -970,18 +982,18 @@ __NEXT_STEP: /*****************************************/ case OP_FORMAT_STRING: { PyVar _0 = POPX(); - const Str& spec = CAST(Str&, co->consts[byte.arg]); + const Str& spec = CAST(Str&, frame->co->consts[byte.arg]); PUSH(__format_object(_0, spec)); } DISPATCH() /*****************************************/ case OP_INC_FAST:{ PyVar* p = &frame->_locals[byte.arg]; - if(*p == PY_NULL) vm->NameError(co->varnames[byte.arg]); + if(*p == PY_NULL) vm->NameError(frame->co->varnames[byte.arg]); *p = VAR(CAST(i64, *p) + 1); } DISPATCH() case OP_DEC_FAST:{ PyVar* p = &frame->_locals[byte.arg]; - if(*p == PY_NULL) vm->NameError(co->varnames[byte.arg]); + if(*p == PY_NULL) vm->NameError(frame->co->varnames[byte.arg]); *p = VAR(CAST(i64, *p) - 1); } DISPATCH() case OP_INC_GLOBAL:{ @@ -1002,20 +1014,20 @@ __NEXT_STEP: } /**********************************************************************/ PK_UNREACHABLE(); - }catch(HandledException){ - continue; - }catch(UnhandledException){ - PyVar e_obj = POPX(); - Exception& _e = PK_OBJ_GET(Exception, e_obj); - bool is_base_frame_to_be_popped = frame == base_frame; - __pop_frame(); - if(callstack.empty()) throw _e; // propagate to the top level - frame = &callstack.top(); - PUSH(e_obj); - if(is_base_frame_to_be_popped) throw ToBeRaisedException(); - need_raise = true; - }catch(ToBeRaisedException){ - need_raise = true; + }catch(InternalException internal){ + this->__internal_exception = internal; + if(internal.type == InternalExceptionType::Unhandled){ + PyVar e_obj = POPX(); + Exception& _e = PK_OBJ_GET(Exception, e_obj); + bool is_base_frame_to_be_popped = frame == base_frame; + __pop_frame(); + if(callstack.empty()) throw _e; // propagate to the top level + frame = &callstack.top(); + PUSH(e_obj); + if(is_base_frame_to_be_popped){ + throw InternalException(InternalExceptionType::ToBeRaised); + } + } } } } @@ -1030,6 +1042,8 @@ __NEXT_STEP: #undef STACK_VIEW #undef DISPATCH +#undef DISPATCH_JUMP +#undef RETURN_OP_YIELD #undef CEVAL_STEP_CALLBACK } // namespace pkpy diff --git a/src/frame.cpp b/src/frame.cpp index cdb78b86..ca843cd7 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -23,22 +23,21 @@ namespace pkpy{ return fn._closure->try_get(name); } - bool Frame::jump_to_exception_handler(ValueStack* _s){ + int Frame::prepare_jump_exception_handler(ValueStack* _s){ // try to find a parent try block int block = co->iblocks[_ip]; while(block >= 0){ if(co->blocks[block].type == CodeBlockType::TRY_EXCEPT) break; block = co->blocks[block].parent; } - if(block < 0) return false; + if(block < 0) return -1; PyVar obj = _s->popx(); // pop exception object // get the stack size of the try block int _stack_size = co->blocks[block].base_stack_size; if(stack_size(_s) < _stack_size) throw std::runtime_error(_S("invalid state: ", stack_size(_s), '<', _stack_size).str()); _s->reset(actual_sp_base() + _locals.size() + _stack_size); // rollback the stack _s->push(obj); // push exception object - _next_ip = co->blocks[block].end; - return true; + return co->blocks[block].end; } int Frame::_exit_block(ValueStack* _s, int i){ @@ -47,10 +46,9 @@ namespace pkpy{ return co->blocks[i].parent; } - void Frame::jump_abs_break(ValueStack* _s, int target){ + void Frame::prepare_jump_break(ValueStack* _s, int target){ int i = co->iblocks[_ip]; - _next_ip = target; - if(_next_ip >= co->codes.size()){ + if(target >= co->codes.size()){ while(i>=0) i = _exit_block(_s, i); }else{ // BUG (solved) diff --git a/src/vm.cpp b/src/vm.cpp index 13485dd7..4039a77a 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -425,7 +425,7 @@ bool VM::__py_bool_non_trivial(PyVar obj){ PyVar len_f = get_unbound_method(obj, __len__, &self, false); if(self != PY_NULL){ PyVar ret = call_method(self, len_f); - return CAST(i64, ret) > 0; + return CAST(i64, ret) != 0; } return true; } @@ -1398,7 +1398,7 @@ void VM::__raise_exc(bool re_raise){ e._ip_on_error = frame->_ip; e._code_on_error = (void*)frame->co; } - bool ok = frame->jump_to_exception_handler(&s_data); + int next_ip = frame->prepare_jump_exception_handler(&s_data); int actual_ip = frame->_ip; if(e._ip_on_error >= 0 && e._code_on_error == (void*)frame->co) actual_ip = e._ip_on_error; @@ -1407,8 +1407,11 @@ void VM::__raise_exc(bool re_raise){ if(frame->_callable == nullptr) current_f_name = ""; // not in a function e.st_push(frame->co->src, current_line, nullptr, current_f_name); - if(ok) throw HandledException(); - else throw UnhandledException(); + if(next_ip >= 0){ + throw InternalException(InternalExceptionType::Handled, next_ip); + }else{ + throw InternalException(InternalExceptionType::Unhandled); + } } void ManagedHeap::mark() {