Explorar o código

Merge branch 'main' into sandbox

blueloveTH hai 6 días
pai
achega
612910ab2a

+ 2 - 2
.github/workflows/main.yml

@@ -56,11 +56,11 @@ jobs:
     - name: Setup Clang
     - name: Setup Clang
       uses: egor-tensin/setup-clang@v1
       uses: egor-tensin/setup-clang@v1
       with:
       with:
-        version: 17
+        version: 19
         platform: x64
         platform: x64
     - name: Run Sanitizers
     - name: Run Sanitizers
       run: |
       run: |
-        sudo apt-get install -y libclang-rt-17-dev
+        sudo apt-get install -y libclang-rt-19-dev
         bash build_g.sh
         bash build_g.sh
         bash run_tests.sh
         bash run_tests.sh
         rm -rf ./main
         rm -rf ./main

+ 1 - 1
include/pocketpy/objects/base.h

@@ -4,7 +4,7 @@
 
 
 typedef struct PyObject PyObject;
 typedef struct PyObject PyObject;
 typedef struct VM VM;
 typedef struct VM VM;
-extern _Thread_local VM* pk_current_vm;
+extern PK_THREAD_LOCAL VM* pk_current_vm;
 
 
 typedef struct py_TValue {
 typedef struct py_TValue {
     py_Type type;
     py_Type type;

+ 1 - 1
run_tests.sh

@@ -17,7 +17,7 @@ rm -rf .coverage
 mkdir .coverage
 mkdir .coverage
 
 
 UNITS=$(find ./ -name "*.gcno")
 UNITS=$(find ./ -name "*.gcno")
-llvm-cov-17 gcov ${UNITS} -r -s include/ -r -s src/ >> .coverage/coverage.txt
+llvm-cov-19 gcov ${UNITS} -r -s include/ -r -s src/ >> .coverage/coverage.txt
 
 
 mv *.gcov .coverage
 mv *.gcov .coverage
 rm *.gcda
 rm *.gcda

+ 15 - 0
src/bindings/py_object.c

@@ -124,6 +124,20 @@ static bool type__annotations__(int argc, py_Ref argv) {
     return true;
     return true;
 }
 }
 
 
+static bool type__subclasses__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(1);
+    py_TypeInfo* base_ti = py_touserdata(argv);
+    py_newlist(py_retval());
+
+    for(py_Type i = 1; i < pk_current_vm->types.length; i++) {
+        py_TypeInfo* ti = pk_typeinfo(i);
+        if(ti->base == base_ti->index) {
+            py_list_append(py_retval(), &ti->self);
+        }
+    }
+    return true;
+}
+
 void pk_object__register() {
 void pk_object__register() {
     py_bindmagic(tp_object, __new__, pk__object_new);
     py_bindmagic(tp_object, __new__, pk__object_new);
 
 
@@ -142,4 +156,5 @@ void pk_object__register() {
     py_bindproperty(tp_type, "__name__", type__name__, NULL);
     py_bindproperty(tp_type, "__name__", type__name__, NULL);
     py_bindproperty(tp_object, "__dict__", object__dict__, NULL);
     py_bindproperty(tp_object, "__dict__", object__dict__, NULL);
     py_bindproperty(tp_type, "__annotations__", type__annotations__, NULL);
     py_bindproperty(tp_type, "__annotations__", type__annotations__, NULL);
+    py_bindmethod(tp_type, "__subclasses__", type__subclasses__);
 }
 }

+ 10 - 3
src/compiler/lexer.c

@@ -189,8 +189,8 @@ static Error* LexerError(Lexer* self, const char* fmt, ...) {
     err->src = self->src;
     err->src = self->src;
     PK_INCREF(self->src);
     PK_INCREF(self->src);
     err->lineno = self->current_line;
     err->lineno = self->current_line;
-    const char* end = self->src->source->data + self->src->source->size;
-    if(self->curr_char <= end && *self->curr_char == '\n') { err->lineno--; }
+    const char* p_end = self->src->source->data + self->src->source->size;
+    if(self->curr_char <= p_end && *self->curr_char == '\n') { err->lineno--; }
     va_list args;
     va_list args;
     va_start(args, fmt);
     va_start(args, fmt);
     vsnprintf(err->msg, sizeof(err->msg), fmt, args);
     vsnprintf(err->msg, sizeof(err->msg), fmt, args);
@@ -282,9 +282,16 @@ static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringTy
                 case 'b': c11_sbuf__write_char(buff, '\b'); break;
                 case 'b': c11_sbuf__write_char(buff, '\b'); break;
                 case 'f': c11_sbuf__write_char(buff, '\f'); break;
                 case 'f': c11_sbuf__write_char(buff, '\f'); break;
                 case 'v': c11_sbuf__write_char(buff, '\v'); break;
                 case 'v': c11_sbuf__write_char(buff, '\v'); break;
-                // Special case for the often used \0 while we don't have full support for octal literals.
+                // Special case for the often used \0 while we don't have full support for octal
+                // literals.
                 case '0': c11_sbuf__write_char(buff, '\0'); break;
                 case '0': c11_sbuf__write_char(buff, '\0'); break;
                 case 'x': {
                 case 'x': {
+                    // check there are at least 2 chars can read
+                    const char* p_end = self->src->source->data + self->src->source->size;
+                    if(p_end - self->curr_char < 2) {
+                        return LexerError(self, "invalid hex escape");
+                    }
+
                     char hex[3] = {eatchar(self), eatchar(self), '\0'};
                     char hex[3] = {eatchar(self), eatchar(self), '\0'};
                     int code;
                     int code;
                     if(sscanf(hex, "%x", &code) != 1 || code > 0xFF) {
                     if(sscanf(hex, "%x", &code) != 1 || code > 0xFF) {

+ 64 - 52
src/interpreter/vm.c

@@ -579,8 +579,9 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
     }
     }
 
 
     if(p0->type == tp_type) {
     if(p0->type == tp_type) {
+        py_Type p0_type = py_totype(p0);
         // [cls, NULL, args..., kwargs...]
         // [cls, NULL, args..., kwargs...]
-        py_Ref new_f = py_tpfindmagic(py_totype(p0), __new__);
+        py_Ref new_f = py_tpfindmagic(p0_type, __new__);
         assert(new_f && py_isnil(p0 + 1));
         assert(new_f && py_isnil(p0 + 1));
         bool is_default_new = new_f->type == tp_nativefunc && new_f->_cfunc == pk__object_new;
         bool is_default_new = new_f->type == tp_nativefunc && new_f->_cfunc == pk__object_new;
 
 
@@ -598,14 +599,16 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
         // NOTE: previously we use `get_unbound_method` but here we just use `tpfindmagic`
         // NOTE: previously we use `get_unbound_method` but here we just use `tpfindmagic`
         // >> [cls, NULL, args..., kwargs...]
         // >> [cls, NULL, args..., kwargs...]
         // >> py_retval() is the new instance
         // >> py_retval() is the new instance
-        py_Ref init_f = py_tpfindmagic(py_totype(p0), __init__);
+        py_Ref init_f = py_tpfindmagic(p0_type, __init__);
         if(init_f) {
         if(init_f) {
-            // do an inplace patch
-            *p0 = *init_f;              // __init__
-            p0[1] = self->last_retval;  // self
-            // [__init__, self, args..., kwargs...]
-            if(VM__vectorcall(self, argc, kwargc, false) == RES_ERROR) return RES_ERROR;
-            *py_retval() = p0[1];  // restore the new instance
+            if(py_isinstance(py_retval(), p0_type)) {
+                // do an inplace patch
+                *p0 = *init_f;              // __init__
+                p0[1] = self->last_retval;  // self
+                // [__init__, self, args..., kwargs...]
+                if(VM__vectorcall(self, argc, kwargc, false) == RES_ERROR) return RES_ERROR;
+                *py_retval() = p0[1];  // restore the new instance
+            }
         } else {
         } else {
             if(is_default_new) {
             if(is_default_new) {
                 if(argc != 0 || kwargc != 0) {
                 if(argc != 0 || kwargc != 0) {
@@ -713,53 +716,62 @@ void ManagedHeap__mark(ManagedHeap* self) {
             }
             }
         }
         }
 
 
-        void* ud = PyObject__userdata(obj);
-        switch(obj->type) {
-            case tp_list: {
-                List* self = ud;
-                for(int i = 0; i < self->length; i++) {
-                    py_TValue* val = c11__at(py_TValue, self, i);
-                    pk__mark_value(val);
-                }
-                break;
+        if(obj->type > tp_object) {
+            // NOTE: `defaultdict` -> `dict` -> `object`
+            // NOTE: native types must extend from `object`.
+            py_TypeInfo* ti = pk_typeinfo(obj->type);
+            while(ti->base != tp_object) {
+                ti = ti->base_ti;
             }
             }
-            case tp_dict: {
-                Dict* self = ud;
-                for(int i = 0; i < self->entries.length; i++) {
-                    DictEntry* entry = c11__at(DictEntry, &self->entries, i);
-                    if(py_isnil(&entry->key)) continue;
-                    pk__mark_value(&entry->key);
-                    pk__mark_value(&entry->val);
+
+            void* ud = PyObject__userdata(obj);
+            switch(ti->index) {
+                case tp_list: {
+                    List* self = ud;
+                    for(int i = 0; i < self->length; i++) {
+                        py_TValue* val = c11__at(py_TValue, self, i);
+                        pk__mark_value(val);
+                    }
+                    break;
                 }
                 }
-                break;
-            }
-            case tp_generator: {
-                Generator* self = ud;
-                if(self->frame) Frame__gc_mark(self->frame, p_stack);
-                break;
-            }
-            case tp_function: {
-                function__gc_mark(ud, p_stack);
-                break;
-            }
-            case tp_BaseException: {
-                BaseException* self = ud;
-                pk__mark_value(&self->args);
-                pk__mark_value(&self->inner_exc);
-                c11__foreach(BaseExceptionFrame, &self->stacktrace, frame) {
-                    pk__mark_value(&frame->locals);
-                    pk__mark_value(&frame->globals);
+                case tp_dict: {
+                    Dict* self = ud;
+                    for(int i = 0; i < self->entries.length; i++) {
+                        DictEntry* entry = c11__at(DictEntry, &self->entries, i);
+                        if(py_isnil(&entry->key)) continue;
+                        pk__mark_value(&entry->key);
+                        pk__mark_value(&entry->val);
+                    }
+                    break;
+                }
+                case tp_generator: {
+                    Generator* self = ud;
+                    if(self->frame) Frame__gc_mark(self->frame, p_stack);
+                    break;
+                }
+                case tp_function: {
+                    function__gc_mark(ud, p_stack);
+                    break;
+                }
+                case tp_BaseException: {
+                    BaseException* self = ud;
+                    pk__mark_value(&self->args);
+                    pk__mark_value(&self->inner_exc);
+                    c11__foreach(BaseExceptionFrame, &self->stacktrace, frame) {
+                        pk__mark_value(&frame->locals);
+                        pk__mark_value(&frame->globals);
+                    }
+                    break;
+                }
+                case tp_code: {
+                    CodeObject* self = ud;
+                    CodeObject__gc_mark(self, p_stack);
+                    break;
+                }
+                case tp_chunked_array2d: {
+                    c11_chunked_array2d__mark(ud, p_stack);
+                    break;
                 }
                 }
-                break;
-            }
-            case tp_code: {
-                CodeObject* self = ud;
-                CodeObject__gc_mark(self, p_stack);
-                break;
-            }
-            case tp_chunked_array2d: {
-                c11_chunked_array2d__mark(ud, p_stack);
-                break;
             }
             }
         }
         }
     }
     }

+ 1 - 1
src/public/GlobalSetup.c

@@ -5,7 +5,7 @@
 #include "pocketpy/common/name.h"
 #include "pocketpy/common/name.h"
 #include "pocketpy/interpreter/vm.h"
 #include "pocketpy/interpreter/vm.h"
 
 
-_Thread_local VM* pk_current_vm;
+PK_THREAD_LOCAL VM* pk_current_vm;
 
 
 static bool pk_initialized;
 static bool pk_initialized;
 static bool pk_finalized;
 static bool pk_finalized;

+ 11 - 1
tests/410_class_ex.py

@@ -167,4 +167,14 @@ class DerivedClass(BaseClass):
         return super().f()
         return super().f()
 
 
     
     
-assert DerivedClass.f() == 'BaseClass'
+assert DerivedClass.f() == 'BaseClass'
+
+# bad __init__
+class A:
+    def __new__(cls, *args, **kwargs):
+        return 1
+
+    def __init__(self):
+        assert False
+
+A()

+ 18 - 0
tests/500_reflection.py

@@ -50,3 +50,21 @@ assert hasattr(a, 'zzz')
 
 
 assert not hasattr(a, '')
 assert not hasattr(a, '')
 
 
+class Base:
+    pass
+
+class Child1(Base):
+    pass
+
+class Child2(Base):
+    pass
+
+class GrandChild(Child1):
+    pass
+
+subs = Base.__subclasses__()
+assert type(subs) is list
+assert Child1 in subs
+assert Child2 in subs
+assert GrandChild not in subs
+