فهرست منبع

support real exceptions

blueloveTH 2 سال پیش
والد
کامیت
c6ec028730
18فایلهای تغییر یافته به همراه181 افزوده شده و 128 حذف شده
  1. 2 0
      .gitignore
  2. 2 2
      amalgamate.py
  3. 1 1
      include/pocketpy/common.h
  4. 3 6
      include/pocketpy/error.h
  5. 5 3
      include/pocketpy/lexer.h
  6. 1 1
      include/pocketpy/obj.h
  7. 1 0
      include/pocketpy/str.h
  8. 23 24
      include/pocketpy/vm.h
  9. 37 3
      python/builtins.py
  10. 19 11
      src/ceval.cpp
  11. 22 19
      src/compiler.cpp
  12. 1 1
      src/gc.cpp
  13. 2 9
      src/lexer.cpp
  14. 25 7
      src/pocketpy.cpp
  15. 9 8
      src/pocketpy_c.cpp
  16. 4 0
      src/str.cpp
  17. 23 10
      src/vm.cpp
  18. 1 23
      tests/99_builtin_func.py

+ 2 - 0
.gitignore

@@ -35,3 +35,5 @@ libpocketpy.dylib
 
 .xmake/
 
+libpocketpy.dylib.dSYM/
+main.dSYM/

+ 2 - 2
amalgamate.py

@@ -6,9 +6,9 @@ with open("include/pocketpy/opcodes.h", "rt", encoding='utf-8') as f:
 	OPCODES_TEXT = '\n' + f.read() + '\n'
 
 pipeline = [
-	["config.h", "export.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"],
+	["config.h", "export.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h"],
 	["obj.h", "dict.h", "codeobject.h", "frame.h"],
-	["gc.h", "vm.h", "ceval.h", "expr.h", "compiler.h", "repl.h"],
+	["gc.h", "vm.h", "ceval.h", "lexer.h", "expr.h", "compiler.h", "repl.h"],
 	["_generated.h", "cffi.h", "bindings.h", "iter.h", "base64.h", "csv.h", "collections.h", "random.h", "re.h", "linalg.h", "easing.h", "io.h"],
 	["pocketpy.h", "pocketpy_c.h"]
 ]

+ 1 - 1
include/pocketpy/common.h

@@ -23,7 +23,7 @@
 #include <bitset>
 #include <deque>
 
-#define PK_VERSION				"1.3.5"
+#define PK_VERSION				"1.3.6"
 
 #include "config.h"
 #include "export.h"

+ 3 - 6
include/pocketpy/error.h

