diff --git a/include/pocketpy/interpreter/typeinfo.h b/include/pocketpy/interpreter/typeinfo.h index c3c500bd..da89426f 100644 --- a/include/pocketpy/interpreter/typeinfo.h +++ b/include/pocketpy/interpreter/typeinfo.h @@ -3,6 +3,10 @@ #include "pocketpy/pocketpy.h" #include "pocketpy/objects/object.h" +#define PK_MAGIC_SLOTS_COMMON_LENGTH (__missing__ - __xor__) +#define PK_MAGIC_SLOTS_UNCOMMON_LENGTH (__xor__ + 1) +#define PK_MAX_CHUNK_LENGTH 256 + typedef struct py_TypeInfo { py_Name name; py_Type base; @@ -22,12 +26,13 @@ typedef struct py_TypeInfo { void (*gc_mark)(void* ud); /* Magic Slots */ - py_TValue magic[64]; + py_TValue magic_0[PK_MAGIC_SLOTS_COMMON_LENGTH]; // common magic slots + py_TValue* magic_1; // uncommon magic slots } py_TypeInfo; typedef struct TypeList { int length; - py_TypeInfo* chunks[256]; + py_TypeInfo* chunks[PK_MAX_CHUNK_LENGTH]; } TypeList; void TypeList__ctor(TypeList* self); @@ -35,3 +40,7 @@ void TypeList__dtor(TypeList* self); py_TypeInfo* TypeList__get(TypeList* self, py_Type index); py_TypeInfo* TypeList__emplace(TypeList* self); void TypeList__apply(TypeList* self, void (*f)(py_TypeInfo*, void*), void* ctx); +py_TValue* TypeList__magic(py_TypeInfo* self, unsigned index); +py_TValue* TypeList__magic_readonly_nullable(py_TypeInfo* self, unsigned index); + +#define TypeList__magic_common(ti, index) ((ti)->magic_0 + ((index)-PK_MAGIC_SLOTS_UNCOMMON_LENGTH)) diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 671bbb5d..1ff28e10 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -1008,11 +1008,11 @@ FrameResult VM__run_top_frame(VM* self) { py_TypeInfo* base_ti = ti->base_ti; if(base_ti->on_end_subclass) base_ti->on_end_subclass(ti); } - if(!py_isnil(&ti->magic[__eq__])) { - if(py_isnil(&ti->magic[__ne__])) { - TypeError("'%n' implements '__eq__' but not '__ne__'", ti->name); - goto __ERROR; - } + py_TValue* slot_eq = TypeList__magic_common(ti, __eq__); + py_TValue* slot_ne = TypeList__magic_common(ti, __ne__); + if(!py_isnil(slot_eq) && py_isnil(slot_ne)) { + TypeError("'%n' implements '__eq__' but not '__ne__'", ti->name); + goto __ERROR; } } // class with decorator is unsafe currently diff --git a/src/interpreter/typeinfo.c b/src/interpreter/typeinfo.c index 361f48e4..8e6f2e5d 100644 --- a/src/interpreter/typeinfo.c +++ b/src/interpreter/typeinfo.c @@ -1,4 +1,5 @@ #include "pocketpy/interpreter/typeinfo.h" +#include "pocketpy/common/utils.h" #define CHUNK_SIZE 128 #define LOG2_CHUNK_SIZE 7 @@ -9,7 +10,11 @@ void TypeList__ctor(TypeList* self) { } void TypeList__dtor(TypeList* self) { - for (int i = 0; i < self->length; i++) { + for(py_Type t = 0; t < self->length; t++) { + py_TypeInfo* info = TypeList__get(self, t); + if(info->magic_1) PK_FREE(info->magic_1); + } + for(int i = 0; i < PK_MAX_CHUNK_LENGTH; i++) { if(self->chunks[i]) PK_FREE(self->chunks[i]); } } @@ -21,11 +26,13 @@ py_TypeInfo* TypeList__get(TypeList* self, py_Type index) { return self->chunks[chunk] + offset; } -py_TypeInfo* TypeList__emplace(TypeList* self){ +py_TypeInfo* TypeList__emplace(TypeList* self) { int chunk = self->length >> LOG2_CHUNK_SIZE; int offset = self->length & (CHUNK_SIZE - 1); - assert(chunk < 256); - if(self->chunks[chunk] == NULL){ + if(self->chunks[chunk] == NULL) { + if(chunk >= PK_MAX_CHUNK_LENGTH) { + c11__abort("TypeList__emplace(): max chunk length exceeded"); + } self->chunks[chunk] = PK_MALLOC(sizeof(py_TypeInfo) * CHUNK_SIZE); memset(self->chunks[chunk], 0, sizeof(py_TypeInfo) * CHUNK_SIZE); } @@ -34,11 +41,38 @@ py_TypeInfo* TypeList__emplace(TypeList* self){ } void TypeList__apply(TypeList* self, void (*f)(py_TypeInfo*, void*), void* ctx) { - for (int i = 0; i < self->length; i++) { + for(int i = 0; i < self->length; i++) { py_TypeInfo* info = TypeList__get(self, i); f(info, ctx); } } +py_TValue* TypeList__magic(py_TypeInfo* self, unsigned index) { + if(index > __xor__) { + // common magic slots + return TypeList__magic_common(self, index); + } + // uncommon magic slots + if(self->magic_1 == NULL) { + self->magic_1 = PK_MALLOC(sizeof(py_TValue) * PK_MAGIC_SLOTS_UNCOMMON_LENGTH); + memset(self->magic_1, 0, sizeof(py_TValue) * PK_MAGIC_SLOTS_UNCOMMON_LENGTH); + } + return self->magic_1 + index; +} + +py_TValue* TypeList__magic_readonly_nullable(py_TypeInfo* self, unsigned index) { + if(index > __xor__) { + // common magic slots + py_TValue* slot = TypeList__magic_common(self, index); + if(py_isnil(slot)) return NULL; + return slot; + } + // uncommon magic slots + if(self->magic_1 == NULL) return NULL; + py_TValue* slot = self->magic_1 + index; + if(py_isnil(slot)) return NULL; + return slot; +} + #undef CHUNK_SIZE #undef LOG2_CHUNK_SIZE \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 5c94ca93..8ab4d7b1 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -649,12 +649,20 @@ void ManagedHeap__mark(ManagedHeap* self) { // 0-th type is placeholder for(py_Type i = 1; i < types_length; i++) { py_TypeInfo* ti = TypeList__get(&vm->types, i); - // mark magic slots - for(int j = 0; j <= __missing__; j++) { - py_TValue* slot = ti->magic + j; + // mark common magic slots + for(int j = 0; j < PK_MAGIC_SLOTS_COMMON_LENGTH; j++) { + py_TValue* slot = ti->magic_0 + j; if(py_isnil(slot)) continue; pk__mark_value(slot); } + // mark uncommon magic slots + if(ti->magic_1) { + for(int j = 0; j < PK_MAGIC_SLOTS_UNCOMMON_LENGTH; j++) { + py_TValue* slot = ti->magic_1 + j; + if(py_isnil(slot)) continue; + pk__mark_value(slot); + } + } // mark type annotations pk__mark_value(&ti->annotations); } diff --git a/src/public/internal.c b/src/public/internal.c index 7ddf0c5d..66c35074 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -225,8 +225,8 @@ py_Ref py_tpfindmagic(py_Type t, py_Name name) { assert(py_ismagicname(name)); py_TypeInfo* ti = pk__type_info(t); do { - py_Ref f = &ti->magic[name]; - if(!py_isnil(f)) return f; + py_Ref f = TypeList__magic_readonly_nullable(ti, name); + if(f != NULL) return f; ti = ti->base_ti; } while(ti); return NULL; @@ -244,7 +244,8 @@ py_Ref py_tpfindname(py_Type t, py_Name name) { py_Ref py_tpgetmagic(py_Type type, py_Name name) { assert(py_ismagicname(name)); - return pk__type_info(type)->magic + name; + py_TypeInfo* ti = pk__type_info(type); + return TypeList__magic(ti, name); } py_Ref py_tpobject(py_Type type) { diff --git a/src/public/py_ops.c b/src/public/py_ops.c index 6825e412..53c1fa43 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -47,12 +47,12 @@ int py_bool(py_Ref val) { bool py_hash(py_Ref val, int64_t* out) { py_TypeInfo* ti = pk__type_info(val->type); do { - py_Ref _hash = &ti->magic[__hash__]; - if(py_isnone(_hash)) break; - py_Ref _eq = &ti->magic[__eq__]; - if(!py_isnil(_eq)) { - if(py_isnil(_hash)) break; - if(!py_call(_hash, 1, val)) return false; + py_Ref slot_hash = TypeList__magic_common(ti, __hash__); + if(py_isnone(slot_hash)) break; + py_Ref slot_eq = TypeList__magic_common(ti, __eq__); + if(!py_isnil(slot_eq)) { + if(py_isnil(slot_hash)) break; + if(!py_call(slot_hash, 1, val)) return false; if(!py_checkint(py_retval())) return false; *out = py_toint(py_retval()); return true;