From 833bef370a892e50ad3a24aff5ea7656f87a6ae0 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 14 Nov 2022 23:51:11 +0800 Subject: [PATCH] fix goto issues --- src/codeobject.h | 33 ++++++++++++++++++++++++++++----- src/compiler.h | 16 +++++++--------- src/opcodes.h | 1 + src/vm.h | 16 ++++++++-------- tests/5.py | 9 +++++++++ 5 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 tests/5.py diff --git a/src/codeobject.h b/src/codeobject.h index 25d2270b..f3353c5d 100644 --- a/src/codeobject.h +++ b/src/codeobject.h @@ -116,6 +116,7 @@ class Frame { private: std::vector s_data; int ip = 0; + std::stack forLoops; // record the FOR_ITER bytecode index public: PyVar _module; PyVarDict f_locals; @@ -152,10 +153,6 @@ public: return v; } - void __clearDataStack(){ - s_data.clear(); - } - inline PyVar __deref_pointer(VM*, PyVar); inline PyVar popValue(VM* vm){ @@ -174,10 +171,36 @@ public: s_data.push_back(v); } - inline void jumpTo(int i){ + + void __reportForIter(){ + int lastIp = ip - 1; + if(forLoops.empty()) forLoops.push(lastIp); + else{ + if(forLoops.top() == lastIp) return; + if(forLoops.top() < lastIp) forLoops.push(lastIp); + else UNREACHABLE(); + } + } + + inline void jump(int i){ this->ip = i; } + void safeJump(int i){ + this->ip = i; + while(!forLoops.empty()){ + int start = forLoops.top(); + int end = code->co_code[start].arg; + if(i < start || i >= end){ + //printf("%d <- [%d, %d)\n", i, start, end); + __pop(); // pop the iterator + forLoops.pop(); + }else{ + break; + } + } + } + PyVarList popNValuesReversed(VM* vm, int n){ PyVarList v(n); for(int i=n-1; i>=0; i--) v[i] = popValue(vm); diff --git a/src/compiler.h b/src/compiler.h index 1f060558..2f6fb423 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -16,10 +16,9 @@ struct GrammarRule{ }; struct Loop { - bool forLoop; int start; std::vector breaks; - Loop(bool forLoop, int start) : forLoop(forLoop), start(start) {} + Loop(int start) : start(start) {} }; class Compiler { @@ -484,7 +483,7 @@ __LISTCOMP: patchJump(_skipPatch); emitCode(OP_GET_ITER); - Loop& loop = enterLoop(true); + Loop& loop = enterLoop(); int patch = emitCode(OP_FOR_ITER); if(_cond_end != _cond_start) { // there is an if condition @@ -682,8 +681,8 @@ __LISTCOMP: } } - Loop& enterLoop(bool forLoop){ - Loop lp(forLoop, (int)getCode()->co_code.size()); + Loop& enterLoop(){ + Loop lp((int)getCode()->co_code.size()); loops.push(lp); return loops.top(); } @@ -695,7 +694,7 @@ __LISTCOMP: } void compileWhileLoop() { - Loop& loop = enterLoop(false); + Loop& loop = enterLoop(); EXPR_TUPLE(); int patch = emitCode(OP_POP_JUMP_IF_FALSE); compileBlockBody(); @@ -716,7 +715,7 @@ __LISTCOMP: void compileForLoop() { EXPR_FOR_VARS();consume(TK("in"));EXPR_TUPLE(); emitCode(OP_GET_ITER); - Loop& loop = enterLoop(true); + Loop& loop = enterLoop(); int patch = emitCode(OP_FOR_ITER); compileBlockBody(); emitCode(OP_JUMP_ABSOLUTE, loop.start); keepOpcodeLine(); @@ -728,8 +727,7 @@ __LISTCOMP: if (match(TK("break"))) { if (loops.empty()) syntaxError("'break' outside loop"); consumeEndStatement(); - if(getLoop().forLoop) emitCode(OP_POP_TOP); // pop the iterator of for loop. - int patch = emitCode(OP_JUMP_ABSOLUTE); + int patch = emitCode(OP_SAFE_JUMP_ABSOLUTE); getLoop().breaks.push_back(patch); } else if (match(TK("continue"))) { if (loops.empty()) syntaxError("'continue' not properly in loop"); diff --git a/src/opcodes.h b/src/opcodes.h index e9d143ac..b7cf01d5 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -30,6 +30,7 @@ OPCODE(FOR_ITER) OPCODE(POP_JUMP_IF_FALSE) OPCODE(JUMP_ABSOLUTE) +OPCODE(SAFE_JUMP_ABSOLUTE) OPCODE(JUMP_IF_TRUE_OR_POP) OPCODE(JUMP_IF_FALSE_OR_POP) diff --git a/src/vm.h b/src/vm.h index 5779a894..b9f186b2 100644 --- a/src/vm.h +++ b/src/vm.h @@ -207,7 +207,7 @@ private: frame->push(PyBool(!PyBool_AS_C(obj_bool))); } break; case OP_POP_JUMP_IF_FALSE: - if(!PyBool_AS_C(asBool(frame->popValue(this)))) frame->jumpTo(byte.arg); + if(!PyBool_AS_C(asBool(frame->popValue(this)))) frame->jump(byte.arg); break; case OP_LOAD_NONE: frame->push(None); break; case OP_LOAD_TRUE: frame->push(True); break; @@ -246,7 +246,8 @@ private: if(ret == __py2py_call_signal) return ret; frame->push(ret); } break; - case OP_JUMP_ABSOLUTE: frame->jumpTo(byte.arg); break; + case OP_JUMP_ABSOLUTE: frame->jump(byte.arg); break; + case OP_SAFE_JUMP_ABSOLUTE: frame->safeJump(byte.arg); break; case OP_GOTO: { PyVar obj = frame->popValue(this); const _Str& label = PyStr_AS_C(obj); @@ -254,8 +255,7 @@ private: if(it == frame->code->co_labels.end()){ _error("KeyError", "label '" + label + "' not found"); } - frame->__clearDataStack(); - frame->jumpTo(it->second); + frame->safeJump(it->second); } break; case OP_GET_ITER: { @@ -271,26 +271,26 @@ private: } break; case OP_FOR_ITER: { + frame->__reportForIter(); const PyVar& iter = frame->topValue(this); auto& it = PyIter_AS_C(iter); if(it->hasNext()){ it->var->set(this, frame, it->next()); } else{ - frame->popValue(this); - frame->jumpTo(byte.arg); + frame->safeJump(byte.arg); } } break; case OP_JUMP_IF_FALSE_OR_POP: { const PyVar& expr = frame->topValue(this); - if(asBool(expr)==False) frame->jumpTo(byte.arg); + if(asBool(expr)==False) frame->jump(byte.arg); else frame->popValue(this); } break; case OP_JUMP_IF_TRUE_OR_POP: { const PyVar& expr = frame->topValue(this); - if(asBool(expr)==True) frame->jumpTo(byte.arg); + if(asBool(expr)==True) frame->jump(byte.arg); else frame->popValue(this); } break; case OP_BUILD_SLICE: diff --git a/tests/5.py b/tests/5.py new file mode 100644 index 00000000..8aad0036 --- /dev/null +++ b/tests/5.py @@ -0,0 +1,9 @@ +for i in range(10): + for j in range(10): + goto .test + print(2) + label .test + print(i) + +# 15, 23 +# 5, 28 \ No newline at end of file