Merge branch 'main' into sandbox

This commit is contained in:
blueloveTH 2026-08-05 23:27:43 +08:00
commit 612910ab2a
9 changed files with 123 additions and 61 deletions

View File

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

View File

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

View File

@ -17,7 +17,7 @@ rm -rf .coverage
mkdir .coverage
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
rm *.gcda

View File

@ -124,6 +124,20 @@ static bool type__annotations__(int argc, py_Ref argv) {
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() {
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_object, "__dict__", object__dict__, NULL);
py_bindproperty(tp_type, "__annotations__", type__annotations__, NULL);
py_bindmethod(tp_type, "__subclasses__", type__subclasses__);
}

View File

@ -189,8 +189,8 @@ static Error* LexerError(Lexer* self, const char* fmt, ...) {
err->src = self->src;
PK_INCREF(self->src);
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_start(args, fmt);
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 'f': c11_sbuf__write_char(buff, '\f'); 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 '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'};
int code;
if(sscanf(hex, "%x", &code) != 1 || code > 0xFF) {

View File

@ -579,8 +579,9 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
}
if(p0->type == tp_type) {
py_Type p0_type = py_totype(p0);
// [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));
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`
// >> [cls, NULL, args..., kwargs...]
// >> 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) {
// 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 {
if(is_default_new) {
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);
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;
}
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_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);
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;
}
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_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;
}
}
}

View File

@ -5,7 +5,7 @@
#include "pocketpy/common/name.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_finalized;

View File

@ -167,4 +167,14 @@ class DerivedClass(BaseClass):
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()

View File

@ -50,3 +50,21 @@ assert hasattr(a, 'zzz')
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