@@ -56,14 +56,11 @@ struct Exception {
 
     int _ip_on_error;
     void* _code_on_error;
+
+    PyObject* _self;    // weak reference
     
     stack<ExceptionLine> stacktrace;
-
-    Exception(StrName type, Str msg): 
-        type(type), msg(msg), is_re(true), _ip_on_error(-1), _code_on_error(nullptr) {}
-    bool match_type(StrName t) const {
-        return this->type==t || t.sv()=="Exception";
-    }
+    Exception(StrName type): type(type), is_re(true), _ip_on_error(-1), _code_on_error(nullptr), _self(nullptr) {}
 
     template<typename... Args>
     void st_push(Args&&... args){

+ 5 - 3
include/pocketpy/lexer.h

@@ -3,6 +3,7 @@
 #include "common.h"
 #include "error.h"
 #include "str.h"
+#include "obj.h"
 
 namespace pkpy{
 
@@ -100,6 +101,7 @@ enum Precedence {
 enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES };
 
 struct Lexer {
+    VM* vm;
     std::shared_ptr<SourceData> src;
     const char* token_start;
     const char* curr_char;
@@ -129,12 +131,12 @@ struct Lexer {
     bool lex_one_token();
 
     /***** Error Reporter *****/
-    void throw_err(Str type, Str msg);
-    void throw_err(Str type, Str msg, int lineno, const char* cursor);
+    void throw_err(StrName type, Str msg);
+    void throw_err(StrName type, Str msg, int lineno, const char* cursor);
     void SyntaxError(Str msg){ throw_err("SyntaxError", msg); }
     void SyntaxError(){ throw_err("SyntaxError", "invalid syntax"); }
     void IndentationError(Str msg){ throw_err("IndentationError", msg); }
-    Lexer(std::shared_ptr<SourceData> src);
+    Lexer(VM* vm, std::shared_ptr<SourceData> src);
     std::vector<Token> run();
 };
 

+ 1 - 1
include/pocketpy/obj.h

@@ -198,7 +198,7 @@ inline void gc_mark_namedict(NameDict& t){
     });
 }
 
-Str obj_type_name(VM* vm, Type type);
+StrName obj_type_name(VM* vm, Type type);
 
 #if PK_DEBUG_NO_BUILTINS
 #define OBJ_NAME(obj) Str("<?>")

+ 1 - 0
include/pocketpy/str.h

@@ -140,6 +140,7 @@ struct SStream{
     SStream& operator<<(const std::string& s);
     SStream& operator<<(std::string_view s);
     SStream& operator<<(char c);
+    SStream& operator<<(StrName sn);
 
     template<typename T>
     SStream& operator<<(T val){

+ 23 - 24
include/pocketpy/vm.h

@@ -52,7 +52,7 @@ struct PyTypeInfo{
     PyObject* obj;      // never be garbage collected
     Type base;
     PyObject* mod;      // never be garbage collected
-    Str name;
+    StrName name;
     bool subclass_enabled;
 
     std::vector<StrName> annotated_fields;
@@ -219,7 +219,7 @@ public:
     }
 
     PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true);
-    Type _new_type_object(StrName name, Type base=0);
+    Type _new_type_object(StrName name, Type base=0, bool subclass_enabled=false);
     PyObject* _find_type_object(const Str& type);
 
     Type _type(const Str& type);
@@ -362,37 +362,36 @@ public:
     PyObject* py_next(PyObject* obj);
     
     /***** Error Reporter *****/
-    void _error(StrName name, const Str& msg){
-        _error(Exception(name, msg));
-    }
-
     void _raise(bool re_raise=false);
 
-    void StackOverflowError() { _error("StackOverflowError", ""); }
-    void IOError(const Str& msg) { _error("IOError", msg); }
-    void NotImplementedError(){ _error("NotImplementedError", ""); }
-    void TypeError(const Str& msg){ _error("TypeError", msg); }
-    void IndexError(const Str& msg){ _error("IndexError", msg); }
-    void ValueError(const Str& msg){ _error("ValueError", msg); }
-    void RuntimeError(const Str& msg){ _error("RuntimeError", msg); }
-    void ZeroDivisionError(const Str& msg){ _error("ZeroDivisionError", msg); }
-    void ZeroDivisionError(){ _error("ZeroDivisionError", "division by zero"); }
-    void NameError(StrName name){ _error("NameError", fmt("name ", name.escape() + " is not defined")); }
-    void UnboundLocalError(StrName name){ _error("UnboundLocalError", fmt("local variable ", name.escape() + " referenced before assignment")); }
-    void KeyError(PyObject* obj){ _error("KeyError", PK_OBJ_GET(Str, py_repr(obj))); }
+    void _builtin_error(StrName type);
+    void _builtin_error(StrName type, PyObject* arg);
+    void _builtin_error(StrName type, const Str& msg);
+
+    void StackOverflowError() { _builtin_error("StackOverflowError"); }
+    void IOError(const Str& msg) { _builtin_error("IOError", msg); }
+    void NotImplementedError(){ _builtin_error("NotImplementedError"); }
+    void TypeError(const Str& msg){ _builtin_error("TypeError", msg); }
+    void IndexError(const Str& msg){ _builtin_error("IndexError", msg); }
+    void ValueError(const Str& msg){ _builtin_error("ValueError", msg); }
+    void RuntimeError(const Str& msg){ _builtin_error("RuntimeError", msg); }
+    void ZeroDivisionError(const Str& msg){ _builtin_error("ZeroDivisionError", msg); }
+    void ZeroDivisionError(){ _builtin_error("ZeroDivisionError", "division by zero"); }
+    void NameError(StrName name){ _builtin_error("NameError", fmt("name ", name.escape() + " is not defined")); }
+    void UnboundLocalError(StrName name){ _builtin_error("UnboundLocalError", fmt("local variable ", name.escape() + " referenced before assignment")); }
+    void KeyError(PyObject* obj){ _builtin_error("KeyError", obj); }
     void BinaryOptError(const char* op) { TypeError(fmt("unsupported operand type(s) for ", op)); }
-    void ImportError(const Str& msg){ _error("ImportError", msg); }
+    void ImportError(const Str& msg){ _builtin_error("ImportError", msg); }
 
     void AttributeError(PyObject* obj, StrName name){
         // OBJ_NAME calls getattr, which may lead to a infinite recursion
         if(isinstance(obj, vm->tp_type)){
-            _error("AttributeError", fmt("type object ", OBJ_NAME(obj).escape(), " has no attribute ", name.escape()));
+            _builtin_error("AttributeError", fmt("type object ", OBJ_NAME(obj).escape(), " has no attribute ", name.escape()));
         }else{
-            _error("AttributeError", fmt(OBJ_NAME(_t(obj)).escape(), " object has no attribute ", name.escape()));
+            _builtin_error("AttributeError", fmt(OBJ_NAME(_t(obj)).escape(), " object has no attribute ", name.escape()));
         }
     }
-
-    void AttributeError(Str msg){ _error("AttributeError", msg); }
+    void AttributeError(const Str& msg){ _builtin_error("AttributeError", msg); }
 
     void check_type(PyObject* obj, Type type){
         if(is_type(obj, type)) return;
@@ -471,7 +470,7 @@ public:
     PyObject* bind_method(PyObject*, Str, NativeFuncC);
     template<int ARGC>
     PyObject* bind_func(PyObject*, Str, NativeFuncC);
-    void _error(Exception);
+    void _error(PyObject*);
     PyObject* _run_top_frame();
     void post_init();
     PyObject* _py_generator(Frame&& frame, ArgsView buffer);

+ 37 - 3
python/builtins.py

@@ -268,9 +268,6 @@ def help(obj):
     print(obj.__doc__)
 
 
-class Exception: pass
-
-
 class classmethod:
     def __init__(self, f):
         self.f = f
@@ -287,3 +284,40 @@ def complex(*args, **kwargs):
 def long(*args, **kwargs):
     import _long
     return _long.long(*args, **kwargs)
+
+
+# builtin exceptions
+class SyntaxError(Exception): pass
+class IndentationError(SyntaxError): pass
+
+class StackOverflowError(Exception): pass
+class IOError(Exception): pass
+class NotImplementedError(Exception): pass
+class TypeError(Exception): pass
+class IndexError(Exception): pass
+class ValueError(Exception): pass
+class RuntimeError(Exception): pass
+class ZeroDivisionError(Exception): pass
+class NameError(Exception): pass
+class UnboundLocalError(Exception): pass
+class AttributeError(Exception): pass
+class ImportError(Exception): pass
+class AssertionError(Exception): pass
+
+class KeyError(Exception):
+    def __init__(self, key=...):
+        self.key = key
+        if key is ...:
+            super().__init__()
+        else:
+            super().__init__(repr(key))
+
+    def __str__(self):
+        if self.key is ...:
+            return ''
+        return str(self.key)
+    
+    def __repr__(self):
+        if self.key is ...:
+            return 'KeyError()'
+        return f'KeyError({self.key!r})'

+ 19 - 11
src/ceval.cpp

@@ -568,7 +568,7 @@ __NEXT_STEP:;
     TARGET(GOTO) {
         StrName _name(byte.arg);
         int index = co->labels.try_get_likely_found(_name);
-        if(index < 0) _error("KeyError", fmt("label ", _name.escape(), " not found"));
+        if(index < 0) RuntimeError(fmt("label ", _name.escape(), " not found"));
         frame->jump_abs_break(index);
     } DISPATCH();
     /*****************************************/
@@ -787,20 +787,28 @@ __NEXT_STEP:;
         DISPATCH();
     /*****************************************/
     TARGET(EXCEPTION_MATCH) {
-        const auto& e = CAST(Exception&, TOP());
-        PUSH(VAR(e.match_type(StrName(byte.arg))));
+        PyObject* assumed_type = POPX();
+        check_non_tagged_type(assumed_type, tp_type);
+        PyObject* e_obj = TOP();
+        bool ok = isinstance(e_obj, PK_OBJ_GET(Type, assumed_type));
+        PUSH(VAR(ok));
     } DISPATCH();
     TARGET(RAISE) {
-        PyObject* _0 = POPX();
-        Str msg = _0 == None ? "" : CAST(Str, py_str(_0));
-        _error(StrName(byte.arg), msg);
+        if(is_non_tagged_type(TOP(), tp_type)){
+            TOP() = call(TOP());
+        }
+        if(!isinstance(TOP(), tp_exception)){
+            _builtin_error("TypeError", "exceptions must derive from Exception");
+            UNREACHABLE();
+        }
+        _error(POPX());
     } DISPATCH();
     TARGET(RAISE_ASSERT)
         if(byte.arg){
             PyObject* _0 = py_str(POPX());
-            _error("AssertionError", CAST(Str, _0));
+            _builtin_error("AssertionError", CAST(Str, _0));
         }else{
-            _error("AssertionError", "");
+            _builtin_error("AssertionError");
         }
         DISPATCH();
     TARGET(RE_RAISE) _raise(true); DISPATCH();
@@ -853,8 +861,8 @@ __NEXT_STEP:;
             continue;
         }catch(UnhandledException& e){
             PK_UNUSED(e);
-            PyObject* obj = POPX();
-            Exception& _e = CAST(Exception&, obj);
+            PyObject* e_obj = POPX();
+            Exception& _e = PK_OBJ_GET(Exception, e_obj);
             _pop_frame();
             if(callstack.empty()){
 #if PK_DEBUG_FULL_EXCEPTION
@@ -863,7 +871,7 @@ __NEXT_STEP:;
                 throw _e;
             }
             frame = top_frame();
-            PUSH(obj);
+            PUSH(e_obj);
             if(frame.index < base_id) throw ToBeRaisedException();
             need_raise = true;
         }catch(ToBeRaisedException& e){

+ 22 - 19
src/compiler.cpp

@@ -38,14 +38,14 @@ namespace pkpy{
             SyntaxError("maximum number of local variables exceeded");
         }
         if(ctx()->co->consts.size() > 65535){
-            std::map<std::string, int> counts;
-            for(PyObject* c: ctx()->co->consts){
-                std::string key = obj_type_name(vm, vm->_tp(c)).str();
-                counts[key] += 1;
-            }
-            for(auto pair: counts){
-                std::cout << pair.first << ": " << pair.second << std::endl;
-            }
+            // std::map<std::string_view, int> counts;
+            // for(PyObject* c: ctx()->co->consts){
+            //     std::string_view key = obj_type_name(vm, vm->_tp(c)).sv();
+            //     counts[key] += 1;
+            // }
+            // for(auto pair: counts){
+            //     std::cout << pair.first << ": " << pair.second << std::endl;
+            // }
             SyntaxError("maximum number of constants exceeded");
         }
         if(codes.size() > 65535 && ctx()->co->src->mode != JSON_MODE){
@@ -684,8 +684,9 @@ __EAT_DOTS_END:
         do {
             StrName as_name;
             consume(TK("except"));
-            if(match(TK("@id"))){
-                ctx()->emit_(OP_EXCEPTION_MATCH, StrName(prev().sv()).index, prev().line);
+            if(is_expression()){
+                EXPR(false);      // push assumed type on to the stack
+                ctx()->emit_(OP_EXCEPTION_MATCH, BC_NOARG, prev().line);
                 if(match(TK("as"))){
                     consume(TK("@id"));
                     as_name = StrName(prev().sv());
@@ -880,14 +881,8 @@ __EAT_DOTS_END:
                 consume_end_stmt();
                 break;
             case TK("raise"): {
-                consume(TK("@id"));
-                int dummy_t = StrName(prev().sv()).index;
-                if(match(TK("(")) && !match(TK(")"))){
-                    EXPR(false); consume(TK(")"));
-                }else{
-                    ctx()->emit_(OP_LOAD_NONE, BC_NOARG, kw_line);
-                }
-                ctx()->emit_(OP_RAISE, dummy_t, kw_line);
+                EXPR(false);
+                ctx()->emit_(OP_RAISE, BC_NOARG, kw_line);
                 consume_end_stmt();
             } break;
             case TK("del"): {
@@ -1161,7 +1156,7 @@ __EAT_DOTS_END:
         this->used = false;
         this->unknown_global_scope = unknown_global_scope;
         this->lexer = std::make_unique<Lexer>(
-            std::make_shared<SourceData>(source, filename, mode)
+            vm, std::make_shared<SourceData>(source, filename, mode)
         );
         init_pratt_rules();
     }
@@ -1202,4 +1197,12 @@ __EAT_DOTS_END:
         return code;
     }
 
+    // TODO: refactor this
+    void Lexer::throw_err(StrName type, Str msg, int lineno, const char* cursor){
+        PyObject* e_obj = vm->call(vm->builtins->attr(type), VAR(msg));
+        Exception& e = PK_OBJ_GET(Exception, e_obj);
+        e.st_push(src, lineno, cursor, "");
+        e._self = e_obj;
+        throw e;
+    }
 }   // namespace pkpy

+ 1 - 1
src/gc.cpp

@@ -52,7 +52,7 @@ namespace pkpy{
         for(PyObject* obj: gen) { obj->~PyObject(); pool64_dealloc(obj); }
 #if PK_DEBUG_GC_STATS
         for(auto& [type, count]: deleted){
-            std::cout << "GC: " << obj_type_name(vm, type) << "=" << count << std::endl;
+            std::cout << "GC: " << obj_type_name(vm, type).sv() << "=" << count << std::endl;
         }
 #endif
     }

+ 2 - 9
src/lexer.cpp

@@ -455,7 +455,7 @@ static bool is_unicode_Lo_char(uint32_t c) {
         return false;
     }
 
-    void Lexer::throw_err(Str type, Str msg){
+    void Lexer::throw_err(StrName type, Str msg){
         int lineno = current_line;
         const char* cursor = curr_char;
         if(peekchar() == '\n'){
@@ -465,14 +465,7 @@ static bool is_unicode_Lo_char(uint32_t c) {
         throw_err(type, msg, lineno, cursor);
     }
 
-    void Lexer::throw_err(Str type, Str msg, int lineno, const char* cursor){
-        Exception e(type, msg);
-        e.st_push(src, lineno, cursor, "");
-        throw e;
-    }
-
-    Lexer::Lexer(std::shared_ptr<SourceData> src) {
-        this->src = src;
+    Lexer::Lexer(VM* vm, std::shared_ptr<SourceData> src) : vm(vm), src(src) {
         this->token_start = src->source.c_str();
         this->curr_char = src->source.c_str();
         this->nexts.push_back(Token{TK("@sof"), token_start, 0, current_line, brackets_level});

+ 25 - 7
src/pocketpy.cpp

@@ -73,8 +73,8 @@ void init_builtins(VM* _vm) {
         vm->check_non_tagged_type(class_arg, vm->tp_type);
         Type type = PK_OBJ_GET(Type, class_arg);
         if(!vm->isinstance(self_arg, type)){
-            Str _0 = obj_type_name(vm, vm->_tp(self_arg));
-            Str _1 = obj_type_name(vm, type);
+            StrName _0 = obj_type_name(vm, vm->_tp(self_arg));
+            StrName _1 = obj_type_name(vm, type);
             vm->TypeError("super(): " + _0.escape() + " is not an instance of " + _1.escape());
         }
         return vm->heap.gcnew<Super>(vm->tp_super, self_arg, vm->_all_types[type].base);
@@ -1296,9 +1296,27 @@ void init_builtins(VM* _vm) {
     // });
 
     // Exception
+    _vm->bind_constructor<-1>("Exception", [](VM* vm, ArgsView args){
+        Type cls = PK_OBJ_GET(Type, args[0]);
+        StrName cls_name = obj_type_name(vm, cls);
+        PyObject* e_obj = vm->heap.gcnew<Exception>(cls, cls_name);
+        e_obj->_enable_instance_dict();
+        return e_obj;
+    });
+
+    _vm->bind(_vm->_t(_vm->tp_exception), "__init__(self, msg=...)", [](VM* vm, ArgsView args){
+        Exception& self = _CAST(Exception&, args[0]);
+        if(args[1] == vm->Ellipsis){
+            self.msg = "";
+        }else{
+            self.msg = CAST(Str, args[1]);
+        }
+        return vm->None;
+    });
+
     _vm->bind__repr__(_vm->tp_exception, [](VM* vm, PyObject* obj) {
         Exception& self = _CAST(Exception&, obj);
-        return VAR(fmt(self.type.sv(), '(', self.msg.escape(), ')'));
+        return VAR(fmt(obj_type_name(vm, obj->type), '(', self.msg.escape(), ')'));
     });
 
     _vm->bind__str__(_vm->tp_exception, [](VM* vm, PyObject* obj) {
@@ -1616,7 +1634,7 @@ void VM::post_init(){
     });
     bind_property(_t(tp_type), "__name__", [](VM* vm, ArgsView args){
         const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])];
-        return VAR(info.name);
+        return VAR(info.name.sv());
     });
     bind_property(_t(tp_type), "__module__", [](VM* vm, ArgsView args){
         const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])];
@@ -1675,7 +1693,7 @@ void VM::post_init(){
         this->_exec(code, this->builtins);
         code = compile(kPythonLibs["_set"], "<set>", EXEC_MODE);
         this->_exec(code, this->builtins);
-    }catch(Exception& e){
+    }catch(const Exception& e){
         std::cerr << e.summary() << std::endl;
         std::cerr << "failed to load builtins module!!" << std::endl;
         exit(1);
@@ -1705,11 +1723,11 @@ CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool unknown
     Compiler compiler(this, std::move(source), filename, mode, unknown_global_scope);
     try{
         return compiler.compile();
-    }catch(Exception& e){
+    }catch(const Exception& e){
 #if PK_DEBUG_FULL_EXCEPTION
         std::cerr << e.summary() << std::endl;
 #endif
-        _error(e);
+        _error(e._self);
         return nullptr;
     }
 }

+ 9 - 8
src/pocketpy_c.cpp

@@ -44,12 +44,12 @@ static PyObject* stack_item(VM* vm, int index){
 
 #define PK_PROTECTED(__B) \
     try{ __B }  \
-    catch(Exception& e ) { \
-        vm->_c.error = py_var(vm, e); \
+    catch(const Exception& e ) { \
+        vm->_c.error = e._self; \
         return false; \
     } catch(const std::exception& re){ \
-        auto e = Exception("std::exception", re.what()); \
-        vm->_c.error = py_var(vm, e); \
+        PyObject* e_t = vm->_t(vm->tp_exception); \
+        vm->_c.error = vm->call(e_t, VAR(re.what())); \
         return false; \
     }
 
@@ -338,9 +338,9 @@ static PyObject* c_function_wrapper(VM* vm, ArgsView args) {
 
     // propagate_if_errored
     if (vm->_c.error != nullptr){
-        Exception e = _py_cast<Exception&>(vm, vm->_c.error);
+        PyObject* e_obj = PK_OBJ_GET(Exception, vm->_c.error)._self;
         vm->_c.error = nullptr;
-        vm->_error(e);
+        vm->_error(e_obj);
         return nullptr;
     }
     PK_ASSERT(retc == vm->s_data._sp-curr_sp);
@@ -495,7 +495,8 @@ bool pkpy_py_str(pkpy_vm* vm_handle) {
 bool pkpy_error(pkpy_vm* vm_handle, const char* name, pkpy_CString message) {
     VM* vm = (VM*) vm_handle;
     PK_ASSERT_NO_ERROR()
-    vm->_c.error = py_var(vm, Exception(name, Str(message.data, message.size)));
+    PyObject* e_t = vm->_t(vm->tp_exception);
+    vm->_c.error = vm->call(e_t, VAR(std::string_view(message.data, message.size)));
     return false;
 }
 
@@ -508,7 +509,7 @@ bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) {
     VM* vm = (VM*) vm_handle;
     // no error
     if (vm->_c.error == nullptr) return false;
-    Exception& e = _py_cast<Exception&>(vm, vm->_c.error);
+    Exception& e = PK_OBJ_GET(Exception, vm->_c.error);
     if (message != nullptr)
         *message = e.summary().c_str_dup();
     else

+ 4 - 0
src/str.cpp

@@ -465,6 +465,10 @@ int utf8len(unsigned char c, bool suppress){
         return *this;
     }
 
+    SStream& SStream::operator<<(StrName sn){
+        return *this << sn.sv();
+    }
+
     SStream& SStream::operator<<(i64 val){
         // str(-2**64).__len__() == 21
         buffer.reserve(buffer.size() + 24);

+ 23 - 10
src/vm.cpp

@@ -1,4 +1,5 @@
 #include "pocketpy/vm.h"
+#include "pocketpy/error.h"
 
 namespace pkpy{
 
@@ -172,11 +173,15 @@ namespace pkpy{
             _stderr(sum.data, sum.size);
         }
 #if !PK_DEBUG_FULL_EXCEPTION
-        catch (const std::exception& e) {
+        catch(const std::exception& e) {
             Str msg = "An std::exception occurred! It could be a bug.\n";
             msg = msg + e.what() + "\n";
             _stderr(msg.data, msg.size);
         }
+        catch(...) {
+            Str msg = "An unknown exception occurred! It could be a bug. Please report it to @blueloveTH on GitHub.\n";
+            _stderr(msg.data, msg.size);
+        }
 #endif
         callstack.clear();
         s_data.clear();
@@ -201,15 +206,15 @@ namespace pkpy{
             obj,
             base,
             mod,
-            name.sv(),
+            name,
             subclass_enabled,
         };
         _all_types.push_back(info);
         return obj;
     }
 
-    Type VM::_new_type_object(StrName name, Type base) {
-        PyObject* obj = new_type_object(nullptr, name, base, false);
+    Type VM::_new_type_object(StrName name, Type base, bool subclass_enabled) {
+        PyObject* obj = new_type_object(nullptr, name, base, subclass_enabled);
         return PK_OBJ_GET(Type, obj);
     }
 
@@ -725,7 +730,7 @@ void VM::init_builtin_types(){
     PK_ASSERT(tp_bound_method == _new_type_object("bound_method"));
 
     PK_ASSERT(tp_super == _new_type_object("super"));
-    PK_ASSERT(tp_exception == _new_type_object("_Exception"));
+    PK_ASSERT(tp_exception == _new_type_object("Exception", 0, true));
     PK_ASSERT(tp_bytes == _new_type_object("bytes"));
     PK_ASSERT(tp_mappingproxy == _new_type_object("mappingproxy"));
     PK_ASSERT(tp_dict == _new_type_object("dict"));
@@ -757,6 +762,7 @@ void VM::init_builtin_types(){
     builtins->attr().set("StopIteration", StopIteration);
     builtins->attr().set("NotImplemented", NotImplemented);
     builtins->attr().set("slice", _t(tp_slice));
+    builtins->attr().set("Exception", _t(tp_exception));
 
     post_init();
     this->_main = new_module("__main__");
@@ -964,7 +970,7 @@ __FAST_CALL:
         if(new_f == cached_object__new__) {
             // fast path for object.__new__
             Type t = PK_OBJ_GET(Type, callable);
-            obj= vm->heap.gcnew<DummyInstance>(t);
+            obj = vm->heap.gcnew<DummyInstance>(t);
         }else{
             PUSH(new_f);
             PUSH(PY_NULL);
@@ -1134,7 +1140,7 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native
     try{
         // fn(a, b, *c, d=1) -> None
         co = compile("def " + Str(sig) + " : pass", "<bind>", EXEC_MODE);
-    }catch(Exception&){
+    }catch(const Exception&){
         throw std::runtime_error("invalid signature: " + std::string(sig));
     }
     if(co->func_decls.size() != 1){
@@ -1163,12 +1169,19 @@ PyObject* VM::bind_property(PyObject* obj, Str name, NativeFuncC fget, NativeFun
     return prop;
 }
 
-void VM::_error(Exception e){
+void VM::_builtin_error(StrName type){ _error(call(builtins->attr(type))); }
+void VM::_builtin_error(StrName type, PyObject* arg){ _error(call(builtins->attr(type), arg)); }
+void VM::_builtin_error(StrName type, const Str& msg){ _builtin_error(type, VAR(msg)); }
+
+void VM::_error(PyObject* e_obj){
+    PK_ASSERT(isinstance(e_obj, tp_exception))
+    Exception& e = PK_OBJ_GET(Exception, e_obj);
+    e._self = e_obj;
     if(callstack.empty()){
         e.is_re = false;
         throw e;
     }
-    PUSH(VAR(std::move(e)));
+    PUSH(e_obj);
     _raise();
 }
 
@@ -1203,7 +1216,7 @@ void ManagedHeap::mark() {
     for(auto [_, co]: vm->_cached_codes) co->_gc_mark();
 }
 
-Str obj_type_name(VM *vm, Type type){
+StrName obj_type_name(VM *vm, Type type){
     return vm->_all_types[type].name;
 }
 

+ 1 - 23
tests/99_builtin_func.py

@@ -1,25 +1,3 @@
-# 无法测试 -----------------------------------------------
-#     #####:   41:static dylib_entry_t load_dylib(const char* path){
-#     #####:   42:    std::error_code ec;
-#     #####:   43:    auto p = std::filesystem::absolute(path, ec);
-#     #####:   44:    if(ec) return nullptr;
-#     #####:   45:    void* handle = dlopen(p.c_str(), RTLD_LAZY);
-#     #####:   46:    if(!handle) return nullptr;
-#     #####:   47:    return (dylib_entry_t)dlsym(handle, "pkpy_module__init__");
-#     #####:   48:}
-
-# -----------------------------------------------
-#       128:  107:    _vm->bind_builtin_func<2>("super", [](VM* vm, ArgsView args) {
-#         8:  108:        vm->check_non_tagged_type(args[0], vm->tp_type);
-#         8:  109:        Type type = PK_OBJ_GET(Type, args[0]);
-#         8:  110:        if(!vm->isinstance(args[1], type)){
-#     #####:  111:            Str _0 = obj_type_name(vm, PK_OBJ_GET(Type, vm->_t(args[1])));
-#     #####:  112:            Str _1 = obj_type_name(vm, type);
-#     #####:  113:            vm->TypeError("super(): " + _0.escape() + " is not an instance of " + _1.escape());
-#     #####:  114:        }
-#         8:  115:        Type base = vm->_all_types[type].base;
-#        16:  116:        return vm->heap.gcnew(vm->tp_super, Super(args[1], base));
-#         8:  117:    });
 # test super:
 class TestSuperBase():
     def __init__(self):
@@ -29,7 +7,7 @@ class TestSuperBase():
         return self.base_attr
     
     def error(self):
-        raise Expection('未能拦截错误')
+        raise Exception('未能拦截错误')
     
 
 class TestSuperChild1(TestSuperBase):