blueloveTH před 2 roky
rodič
revize
684ebe8e79
9 změnil soubory, kde provedl 94 přidání a 63 odebrání
  1. 36 32
      src/ceval.h
  2. 5 1
      src/codeobject.h
  3. 8 2
      src/common.h
  4. 0 2
      src/compiler.h
  5. 6 3
      src/expr.h
  6. 16 21
      src/frame.h
  7. 0 2
      src/opcodes.h
  8. 5 0
      src/vector.h
  9. 18 0
      tests/41_exception.py

+ 36 - 32
src/ceval.h

@@ -18,42 +18,37 @@ inline PyObject* VM::_run_top_frame(){
         try{
             if(need_raise){ need_raise = false; _raise(); }
 /**********************************************************************/
-#define USE_COMPUTED_GOTO 0
+/* 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`
+ */
+{
+    Bytecode byte = frame->next_bytecode();
 
-#if USE_COMPUTED_GOTO
+#if PK_ENABLE_COMPUTED_GOTO
 static void* OP_LABELS[] = {
     #define OPCODE(name) &&CASE_OP_##name,
     #include "opcodes.h"
     #undef OPCODE
 };
 
-#define DISPATCH() {heap._auto_collect(); byte = frame->next_bytecode(); goto *OP_LABELS[byte.op];}
-#define PREDICTED_DISPATCH(x) {heap._auto_collect(); byte = frame->next_bytecode(); if(byte.op == OP_##x) goto CASE_OP_##x; goto *OP_LABELS[byte.op];}
-#else
-#define DISPATCH() {heap._auto_collect(); byte = frame->next_bytecode(); goto __NEXT_STEP;}
-#define PREDICTED_DISPATCH(x) {heap._auto_collect(); byte = frame->next_bytecode(); if(byte.op == OP_##x) goto CASE_OP_##x; goto __NEXT_STEP;}
-#endif
+#define DISPATCH() { byte = frame->next_bytecode(); goto *OP_LABELS[byte.op];}
+#define TARGET(op) CASE_OP_##op:
+goto *OP_LABELS[byte.op];
 
-#define TARGET(op) case OP_##op:    \
-    CASE_OP_##op:
+#else
+#define TARGET(op) case OP_##op:
+#define DISPATCH() { byte = frame->next_bytecode(); goto __NEXT_STEP;}
 
-{
-    Bytecode byte = frame->next_bytecode();
-#if !USE_COMPUTED_GOTO
 __NEXT_STEP:;
-#endif
-    /* 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`
-    */
 #if DEBUG_CEVAL_STEP
     std::cout << frame->stack_info() << " " << OP_NAMES[byte.op] << std::endl;
 #endif
-
     switch (byte.op)
     {
+#endif
     TARGET(NO_OP) DISPATCH();
     /*****************************************/
     TARGET(POP_TOP) frame->pop(); DISPATCH();
@@ -65,7 +60,10 @@ __NEXT_STEP:;
         frame->pop();
     } DISPATCH();
     /*****************************************/
-    TARGET(LOAD_CONST) frame->push(frame->co->consts[byte.arg]); DISPATCH();
+    TARGET(LOAD_CONST)
+        heap._auto_collect();
+        frame->push(frame->co->consts[byte.arg]);
+        DISPATCH();
     TARGET(LOAD_NONE) frame->push(None); DISPATCH();
     TARGET(LOAD_TRUE) frame->push(True); DISPATCH();
     TARGET(LOAD_FALSE) frame->push(False); DISPATCH();
@@ -79,6 +77,7 @@ __NEXT_STEP:;
     TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH();
     /*****************************************/
     TARGET(LOAD_NAME) {
+        heap._auto_collect();
         StrName name = frame->co->names[byte.arg];
         PyObject* val;
         val = frame->f_locals().try_get(name);
@@ -92,6 +91,7 @@ __NEXT_STEP:;
         vm->NameError(name);
     } DISPATCH();
     TARGET(LOAD_GLOBAL) {
+        heap._auto_collect();
         StrName name = frame->co->names[byte.arg];
         PyObject* val = frame->f_globals().try_get(name);
         if(val != nullptr) { frame->push(val); DISPATCH(); }
@@ -239,22 +239,22 @@ __NEXT_STEP:;
         DISPATCH()
     TARGET(COMPARE_LT)
         INT_BINARY_OP(<, __lt__)
-        PREDICTED_DISPATCH(POP_JUMP_IF_FALSE)
+        DISPATCH()
     TARGET(COMPARE_LE)
         INT_BINARY_OP(<=, __le__)
-        PREDICTED_DISPATCH(POP_JUMP_IF_FALSE)
+        DISPATCH()
     TARGET(COMPARE_EQ)
         INT_BINARY_OP(==, __eq__)
-        PREDICTED_DISPATCH(POP_JUMP_IF_FALSE)
+        DISPATCH()
     TARGET(COMPARE_NE)
         INT_BINARY_OP(!=, __ne__)
-        PREDICTED_DISPATCH(POP_JUMP_IF_FALSE)
+        DISPATCH()
     TARGET(COMPARE_GT)
         INT_BINARY_OP(>, __gt__)
-        PREDICTED_DISPATCH(POP_JUMP_IF_FALSE)
+        DISPATCH()
     TARGET(COMPARE_GE)
         INT_BINARY_OP(>=, __ge__)
-        PREDICTED_DISPATCH(POP_JUMP_IF_FALSE)
+        DISPATCH()
     TARGET(BITWISE_LSHIFT)
         INT_BINARY_OP(<<, __lshift__)
         DISPATCH()
@@ -469,9 +469,6 @@ __NEXT_STEP:;
     // TARGET(WITH_ENTER) call(frame->pop_value(this), __enter__, no_arg()); DISPATCH();
     // TARGET(WITH_EXIT) call(frame->pop_value(this), __exit__, no_arg()); DISPATCH();
     /*****************************************/
-    TARGET(TRY_BLOCK_ENTER) frame->on_try_block_enter(); DISPATCH();
-    TARGET(TRY_BLOCK_EXIT) frame->on_try_block_exit(); DISPATCH();
-    /*****************************************/
     TARGET(ASSERT) {
         PyObject* obj = frame->top();
         Str msg;
@@ -497,10 +494,17 @@ __NEXT_STEP:;
         _error(type, msg);
     } DISPATCH();
     TARGET(RE_RAISE) _raise(); DISPATCH();
+#if !PK_ENABLE_COMPUTED_GOTO
+#if DEBUG_EXTRA_CHECK
     default: throw std::runtime_error(fmt(OP_NAMES[byte.op], " is not implemented"));
+#else
+    default: __builtin_unreachable();
+#endif
     }
-    UNREACHABLE();
+#endif
 }
+
+
 #undef DISPATCH
 #undef TARGET
 /**********************************************************************/

+ 5 - 1
src/codeobject.h

@@ -39,8 +39,12 @@ enum CodeBlockType {
 struct CodeBlock {
     CodeBlockType type;
     int parent;         // parent index in blocks
+    int for_loop_depth; // this is used for exception handling
     int start;          // start index of this block in codes, inclusive
     int end;            // end index of this block in codes, exclusive
+
+    CodeBlock(CodeBlockType type, int parent, int for_loop_depth, int start):
+        type(type), parent(parent), for_loop_depth(for_loop_depth), start(start), end(-1) {}
 };
 
 struct CodeObject {
@@ -58,7 +62,7 @@ struct CodeObject {
     List consts;
     std::vector<StrName> names;
     std::set<Str> global_names;
-    std::vector<CodeBlock> blocks = { CodeBlock{NO_BLOCK, -1} };
+    std::vector<CodeBlock> blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) };
     std::map<StrName, int> labels;
     std::vector<FuncDecl_> func_decls;
 

+ 8 - 2
src/common.h

@@ -44,9 +44,15 @@
 #define DEBUG_GC_STATS				0
 
 #if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__)
-#define PK_ENABLE_FILEIO 		0
+#define PK_ENABLE_FILEIO 			0
 #else
-#define PK_ENABLE_FILEIO 		0	// TODO: refactor this
+#define PK_ENABLE_FILEIO 			0	// TODO: refactor this
+#endif
+
+#if _MSC_VER
+#define PK_ENABLE_COMPUTED_GOTO		0
+#else
+#define PK_ENABLE_COMPUTED_GOTO		1
 #endif
 
 #if defined(__EMSCRIPTEN__) || defined(__arm__) || defined(__i386__)

+ 0 - 2
src/compiler.h

@@ -607,9 +607,7 @@ __SUBSCR_END:
 
     void compile_try_except() {
         ctx()->enter_block(TRY_EXCEPT);
-        ctx()->emit(OP_TRY_BLOCK_ENTER, BC_NOARG, prev().line);
         compile_block_body();
-        ctx()->emit(OP_TRY_BLOCK_EXIT, BC_NOARG, BC_KEEPLINE);
         std::vector<int> patches = {
             ctx()->emit(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE)
         };

+ 6 - 3
src/expr.h

@@ -37,19 +37,22 @@ struct CodeEmitContext{
 
     int curr_block_i = 0;
     bool is_compiling_class = false;
+    int for_loop_depth = 0;
 
     bool is_curr_block_loop() const {
         return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP;
     }
 
     void enter_block(CodeBlockType type){
-        co->blocks.push_back(CodeBlock{
-            type, curr_block_i, (int)co->codes.size()
-        });
+        if(type == FOR_LOOP) for_loop_depth++;
+        co->blocks.push_back(CodeBlock(
+            type, curr_block_i, for_loop_depth, (int)co->codes.size()
+        ));
         curr_block_i = co->blocks.size()-1;
     }
 
     void exit_block(){
+        if(co->blocks[curr_block_i].type == FOR_LOOP) for_loop_depth--;
         co->blocks[curr_block_i].end = co->codes.size();
         curr_block_i = co->blocks[curr_block_i].parent;
         if(curr_block_i < 0) UNREACHABLE();

+ 16 - 21
src/frame.h

@@ -17,7 +17,6 @@ struct Frame {
     PyObject* _module;
     NameDict_ _locals;
     NameDict_ _closure;
-    std::vector<std::pair<int, ValueStack>> s_try_block;
 
     NameDict& f_locals() noexcept { return _locals!=nullptr ? *_locals : _module->attr(); }
     NameDict& f_globals() noexcept { return _module->attr(); }
@@ -104,28 +103,27 @@ struct Frame {
     void jump_abs(int i){ _next_ip = i; }
     void jump_rel(int i){ _next_ip += i; }
 
-    void on_try_block_enter(){
-        s_try_block.emplace_back(co->codes[_ip].block, _data);
-    }
-
-    void on_try_block_exit(){
-        s_try_block.pop_back();
-    }
-
     bool jump_to_exception_handler(){
-        if(s_try_block.empty()) return false;
-        PyObject* obj = popx();
-        auto& p = s_try_block.back();
-        _data = std::move(p.second);
-        _data.push_back(obj);
-        _next_ip = co->blocks[p.first].end;
-        on_try_block_exit();
+        // try to find a parent try block
+        int block = co->codes[_ip].block;
+        while(block >= 0){
+            if(co->blocks[block].type == TRY_EXCEPT) break;
+            block = co->blocks[block].parent;
+        }
+        if(block < 0) return false;
+        PyObject* obj = popx();         // pop exception object
+        // get the stack size of the try block (depth of for loops)
+        int stack_size = co->blocks[block].for_loop_depth;
+        // std::cout << "stack_size: " << stack_size << std::endl;
+        if(_data.size() < stack_size) throw std::runtime_error("invalid stack size");
+        _data.resize(stack_size);       // rollback the stack
+        _data.push_back(obj);           // push exception object
+        _next_ip = co->blocks[block].end;
         return true;
     }
 
     int _exit_block(int i){
         if(co->blocks[i].type == FOR_LOOP) pop();
-        else if(co->blocks[i].type == TRY_EXCEPT) on_try_block_exit();
         return co->blocks[i].parent;
     }
 
@@ -153,15 +151,12 @@ struct Frame {
     }
 
     void _gc_mark() const {
-        // this frame has been moved
+        // do return if this frame has been moved
         if(_data._data == nullptr) return;
         for(PyObject* obj : _data) OBJ_MARK(obj);
         OBJ_MARK(_module);
         if(_locals != nullptr) _locals->_gc_mark();
         if(_closure != nullptr) _closure->_gc_mark();
-        for(auto& p : s_try_block){
-            for(PyObject* obj : p.second) OBJ_MARK(obj);
-        }
         co->_gc_mark();
     }
 };

+ 0 - 2
src/opcodes.h

@@ -102,8 +102,6 @@ OPCODE(STORE_CLASS_ATTR)
 // OPCODE(WITH_ENTER)
 // OPCODE(WITH_EXIT)
 /**************************/
-OPCODE(TRY_BLOCK_ENTER)
-OPCODE(TRY_BLOCK_EXIT)
 OPCODE(ASSERT)
 OPCODE(EXCEPTION_MATCH)
 OPCODE(RAISE)

+ 5 - 0
src/vector.h

@@ -105,6 +105,11 @@ struct pod_vector{
         _size--;
     }
 
+    void resize(int size){
+        if(size > _capacity) reserve(size);
+        _size = size;
+    }
+
     ~pod_vector() {
         if(_data!=nullptr) pool128.dealloc(_data);
     }

+ 18 - 0
tests/41_exception.py

@@ -1,3 +1,21 @@
+try:
+    for i in range(5):
+        raise KeyError(i)
+    exit(1)
+except KeyError:
+    pass
+
+x = 0
+for i in range(5):
+    try:
+        for j in range(5):
+            while True:
+                raise KeyError(i)
+                x += 3
+    except KeyError:
+        x += i
+assert x == 10
+
 class A:
     def __getitem__(self, i):
         raise KeyError(i)