diff --git a/include/pocketpy/interpreter/typeinfo.h b/include/pocketpy/interpreter/typeinfo.h new file mode 100644 index 00000000..ffd0b060 --- /dev/null +++ b/include/pocketpy/interpreter/typeinfo.h @@ -0,0 +1,36 @@ +#pragma once + +#include "pocketpy/pocketpy.h" +#include "pocketpy/objects/object.h" + +typedef struct py_TypeInfo { + py_Name name; + py_Type base; + + py_TValue self; + py_TValue module; // the module where the type is defined + + bool is_python; // is it a python class? (not derived from c object) + bool is_sealed; // can it be subclassed? + + void (*dtor)(void*); + + py_TValue annotations; // type annotations + + void (*on_end_subclass)(struct py_TypeInfo*); // backdoor for enum module + void (*gc_mark)(void* ud); + + /* Magic Slots */ + py_TValue magic[64]; +} py_TypeInfo; + +typedef struct TypeList { + int length; + py_TypeInfo* chunks[256]; +} TypeList; + +void TypeList__ctor(TypeList* self); +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); diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index fd720667..aa1887b4 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -5,6 +5,7 @@ #include "pocketpy/interpreter/heap.h" #include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/modules.h" +#include "pocketpy/interpreter/typeinfo.h" // TODO: // 1. __eq__ and __ne__ fallbacks @@ -15,32 +16,11 @@ // 6. py_TypeInfo // 7. Direct assignment of py_NIL, py_True, py_False, py_None. They are slow. -typedef struct py_TypeInfo { - py_Name name; - py_Type base; - - py_TValue self; - py_TValue module; // the module where the type is defined - - bool is_python; // is it a python class? (not derived from c object) - bool is_sealed; // can it be subclassed? - - void (*dtor)(void*); - - py_TValue annotations; // type annotations - - void (*on_end_subclass)(struct py_TypeInfo*); // backdoor for enum module - void (*gc_mark)(void* ud); - - /* Magic Slots */ - py_TValue magic[64]; -} py_TypeInfo; - typedef struct VM { Frame* top_frame; ModuleDict modules; - c11_vector /*T=py_TypeInfo*/ types; + TypeList types; py_TValue builtins; // builtins module py_TValue main; // __main__ module @@ -74,6 +54,7 @@ void pk__mark_value(py_TValue*); void pk__mark_namedict(NameDict*); void pk__tp_set_marker(py_Type type, void (*gc_mark)(void*)); bool pk__object_new(int argc, py_Ref argv); +py_TypeInfo* pk__type_info(py_Type type); bool pk_wrapper__self(int argc, py_Ref argv); bool pk_wrapper__NotImplementedError(int argc, py_Ref argv); diff --git a/include/pocketpy/objects/namedict.h b/include/pocketpy/objects/namedict.h index c68463db..c74e6ebf 100644 --- a/include/pocketpy/objects/namedict.h +++ b/include/pocketpy/objects/namedict.h @@ -24,4 +24,4 @@ void ModuleDict__ctor(ModuleDict* self, const char* path, py_TValue module); void ModuleDict__dtor(ModuleDict* self); void ModuleDict__set(ModuleDict* self, const char* key, py_TValue val); py_TValue* ModuleDict__try_get(ModuleDict* self, const char* path); -bool ModuleDict__contains(ModuleDict* self, const char* path); \ No newline at end of file +bool ModuleDict__contains(ModuleDict* self, const char* path); diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 2a0a47a5..0746ed8b 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -250,18 +250,18 @@ PK_EXPORT bool py_isinstance(py_Ref obj, py_Type type); /// Check if the derived type is a subclass of the base type. PK_EXPORT bool py_issubclass(py_Type derived, py_Type base); +/// Get the magic method from the given type only. +/// The returned reference is always valid. However, its value may be `nil`. +PK_EXPORT py_GlobalRef py_tpgetmagic(py_Type type, py_Name name); /// Search the magic method from the given type to the base type. /// Return `NULL` if not found. -PK_EXPORT py_ItemRef py_tpfindmagic(py_Type, py_Name name); +PK_EXPORT py_GlobalRef py_tpfindmagic(py_Type, py_Name name); /// Search the name from the given type to the base type. /// Return `NULL` if not found. PK_EXPORT py_ItemRef py_tpfindname(py_Type, py_Name name); -/// Get the magic method from the given type only. -/// The returned reference is always valid. However, its value may be `nil`. -PK_EXPORT py_ItemRef py_tpgetmagic(py_Type type, py_Name name); /// Get the type object of the given type. -PK_EXPORT py_ItemRef py_tpobject(py_Type type); +PK_EXPORT py_GlobalRef py_tpobject(py_Type type); /// Get the type name. PK_EXPORT const char* py_tpname(py_Type type); /// Call a type to create a new instance. diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 142196db..98aac846 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -871,7 +871,7 @@ FrameResult VM__run_top_frame(VM* self) { } POP(); - py_TypeInfo* base_ti = c11__at(py_TypeInfo, &self->types, base); + py_TypeInfo* base_ti = TypeList__get(&self->types, base); if(base_ti->is_sealed) { TypeError("type '%t' is not an acceptable base type", base); goto __ERROR; @@ -892,9 +892,9 @@ FrameResult VM__run_top_frame(VM* self) { if(py_istype(TOP(), tp_type)) { // call on_end_subclass - py_TypeInfo* ti = c11__at(py_TypeInfo, &self->types, py_totype(TOP())); + py_TypeInfo* ti = TypeList__get(&self->types, py_totype(TOP())); if(ti->base != tp_object) { - py_TypeInfo* base_ti = c11__at(py_TypeInfo, &self->types, ti->base); + py_TypeInfo* base_ti = TypeList__get(&self->types, ti->base); if(base_ti->on_end_subclass) base_ti->on_end_subclass(ti); } } @@ -915,7 +915,7 @@ FrameResult VM__run_top_frame(VM* self) { case OP_ADD_CLASS_ANNOTATION: { // [type_hint string] py_Type type = py_totype(self->__curr_class); - py_TypeInfo* ti = c11__at(py_TypeInfo, &self->types, type); + py_TypeInfo* ti = TypeList__get(&self->types, type); if(py_isnil(&ti->annotations)) py_newdict(&ti->annotations); bool ok = py_dict_setitem_by_str(&ti->annotations, py_name2str(byte.arg), TOP()); if(!ok) goto __ERROR; diff --git a/src/interpreter/typeinfo.c b/src/interpreter/typeinfo.c new file mode 100644 index 00000000..f9dac167 --- /dev/null +++ b/src/interpreter/typeinfo.c @@ -0,0 +1,44 @@ +#include "pocketpy/interpreter/typeinfo.h" + +#define CHUNK_SIZE 128 +#define LOG2_CHUNK_SIZE 7 + +static py_TypeInfo firstChunk[CHUNK_SIZE]; + +void TypeList__ctor(TypeList* self) { + self->length = 0; + memset(self->chunks, 0, sizeof(self->chunks)); + self->chunks[0] = firstChunk; +} + +void TypeList__dtor(TypeList* self) { + for (int i = 1; i < self->length; i++) { + if(self->chunks[i]) free(self->chunks[i]); + } +} + +py_TypeInfo* TypeList__get(TypeList* self, py_Type index) { + assert(index < self->length); + int chunk = index >> LOG2_CHUNK_SIZE; + int offset = index & (CHUNK_SIZE - 1); + return self->chunks[chunk] + offset; +} + +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){ + self->chunks[chunk] = malloc(sizeof(py_TypeInfo) * CHUNK_SIZE); + memset(self->chunks[chunk], 0, sizeof(py_TypeInfo) * CHUNK_SIZE); + } + self->length++; + return self->chunks[chunk] + offset; +} + +void TypeList__apply(TypeList* self, void (*f)(py_TypeInfo*, void*), void* ctx) { + for (int i = 0; i < self->length; i++) { + py_TypeInfo* info = TypeList__get(self, i); + f(info, ctx); + } +} \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 08d4fb38..22838690 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -4,6 +4,7 @@ #include "pocketpy/common/utils.h" #include "pocketpy/interpreter/generator.h" #include "pocketpy/interpreter/modules.h" +#include "pocketpy/interpreter/typeinfo.h" #include "pocketpy/objects/base.h" #include "pocketpy/common/_generated.h" #include "pocketpy/pocketpy.h" @@ -56,7 +57,7 @@ void VM__ctor(VM* self) { self->top_frame = NULL; ModuleDict__ctor(&self->modules, NULL, *py_NIL); - c11_vector__ctor(&self->types, sizeof(py_TypeInfo)); + TypeList__ctor(&self->types); self->builtins = *py_NIL; self->main = *py_NIL; @@ -76,7 +77,7 @@ void VM__ctor(VM* self) { /* Init Builtin Types */ // 0: unused - void* placeholder = c11_vector__emplace(&self->types); + void* placeholder = TypeList__emplace(&self->types); memset(placeholder, 0, sizeof(py_TypeInfo)); #define validate(t, expr) \ @@ -187,7 +188,7 @@ void VM__ctor(VM* self) { }; for(int i = 0; i < c11__count_array(public_types); i++) { - py_TypeInfo* ti = c11__at(py_TypeInfo, &self->types, public_types[i]); + py_TypeInfo* ti = pk__type_info(public_types[i]); py_setdict(&self->builtins, ti->name, &ti->self); } @@ -228,7 +229,7 @@ void VM__dtor(VM* self) { while(self->top_frame) VM__pop_frame(self); ModuleDict__dtor(&self->modules); - c11_vector__dtor(&self->types); + TypeList__dtor(&self->types); ValueStack__clear(&self->stack); } @@ -316,10 +317,9 @@ py_Type pk_newtype(const char* name, void (*dtor)(void*), bool is_python, bool is_sealed) { - c11_vector* types = &pk_current_vm->types; - py_Type index = types->length; - py_TypeInfo* ti = c11_vector__emplace(types); - py_TypeInfo* base_ti = base ? c11__at(py_TypeInfo, types, base) : NULL; + py_Type index = pk_current_vm->types.length; + py_TypeInfo* ti = TypeList__emplace(&pk_current_vm->types); + py_TypeInfo* base_ti = base ? pk__type_info(base) : NULL; if(base_ti && base_ti->is_sealed) { c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name)); } @@ -540,7 +540,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall /****************************************/ void PyObject__delete(PyObject* self) { - py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, self->type); + py_TypeInfo* ti = pk__type_info(self->type); if(ti->dtor) ti->dtor(PyObject__userdata(self)); if(self->slots == -1) NameDict__dtor(PyObject__dict(self)); if(self->gc_is_large) { @@ -564,7 +564,7 @@ void pk__mark_namedict(NameDict* dict) { } void pk__tp_set_marker(py_Type type, void (*gc_mark)(void*)) { - py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + py_TypeInfo* ti = pk__type_info(type); assert(ti->gc_mark == NULL); ti->gc_mark = gc_mark; } @@ -582,8 +582,8 @@ static void mark_object(PyObject* obj) { pk__mark_namedict(dict); } - py_TypeInfo* types = c11__at(py_TypeInfo, &pk_current_vm->types, obj->type); - if(types->gc_mark) types->gc_mark(PyObject__userdata(obj)); + py_TypeInfo* ti = pk__type_info(obj->type); + if(ti->gc_mark) ti->gc_mark(PyObject__userdata(obj)); } void CodeObject__gc_mark(const CodeObject* self) { @@ -603,18 +603,18 @@ void ManagedHeap__mark(ManagedHeap* self) { pk__mark_value(p); } // mark types - py_TypeInfo* types = vm->types.data; int types_length = vm->types.length; // 0-th type is placeholder - for(int i = 1; i < types_length; i++) { + 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 = types[i].magic + j; + py_TValue* slot = ti->magic + j; if(py_isnil(slot)) continue; pk__mark_value(slot); } // mark type annotations - pk__mark_value(&types[i].annotations); + pk__mark_value(&ti->annotations); } // mark frame for(Frame* frame = vm->top_frame; frame; frame = frame->f_back) { @@ -695,4 +695,8 @@ bool pk_wrapper__self(int argc, py_Ref argv) { bool pk_wrapper__NotImplementedError(int argc, py_Ref argv) { return py_exception(tp_NotImplementedError, ""); -} \ No newline at end of file +} + +py_TypeInfo* pk__type_info(py_Type type) { + return TypeList__get(&pk_current_vm->types, type); +} diff --git a/src/modules/enum.c b/src/modules/enum.c index ac1f5630..c2f170a4 100644 --- a/src/modules/enum.c +++ b/src/modules/enum.c @@ -86,6 +86,6 @@ void pk__add_module_enum() { py_bindproperty(type, "name", Enum__name, NULL); py_bindproperty(type, "value", Enum__value, NULL); - py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + py_TypeInfo* ti = pk__type_info(type); ti->on_end_subclass = Enum__on_end_subclass; } \ No newline at end of file diff --git a/src/public/cast.c b/src/public/cast.c index 5369663b..45b6830b 100644 --- a/src/public/cast.c +++ b/src/public/cast.c @@ -1,4 +1,5 @@ #include "pocketpy/common/str.h" +#include "pocketpy/objects/base.h" #include "pocketpy/pocketpy.h" #include "pocketpy/common/utils.h" @@ -49,10 +50,10 @@ bool py_checktype(py_Ref self, py_Type type) { bool py_isinstance(py_Ref obj, py_Type type) { return py_issubclass(obj->type, type); } bool py_issubclass(py_Type derived, py_Type base) { - py_TypeInfo* types = pk_current_vm->types.data; + TypeList* types = &pk_current_vm->types; do { if(derived == base) return true; - derived = types[derived].base; + derived = TypeList__get(types, derived)->base; } while(derived); return false; } diff --git a/src/public/internal.c b/src/public/internal.c index 019a27b6..495b50f2 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -1,3 +1,4 @@ +#include "pocketpy/interpreter/typeinfo.h" #include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/sourcedata.h" #include "pocketpy/pocketpy.h" @@ -178,7 +179,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name) { break; case tp_classmethod: self[0] = *py_getslot(cls_var, 0); - self[1] = c11__getitem(py_TypeInfo, &pk_current_vm->types, type).self; + self[1] = pk__type_info(type)->self; break; default: c11__unreachedable(); } @@ -189,41 +190,40 @@ bool pk_loadmethod(py_StackRef self, py_Name name) { py_Ref py_tpfindmagic(py_Type t, py_Name name) { assert(py_ismagicname(name)); - py_TypeInfo* types = (py_TypeInfo*)pk_current_vm->types.data; + TypeList* types = &pk_current_vm->types; do { - py_Ref f = &types[t].magic[name]; + py_TypeInfo* ti = TypeList__get(types, t); + py_Ref f = &ti->magic[name]; if(!py_isnil(f)) return f; - t = types[t].base; + t = ti->base; } while(t); return NULL; } py_Ref py_tpfindname(py_Type t, py_Name name) { - py_TypeInfo* types = (py_TypeInfo*)pk_current_vm->types.data; + TypeList* types = &pk_current_vm->types; do { - py_Ref res = py_getdict(&types[t].self, name); + py_TypeInfo* ti = TypeList__get(types, t); + py_Ref res = py_getdict(&ti->self, name); if(res) return res; - t = types[t].base; + t = ti->base; } while(t); return NULL; } py_Ref py_tpgetmagic(py_Type type, py_Name name) { assert(py_ismagicname(name)); - VM* vm = pk_current_vm; - return &c11__at(py_TypeInfo, &vm->types, type)->magic[name]; + return pk__type_info(type)->magic + name; } py_Ref py_tpobject(py_Type type) { assert(type); - VM* vm = pk_current_vm; - return &c11__at(py_TypeInfo, &vm->types, type)->self; + return &pk__type_info(type)->self; } const char* py_tpname(py_Type type) { if(!type) return "nil"; - VM* vm = pk_current_vm; - py_Name name = c11__at(py_TypeInfo, &vm->types, type)->name; + py_Name name = pk__type_info(type)->name; return py_name2str(name); } diff --git a/src/public/modules.c b/src/public/modules.c index d9d628f4..72306107 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -675,8 +675,7 @@ static bool super__new__(int argc, py_Ref argv) { return TypeError("super() takes 0 or 2 arguments"); } - py_TypeInfo* types = pk_current_vm->types.data; - *class_arg = types[*class_arg].base; + *class_arg = pk__type_info(*class_arg)->base; if(*class_arg == 0) return RuntimeError("super(): base class is invalid"); py_setslot(py_retval(), 0, self_arg); diff --git a/src/public/py_object.c b/src/public/py_object.c index 70781597..fd577130 100644 --- a/src/public/py_object.c +++ b/src/public/py_object.c @@ -5,7 +5,7 @@ bool pk__object_new(int argc, py_Ref argv) { if(argc == 0) return TypeError("object.__new__(): not enough arguments"); py_Type cls = py_totype(py_arg(0)); - py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, cls); + py_TypeInfo* ti = pk__type_info(cls); if(!ti->is_python) { return TypeError("object.__new__(%t) is not safe, use %t.__new__()", cls, cls); } @@ -72,8 +72,7 @@ static bool type__new__(int argc, py_Ref argv) { static bool type__base__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); - py_Type type = py_totype(argv); - py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + py_TypeInfo* ti = pk__type_info(py_totype(argv)); if(ti->base) { py_assign(py_retval(), py_tpobject(ti->base)); } else { @@ -84,8 +83,7 @@ static bool type__base__(int argc, py_Ref argv) { static bool type__name__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); - py_Type type = py_totype(argv); - py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + py_TypeInfo* ti = pk__type_info(py_totype(argv)); py_newstr(py_retval(), py_name2str(ti->name)); return true; } @@ -97,8 +95,7 @@ static bool type__getitem__(int argc, py_Ref argv) { static bool type__module__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); - py_Type type = py_totype(argv); - py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + py_TypeInfo* ti = pk__type_info(py_totype(argv)); if(py_isnil(&ti->module)) { py_newnone(py_retval()); } else { @@ -110,8 +107,7 @@ static bool type__module__(int argc, py_Ref argv) { static bool type__annotations__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); - py_Type type = py_totype(argv); - py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + py_TypeInfo* ti = pk__type_info(py_totype(argv)); if(py_isnil(&ti->annotations)) { py_newdict(py_retval()); } else { @@ -128,7 +124,7 @@ void pk_object__register() { py_bindmagic(tp_object, __eq__, object__eq__); py_bindmagic(tp_object, __ne__, object__ne__); py_bindmagic(tp_object, __repr__, object__repr__); - + py_bindmagic(tp_type, __repr__, type__repr__); py_bindmagic(tp_type, __new__, type__new__); py_bindmagic(tp_type, __getitem__, type__getitem__); diff --git a/src/public/py_ops.c b/src/public/py_ops.c index 770804e4..da2a1aab 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -1,3 +1,4 @@ +#include "pocketpy/interpreter/typeinfo.h" #include "pocketpy/interpreter/vm.h" #include "pocketpy/objects/base.h" #include "pocketpy/pocketpy.h" @@ -45,11 +46,12 @@ int py_bool(py_Ref val) { bool py_hash(py_Ref val, int64_t* out) { py_Type t = val->type; - py_TypeInfo* types = (py_TypeInfo*)pk_current_vm->types.data; + TypeList* types = &pk_current_vm->types; do { - py_Ref _hash = &types[t].magic[__hash__]; + py_TypeInfo* ti = TypeList__get(types, t); + py_Ref _hash = &ti->magic[__hash__]; if(py_isnone(_hash)) break; - py_Ref _eq = &types[t].magic[__eq__]; + py_Ref _eq = &ti->magic[__eq__]; if(!py_isnil(_eq)) { if(py_isnil(_hash)) break; if(!py_call(_hash, 1, val)) return false; @@ -57,7 +59,7 @@ bool py_hash(py_Ref val, int64_t* out) { *out = py_toint(py_retval()); return true; } - t = types[t].base; + t = ti->base; } while(t); return TypeError("unhashable type: '%t'", val->type); }