blueloveTH 8 месяцев назад
Родитель
Сommit
0a44220f66
1 измененных файлов с 1008 добавлено и 1013 удалено
  1. 1008 1013
      src/interpreter/ceval.c

+ 1008 - 1013
src/interpreter/ceval.c

@@ -88,524 +88,523 @@ FrameResult VM__run_top_frame(VM* self) {
     py_Frame* frame = self->top_frame;
     Bytecode* co_codes;
     py_Name* co_names;
+    Bytecode byte;
 
     const py_Frame* base_frame = frame;
 
-    while(true) {
-        Bytecode byte;
-    __NEXT_FRAME:
-        if(self->recursion_depth >= self->max_recursion_depth) {
-            py_exception(tp_RecursionError, "maximum recursion depth exceeded");
-            goto __ERROR;
-        }
-        RESET_CO_CACHE();
-        frame->ip++;
-
-    __NEXT_STEP:
-        byte = co_codes[frame->ip];
-
-        if(self->trace_info.func) {
-            SourceLocation loc = Frame__source_location(frame);
-            SourceLocation prev_loc = self->trace_info.prev_loc;
-            if(loc.lineno != prev_loc.lineno || loc.src != prev_loc.src) {
-                if(prev_loc.src) PK_DECREF(prev_loc.src);
-                PK_INCREF(loc.src);
-                self->trace_info.prev_loc = loc;
-                self->trace_info.func(frame, TRACE_EVENT_LINE);
-            }
+__NEXT_FRAME:
+    if(self->recursion_depth >= self->max_recursion_depth) {
+        py_exception(tp_RecursionError, "maximum recursion depth exceeded");
+        goto __ERROR;
+    }
+    RESET_CO_CACHE();
+    frame->ip++;
+
+__NEXT_STEP:
+    byte = co_codes[frame->ip];
+
+    if(self->trace_info.func) {
+        SourceLocation loc = Frame__source_location(frame);
+        SourceLocation prev_loc = self->trace_info.prev_loc;
+        if(loc.lineno != prev_loc.lineno || loc.src != prev_loc.src) {
+            if(prev_loc.src) PK_DECREF(prev_loc.src);
+            PK_INCREF(loc.src);
+            self->trace_info.prev_loc = loc;
+            self->trace_info.func(frame, TRACE_EVENT_LINE);
         }
+    }
 
 #if PK_ENABLE_WATCHDOG
-        if(self->watchdog_info.max_reset_time > 0) {
-            clock_t now = clock();
-            if(now > self->watchdog_info.max_reset_time) {
-                self->watchdog_info.max_reset_time = 0;
-                TimeoutError("watchdog timeout");
-                goto __ERROR;
-            }
+    if(self->watchdog_info.max_reset_time > 0) {
+        clock_t now = clock();
+        if(now > self->watchdog_info.max_reset_time) {
+            self->watchdog_info.max_reset_time = 0;
+            TimeoutError("watchdog timeout");
+            goto __ERROR;
         }
+    }
 #endif
 
 #ifndef NDEBUG
-        pk_print_stack(self, frame, byte);
+    pk_print_stack(self, frame, byte);
 #endif
 
-        switch((Opcode)byte.op) {
-            case OP_NO_OP: DISPATCH();
-            /*****************************************/
-            case OP_POP_TOP: POP(); DISPATCH();
-            case OP_DUP_TOP: PUSH(TOP()); DISPATCH();
-            case OP_DUP_TOP_TWO:
-                // [a, b]
-                PUSH(SECOND());  // [a, b, a]
-                PUSH(SECOND());  // [a, b, a, b]
-                DISPATCH();
-            case OP_ROT_TWO: {
-                py_TValue tmp = *TOP();
-                *TOP() = *SECOND();
-                *SECOND() = tmp;
-                DISPATCH();
+    switch((Opcode)byte.op) {
+        case OP_NO_OP: DISPATCH();
+        /*****************************************/
+        case OP_POP_TOP: POP(); DISPATCH();
+        case OP_DUP_TOP: PUSH(TOP()); DISPATCH();
+        case OP_DUP_TOP_TWO:
+            // [a, b]
+            PUSH(SECOND());  // [a, b, a]
+            PUSH(SECOND());  // [a, b, a, b]
+            DISPATCH();
+        case OP_ROT_TWO: {
+            py_TValue tmp = *TOP();
+            *TOP() = *SECOND();
+            *SECOND() = tmp;
+            DISPATCH();
+        }
+        case OP_ROT_THREE: {
+            // [a, b, c] -> [c, a, b]
+            py_TValue tmp = *TOP();
+            *TOP() = *SECOND();
+            *SECOND() = *THIRD();
+            *THIRD() = tmp;
+            DISPATCH();
+        }
+        case OP_PRINT_EXPR:
+            if(TOP()->type != tp_NoneType) {
+                bool ok = py_repr(TOP());
+                if(!ok) goto __ERROR;
+                self->callbacks.print(py_tostr(&self->last_retval));
+                self->callbacks.print("\n");
+            }
+            POP();
+            DISPATCH();
+        /*****************************************/
+        case OP_LOAD_CONST: {
+            PUSH(c11__at(py_TValue, &frame->co->consts, byte.arg));
+            DISPATCH();
+        }
+        case OP_LOAD_NONE: {
+            py_newnone(SP()++);
+            DISPATCH();
+        }
+        case OP_LOAD_TRUE: {
+            py_newbool(SP()++, true);
+            DISPATCH();
+        }
+        case OP_LOAD_FALSE: {
+            py_newbool(SP()++, false);
+            DISPATCH();
+        }
+        /*****************************************/
+        case OP_LOAD_SMALL_INT: {
+            py_newint(SP()++, (int16_t)byte.arg);
+            DISPATCH();
+        }
+        /*****************************************/
+        case OP_LOAD_ELLIPSIS: {
+            py_newellipsis(SP()++);
+            DISPATCH();
+        }
+        case OP_LOAD_FUNCTION: {
+            FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg);
+            Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function));
+            Function__ctor(ud, decl, frame->module, frame->globals);
+            if(decl->nested) {
+                if(frame->is_locals_special) {
+                    RuntimeError("cannot create closure from special locals");
+                    goto __ERROR;
+                }
+                ud->closure = FastLocals__to_namedict(frame->locals, frame->co);
+                py_Name name = py_name(decl->code.name->data);
+                // capture itself to allow recursion
+                NameDict__set(ud->closure, name, SP());
             }
-            case OP_ROT_THREE: {
-                // [a, b, c] -> [c, a, b]
-                py_TValue tmp = *TOP();
-                *TOP() = *SECOND();
-                *SECOND() = *THIRD();
-                *THIRD() = tmp;
+            SP()++;
+            DISPATCH();
+        }
+        case OP_LOAD_NULL:
+            py_newnil(SP()++);
+            DISPATCH();
+            /*****************************************/
+        case OP_LOAD_FAST: {
+            assert(!frame->is_locals_special);
+            py_Ref val = &frame->locals[byte.arg];
+            if(!py_isnil(val)) {
+                PUSH(val);
                 DISPATCH();
             }
-            case OP_PRINT_EXPR:
-                if(TOP()->type != tp_NoneType) {
-                    bool ok = py_repr(TOP());
-                    if(!ok) goto __ERROR;
-                    self->callbacks.print(py_tostr(&self->last_retval));
-                    self->callbacks.print("\n");
+            py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg);
+            UnboundLocalError(name);
+            goto __ERROR;
+        }
+        case OP_LOAD_NAME: {
+            assert(frame->is_locals_special);
+            py_Name name = co_names[byte.arg];
+            // locals
+            switch(frame->locals->type) {
+                case tp_locals: {
+                    py_Frame* noproxy = frame->locals->_ptr;
+                    py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
+                    if(slot == NULL) break;
+                    if(py_isnil(slot)) {
+                        UnboundLocalError(name);
+                        goto __ERROR;
+                    }
+                    PUSH(slot);
+                    DISPATCH();
                 }
-                POP();
-                DISPATCH();
-            /*****************************************/
-            case OP_LOAD_CONST: {
-                PUSH(c11__at(py_TValue, &frame->co->consts, byte.arg));
-                DISPATCH();
+                case tp_dict: {
+                    int res = py_dict_getitem(frame->locals, py_name2ref(name));
+                    if(res == 1) {
+                        PUSH(&self->last_retval);
+                        DISPATCH();
+                    }
+                    if(res == 0) break;
+                    assert(res == -1);
+                    goto __ERROR;
+                }
+                case tp_nil: break;
+                default: c11__unreachable();
             }
-            case OP_LOAD_NONE: {
-                py_newnone(SP()++);
+            // globals
+            int res = Frame__getglobal(frame, name);
+            if(res == 1) {
+                PUSH(&self->last_retval);
                 DISPATCH();
             }
-            case OP_LOAD_TRUE: {
-                py_newbool(SP()++, true);
+            if(res == -1) goto __ERROR;
+            // builtins
+            py_Ref tmp = py_getdict(self->builtins, name);
+            if(tmp != NULL) {
+                PUSH(tmp);
                 DISPATCH();
             }
-            case OP_LOAD_FALSE: {
-                py_newbool(SP()++, false);
+            NameError(name);
+            goto __ERROR;
+        }
+        case OP_LOAD_NONLOCAL: {
+            py_Name name = co_names[byte.arg];
+            py_Ref tmp = Frame__getclosure(frame, name);
+            if(tmp != NULL) {
+                PUSH(tmp);
                 DISPATCH();
             }
-            /*****************************************/
-            case OP_LOAD_SMALL_INT: {
-                py_newint(SP()++, (int16_t)byte.arg);
+            int res = Frame__getglobal(frame, name);
+            if(res == 1) {
+                PUSH(&self->last_retval);
                 DISPATCH();
             }
-            /*****************************************/
-            case OP_LOAD_ELLIPSIS: {
-                py_newellipsis(SP()++);
+            if(res == -1) goto __ERROR;
+
+            tmp = py_getdict(self->builtins, name);
+            if(tmp != NULL) {
+                PUSH(tmp);
                 DISPATCH();
             }
-            case OP_LOAD_FUNCTION: {
-                FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg);
-                Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function));
-                Function__ctor(ud, decl, frame->module, frame->globals);
-                if(decl->nested) {
-                    if(frame->is_locals_special) {
-                        RuntimeError("cannot create closure from special locals");
-                        goto __ERROR;
-                    }
-                    ud->closure = FastLocals__to_namedict(frame->locals, frame->co);
-                    py_Name name = py_name(decl->code.name->data);
-                    // capture itself to allow recursion
-                    NameDict__set(ud->closure, name, SP());
-                }
-                SP()++;
+            NameError(name);
+            goto __ERROR;
+        }
+        case OP_LOAD_GLOBAL: {
+            py_Name name = co_names[byte.arg];
+            int res = Frame__getglobal(frame, name);
+            if(res == 1) {
+                PUSH(&self->last_retval);
                 DISPATCH();
             }
-            case OP_LOAD_NULL:
-                py_newnil(SP()++);
+            if(res == -1) goto __ERROR;
+            py_Ref tmp = py_getdict(self->builtins, name);
+            if(tmp != NULL) {
+                PUSH(tmp);
                 DISPATCH();
-                /*****************************************/
-            case OP_LOAD_FAST: {
-                assert(!frame->is_locals_special);
-                py_Ref val = &frame->locals[byte.arg];
-                if(!py_isnil(val)) {
-                    PUSH(val);
-                    DISPATCH();
-                }
-                py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg);
-                UnboundLocalError(name);
-                goto __ERROR;
             }
-            case OP_LOAD_NAME: {
-                assert(frame->is_locals_special);
-                py_Name name = co_names[byte.arg];
-                // locals
-                switch(frame->locals->type) {
-                    case tp_locals: {
-                        py_Frame* noproxy = frame->locals->_ptr;
-                        py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
-                        if(slot == NULL) break;
-                        if(py_isnil(slot)) {
-                            UnboundLocalError(name);
-                            goto __ERROR;
-                        }
-                        PUSH(slot);
-                        DISPATCH();
-                    }
-                    case tp_dict: {
-                        int res = py_dict_getitem(frame->locals, py_name2ref(name));
-                        if(res == 1) {
-                            PUSH(&self->last_retval);
-                            DISPATCH();
-                        }
-                        if(res == 0) break;
-                        assert(res == -1);
-                        goto __ERROR;
-                    }
-                    case tp_nil: break;
-                    default: c11__unreachable();
-                }
-                // globals
-                int res = Frame__getglobal(frame, name);
-                if(res == 1) {
-                    PUSH(&self->last_retval);
-                    DISPATCH();
-                }
-                if(res == -1) goto __ERROR;
-                // builtins
-                py_Ref tmp = py_getdict(self->builtins, name);
-                if(tmp != NULL) {
-                    PUSH(tmp);
-                    DISPATCH();
-                }
-                NameError(name);
+            NameError(name);
+            goto __ERROR;
+        }
+        case OP_LOAD_ATTR: {
+            py_Name name = co_names[byte.arg];
+            if(py_getattr(TOP(), name)) {
+                py_assign(TOP(), py_retval());
+            } else {
                 goto __ERROR;
             }
-            case OP_LOAD_NONLOCAL: {
-                py_Name name = co_names[byte.arg];
-                py_Ref tmp = Frame__getclosure(frame, name);
-                if(tmp != NULL) {
-                    PUSH(tmp);
-                    DISPATCH();
-                }
-                int res = Frame__getglobal(frame, name);
-                if(res == 1) {
-                    PUSH(&self->last_retval);
-                    DISPATCH();
-                }
-                if(res == -1) goto __ERROR;
-
-                tmp = py_getdict(self->builtins, name);
-                if(tmp != NULL) {
-                    PUSH(tmp);
-                    DISPATCH();
-                }
-                NameError(name);
-                goto __ERROR;
+            DISPATCH();
+        }
+        case OP_LOAD_CLASS_GLOBAL: {
+            assert(self->curr_class);
+            py_Name name = co_names[byte.arg];
+            py_Ref tmp = py_getdict(self->curr_class, name);
+            if(tmp) {
+                PUSH(tmp);
+                DISPATCH();
             }
-            case OP_LOAD_GLOBAL: {
-                py_Name name = co_names[byte.arg];
-                int res = Frame__getglobal(frame, name);
-                if(res == 1) {
-                    PUSH(&self->last_retval);
-                    DISPATCH();
-                }
-                if(res == -1) goto __ERROR;
-                py_Ref tmp = py_getdict(self->builtins, name);
-                if(tmp != NULL) {
-                    PUSH(tmp);
-                    DISPATCH();
-                }
-                NameError(name);
-                goto __ERROR;
+            // load global if attribute not found
+            int res = Frame__getglobal(frame, name);
+            if(res == 1) {
+                PUSH(&self->last_retval);
+                DISPATCH();
             }
-            case OP_LOAD_ATTR: {
-                py_Name name = co_names[byte.arg];
+            if(res == -1) goto __ERROR;
+            tmp = py_getdict(self->builtins, name);
+            if(tmp) {
+                PUSH(tmp);
+                DISPATCH();
+            }
+            NameError(name);
+            goto __ERROR;
+        }
+        case OP_LOAD_METHOD: {
+            // [self] -> [unbound, self]
+            py_Name name = co_names[byte.arg];
+            bool ok = py_pushmethod(name);
+            if(!ok) {
+                // fallback to getattr
                 if(py_getattr(TOP(), name)) {
                     py_assign(TOP(), py_retval());
+                    py_newnil(SP()++);
                 } else {
                     goto __ERROR;
                 }
+            }
+            DISPATCH();
+        }
+        case OP_LOAD_SUBSCR: {
+            // [a, b] -> a[b]
+            py_Ref magic = py_tpfindmagic(SECOND()->type, __getitem__);
+            if(magic) {
+                if(magic->type == tp_nativefunc) {
+                    if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
+                    POP();
+                    py_assign(TOP(), py_retval());
+                } else {
+                    INSERT_THIRD();     // [?, a, b]
+                    *THIRD() = *magic;  // [__getitem__, a, b]
+                    vectorcall_opcall(1, 0);
+                }
                 DISPATCH();
             }
-            case OP_LOAD_CLASS_GLOBAL: {
-                assert(self->curr_class);
-                py_Name name = co_names[byte.arg];
-                py_Ref tmp = py_getdict(self->curr_class, name);
-                if(tmp) {
-                    PUSH(tmp);
+            TypeError("'%t' object is not subscriptable", SECOND()->type);
+            goto __ERROR;
+        }
+        case OP_STORE_FAST: {
+            assert(!frame->is_locals_special);
+            frame->locals[byte.arg] = POPX();
+            DISPATCH();
+        }
+        case OP_STORE_NAME: {
+            assert(frame->is_locals_special);
+            py_Name name = co_names[byte.arg];
+            switch(frame->locals->type) {
+                case tp_locals: {
+                    py_Frame* noproxy = frame->locals->_ptr;
+                    py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
+                    if(slot == NULL) {
+                        UnboundLocalError(name);
+                        goto __ERROR;
+                    }
+                    *slot = POPX();
                     DISPATCH();
                 }
-                // load global if attribute not found
-                int res = Frame__getglobal(frame, name);
-                if(res == 1) {
-                    PUSH(&self->last_retval);
+                case tp_dict: {
+                    if(!py_dict_setitem(frame->locals, py_name2ref(name), TOP())) goto __ERROR;
+                    POP();
                     DISPATCH();
                 }
-                if(res == -1) goto __ERROR;
-                tmp = py_getdict(self->builtins, name);
-                if(tmp) {
-                    PUSH(tmp);
+                case tp_nil: {
+                    // globals
+                    if(!Frame__setglobal(frame, name, TOP())) goto __ERROR;
+                    POP();
                     DISPATCH();
                 }
-                NameError(name);
-                goto __ERROR;
+                default: c11__unreachable();
             }
-            case OP_LOAD_METHOD: {
-                // [self] -> [unbound, self]
-                py_Name name = co_names[byte.arg];
-                bool ok = py_pushmethod(name);
-                if(!ok) {
-                    // fallback to getattr
-                    if(py_getattr(TOP(), name)) {
-                        py_assign(TOP(), py_retval());
-                        py_newnil(SP()++);
-                    } else {
-                        goto __ERROR;
-                    }
+        }
+        case OP_STORE_GLOBAL: {
+            py_Name name = co_names[byte.arg];
+            if(!Frame__setglobal(frame, name, TOP())) goto __ERROR;
+            POP();
+            DISPATCH();
+        }
+        case OP_STORE_ATTR: {
+            // [val, a] -> a.b = val
+            py_Name name = co_names[byte.arg];
+            if(!py_setattr(TOP(), name, SECOND())) goto __ERROR;
+            STACK_SHRINK(2);
+            DISPATCH();
+        }
+        case OP_STORE_SUBSCR: {
+            // [val, a, b] -> a[b] = val
+            py_Ref magic = py_tpfindmagic(SECOND()->type, __setitem__);
+            if(magic) {
+                PUSH(THIRD());  // [val, a, b, val]
+                if(magic->type == tp_nativefunc) {
+                    if(!py_callcfunc(magic->_cfunc, 3, THIRD())) goto __ERROR;
+                    STACK_SHRINK(4);
+                } else {
+                    *FOURTH() = *magic;  // [__setitem__, a, b, val]
+                    if(!py_vectorcall(2, 0)) goto __ERROR;
                 }
                 DISPATCH();
             }
-            case OP_LOAD_SUBSCR: {
-                // [a, b] -> a[b]
-                py_Ref magic = py_tpfindmagic(SECOND()->type, __getitem__);
-                if(magic) {
-                    if(magic->type == tp_nativefunc) {
-                        if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
-                        POP();
-                        py_assign(TOP(), py_retval());
-                    } else {
-                        INSERT_THIRD();     // [?, a, b]
-                        *THIRD() = *magic;  // [__getitem__, a, b]
-                        vectorcall_opcall(1, 0);
-                    }
-                    DISPATCH();
-                }
-                TypeError("'%t' object is not subscriptable", SECOND()->type);
+            TypeError("'%t' object does not support item assignment", SECOND()->type);
+            goto __ERROR;
+        }
+        case OP_DELETE_FAST: {
+            assert(!frame->is_locals_special);
+            py_Ref tmp = &frame->locals[byte.arg];
+            if(py_isnil(tmp)) {
+                py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg);
+                UnboundLocalError(name);
                 goto __ERROR;
             }
-            case OP_STORE_FAST: {
-                assert(!frame->is_locals_special);
-                frame->locals[byte.arg] = POPX();
-                DISPATCH();
-            }
-            case OP_STORE_NAME: {
-                assert(frame->is_locals_special);
-                py_Name name = co_names[byte.arg];
-                switch(frame->locals->type) {
-                    case tp_locals: {
-                        py_Frame* noproxy = frame->locals->_ptr;
-                        py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
-                        if(slot == NULL) {
-                            UnboundLocalError(name);
-                            goto __ERROR;
-                        }
-                        *slot = POPX();
-                        DISPATCH();
-                    }
-                    case tp_dict: {
-                        if(!py_dict_setitem(frame->locals, py_name2ref(name), TOP())) goto __ERROR;
-                        POP();
-                        DISPATCH();
-                    }
-                    case tp_nil: {
-                        // globals
-                        if(!Frame__setglobal(frame, name, TOP())) goto __ERROR;
-                        POP();
-                        DISPATCH();
-                    }
-                    default: c11__unreachable();
-                }
-            }
-            case OP_STORE_GLOBAL: {
-                py_Name name = co_names[byte.arg];
-                if(!Frame__setglobal(frame, name, TOP())) goto __ERROR;
-                POP();
-                DISPATCH();
-            }
-            case OP_STORE_ATTR: {
-                // [val, a] -> a.b = val
-                py_Name name = co_names[byte.arg];
-                if(!py_setattr(TOP(), name, SECOND())) goto __ERROR;
-                STACK_SHRINK(2);
-                DISPATCH();
-            }
-            case OP_STORE_SUBSCR: {
-                // [val, a, b] -> a[b] = val
-                py_Ref magic = py_tpfindmagic(SECOND()->type, __setitem__);
-                if(magic) {
-                    PUSH(THIRD());  // [val, a, b, val]
-                    if(magic->type == tp_nativefunc) {
-                        if(!py_callcfunc(magic->_cfunc, 3, THIRD())) goto __ERROR;
-                        STACK_SHRINK(4);
-                    } else {
-                        *FOURTH() = *magic;  // [__setitem__, a, b, val]
-                        if(!py_vectorcall(2, 0)) goto __ERROR;
+            py_newnil(tmp);
+            DISPATCH();
+        }
+        case OP_DELETE_NAME: {
+            assert(frame->is_locals_special);
+            py_Name name = co_names[byte.arg];
+            switch(frame->locals->type) {
+                case tp_locals: {
+                    py_Frame* noproxy = frame->locals->_ptr;
+                    py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
+                    if(slot == NULL || py_isnil(slot)) {
+                        UnboundLocalError(name);
+                        goto __ERROR;
                     }
+                    py_newnil(slot);
                     DISPATCH();
                 }
-                TypeError("'%t' object does not support item assignment", SECOND()->type);
-                goto __ERROR;
-            }
-            case OP_DELETE_FAST: {
-                assert(!frame->is_locals_special);
-                py_Ref tmp = &frame->locals[byte.arg];
-                if(py_isnil(tmp)) {
-                    py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg);
-                    UnboundLocalError(name);
+                case tp_dict: {
+                    int res = py_dict_delitem(frame->locals, py_name2ref(name));
+                    if(res == 1) DISPATCH();
+                    if(res == 0) UnboundLocalError(name);
                     goto __ERROR;
                 }
-                py_newnil(tmp);
-                DISPATCH();
-            }
-            case OP_DELETE_NAME: {
-                assert(frame->is_locals_special);
-                py_Name name = co_names[byte.arg];
-                switch(frame->locals->type) {
-                    case tp_locals: {
-                        py_Frame* noproxy = frame->locals->_ptr;
-                        py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
-                        if(slot == NULL || py_isnil(slot)) {
-                            UnboundLocalError(name);
-                            goto __ERROR;
-                        }
-                        py_newnil(slot);
-                        DISPATCH();
-                    }
-                    case tp_dict: {
-                        int res = py_dict_delitem(frame->locals, py_name2ref(name));
-                        if(res == 1) DISPATCH();
-                        if(res == 0) UnboundLocalError(name);
-                        goto __ERROR;
-                    }
-                    case tp_nil: {
-                        // globals
-                        int res = Frame__delglobal(frame, name);
-                        if(res == 1) DISPATCH();
-                        if(res == 0) NameError(name);
-                        goto __ERROR;
-                    }
-                    default: c11__unreachable();
+                case tp_nil: {
+                    // globals
+                    int res = Frame__delglobal(frame, name);
+                    if(res == 1) DISPATCH();
+                    if(res == 0) NameError(name);
+                    goto __ERROR;
                 }
+                default: c11__unreachable();
             }
-            case OP_DELETE_GLOBAL: {
-                py_Name name = co_names[byte.arg];
-                int res = Frame__delglobal(frame, name);
-                if(res == 1) DISPATCH();
-                if(res == -1) goto __ERROR;
-                NameError(name);
-                goto __ERROR;
-            }
+        }
+        case OP_DELETE_GLOBAL: {
+            py_Name name = co_names[byte.arg];
+            int res = Frame__delglobal(frame, name);
+            if(res == 1) DISPATCH();
+            if(res == -1) goto __ERROR;
+            NameError(name);
+            goto __ERROR;
+        }
 
-            case OP_DELETE_ATTR: {
-                py_Name name = co_names[byte.arg];
-                if(!py_delattr(TOP(), name)) goto __ERROR;
-                DISPATCH();
-            }
+        case OP_DELETE_ATTR: {
+            py_Name name = co_names[byte.arg];
+            if(!py_delattr(TOP(), name)) goto __ERROR;
+            DISPATCH();
+        }
 
-            case OP_DELETE_SUBSCR: {
-                // [a, b] -> del a[b]
-                py_Ref magic = py_tpfindmagic(SECOND()->type, __delitem__);
-                if(magic) {
-                    if(magic->type == tp_nativefunc) {
-                        if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
-                        STACK_SHRINK(2);
-                    } else {
-                        INSERT_THIRD();     // [?, a, b]
-                        *THIRD() = *magic;  // [__delitem__, a, b]
-                        if(!py_vectorcall(1, 0)) goto __ERROR;
-                    }
-                    DISPATCH();
-                }
-                TypeError("'%t' object does not support item deletion", SECOND()->type);
-                goto __ERROR;
-            }
-            /*****************************************/
-            case OP_BUILD_IMAG: {
-                // [x]
-                py_Ref f = py_getdict(self->builtins, py_name("complex"));
-                assert(f != NULL);
-                py_TValue tmp = *TOP();
-                *TOP() = *f;           // [complex]
-                py_newnil(SP()++);     // [complex, NULL]
-                py_newint(SP()++, 0);  // [complex, NULL, 0]
-                *SP()++ = tmp;         // [complex, NULL, 0, x]
-                vectorcall_opcall(2, 0);
-                DISPATCH();
-            }
-            case OP_BUILD_BYTES: {
-                int size;
-                py_Ref string = c11__at(py_TValue, &frame->co->consts, byte.arg);
-                const char* data = py_tostrn(string, &size);
-                unsigned char* p = py_newbytes(SP()++, size);
-                memcpy(p, data, size);
-                DISPATCH();
-            }
-            case OP_BUILD_TUPLE: {
-                py_TValue tmp;
-                py_Ref p = py_newtuple(&tmp, byte.arg);
-                py_TValue* begin = SP() - byte.arg;
-                for(int i = 0; i < byte.arg; i++)
-                    p[i] = begin[i];
-                SP() = begin;
-                PUSH(&tmp);
-                DISPATCH();
-            }
-            case OP_BUILD_LIST: {
-                py_TValue tmp;
-                py_newlistn(&tmp, byte.arg);
-                py_TValue* begin = SP() - byte.arg;
-                for(int i = 0; i < byte.arg; i++) {
-                    py_list_setitem(&tmp, i, begin + i);
-                }
-                SP() = begin;
-                PUSH(&tmp);
-                DISPATCH();
-            }
-            case OP_BUILD_DICT: {
-                py_TValue* begin = SP() - byte.arg * 2;
-                py_Ref tmp = py_pushtmp();
-                py_newdict(tmp);
-                for(int i = 0; i < byte.arg * 2; i += 2) {
-                    bool ok = py_dict_setitem(tmp, begin + i, begin + i + 1);
-                    if(!ok) goto __ERROR;
-                }
-                SP() = begin;
-                PUSH(tmp);
-                DISPATCH();
-            }
-            case OP_BUILD_SET: {
-                py_TValue* begin = SP() - byte.arg;
-                py_Ref typeobject_set = py_getdict(self->builtins, py_name("set"));
-                assert(typeobject_set != NULL);
-                py_push(typeobject_set);
-                py_pushnil();
-                if(!py_vectorcall(0, 0)) goto __ERROR;
-                py_push(py_retval());  // empty set
-                py_Name id_add = py_name("add");
-                for(int i = 0; i < byte.arg; i++) {
-                    py_push(TOP());
-                    if(!py_pushmethod(id_add)) {
-                        c11__abort("OP_BUILD_SET: failed to load method 'add'");
-                    }
-                    py_push(begin + i);
+        case OP_DELETE_SUBSCR: {
+            // [a, b] -> del a[b]
+            py_Ref magic = py_tpfindmagic(SECOND()->type, __delitem__);
+            if(magic) {
+                if(magic->type == tp_nativefunc) {
+                    if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
+                    STACK_SHRINK(2);
+                } else {
+                    INSERT_THIRD();     // [?, a, b]
+                    *THIRD() = *magic;  // [__delitem__, a, b]
                     if(!py_vectorcall(1, 0)) goto __ERROR;
                 }
-                py_TValue tmp = *TOP();
-                SP() = begin;
-                PUSH(&tmp);
                 DISPATCH();
             }
-            case OP_BUILD_SLICE: {
-                // [start, stop, step]
-                py_TValue tmp;
-                py_newslice(&tmp);
-                py_setslot(&tmp, 0, THIRD());
-                py_setslot(&tmp, 1, SECOND());
-                py_setslot(&tmp, 2, TOP());
-                STACK_SHRINK(3);
-                PUSH(&tmp);
-                DISPATCH();
+            TypeError("'%t' object does not support item deletion", SECOND()->type);
+            goto __ERROR;
+        }
+        /*****************************************/
+        case OP_BUILD_IMAG: {
+            // [x]
+            py_Ref f = py_getdict(self->builtins, py_name("complex"));
+            assert(f != NULL);
+            py_TValue tmp = *TOP();
+            *TOP() = *f;           // [complex]
+            py_newnil(SP()++);     // [complex, NULL]
+            py_newint(SP()++, 0);  // [complex, NULL, 0]
+            *SP()++ = tmp;         // [complex, NULL, 0, x]
+            vectorcall_opcall(2, 0);
+            DISPATCH();
+        }
+        case OP_BUILD_BYTES: {
+            int size;
+            py_Ref string = c11__at(py_TValue, &frame->co->consts, byte.arg);
+            const char* data = py_tostrn(string, &size);
+            unsigned char* p = py_newbytes(SP()++, size);
+            memcpy(p, data, size);
+            DISPATCH();
+        }
+        case OP_BUILD_TUPLE: {
+            py_TValue tmp;
+            py_Ref p = py_newtuple(&tmp, byte.arg);
+            py_TValue* begin = SP() - byte.arg;
+            for(int i = 0; i < byte.arg; i++)
+                p[i] = begin[i];
+            SP() = begin;
+            PUSH(&tmp);
+            DISPATCH();
+        }
+        case OP_BUILD_LIST: {
+            py_TValue tmp;
+            py_newlistn(&tmp, byte.arg);
+            py_TValue* begin = SP() - byte.arg;
+            for(int i = 0; i < byte.arg; i++) {
+                py_list_setitem(&tmp, i, begin + i);
+            }
+            SP() = begin;
+            PUSH(&tmp);
+            DISPATCH();
+        }
+        case OP_BUILD_DICT: {
+            py_TValue* begin = SP() - byte.arg * 2;
+            py_Ref tmp = py_pushtmp();
+            py_newdict(tmp);
+            for(int i = 0; i < byte.arg * 2; i += 2) {
+                bool ok = py_dict_setitem(tmp, begin + i, begin + i + 1);
+                if(!ok) goto __ERROR;
             }
-            case OP_BUILD_STRING: {
-                py_TValue* begin = SP() - byte.arg;
-                c11_sbuf ss;
-                c11_sbuf__ctor(&ss);
-                for(int i = 0; i < byte.arg; i++) {
-                    if(!py_str(begin + i)) goto __ERROR;
-                    c11_sbuf__write_sv(&ss, py_tosv(&self->last_retval));
+            SP() = begin;
+            PUSH(tmp);
+            DISPATCH();
+        }
+        case OP_BUILD_SET: {
+            py_TValue* begin = SP() - byte.arg;
+            py_Ref typeobject_set = py_getdict(self->builtins, py_name("set"));
+            assert(typeobject_set != NULL);
+            py_push(typeobject_set);
+            py_pushnil();
+            if(!py_vectorcall(0, 0)) goto __ERROR;
+            py_push(py_retval());  // empty set
+            py_Name id_add = py_name("add");
+            for(int i = 0; i < byte.arg; i++) {
+                py_push(TOP());
+                if(!py_pushmethod(id_add)) {
+                    c11__abort("OP_BUILD_SET: failed to load method 'add'");
                 }
-                SP() = begin;
-                c11_sbuf__py_submit(&ss, SP()++);
-                DISPATCH();
+                py_push(begin + i);
+                if(!py_vectorcall(1, 0)) goto __ERROR;
             }
-            /*****************************/
+            py_TValue tmp = *TOP();
+            SP() = begin;
+            PUSH(&tmp);
+            DISPATCH();
+        }
+        case OP_BUILD_SLICE: {
+            // [start, stop, step]
+            py_TValue tmp;
+            py_newslice(&tmp);
+            py_setslot(&tmp, 0, THIRD());
+            py_setslot(&tmp, 1, SECOND());
+            py_setslot(&tmp, 2, TOP());
+            STACK_SHRINK(3);
+            PUSH(&tmp);
+            DISPATCH();
+        }
+        case OP_BUILD_STRING: {
+            py_TValue* begin = SP() - byte.arg;
+            c11_sbuf ss;
+            c11_sbuf__ctor(&ss);
+            for(int i = 0; i < byte.arg; i++) {
+                if(!py_str(begin + i)) goto __ERROR;
+                c11_sbuf__write_sv(&ss, py_tosv(&self->last_retval));
+            }
+            SP() = begin;
+            c11_sbuf__py_submit(&ss, SP()++);
+            DISPATCH();
+        }
+        /*****************************/
 #define CASE_BINARY_OP(label, op, rop)                                                             \
     case label: {                                                                                  \
         if(!pk_stack_binaryop(self, op, rop)) goto __ERROR;                                        \
@@ -613,642 +612,638 @@ FrameResult VM__run_top_frame(VM* self) {
         *TOP() = self->last_retval;                                                                \
         DISPATCH();                                                                                \
     }
-                CASE_BINARY_OP(OP_BINARY_ADD, __add__, __radd__)
-                CASE_BINARY_OP(OP_BINARY_SUB, __sub__, __rsub__)
-                CASE_BINARY_OP(OP_BINARY_MUL, __mul__, __rmul__)
-                CASE_BINARY_OP(OP_BINARY_TRUEDIV, __truediv__, __rtruediv__)
-                CASE_BINARY_OP(OP_BINARY_FLOORDIV, __floordiv__, __rfloordiv__)
-                CASE_BINARY_OP(OP_BINARY_MOD, __mod__, __rmod__)
-                CASE_BINARY_OP(OP_BINARY_POW, __pow__, __rpow__)
-                CASE_BINARY_OP(OP_BINARY_LSHIFT, __lshift__, 0)
-                CASE_BINARY_OP(OP_BINARY_RSHIFT, __rshift__, 0)
-                CASE_BINARY_OP(OP_BINARY_AND, __and__, 0)
-                CASE_BINARY_OP(OP_BINARY_OR, __or__, 0)
-                CASE_BINARY_OP(OP_BINARY_XOR, __xor__, 0)
-                CASE_BINARY_OP(OP_BINARY_MATMUL, __matmul__, 0)
-                CASE_BINARY_OP(OP_COMPARE_LT, __lt__, __gt__)
-                CASE_BINARY_OP(OP_COMPARE_LE, __le__, __ge__)
-                CASE_BINARY_OP(OP_COMPARE_EQ, __eq__, __eq__)
-                CASE_BINARY_OP(OP_COMPARE_NE, __ne__, __ne__)
-                CASE_BINARY_OP(OP_COMPARE_GT, __gt__, __lt__)
-                CASE_BINARY_OP(OP_COMPARE_GE, __ge__, __le__)
+            CASE_BINARY_OP(OP_BINARY_ADD, __add__, __radd__)
+            CASE_BINARY_OP(OP_BINARY_SUB, __sub__, __rsub__)
+            CASE_BINARY_OP(OP_BINARY_MUL, __mul__, __rmul__)
+            CASE_BINARY_OP(OP_BINARY_TRUEDIV, __truediv__, __rtruediv__)
+            CASE_BINARY_OP(OP_BINARY_FLOORDIV, __floordiv__, __rfloordiv__)
+            CASE_BINARY_OP(OP_BINARY_MOD, __mod__, __rmod__)
+            CASE_BINARY_OP(OP_BINARY_POW, __pow__, __rpow__)
+            CASE_BINARY_OP(OP_BINARY_LSHIFT, __lshift__, 0)
+            CASE_BINARY_OP(OP_BINARY_RSHIFT, __rshift__, 0)
+            CASE_BINARY_OP(OP_BINARY_AND, __and__, 0)
+            CASE_BINARY_OP(OP_BINARY_OR, __or__, 0)
+            CASE_BINARY_OP(OP_BINARY_XOR, __xor__, 0)
+            CASE_BINARY_OP(OP_BINARY_MATMUL, __matmul__, 0)
+            CASE_BINARY_OP(OP_COMPARE_LT, __lt__, __gt__)
+            CASE_BINARY_OP(OP_COMPARE_LE, __le__, __ge__)
+            CASE_BINARY_OP(OP_COMPARE_EQ, __eq__, __eq__)
+            CASE_BINARY_OP(OP_COMPARE_NE, __ne__, __ne__)
+            CASE_BINARY_OP(OP_COMPARE_GT, __gt__, __lt__)
+            CASE_BINARY_OP(OP_COMPARE_GE, __ge__, __le__)
 #undef CASE_BINARY_OP
-            case OP_IS_OP: {
-                bool res = py_isidentical(SECOND(), TOP());
-                POP();
-                if(byte.arg) res = !res;
-                py_newbool(TOP(), res);
-                DISPATCH();
-            }
-            case OP_CONTAINS_OP: {
-                // [b, a] -> b __contains__ a (a in b) -> [retval]
-                py_Ref magic = py_tpfindmagic(SECOND()->type, __contains__);
-                if(magic) {
-                    if(magic->type == tp_nativefunc) {
-                        if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
-                        STACK_SHRINK(2);
-                    } else {
-                        INSERT_THIRD();     // [?, b, a]
-                        *THIRD() = *magic;  // [__contains__, a, b]
-                        if(!py_vectorcall(1, 0)) goto __ERROR;
-                    }
-                    bool res = py_tobool(py_retval());
-                    if(byte.arg) res = !res;
-                    py_newbool(SP()++, res);
-                    DISPATCH();
+        case OP_IS_OP: {
+            bool res = py_isidentical(SECOND(), TOP());
+            POP();
+            if(byte.arg) res = !res;
+            py_newbool(TOP(), res);
+            DISPATCH();
+        }
+        case OP_CONTAINS_OP: {
+            // [b, a] -> b __contains__ a (a in b) -> [retval]
+            py_Ref magic = py_tpfindmagic(SECOND()->type, __contains__);
+            if(magic) {
+                if(magic->type == tp_nativefunc) {
+                    if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
+                    STACK_SHRINK(2);
+                } else {
+                    INSERT_THIRD();     // [?, b, a]
+                    *THIRD() = *magic;  // [__contains__, a, b]
+                    if(!py_vectorcall(1, 0)) goto __ERROR;
                 }
-                TypeError("'%t' type does not support '__contains__'", SECOND()->type);
-                goto __ERROR;
-            }
-                /*****************************************/
-            case OP_JUMP_FORWARD: DISPATCH_JUMP((int16_t)byte.arg);
-            case OP_POP_JUMP_IF_NOT_MATCH: {
-                int res = py_equal(SECOND(), TOP());
-                if(res < 0) goto __ERROR;
-                STACK_SHRINK(2);
-                if(!res) DISPATCH_JUMP((int16_t)byte.arg);
+                bool res = py_tobool(py_retval());
+                if(byte.arg) res = !res;
+                py_newbool(SP()++, res);
                 DISPATCH();
             }
-            case OP_POP_JUMP_IF_FALSE: {
-                int res = py_bool(TOP());
-                if(res < 0) goto __ERROR;
+            TypeError("'%t' type does not support '__contains__'", SECOND()->type);
+            goto __ERROR;
+        }
+            /*****************************************/
+        case OP_JUMP_FORWARD: DISPATCH_JUMP((int16_t)byte.arg);
+        case OP_POP_JUMP_IF_NOT_MATCH: {
+            int res = py_equal(SECOND(), TOP());
+            if(res < 0) goto __ERROR;
+            STACK_SHRINK(2);
+            if(!res) DISPATCH_JUMP((int16_t)byte.arg);
+            DISPATCH();
+        }
+        case OP_POP_JUMP_IF_FALSE: {
+            int res = py_bool(TOP());
+            if(res < 0) goto __ERROR;
+            POP();
+            if(!res) DISPATCH_JUMP((int16_t)byte.arg);
+            DISPATCH();
+        }
+        case OP_POP_JUMP_IF_TRUE: {
+            int res = py_bool(TOP());
+            if(res < 0) goto __ERROR;
+            POP();
+            if(res) DISPATCH_JUMP((int16_t)byte.arg);
+            DISPATCH();
+        }
+        case OP_JUMP_IF_TRUE_OR_POP: {
+            int res = py_bool(TOP());
+            if(res < 0) goto __ERROR;
+            if(res) {
+                DISPATCH_JUMP((int16_t)byte.arg);
+            } else {
                 POP();
-                if(!res) DISPATCH_JUMP((int16_t)byte.arg);
                 DISPATCH();
             }
-            case OP_POP_JUMP_IF_TRUE: {
-                int res = py_bool(TOP());
-                if(res < 0) goto __ERROR;
+        }
+        case OP_JUMP_IF_FALSE_OR_POP: {
+            int res = py_bool(TOP());
+            if(res < 0) goto __ERROR;
+            if(!res) {
+                DISPATCH_JUMP((int16_t)byte.arg);
+            } else {
                 POP();
-                if(res) DISPATCH_JUMP((int16_t)byte.arg);
                 DISPATCH();
             }
-            case OP_JUMP_IF_TRUE_OR_POP: {
-                int res = py_bool(TOP());
-                if(res < 0) goto __ERROR;
-                if(res) {
-                    DISPATCH_JUMP((int16_t)byte.arg);
-                } else {
-                    POP();
-                    DISPATCH();
-                }
-            }
-            case OP_JUMP_IF_FALSE_OR_POP: {
-                int res = py_bool(TOP());
-                if(res < 0) goto __ERROR;
-                if(!res) {
-                    DISPATCH_JUMP((int16_t)byte.arg);
-                } else {
-                    POP();
-                    DISPATCH();
-                }
-            }
-            case OP_SHORTCUT_IF_FALSE_OR_POP: {
-                int res = py_bool(TOP());
-                if(res < 0) goto __ERROR;
-                if(!res) {                      // [b, False]
-                    STACK_SHRINK(2);            // []
-                    py_newbool(SP()++, false);  // [False]
-                    DISPATCH_JUMP((int16_t)byte.arg);
-                } else {
-                    POP();  // [b]
-                    DISPATCH();
-                }
-            }
-            case OP_LOOP_CONTINUE: {
-                DISPATCH_JUMP((int16_t)byte.arg);
-            }
-            case OP_LOOP_BREAK: {
+        }
+        case OP_SHORTCUT_IF_FALSE_OR_POP: {
+            int res = py_bool(TOP());
+            if(res < 0) goto __ERROR;
+            if(!res) {                      // [b, False]
+                STACK_SHRINK(2);            // []
+                py_newbool(SP()++, false);  // [False]
                 DISPATCH_JUMP((int16_t)byte.arg);
-            }
-            /*****************************************/
-            case OP_CALL: {
-                ManagedHeap__collect_if_needed(&self->heap);
-                vectorcall_opcall(byte.arg & 0xFF, byte.arg >> 8);
+            } else {
+                POP();  // [b]
                 DISPATCH();
             }
-            case OP_CALL_VARGS: {
-                // [_0, _1, _2 | k1, v1, k2, v2]
-                uint16_t argc = byte.arg & 0xFF;
-                uint16_t kwargc = byte.arg >> 8;
-
-                int n = 0;
-                py_TValue* sp = SP();
-                py_TValue* p1 = sp - kwargc * 2;
-                py_TValue* base = p1 - argc;
-                py_TValue* buf = self->vectorcall_buffer;
-
-                for(py_TValue* curr = base; curr != p1; curr++) {
-                    if(curr->type != tp_star_wrapper) {
-                        buf[n++] = *curr;
-                    } else {
-                        py_TValue* args = py_getslot(curr, 0);
-                        py_TValue* p;
-                        int length = pk_arrayview(args, &p);
-                        if(length != -1) {
-                            for(int j = 0; j < length; j++) {
-                                buf[n++] = p[j];
-                            }
-                            argc += length - 1;
-                        } else {
-                            TypeError("*args must be a list or tuple, got '%t'", args->type);
-                            goto __ERROR;
+        }
+        case OP_LOOP_CONTINUE: {
+            DISPATCH_JUMP((int16_t)byte.arg);
+        }
+        case OP_LOOP_BREAK: {
+            DISPATCH_JUMP((int16_t)byte.arg);
+        }
+        /*****************************************/
+        case OP_CALL: {
+            ManagedHeap__collect_if_needed(&self->heap);
+            vectorcall_opcall(byte.arg & 0xFF, byte.arg >> 8);
+            DISPATCH();
+        }
+        case OP_CALL_VARGS: {
+            // [_0, _1, _2 | k1, v1, k2, v2]
+            uint16_t argc = byte.arg & 0xFF;
+            uint16_t kwargc = byte.arg >> 8;
+
+            int n = 0;
+            py_TValue* sp = SP();
+            py_TValue* p1 = sp - kwargc * 2;
+            py_TValue* base = p1 - argc;
+            py_TValue* buf = self->vectorcall_buffer;
+
+            for(py_TValue* curr = base; curr != p1; curr++) {
+                if(curr->type != tp_star_wrapper) {
+                    buf[n++] = *curr;
+                } else {
+                    py_TValue* args = py_getslot(curr, 0);
+                    py_TValue* p;
+                    int length = pk_arrayview(args, &p);
+                    if(length != -1) {
+                        for(int j = 0; j < length; j++) {
+                            buf[n++] = p[j];
                         }
+                        argc += length - 1;
+                    } else {
+                        TypeError("*args must be a list or tuple, got '%t'", args->type);
+                        goto __ERROR;
                     }
                 }
+            }
 
-                for(py_TValue* curr = p1; curr != sp; curr += 2) {
-                    if(curr[1].type != tp_star_wrapper) {
-                        buf[n++] = curr[0];
-                        buf[n++] = curr[1];
+            for(py_TValue* curr = p1; curr != sp; curr += 2) {
+                if(curr[1].type != tp_star_wrapper) {
+                    buf[n++] = curr[0];
+                    buf[n++] = curr[1];
+                } else {
+                    assert(py_toint(&curr[0]) == 0);
+                    py_TValue* kwargs = py_getslot(&curr[1], 0);
+                    if(kwargs->type == tp_dict) {
+                        py_TValue* p = buf + n;
+                        if(!py_dict_apply(kwargs, unpack_dict_to_buffer, &p)) goto __ERROR;
+                        n = p - buf;
+                        kwargc += py_dict_len(kwargs) - 1;
                     } else {
-                        assert(py_toint(&curr[0]) == 0);
-                        py_TValue* kwargs = py_getslot(&curr[1], 0);
-                        if(kwargs->type == tp_dict) {
-                            py_TValue* p = buf + n;
-                            if(!py_dict_apply(kwargs, unpack_dict_to_buffer, &p)) goto __ERROR;
-                            n = p - buf;
-                            kwargc += py_dict_len(kwargs) - 1;
-                        } else {
-                            TypeError("**kwargs must be a dict, got '%t'", kwargs->type);
-                            goto __ERROR;
-                        }
+                        TypeError("**kwargs must be a dict, got '%t'", kwargs->type);
+                        goto __ERROR;
                     }
                 }
+            }
 
-                memcpy(base, buf, n * sizeof(py_TValue));
-                SP() = base + n;
+            memcpy(base, buf, n * sizeof(py_TValue));
+            SP() = base + n;
 
-                vectorcall_opcall(argc, kwargc);
-                DISPATCH();
-            }
-            case OP_RETURN_VALUE: {
-                CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
-                if(byte.arg == BC_NOARG) {
-                    self->last_retval = POPX();
-                } else {
-                    py_newnone(&self->last_retval);
-                }
-                VM__pop_frame(self);
-                if(frame == base_frame) {  // [ frameBase<- ]
-                    return RES_RETURN;
-                } else {
-                    frame = self->top_frame;
-                    PUSH(&self->last_retval);
-                    goto __NEXT_FRAME;
-                }
-                DISPATCH();
-            }
-            case OP_YIELD_VALUE: {
-                CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
-                if(byte.arg == 1) {
-                    py_newnone(py_retval());
-                } else {
-                    py_assign(py_retval(), TOP());
-                    POP();
-                }
-                return RES_YIELD;
-            }
-            case OP_FOR_ITER_YIELD_VALUE: {
-                CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
-                int res = py_next(TOP());
-                if(res == -1) goto __ERROR;
-                if(res) {
-                    return RES_YIELD;
-                } else {
-                    assert(self->last_retval.type == tp_StopIteration);
-                    py_ObjectRef value = py_getslot(&self->last_retval, 0);
-                    if(py_isnil(value)) value = py_None();
-                    *TOP() = *value;  // [iter] -> [retval]
-                    DISPATCH_JUMP((int16_t)byte.arg);
-                }
-            }
-            /////////
-            case OP_LIST_APPEND: {
-                // [list, iter, value]
-                py_list_append(THIRD(), TOP());
-                POP();
-                DISPATCH();
-            }
-            case OP_DICT_ADD: {
-                // [dict, iter, key, value]
-                bool ok = py_dict_setitem(FOURTH(), SECOND(), TOP());
-                if(!ok) goto __ERROR;
-                STACK_SHRINK(2);
-                DISPATCH();
+            vectorcall_opcall(argc, kwargc);
+            DISPATCH();
+        }
+        case OP_RETURN_VALUE: {
+            CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
+            if(byte.arg == BC_NOARG) {
+                self->last_retval = POPX();
+            } else {
+                py_newnone(&self->last_retval);
             }
-            case OP_SET_ADD: {
-                // [set, iter, value]
-                py_push(THIRD());  // [| set]
-                if(!py_pushmethod(py_name("add"))) {
-                    c11__abort("OP_SET_ADD: failed to load method 'add'");
-                }  // [|add() set]
-                py_push(THIRD());
-                if(!py_vectorcall(1, 0)) goto __ERROR;
+            VM__pop_frame(self);
+            if(frame == base_frame) {  // [ frameBase<- ]
+                return RES_RETURN;
+            } else {
+                frame = self->top_frame;
+                PUSH(&self->last_retval);
+                goto __NEXT_FRAME;
+            }
+            DISPATCH();
+        }
+        case OP_YIELD_VALUE: {
+            CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
+            if(byte.arg == 1) {
+                py_newnone(py_retval());
+            } else {
+                py_assign(py_retval(), TOP());
                 POP();
-                DISPATCH();
             }
-            /////////
-            case OP_UNARY_NEGATIVE: {
-                if(!pk_callmagic(__neg__, 1, TOP())) goto __ERROR;
-                *TOP() = self->last_retval;
-                DISPATCH();
-            }
-            case OP_UNARY_NOT: {
-                int res = py_bool(TOP());
-                if(res < 0) goto __ERROR;
-                py_newbool(TOP(), !res);
-                DISPATCH();
-            }
-            case OP_UNARY_STAR: {
-                py_TValue value = POPX();
-                int* level = py_newobject(SP()++, tp_star_wrapper, 1, sizeof(int));
-                *level = byte.arg;
-                py_setslot(TOP(), 0, &value);
-                DISPATCH();
-            }
-            case OP_UNARY_INVERT: {
-                if(!pk_callmagic(__invert__, 1, TOP())) goto __ERROR;
-                *TOP() = self->last_retval;
-                DISPATCH();
-            }
-            ////////////////
-            case OP_GET_ITER: {
-                if(!py_iter(TOP())) goto __ERROR;
-                *TOP() = *py_retval();
-                DISPATCH();
-            }
-            case OP_FOR_ITER: {
-                int res = py_next(TOP());
-                if(res == -1) goto __ERROR;
-                if(res) {
-                    PUSH(py_retval());
-                    DISPATCH();
-                } else {
-                    assert(self->last_retval.type == tp_StopIteration);
-                    POP();  // [iter] -> []
-                    DISPATCH_JUMP((int16_t)byte.arg);
-                }
+            return RES_YIELD;
+        }
+        case OP_FOR_ITER_YIELD_VALUE: {
+            CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
+            int res = py_next(TOP());
+            if(res == -1) goto __ERROR;
+            if(res) {
+                return RES_YIELD;
+            } else {
+                assert(self->last_retval.type == tp_StopIteration);
+                py_ObjectRef value = py_getslot(&self->last_retval, 0);
+                if(py_isnil(value)) value = py_None();
+                *TOP() = *value;  // [iter] -> [retval]
+                DISPATCH_JUMP((int16_t)byte.arg);
             }
-            ////////
-            case OP_IMPORT_PATH: {
-                py_Ref path_object = c11__at(py_TValue, &frame->co->consts, byte.arg);
-                const char* path = py_tostr(path_object);
-                int res = py_import(path);
-                if(res == -1) goto __ERROR;
-                if(res == 0) {
-                    ImportError("No module named '%s'", path);
-                    goto __ERROR;
-                }
+        }
+        /////////
+        case OP_LIST_APPEND: {
+            // [list, iter, value]
+            py_list_append(THIRD(), TOP());
+            POP();
+            DISPATCH();
+        }
+        case OP_DICT_ADD: {
+            // [dict, iter, key, value]
+            bool ok = py_dict_setitem(FOURTH(), SECOND(), TOP());
+            if(!ok) goto __ERROR;
+            STACK_SHRINK(2);
+            DISPATCH();
+        }
+        case OP_SET_ADD: {
+            // [set, iter, value]
+            py_push(THIRD());  // [| set]
+            if(!py_pushmethod(py_name("add"))) {
+                c11__abort("OP_SET_ADD: failed to load method 'add'");
+            }  // [|add() set]
+            py_push(THIRD());
+            if(!py_vectorcall(1, 0)) goto __ERROR;
+            POP();
+            DISPATCH();
+        }
+        /////////
+        case OP_UNARY_NEGATIVE: {
+            if(!pk_callmagic(__neg__, 1, TOP())) goto __ERROR;
+            *TOP() = self->last_retval;
+            DISPATCH();
+        }
+        case OP_UNARY_NOT: {
+            int res = py_bool(TOP());
+            if(res < 0) goto __ERROR;
+            py_newbool(TOP(), !res);
+            DISPATCH();
+        }
+        case OP_UNARY_STAR: {
+            py_TValue value = POPX();
+            int* level = py_newobject(SP()++, tp_star_wrapper, 1, sizeof(int));
+            *level = byte.arg;
+            py_setslot(TOP(), 0, &value);
+            DISPATCH();
+        }
+        case OP_UNARY_INVERT: {
+            if(!pk_callmagic(__invert__, 1, TOP())) goto __ERROR;
+            *TOP() = self->last_retval;
+            DISPATCH();
+        }
+        ////////////////
+        case OP_GET_ITER: {
+            if(!py_iter(TOP())) goto __ERROR;
+            *TOP() = *py_retval();
+            DISPATCH();
+        }
+        case OP_FOR_ITER: {
+            int res = py_next(TOP());
+            if(res == -1) goto __ERROR;
+            if(res) {
                 PUSH(py_retval());
                 DISPATCH();
+            } else {
+                assert(self->last_retval.type == tp_StopIteration);
+                POP();  // [iter] -> []
+                DISPATCH_JUMP((int16_t)byte.arg);
             }
-            case OP_POP_IMPORT_STAR: {
-                // [module]
-                NameDict* dict = PyObject__dict(TOP()->_obj);
-                py_ItemRef all = NameDict__try_get(dict, __all__);
-                if(all) {
-                    py_TValue* p;
-                    int length = pk_arrayview(all, &p);
-                    if(length == -1) {
-                        TypeError("'__all__' must be a list or tuple, got '%t'", all->type);
-                        goto __ERROR;
-                    }
-                    for(int i = 0; i < length; i++) {
-                        py_Name name = py_namev(py_tosv(p + i));
-                        py_ItemRef value = NameDict__try_get(dict, name);
-                        if(value == NULL) {
-                            ImportError("cannot import name '%n'", name);
-                            goto __ERROR;
-                        } else {
-                            if(!Frame__setglobal(frame, name, value)) goto __ERROR;
-                        }
-                    }
-                } else {
-                    for(int i = 0; i < dict->capacity; i++) {
-                        NameDict_KV* kv = &dict->items[i];
-                        if(kv->key == NULL) continue;
-                        c11_sv name = py_name2sv(kv->key);
-                        if(name.size == 0 || name.data[0] == '_') continue;
-                        if(!Frame__setglobal(frame, kv->key, &kv->value)) goto __ERROR;
-                    }
-                }
-                POP();
-                DISPATCH();
+        }
+        ////////
+        case OP_IMPORT_PATH: {
+            py_Ref path_object = c11__at(py_TValue, &frame->co->consts, byte.arg);
+            const char* path = py_tostr(path_object);
+            int res = py_import(path);
+            if(res == -1) goto __ERROR;
+            if(res == 0) {
+                ImportError("No module named '%s'", path);
+                goto __ERROR;
             }
-            ////////
-            case OP_UNPACK_SEQUENCE: {
+            PUSH(py_retval());
+            DISPATCH();
+        }
+        case OP_POP_IMPORT_STAR: {
+            // [module]
+            NameDict* dict = PyObject__dict(TOP()->_obj);
+            py_ItemRef all = NameDict__try_get(dict, __all__);
+            if(all) {
                 py_TValue* p;
-                int length;
-
-                switch(TOP()->type) {
-                    case tp_tuple: {
-                        length = py_tuple_len(TOP());
-                        p = py_tuple_data(TOP());
-                        break;
-                    }
-                    case tp_list: {
-                        length = py_list_len(TOP());
-                        p = py_list_data(TOP());
-                        break;
-                    }
-                    case tp_vec2i: {
-                        length = 2;
-                        if(byte.arg != length) break;
-                        c11_vec2i val = py_tovec2i(TOP());
-                        POP();
-                        py_newint(SP()++, val.x);
-                        py_newint(SP()++, val.y);
-                        DISPATCH();
-                    }
-                    case tp_vec2: {
-                        length = 2;
-                        if(byte.arg != length) break;
-                        c11_vec2 val = py_tovec2(TOP());
-                        POP();
-                        py_newfloat(SP()++, val.x);
-                        py_newfloat(SP()++, val.y);
-                        DISPATCH();
-                    }
-                    case tp_vec3i: {
-                        length = 3;
-                        if(byte.arg != length) break;
-                        c11_vec3i val = py_tovec3i(TOP());
-                        POP();
-                        py_newint(SP()++, val.x);
-                        py_newint(SP()++, val.y);
-                        py_newint(SP()++, val.z);
-                        DISPATCH();
-                    }
-                    case tp_vec3: {
-                        length = 3;
-                        if(byte.arg != length) break;
-                        c11_vec3 val = py_tovec3(TOP());
-                        POP();
-                        py_newfloat(SP()++, val.x);
-                        py_newfloat(SP()++, val.y);
-                        py_newfloat(SP()++, val.z);
-                        DISPATCH();
-                    }
-                    default: {
-                        TypeError("expected list or tuple to unpack, got %t", TOP()->type);
-                        goto __ERROR;
-                    }
-                }
-                if(length != byte.arg) {
-                    ValueError("expected %d values to unpack, got %d", byte.arg, length);
+                int length = pk_arrayview(all, &p);
+                if(length == -1) {
+                    TypeError("'__all__' must be a list or tuple, got '%t'", all->type);
                     goto __ERROR;
                 }
-                POP();
                 for(int i = 0; i < length; i++) {
-                    PUSH(p + i);
+                    py_Name name = py_namev(py_tosv(p + i));
+                    py_ItemRef value = NameDict__try_get(dict, name);
+                    if(value == NULL) {
+                        ImportError("cannot import name '%n'", name);
+                        goto __ERROR;
+                    } else {
+                        if(!Frame__setglobal(frame, name, value)) goto __ERROR;
+                    }
+                }
+            } else {
+                for(int i = 0; i < dict->capacity; i++) {
+                    NameDict_KV* kv = &dict->items[i];
+                    if(kv->key == NULL) continue;
+                    c11_sv name = py_name2sv(kv->key);
+                    if(name.size == 0 || name.data[0] == '_') continue;
+                    if(!Frame__setglobal(frame, kv->key, &kv->value)) goto __ERROR;
                 }
-                DISPATCH();
             }
-            case OP_UNPACK_EX: {
-                py_TValue* p;
-                int length = pk_arrayview(TOP(), &p);
-                if(length == -1) {
-                    TypeError("expected list or tuple to unpack, got %t", TOP()->type);
-                    goto __ERROR;
+            POP();
+            DISPATCH();
+        }
+        ////////
+        case OP_UNPACK_SEQUENCE: {
+            py_TValue* p;
+            int length;
+
+            switch(TOP()->type) {
+                case tp_tuple: {
+                    length = py_tuple_len(TOP());
+                    p = py_tuple_data(TOP());
+                    break;
                 }
-                int exceed = length - byte.arg;
-                if(exceed < 0) {
-                    ValueError("not enough values to unpack");
-                    goto __ERROR;
+                case tp_list: {
+                    length = py_list_len(TOP());
+                    p = py_list_data(TOP());
+                    break;
                 }
-                POP();
-                for(int i = 0; i < byte.arg; i++) {
-                    PUSH(p + i);
+                case tp_vec2i: {
+                    length = 2;
+                    if(byte.arg != length) break;
+                    c11_vec2i val = py_tovec2i(TOP());
+                    POP();
+                    py_newint(SP()++, val.x);
+                    py_newint(SP()++, val.y);
+                    DISPATCH();
+                }
+                case tp_vec2: {
+                    length = 2;
+                    if(byte.arg != length) break;
+                    c11_vec2 val = py_tovec2(TOP());
+                    POP();
+                    py_newfloat(SP()++, val.x);
+                    py_newfloat(SP()++, val.y);
+                    DISPATCH();
                 }
-                py_newlistn(SP()++, exceed);
-                for(int i = 0; i < exceed; i++) {
-                    py_list_setitem(TOP(), i, p + byte.arg + i);
+                case tp_vec3i: {
+                    length = 3;
+                    if(byte.arg != length) break;
+                    c11_vec3i val = py_tovec3i(TOP());
+                    POP();
+                    py_newint(SP()++, val.x);
+                    py_newint(SP()++, val.y);
+                    py_newint(SP()++, val.z);
+                    DISPATCH();
                 }
-                DISPATCH();
-            }
-            ///////////
-            case OP_BEGIN_CLASS: {
-                // [base]
-                py_Name name = co_names[byte.arg];
-                py_Type base;
-                if(py_isnone(TOP())) {
-                    base = tp_object;
-                } else {
-                    if(!py_checktype(TOP(), tp_type)) goto __ERROR;
-                    base = py_totype(TOP());
+                case tp_vec3: {
+                    length = 3;
+                    if(byte.arg != length) break;
+                    c11_vec3 val = py_tovec3(TOP());
+                    POP();
+                    py_newfloat(SP()++, val.x);
+                    py_newfloat(SP()++, val.y);
+                    py_newfloat(SP()++, val.z);
+                    DISPATCH();
                 }
-                POP();
-
-                py_TypeInfo* base_ti = pk_typeinfo(base);
-                if(base_ti->is_sealed) {
-                    TypeError("type '%t' is not an acceptable base type", base);
+                default: {
+                    TypeError("expected list or tuple to unpack, got %t", TOP()->type);
                     goto __ERROR;
                 }
-
-                py_Type type = pk_newtypewithmode(name,
-                                                  base,
-                                                  frame->module,
-                                                  NULL,
-                                                  base_ti->is_python,
-                                                  false,
-                                                  frame->co->src->mode);
-                PUSH(py_tpobject(type));
-                self->curr_class = TOP();
-                DISPATCH();
             }
-            case OP_END_CLASS: {
-                // [cls or decorated]
-                py_Name name = co_names[byte.arg];
-                if(!Frame__setglobal(frame, name, TOP())) goto __ERROR;
-
-                if(py_istype(TOP(), tp_type)) {
-                    // call on_end_subclass
-                    py_TypeInfo* ti = py_touserdata(TOP());
-                    if(ti->base != tp_object) {
-                        py_TypeInfo* base_ti = ti->base_ti;
-                        if(base_ti->on_end_subclass) base_ti->on_end_subclass(ti);
-                    }
-                    py_TValue* slot_eq = py_getdict(&ti->self, __eq__);
-                    py_TValue* slot_ne = py_getdict(&ti->self, __ne__);
-                    if(slot_eq && !slot_ne) {
-                        TypeError("'%n' implements '__eq__' but not '__ne__'", ti->name);
-                        goto __ERROR;
-                    }
-                }
-                // class with decorator is unsafe currently
-                // it skips the above check
-                POP();
-                self->curr_class = NULL;
-                DISPATCH();
+            if(length != byte.arg) {
+                ValueError("expected %d values to unpack, got %d", byte.arg, length);
+                goto __ERROR;
             }
-            case OP_STORE_CLASS_ATTR: {
-                assert(self->curr_class);
-                py_Name name = co_names[byte.arg];
-                // TOP() can be a function, classmethod or custom decorator
-                py_Ref actual_func = TOP();
-                if(actual_func->type == tp_classmethod) {
-                    actual_func = py_getslot(actual_func, 0);
-                }
-                if(actual_func->type == tp_function) {
-                    Function* ud = py_touserdata(actual_func);
-                    ud->clazz = self->curr_class->_obj;
-                }
-                py_setdict(self->curr_class, name, TOP());
-                POP();
-                DISPATCH();
+            POP();
+            for(int i = 0; i < length; i++) {
+                PUSH(p + i);
             }
-            case OP_ADD_CLASS_ANNOTATION: {
-                assert(self->curr_class);
-                // [type_hint string]
-                py_TypeInfo* ti = py_touserdata(self->curr_class);
-                if(py_isnil(&ti->annotations)) py_newdict(&ti->annotations);
-                py_Name name = co_names[byte.arg];
-                bool ok = py_dict_setitem_by_str(&ti->annotations, py_name2str(name), TOP());
-                if(!ok) goto __ERROR;
-                POP();
-                DISPATCH();
+            DISPATCH();
+        }
+        case OP_UNPACK_EX: {
+            py_TValue* p;
+            int length = pk_arrayview(TOP(), &p);
+            if(length == -1) {
+                TypeError("expected list or tuple to unpack, got %t", TOP()->type);
+                goto __ERROR;
             }
-            ///////////
-            case OP_WITH_ENTER: {
-                // [expr]
-                py_push(TOP());
-                if(!py_pushmethod(__enter__)) {
-                    TypeError("'%t' object does not support the context manager protocol",
-                              TOP()->type);
-                    goto __ERROR;
-                }
-                vectorcall_opcall(0, 0);
-                DISPATCH();
+            int exceed = length - byte.arg;
+            if(exceed < 0) {
+                ValueError("not enough values to unpack");
+                goto __ERROR;
             }
-            case OP_WITH_EXIT: {
-                // [expr]
-                py_push(TOP());
-                if(!py_pushmethod(__exit__)) {
-                    TypeError("'%t' object does not support the context manager protocol",
-                              TOP()->type);
-                    goto __ERROR;
-                }
-                if(!py_vectorcall(0, 0)) goto __ERROR;
-                POP();
-                DISPATCH();
+            POP();
+            for(int i = 0; i < byte.arg; i++) {
+                PUSH(p + i);
             }
-            ///////////
-            case OP_TRY_ENTER: {
-                Frame__set_unwind_target(frame, SP());
-                DISPATCH();
+            py_newlistn(SP()++, exceed);
+            for(int i = 0; i < exceed; i++) {
+                py_list_setitem(TOP(), i, p + byte.arg + i);
             }
-            case OP_EXCEPTION_MATCH: {
+            DISPATCH();
+        }
+        ///////////
+        case OP_BEGIN_CLASS: {
+            // [base]
+            py_Name name = co_names[byte.arg];
+            py_Type base;
+            if(py_isnone(TOP())) {
+                base = tp_object;
+            } else {
                 if(!py_checktype(TOP(), tp_type)) goto __ERROR;
-                bool ok = py_isinstance(&self->curr_exception, py_totype(TOP()));
-                py_newbool(TOP(), ok);
-                DISPATCH();
+                base = py_totype(TOP());
             }
-            case OP_RAISE: {
-                // [exception]
-                if(py_istype(TOP(), tp_type)) {
-                    if(!py_tpcall(py_totype(TOP()), 0, NULL)) goto __ERROR;
-                    py_assign(TOP(), py_retval());
+            POP();
+
+            py_TypeInfo* base_ti = pk_typeinfo(base);
+            if(base_ti->is_sealed) {
+                TypeError("type '%t' is not an acceptable base type", base);
+                goto __ERROR;
+            }
+
+            py_Type type = pk_newtypewithmode(name,
+                                              base,
+                                              frame->module,
+                                              NULL,
+                                              base_ti->is_python,
+                                              false,
+                                              frame->co->src->mode);
+            PUSH(py_tpobject(type));
+            self->curr_class = TOP();
+            DISPATCH();
+        }
+        case OP_END_CLASS: {
+            // [cls or decorated]
+            py_Name name = co_names[byte.arg];
+            if(!Frame__setglobal(frame, name, TOP())) goto __ERROR;
+
+            if(py_istype(TOP(), tp_type)) {
+                // call on_end_subclass
+                py_TypeInfo* ti = py_touserdata(TOP());
+                if(ti->base != tp_object) {
+                    py_TypeInfo* base_ti = ti->base_ti;
+                    if(base_ti->on_end_subclass) base_ti->on_end_subclass(ti);
                 }
-                if(!py_isinstance(TOP(), tp_BaseException)) {
-                    TypeError("exceptions must derive from BaseException");
+                py_TValue* slot_eq = py_getdict(&ti->self, __eq__);
+                py_TValue* slot_ne = py_getdict(&ti->self, __ne__);
+                if(slot_eq && !slot_ne) {
+                    TypeError("'%n' implements '__eq__' but not '__ne__'", ti->name);
                     goto __ERROR;
                 }
-                py_raise(TOP());
+            }
+            // class with decorator is unsafe currently
+            // it skips the above check
+            POP();
+            self->curr_class = NULL;
+            DISPATCH();
+        }
+        case OP_STORE_CLASS_ATTR: {
+            assert(self->curr_class);
+            py_Name name = co_names[byte.arg];
+            // TOP() can be a function, classmethod or custom decorator
+            py_Ref actual_func = TOP();
+            if(actual_func->type == tp_classmethod) { actual_func = py_getslot(actual_func, 0); }
+            if(actual_func->type == tp_function) {
+                Function* ud = py_touserdata(actual_func);
+                ud->clazz = self->curr_class->_obj;
+            }
+            py_setdict(self->curr_class, name, TOP());
+            POP();
+            DISPATCH();
+        }
+        case OP_ADD_CLASS_ANNOTATION: {
+            assert(self->curr_class);
+            // [type_hint string]
+            py_TypeInfo* ti = py_touserdata(self->curr_class);
+            if(py_isnil(&ti->annotations)) py_newdict(&ti->annotations);
+            py_Name name = co_names[byte.arg];
+            bool ok = py_dict_setitem_by_str(&ti->annotations, py_name2str(name), TOP());
+            if(!ok) goto __ERROR;
+            POP();
+            DISPATCH();
+        }
+        ///////////
+        case OP_WITH_ENTER: {
+            // [expr]
+            py_push(TOP());
+            if(!py_pushmethod(__enter__)) {
+                TypeError("'%t' object does not support the context manager protocol", TOP()->type);
                 goto __ERROR;
             }
-            case OP_RAISE_ASSERT: {
-                if(byte.arg) {
-                    if(!py_str(TOP())) goto __ERROR;
-                    POP();
-                    py_exception(tp_AssertionError, "%s", py_tostr(py_retval()));
-                } else {
-                    py_exception(tp_AssertionError, "");
-                }
+            vectorcall_opcall(0, 0);
+            DISPATCH();
+        }
+        case OP_WITH_EXIT: {
+            // [expr]
+            py_push(TOP());
+            if(!py_pushmethod(__exit__)) {
+                TypeError("'%t' object does not support the context manager protocol", TOP()->type);
                 goto __ERROR;
             }
-            case OP_RE_RAISE: {
-                if(self->curr_exception.type) {
-                    assert(!self->is_curr_exc_handled);
-                    goto __ERROR_RE_RAISE;
-                }
-                DISPATCH();
+            if(!py_vectorcall(0, 0)) goto __ERROR;
+            POP();
+            DISPATCH();
+        }
+        ///////////
+        case OP_TRY_ENTER: {
+            Frame__set_unwind_target(frame, SP());
+            DISPATCH();
+        }
+        case OP_EXCEPTION_MATCH: {
+            if(!py_checktype(TOP(), tp_type)) goto __ERROR;
+            bool ok = py_isinstance(&self->curr_exception, py_totype(TOP()));
+            py_newbool(TOP(), ok);
+            DISPATCH();
+        }
+        case OP_RAISE: {
+            // [exception]
+            if(py_istype(TOP(), tp_type)) {
+                if(!py_tpcall(py_totype(TOP()), 0, NULL)) goto __ERROR;
+                py_assign(TOP(), py_retval());
+            }
+            if(!py_isinstance(TOP(), tp_BaseException)) {
+                TypeError("exceptions must derive from BaseException");
+                goto __ERROR;
             }
-            case OP_PUSH_EXCEPTION: {
-                assert(self->curr_exception.type);
-                PUSH(&self->curr_exception);
-                DISPATCH();
+            py_raise(TOP());
+            goto __ERROR;
+        }
+        case OP_RAISE_ASSERT: {
+            if(byte.arg) {
+                if(!py_str(TOP())) goto __ERROR;
+                POP();
+                py_exception(tp_AssertionError, "%s", py_tostr(py_retval()));
+            } else {
+                py_exception(tp_AssertionError, "");
             }
-            case OP_BEGIN_EXC_HANDLING: {
-                assert(self->curr_exception.type);
-                self->is_curr_exc_handled = true;
-                DISPATCH();
+            goto __ERROR;
+        }
+        case OP_RE_RAISE: {
+            if(self->curr_exception.type) {
+                assert(!self->is_curr_exc_handled);
+                goto __ERROR_RE_RAISE;
             }
-            case OP_END_EXC_HANDLING: {
-                assert(self->curr_exception.type);
-                py_clearexc(NULL);
-                DISPATCH();
+            DISPATCH();
+        }
+        case OP_PUSH_EXCEPTION: {
+            assert(self->curr_exception.type);
+            PUSH(&self->curr_exception);
+            DISPATCH();
+        }
+        case OP_BEGIN_EXC_HANDLING: {
+            assert(self->curr_exception.type);
+            self->is_curr_exc_handled = true;
+            DISPATCH();
+        }
+        case OP_END_EXC_HANDLING: {
+            assert(self->curr_exception.type);
+            py_clearexc(NULL);
+            DISPATCH();
+        }
+        case OP_BEGIN_FINALLY: {
+            if(self->curr_exception.type) {
+                assert(!self->is_curr_exc_handled);
+                // temporarily handle the exception if any
+                self->is_curr_exc_handled = true;
             }
-            case OP_BEGIN_FINALLY: {
+            DISPATCH();
+        }
+        case OP_END_FINALLY: {
+            if(byte.arg == BC_NOARG) {
                 if(self->curr_exception.type) {
-                    assert(!self->is_curr_exc_handled);
-                    // temporarily handle the exception if any
-                    self->is_curr_exc_handled = true;
-                }
-                DISPATCH();
-            }
-            case OP_END_FINALLY: {
-                if(byte.arg == BC_NOARG) {
-                    if(self->curr_exception.type) {
-                        assert(self->is_curr_exc_handled);
-                        // revert the exception handling if needed
-                        self->is_curr_exc_handled = false;
-                    }
-                } else {
-                    // break or continue inside finally block
-                    py_clearexc(NULL);
+                    assert(self->is_curr_exc_handled);
+                    // revert the exception handling if needed
+                    self->is_curr_exc_handled = false;
                 }
-                DISPATCH();
-            }
-            //////////////////
-            case OP_FORMAT_STRING: {
-                py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg);
-                bool ok = stack_format_object(self, py_tosv(spec));
-                if(!ok) goto __ERROR;
-                DISPATCH();
+            } else {
+                // break or continue inside finally block
+                py_clearexc(NULL);
             }
-            default: c11__unreachable();
+            DISPATCH();
+        }
+        //////////////////
+        case OP_FORMAT_STRING: {
+            py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg);
+            bool ok = stack_format_object(self, py_tosv(spec));
+            if(!ok) goto __ERROR;
+            DISPATCH();
         }
+        default: c11__unreachable();
+    }
 
-        c11__unreachable();
-
-    __ERROR:
-        py_BaseException__stpush(&self->curr_exception,
-                                 frame->co->src,
-                                 Frame__lineno(frame),
-                                 !frame->is_locals_special ? frame->co->name->data : NULL);
-    __ERROR_RE_RAISE:
-        do {
-        } while(0);
-        int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
-        if(target >= 0) {
-            // 1. Exception can be handled inside the current frame
-            DISPATCH_JUMP_ABSOLUTE(target);
-        } else {
-            // 2. Exception need to be propagated to the upper frame
-            bool is_base_frame_to_be_popped = frame == base_frame;
-            VM__pop_frame(self);
-            if(self->top_frame == NULL || is_base_frame_to_be_popped) {
-                // propagate to the top level
-                return RES_ERROR;
-            }
-            frame = self->top_frame;
-            RESET_CO_CACHE();
-            goto __ERROR;
+    c11__unreachable();
+
+__ERROR:
+    py_BaseException__stpush(&self->curr_exception,
+                             frame->co->src,
+                             Frame__lineno(frame),
+                             !frame->is_locals_special ? frame->co->name->data : NULL);
+__ERROR_RE_RAISE:
+    do {
+    } while(0);
+    
+    int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
+    if(target >= 0) {
+        // 1. Exception can be handled inside the current frame
+        DISPATCH_JUMP_ABSOLUTE(target);
+    } else {
+        // 2. Exception need to be propagated to the upper frame
+        bool is_base_frame_to_be_popped = frame == base_frame;
+        VM__pop_frame(self);
+        if(self->top_frame == NULL || is_base_frame_to_be_popped) {
+            // propagate to the top level
+            return RES_ERROR;
         }
+        frame = self->top_frame;
+        RESET_CO_CACHE();
+        goto __ERROR;
     }
 
-    return RES_RETURN;
+    c11__unreachable();
 }
 
 const char* pk_op2str(py_Name op) {