From aa06b029d3e3f06d64a687e5248fd7b0fe9342d6 Mon Sep 17 00:00:00 2001 From: qqfunc Date: Sat, 25 Apr 2026 20:56:35 +0900 Subject: [PATCH 1/8] refactor: use type data --- csrc/objcbool.c | 94 ++++++++++++++++++++++++--------- csrc/objcbool.h | 20 ------- csrc/objcclass.c | 81 ++++++++++++---------------- csrc/objcclass.h | 21 -------- csrc/objcmetaclass.c | 46 +++++++--------- csrc/objcmetaclass.h | 21 -------- csrc/objcmethod.c | 99 +++++++++++++++++++++++++---------- csrc/objcmethod.h | 22 -------- csrc/objcobject.c | 70 +++++++++++++++++-------- csrc/objcobject.h | 22 -------- csrc/objcselector.c | 113 ++++++++++++++++++++++++++++------------ csrc/objcselector.h | 22 -------- csrc/objctypes.c | 6 --- csrc/objctypes_cache.cc | 31 +++++------ csrc/objctypes_cache.h | 18 +++---- csrc/objctypes_module.h | 72 +++++++++++++++++++------ 16 files changed, 398 insertions(+), 360 deletions(-) delete mode 100644 csrc/objcbool.h delete mode 100644 csrc/objcclass.h delete mode 100644 csrc/objcmetaclass.h delete mode 100644 csrc/objcmethod.h delete mode 100644 csrc/objcobject.h delete mode 100644 csrc/objcselector.h diff --git a/csrc/objcbool.c b/csrc/objcbool.c index 10547f4..25e843d 100644 --- a/csrc/objcbool.c +++ b/csrc/objcbool.c @@ -5,42 +5,56 @@ #include -#include "objcbool.h" - #include "objctypes.h" #include "objctypes_module.h" /// @brief Destruct an ObjCBool. static void -ObjCBool_dealloc(ObjCBoolObject *self) +ObjCBool_dealloc(PyObject *self) { - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); } /// @brief `ObjCBool.__repr__()` static PyObject * -ObjCBool_repr(ObjCBoolObject *self) +ObjCBool_repr(PyObject *self) { + // Get the type data of the ObjCBool object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + return PyUnicode_FromFormat("ObjCBool(%s)", - self->value ? "True" : "False"); + data->value ? "True" : "False"); } /// @brief `ObjCBool.__str__()` static PyObject * -ObjCBool_str(ObjCBoolObject *self) +ObjCBool_str(PyObject *self) { - return PyUnicode_FromString(self->value ? "YES" : "NO"); + // Get the type data of the ObjCBool object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + + return PyUnicode_FromString(data->value ? "YES" : "NO"); } /// @brief Get an ObjCBool from a Python type and an long. -static ObjCBoolObject * +static PyObject * _ObjCBool_FromLong(PyTypeObject *type, long v) { + // Get the module state PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); if (v) { @@ -48,20 +62,24 @@ _ObjCBool_FromLong(PyTypeObject *type, long v) if (state->ObjCBool_YES == NULL) { state->ObjCBool_YES = type->tp_alloc(type, 0); if (state->ObjCBool_YES != NULL) { - ((ObjCBoolObject *)state->ObjCBool_YES)->value = YES; + ObjCBoolData *data = PyObject_GetTypeData( + state->ObjCBool_YES, state->ObjCBool_Type); + data->value = YES; } } - return (ObjCBoolObject *)Py_NewRef(state->ObjCBool_YES); + return Py_NewRef(state->ObjCBool_YES); } // Cache the NO value if it hasn't been cached yet if (state->ObjCBool_NO == NULL) { state->ObjCBool_NO = type->tp_alloc(type, 0); if (state->ObjCBool_NO != NULL) { - ((ObjCBoolObject *)state->ObjCBool_NO)->value = NO; + ObjCBoolData *data = + PyObject_GetTypeData(state->ObjCBool_NO, state->ObjCBool_Type); + data->value = NO; } } - return (ObjCBoolObject *)Py_NewRef(state->ObjCBool_NO); + return Py_NewRef(state->ObjCBool_NO); } /// @brief `ObjCBool.__new__()` @@ -75,40 +93,67 @@ ObjCBool_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } - return (PyObject *)_ObjCBool_FromLong(type, v); + return _ObjCBool_FromLong(type, v); } /// @brief `ObjCBool.__bool__()` static int -ObjCBool_bool(ObjCBoolObject *self) +ObjCBool_bool(PyObject *self) { - return self->value ? 1 : 0; + // Get the type data of the ObjCBool object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return -1; + } + objctypes_state *state = PyModule_GetState(module); + ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + + return data->value ? 1 : 0; } /// @brief `ObjCBool.__invert__()` static PyObject * -ObjCBool_invert(ObjCBoolObject *self) +ObjCBool_invert(PyObject *self) { + // Get the type data of the ObjCBool object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module == NULL) { return NULL; } + objctypes_state *state = PyModule_GetState(module); + ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); - return (PyObject *)ObjCBool_FromLong(module, !(self->value)); + return _ObjCBool_FromLong(Py_TYPE(self), !(data->value)); } /// @brief `ObjCBool.__int__()` static PyObject * -ObjCBool_int(ObjCBoolObject *self) +ObjCBool_int(PyObject *self) { - return PyLong_FromLong(self->value ? 1 : 0); + // Get the type data of the ObjCBool object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + + return PyLong_FromLong(data->value ? 1 : 0); } /// @brief `ObjCBool.__float__()` static PyObject * -ObjCBool_float(ObjCBoolObject *self) +ObjCBool_float(PyObject *self) { - return PyFloat_FromDouble(self->value ? 1 : 0); + // Get the type data of the ObjCBool object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + + return PyFloat_FromDouble(data->value ? 1 : 0); } static PyType_Slot ObjCBool_slots[] = { @@ -126,7 +171,7 @@ static PyType_Slot ObjCBool_slots[] = { PyType_Spec ObjCBool_spec = { .name = "objctypes.ObjCBool", - .basicsize = sizeof(ObjCBoolObject), + .basicsize = -(long)sizeof(ObjCBoolData), .itemsize = 0, .flags = Py_TPFLAGS_DEFAULT, .slots = ObjCBool_slots, @@ -135,6 +180,7 @@ PyType_Spec ObjCBool_spec = { PyObject * ObjCBool_FromLong(PyObject *module, long v) { + // Get the module state objctypes_state *state = PyModule_GetState(module); if (state->ObjCBool_Type == NULL) { return NULL; diff --git a/csrc/objcbool.h b/csrc/objcbool.h deleted file mode 100644 index cdd8493..0000000 --- a/csrc/objcbool.h +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @file objcbool.h - * @brief Source declarations and definitions for objcbool.h. - */ - -#ifndef OBJCBOOL_H -#define OBJCBOOL_H - -#include - -#include - -typedef struct { - PyObject_HEAD - BOOL value; -} ObjCBoolObject; - -extern PyType_Spec ObjCBool_spec; - -#endif // OBJCBOOL_H diff --git a/csrc/objcclass.c b/csrc/objcclass.c index cc22e8b..89dec2d 100644 --- a/csrc/objcclass.c +++ b/csrc/objcclass.c @@ -5,8 +5,6 @@ #include -#include "objcclass.h" - #include "objctypes.h" #include "objctypes_cache.h" #include "objctypes_module.h" @@ -18,87 +16,83 @@ ObjCClass_dealloc(PyObject *self) PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module != NULL) { objctypes_state *state = PyModule_GetState(module); - ObjCClassState *cls_state = + ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); - if (cls_state != NULL) { + if (data != NULL) { PyMutex_Lock(&state->ObjCClass_cache_mutex); - ObjCClass_cache_del(module, cls_state->value); + ObjCClass_cache_del(module, data->value); PyMutex_Unlock(&state->ObjCClass_cache_mutex); } } - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); } /// @brief `ObjCClass.__repr__()` static PyObject * ObjCClass_repr(PyObject *self) { + // Get the type data of the ObjCClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); - ObjCClassState *cls_state = - PyObject_GetTypeData(self, state->ObjCClass_Type); + ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); - if (cls_state->value == NULL) { + if (data->value == NULL) { return PyUnicode_FromString(""); } return PyUnicode_FromFormat("", - class_getName(cls_state->value)); + class_getName(data->value)); } /// @brief `ObjCClass.address` static PyObject * ObjCClass_address(PyObject *self, void *Py_UNUSED(closure)) { + // Get the type data of the ObjCClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); - ObjCClassState *cls_state = - PyObject_GetTypeData(self, state->ObjCClass_Type); + ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); - return PyLong_FromVoidPtr(cls_state->value); + return PyLong_FromVoidPtr(data->value); } /// @brief `ObjCClass.name` static PyObject * ObjCClass_name(PyObject *self, void *Py_UNUSED(closure)) { + // Get the type data of the ObjCClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); - ObjCClassState *cls_state = - PyObject_GetTypeData(self, state->ObjCClass_Type); + ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); - if (cls_state->value == NULL) { + if (data->value == NULL) { return Py_GetConstant(Py_CONSTANT_EMPTY_STR); } - return PyUnicode_FromString(class_getName(cls_state->value)); + return PyUnicode_FromString(class_getName(data->value)); } /// @brief `ObjCClass.load_methods()` static PyObject * ObjCClass_load_methods(PyObject *self, PyObject *Py_UNUSED(args)) { + // Get the type data of the ObjCClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); - ObjCClassState *cls_state = - PyObject_GetTypeData(self, state->ObjCClass_Type); + ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); unsigned int outCount; - Method *methods = class_copyMethodList(cls_state->value, &outCount); + Method *methods = class_copyMethodList(data->value, &outCount); for (unsigned int num = 0; num < outCount; num++) { printf("Method %s\n", sel_getName(method_getName(methods[num]))); } @@ -111,11 +105,11 @@ ObjCClass_load_methods(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * _ObjCClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) { + // Get the module state PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); if (lock_cache) { @@ -123,12 +117,10 @@ _ObjCClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) } PyObject *self = ObjCClass_cache_get(module, cls); - if (self == NULL) { - objctypes_state *state = PyModule_GetState(module); - PyObject *base; + // Determine the Python base class of the ObjCClass Class super_cls = class_getSuperclass(cls); if (super_cls == NULL) { // The class is a root class @@ -145,6 +137,7 @@ _ObjCClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) } } + // Create a new ObjCClass object PyObject *args = Py_BuildValue("(s(O){})", class_getName(cls), base); PyObject *kwds = PyDict_New(); self = PyType_Type.tp_new(type, args, kwds); @@ -152,9 +145,9 @@ _ObjCClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) Py_XDECREF(kwds); if (self != NULL) { - ObjCClassState *cls_state = + ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); - cls_state->value = cls; + data->value = cls; ObjCClass_cache_set(module, cls, self); } } @@ -180,11 +173,12 @@ ObjCClass_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } + // Get the type data of the ObjCClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); objctypes_state *state = PyModule_GetState(module); - ObjCClassState *cls_state = - PyObject_GetTypeData(self, state->ObjCClass_Type); - cls_state->value = NULL; + ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + + data->value = NULL; return 0; } @@ -227,29 +221,22 @@ ObjCClass_from_address(PyTypeObject *type, PyObject *address) return NULL; } - return (PyObject *)_ObjCClass_FromClass(type, cls, 1); + return _ObjCClass_FromClass(type, cls, 1); } /// @brief `ObjCClass.from_name()` static PyObject * ObjCClass_from_name(PyTypeObject *type, PyObject *name) { - PyObject *self; - if (!PyUnicode_Check(name)) { PyErr_Format(PyExc_TypeError, "ObjCClass.from_name() argument 1 must be str, not %T", name); return NULL; } - - PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); - if (module == NULL) { - return NULL; - } - const char *cls_name = PyUnicode_AsUTF8(name); + // Look up the class by name Class cls = objc_getClass(cls_name); if (cls == NULL) { PyErr_Format(PyExc_NameError, "Objective-C class '%s' is not defined", @@ -257,9 +244,7 @@ ObjCClass_from_name(PyTypeObject *type, PyObject *name) return NULL; } - self = _ObjCClass_FromClass(type, cls, 1); - - return (PyObject *)self; + return _ObjCClass_FromClass(type, cls, 1); } static PyMethodDef ObjCClass_methods[] = { @@ -314,7 +299,7 @@ static PyType_Slot ObjCClass_slots[] = { PyType_Spec ObjCClass_spec = { .name = "objctypes.ObjCClass", - .basicsize = -(long)sizeof(ObjCClassState), + .basicsize = -(long)sizeof(ObjCClassData), .itemsize = 0, .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_TYPE_SUBCLASS, .slots = ObjCClass_slots, @@ -323,11 +308,11 @@ PyType_Spec ObjCClass_spec = { PyObject * ObjCClass_FromClass(PyObject *module, Class cls) { + // Get the module state objctypes_state *state = PyModule_GetState(module); if (state->ObjCClass_Type == NULL) { return NULL; } - return (PyObject *)_ObjCClass_FromClass( - (PyTypeObject *)state->ObjCClass_Type, cls, 1); + return _ObjCClass_FromClass((PyTypeObject *)state->ObjCClass_Type, cls, 1); } diff --git a/csrc/objcclass.h b/csrc/objcclass.h deleted file mode 100644 index bc8bab1..0000000 --- a/csrc/objcclass.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @file objcclass.h - * @brief Source declarations and definitions for objcclass.h. - */ - -#ifndef OBJCCLASS_H -#define OBJCCLASS_H - -#include - -#include - -/// ObjCClass - -typedef struct { - Class value; -} ObjCClassState; - -extern PyType_Spec ObjCClass_spec; - -#endif // OBJCCLASS_H diff --git a/csrc/objcmetaclass.c b/csrc/objcmetaclass.c index 471a57e..b6618ff 100644 --- a/csrc/objcmetaclass.c +++ b/csrc/objcmetaclass.c @@ -5,8 +5,6 @@ #include -#include "objcmetaclass.h" - #include "objctypes.h" #include "objctypes_cache.h" #include "objctypes_module.h" @@ -18,70 +16,70 @@ ObjCMetaClass_dealloc(PyObject *self) PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module != NULL) { objctypes_state *state = PyModule_GetState(module); - ObjCMetaClassState *cls_state = + ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); - if (cls_state != NULL) { + if (data != NULL) { PyMutex_Lock(&state->ObjCMetaClass_cache_mutex); - ObjCMetaClass_cache_del(module, cls_state->value); + ObjCMetaClass_cache_del(module, data->value); PyMutex_Unlock(&state->ObjCMetaClass_cache_mutex); } } - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); } /// @brief `ObjCMetaClass.__repr__()` static PyObject * ObjCMetaClass_repr(PyObject *self) { + // Get the type data of the ObjCMetaClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); - ObjCMetaClassState *cls_state = + ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); - if (cls_state->value == NULL) { + if (data->value == NULL) { return PyUnicode_FromString(""); } return PyUnicode_FromFormat("", - class_getName(cls_state->value)); + class_getName(data->value)); } /// @brief `ObjCMetaClass.address` static PyObject * ObjCMetaClass_address(PyObject *self, void *Py_UNUSED(closure)) { + // Get the type data of the ObjCMetaClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); - ObjCMetaClassState *cls_state = + ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); - return PyLong_FromVoidPtr(cls_state->value); + return PyLong_FromVoidPtr(data->value); } /// @brief `ObjCMetaClass.name` static PyObject * ObjCMetaClass_name(PyObject *self, void *Py_UNUSED(closure)) { + // Get the type data of the ObjCMetaClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); - ObjCMetaClassState *cls_state = + ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); - if (cls_state->value == NULL) { + if (data->value == NULL) { return Py_GetConstant(Py_CONSTANT_EMPTY_STR); } - return PyUnicode_FromString(class_getName(cls_state->value)); + return PyUnicode_FromString(class_getName(data->value)); } /// @brief Get an ObjCMetaClass from a Python type and an Objective-C @@ -89,11 +87,11 @@ ObjCMetaClass_name(PyObject *self, void *Py_UNUSED(closure)) static PyObject * _ObjCMetaClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) { + // Get the module state PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); if (lock_cache) { @@ -111,7 +109,7 @@ _ObjCMetaClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) Py_XDECREF(kwds); if (self != NULL) { - ObjCMetaClassState *cls_state = + ObjCMetaClassData *cls_state = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); cls_state->value = cls; ObjCMetaClass_cache_set(module, cls, self); @@ -175,14 +173,9 @@ ObjCMetaClass_from_name(PyTypeObject *type, PyObject *name) "ObjCMetaClass.from_name() argument 1 must be str, not %T", name); return NULL; } - - PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); - if (module == NULL) { - return NULL; - } - const char *cls_name = PyUnicode_AsUTF8(name); + // Look up the metaclass by name Class cls = objc_getMetaClass(cls_name); if (cls == NULL) { PyErr_Format(PyExc_NameError, "Objective-C class '%s' is not defined", @@ -238,7 +231,7 @@ static PyType_Slot ObjCMetaClass_slots[] = { PyType_Spec ObjCMetaClass_spec = { .name = "objctypes.ObjCMetaClass", - .basicsize = -(long)sizeof(ObjCMetaClassState), + .basicsize = -(long)sizeof(ObjCMetaClassData), .itemsize = 0, .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_TYPE_SUBCLASS, .slots = ObjCMetaClass_slots, @@ -247,6 +240,7 @@ PyType_Spec ObjCMetaClass_spec = { PyObject * ObjCMetaClass_FromClass(PyObject *module, Class cls) { + // Get the module state objctypes_state *state = PyModule_GetState(module); if (state->ObjCMetaClass_Type == NULL) { return NULL; diff --git a/csrc/objcmetaclass.h b/csrc/objcmetaclass.h deleted file mode 100644 index 44ee504..0000000 --- a/csrc/objcmetaclass.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @file objcmetaclass.h - * @brief Source declarations and definitions for objcmetaclass.c. - */ - -#ifndef OBJCMETACLASS_H -#define OBJCMETACLASS_H - -#include - -#include - -/// ObjCMetaClass - -typedef struct { - Class value; -} ObjCMetaClassState; - -extern PyType_Spec ObjCMetaClass_spec; - -#endif // OBJCMETACLASS_H diff --git a/csrc/objcmethod.c b/csrc/objcmethod.c index c31a5f2..9a1de59 100644 --- a/csrc/objcmethod.c +++ b/csrc/objcmethod.c @@ -5,76 +5,118 @@ #include -#include "objcmethod.h" - #include "objctypes.h" #include "objctypes_cache.h" #include "objctypes_module.h" /// @brief Destruct an ObjCMethod. static void -ObjCMethod_dealloc(ObjCMethodObject *self) +ObjCMethod_dealloc(PyObject *self) { PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module != NULL) { objctypes_state *state = PyModule_GetState(module); - PyMutex_Lock(&state->ObjCMethod_cache_mutex); - ObjCMethod_cache_del(module, self->value); - PyMutex_Unlock(&state->ObjCMethod_cache_mutex); + ObjCMethodData *data = + PyObject_GetTypeData(self, state->ObjCMethod_Type); + if (data != NULL) { + PyMutex_Lock(&state->ObjCMethod_cache_mutex); + ObjCMethod_cache_del(module, data->value); + PyMutex_Unlock(&state->ObjCMethod_cache_mutex); + } } - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); } /// @brief `ObjCMethod.__repr__()` static PyObject * -ObjCMethod_repr(ObjCMethodObject *self) +ObjCMethod_repr(PyObject *self) { + // Get the type data of the ObjCMethod object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); + return PyUnicode_FromFormat("", - sel_getName(method_getName(self->value)), - self->value); + sel_getName(method_getName(data->value)), + data->value); } /// @brief `ObjCMethod.__str__()` static PyObject * -ObjCMethod_str(ObjCMethodObject *self) +ObjCMethod_str(PyObject *self) { - return PyUnicode_FromString(sel_getName(method_getName(self->value))); + // Get the type data of the ObjCMethod object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); + + return PyUnicode_FromString(sel_getName(method_getName(data->value))); } /// @brief `ObjCMethod.name` static PyObject * -ObjCMethod_name(ObjCMethodObject *self, void *Py_UNUSED(closure)) +ObjCMethod_name(PyObject *self, void *Py_UNUSED(closure)) { - return PyUnicode_FromString(sel_getName(method_getName(self->value))); + // Get the type data of the ObjCMethod object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); + + return PyUnicode_FromString(sel_getName(method_getName(data->value))); } /// @brief `ObjCMethod.address` static PyObject * -ObjCMethod_address(ObjCMethodObject *self, void *Py_UNUSED(closure)) +ObjCMethod_address(PyObject *self, void *Py_UNUSED(closure)) { - return PyLong_FromVoidPtr(self->value); + // Get the type data of the ObjCMethod object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); + + return PyLong_FromVoidPtr(data->value); } /// @brief Get an ObjCMethod from a Python type and an Objective-C Method. -static ObjCMethodObject * +static PyObject * _ObjCMethod_FromMethod(PyTypeObject *type, Method method) { + // Get the module state PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); PyMutex_Lock(&state->ObjCMethod_cache_mutex); - ObjCMethodObject *self = ObjCMethod_cache_get(module, method); + PyObject *self = ObjCMethod_cache_get(module, method); if (self == NULL) { - self = (ObjCMethodObject *)type->tp_alloc(type, 0); + self = type->tp_alloc(type, 0); if (self != NULL) { - self->value = method; - ObjCMethod_cache_set(module, method, self); + ObjCMethodData *data = + PyObject_GetTypeData(self, state->ObjCMethod_Type); + if (data != NULL) { + data->value = method; + ObjCMethod_cache_set(module, method, self); + } + else { + Py_DECREF(self); + self = NULL; + } } } @@ -95,7 +137,7 @@ ObjCMethod_from_address(PyTypeObject *type, PyObject *address) return NULL; } - return (PyObject *)_ObjCMethod_FromMethod(type, PyLong_AsVoidPtr(address)); + return _ObjCMethod_FromMethod(type, PyLong_AsVoidPtr(address)); } /// @brief `ObjCMethod.__new__()` @@ -120,14 +162,14 @@ static PyMethodDef ObjCMethod_methods[] = { static PyGetSetDef ObjCMethod_getset[] = { { "address", - (getter)ObjCMethod_address, + ObjCMethod_address, NULL, PyDoc_STR("The address of the Objective-C method."), NULL, }, { "name", - (getter)ObjCMethod_name, + ObjCMethod_name, NULL, PyDoc_STR("The name of the Objective-C method."), NULL, @@ -148,7 +190,7 @@ static PyType_Slot ObjCMethod_slots[] = { PyType_Spec ObjCMethod_spec = { .name = "objctypes.ObjCMethod", - .basicsize = sizeof(ObjCMethodObject), + .basicsize = -(long)sizeof(ObjCMethodData), .itemsize = 0, .flags = Py_TPFLAGS_DEFAULT, .slots = ObjCMethod_slots, @@ -157,11 +199,12 @@ PyType_Spec ObjCMethod_spec = { PyObject * ObjCMethod_FromMethod(PyObject *module, Method method) { + // Get the module state objctypes_state *state = PyModule_GetState(module); if (state->ObjCMethod_Type == NULL) { return NULL; } - return (PyObject *)_ObjCMethod_FromMethod( - (PyTypeObject *)state->ObjCMethod_Type, method); + return _ObjCMethod_FromMethod((PyTypeObject *)state->ObjCMethod_Type, + method); } diff --git a/csrc/objcmethod.h b/csrc/objcmethod.h deleted file mode 100644 index 1085907..0000000 --- a/csrc/objcmethod.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file objcmethod.h - * @brief Source declarations and definitions for objcmethod.h. - */ - -#ifndef OBJCMETHOD_H -#define OBJCMETHOD_H - -#include - -#include - -/// ObjCMethod - -typedef struct { - PyObject_HEAD - Method value; -} ObjCMethodObject; - -extern PyType_Spec ObjCMethod_spec; - -#endif // OBJCMETHOD_H diff --git a/csrc/objcobject.c b/csrc/objcobject.c index def5083..6025f10 100644 --- a/csrc/objcobject.c +++ b/csrc/objcobject.c @@ -5,61 +5,86 @@ #include -#include "objcobject.h" - #include "objctypes.h" #include "objctypes_cache.h" #include "objctypes_module.h" /// @brief Destruct an ObjCObject. static void -ObjCObject_dealloc(ObjCObjectObject *self) +ObjCObject_dealloc(PyObject *self) { PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module != NULL) { objctypes_state *state = PyModule_GetState(module); - PyMutex_Lock(&state->ObjCObject_cache_mutex); - ObjCObject_cache_del(module, self->value); - PyMutex_Unlock(&state->ObjCObject_cache_mutex); + ObjCObjectData *data = + PyObject_GetTypeData(self, state->ObjCObject_Type); + if (data != NULL) { + PyMutex_Lock(&state->ObjCObject_cache_mutex); + ObjCObject_cache_del(module, data->value); + PyMutex_Unlock(&state->ObjCObject_cache_mutex); + } } - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); } /// @brief `ObjCObject.__repr__()` static PyObject * -ObjCObject_repr(ObjCObjectObject *self) +ObjCObject_repr(PyObject *self) { + // Get the type data of the ObjCObject object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCObjectData *data = PyObject_GetTypeData(self, state->ObjCObject_Type); + return PyUnicode_FromFormat("", - object_getClassName(self->value), self->value); + object_getClassName(data->value), data->value); } /// @brief `ObjCObject.address` static PyObject * -ObjCObject_address(ObjCObjectObject *self, void *Py_UNUSED(closure)) +ObjCObject_address(PyObject *self, void *Py_UNUSED(closure)) { - return PyLong_FromVoidPtr(self->value); + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCObjectData *data = PyObject_GetTypeData(self, state->ObjCObject_Type); + + return PyLong_FromVoidPtr(data->value); } /// @brief Get an ObjCObject from a Python type and an Objective-C id. -static ObjCObjectObject * +static PyObject * _ObjCObject_FromId(PyTypeObject *type, id obj) { + // Get the module state PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); PyMutex_Lock(&state->ObjCObject_cache_mutex); - ObjCObjectObject *self = ObjCObject_cache_get(module, obj); + PyObject *self = ObjCObject_cache_get(module, obj); if (self == NULL) { - self = (ObjCObjectObject *)type->tp_alloc(type, 0); + self = type->tp_alloc(type, 0); if (self != NULL) { - self->value = obj; - ObjCObject_cache_set(module, obj, self); + ObjCObjectData *data = + PyObject_GetTypeData(self, state->ObjCObject_Type); + if (data != NULL) { + data->value = obj; + ObjCObject_cache_set(module, obj, self); + } + else { + Py_DECREF(self); + self = NULL; + } } } @@ -106,7 +131,7 @@ ObjCObject_from_address(PyTypeObject *type, PyObject *address) } } - return (PyObject *)_ObjCObject_FromId(type, obj); + return _ObjCObject_FromId(type, obj); } /// @brief `ObjCObject.__new__()` @@ -114,11 +139,11 @@ static PyObject * ObjCObject_new(PyTypeObject *type, PyObject *Py_UNUSED(args), PyObject *Py_UNUSED(kwds)) { + // Get the module state PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); if (type == state->ObjCObject_Type) { @@ -150,7 +175,7 @@ static PyMethodDef ObjCObject_methods[] = { static PyGetSetDef ObjCObject_getset[] = { { "address", - (getter)ObjCObject_address, + ObjCObject_address, NULL, PyDoc_STR("The address of the Objective-C object."), NULL, @@ -170,7 +195,7 @@ static PyType_Slot ObjCObject_slots[] = { PyType_Spec ObjCObject_spec = { .name = "objctypes.ObjCObject", - .basicsize = sizeof(ObjCObjectObject), + .basicsize = -(long)sizeof(ObjCObjectData), .itemsize = 0, .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .slots = ObjCObject_slots, @@ -179,10 +204,11 @@ PyType_Spec ObjCObject_spec = { PyObject * ObjCObject_FromId(PyObject *module, id obj) { + // Get the module state objctypes_state *state = PyModule_GetState(module); if (state->ObjCObject_Type == NULL) { return NULL; } - return (PyObject *)_ObjCObject_FromId(state->ObjCObject_Type, obj); + return _ObjCObject_FromId(state->ObjCObject_Type, obj); } diff --git a/csrc/objcobject.h b/csrc/objcobject.h deleted file mode 100644 index f95ae17..0000000 --- a/csrc/objcobject.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file objcobject.h - * @brief Source declarations and definitions for objcobject.h. - */ - -#ifndef OBJCOBJECT_H -#define OBJCOBJECT_H - -#include - -#include - -/// ObjCObject - -typedef struct { - PyObject_HEAD - id value; -} ObjCObjectObject; - -extern PyType_Spec ObjCObject_spec; - -#endif // OBJCOBJECT_H diff --git a/csrc/objcselector.c b/csrc/objcselector.c index c2d0c8f..1c821e2 100644 --- a/csrc/objcselector.c +++ b/csrc/objcselector.c @@ -5,81 +5,129 @@ #include -#include "objcselector.h" - #include "objctypes.h" #include "objctypes_cache.h" #include "objctypes_module.h" /// @brief Destruct an ObjCSelector. static void -ObjCSelector_dealloc(ObjCSelectorObject *self) +ObjCSelector_dealloc(PyObject *self) { PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); if (module != NULL) { objctypes_state *state = PyModule_GetState(module); - PyMutex_Lock(&state->ObjCSelector_cache_mutex); - ObjCSelector_cache_del(module, self->value); - PyMutex_Unlock(&state->ObjCSelector_cache_mutex); + ObjCSelectorData *data = + PyObject_GetTypeData(self, state->ObjCSelector_Type); + if (data != NULL) { + PyMutex_Lock(&state->ObjCSelector_cache_mutex); + ObjCSelector_cache_del(module, data->value); + PyMutex_Unlock(&state->ObjCSelector_cache_mutex); + } } - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); } /// @brief `ObjCSelector.__repr__()` static PyObject * -ObjCSelector_repr(ObjCSelectorObject *self) +ObjCSelector_repr(PyObject *self) { + // Get the type data of the ObjCSelector object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCSelectorData *data = + PyObject_GetTypeData(self, state->ObjCSelector_Type); + return PyUnicode_FromFormat("ObjCSelector('%s')", - sel_getName(self->value)); + sel_getName(data->value)); } /// @brief `ObjCSelector.__str__()` static PyObject * -ObjCSelector_str(ObjCSelectorObject *self) +ObjCSelector_str(PyObject *self) { - return PyUnicode_FromString(sel_getName(self->value)); + // Get the type data of the ObjCSelector object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCSelectorData *data = + PyObject_GetTypeData(self, state->ObjCSelector_Type); + + return PyUnicode_FromString(sel_getName(data->value)); } /// @brief `ObjCSelector.address` static PyObject * -ObjCSelector_address(ObjCSelectorObject *self, void *Py_UNUSED(closure)) +ObjCSelector_address(PyObject *self, void *Py_UNUSED(closure)) { - return PyLong_FromVoidPtr(self->value); + // Get the type data of the ObjCSelector object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCSelectorData *data = + PyObject_GetTypeData(self, state->ObjCSelector_Type); + + return PyLong_FromVoidPtr(data->value); } /// @brief `ObjCSelector.name` static PyObject * -ObjCSelector_name(ObjCSelectorObject *self, void *Py_UNUSED(closure)) +ObjCSelector_name(PyObject *self, void *Py_UNUSED(closure)) { - return PyUnicode_FromString(sel_getName(self->value)); + // Get the type data of the ObjCSelector object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCSelectorData *data = + PyObject_GetTypeData(self, state->ObjCSelector_Type); + + return PyUnicode_FromString(sel_getName(data->value)); } /// @brief `ObjCSelector.is_mapped` static PyObject * -ObjCSelector_is_mapped(ObjCSelectorObject *self, void *Py_UNUSED(closure)) +ObjCSelector_is_mapped(PyObject *self, void *Py_UNUSED(closure)) { - return sel_isMapped(self->value) ? Py_True : Py_False; + // Get the type data of the ObjCSelector object + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return NULL; + } + objctypes_state *state = PyModule_GetState(module); + ObjCSelectorData *data = + PyObject_GetTypeData(self, state->ObjCSelector_Type); + + return sel_isMapped(data->value) ? Py_True : Py_False; } /// @brief Get an ObjCSelector from a Python type and an Objective-C SEL. -static ObjCSelectorObject * +static PyObject * _ObjCSelector_FromSEL(PyTypeObject *type, SEL sel) { + // Get the module state PyObject *module = PyType_GetModuleByDef(type, &objctypes_module); if (module == NULL) { return NULL; } - objctypes_state *state = PyModule_GetState(module); PyMutex_Lock(&state->ObjCSelector_cache_mutex); - ObjCSelectorObject *self = ObjCSelector_cache_get(module, sel); - + PyObject *self = ObjCSelector_cache_get(module, sel); if (self == NULL) { - self = (ObjCSelectorObject *)type->tp_alloc(type, 0); + self = type->tp_alloc(type, 0); if (self != NULL) { - self->value = sel; + ObjCSelectorData *data = + PyObject_GetTypeData(self, state->ObjCSelector_Type); + data->value = sel; ObjCSelector_cache_set(module, sel, self); } } @@ -108,7 +156,7 @@ ObjCSelector_from_address(PyTypeObject *type, PyObject *address) return NULL; } - return (PyObject *)_ObjCSelector_FromSEL(type, sel); + return _ObjCSelector_FromSEL(type, sel); } /// @brief `ObjCSelector.__new__()` @@ -123,9 +171,7 @@ ObjCSelector_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } - ObjCSelectorObject *self = - _ObjCSelector_FromSEL(type, sel_registerName(name)); - return (PyObject *)self; + return _ObjCSelector_FromSEL(type, sel_registerName(name)); } static PyMethodDef ObjCSelector_methods[] = { @@ -141,21 +187,21 @@ static PyMethodDef ObjCSelector_methods[] = { static PyGetSetDef ObjCSelector_getset[] = { { "address", - (getter)ObjCSelector_address, + ObjCSelector_address, NULL, PyDoc_STR("The address of the Objective-C selector."), NULL, }, { "name", - (getter)ObjCSelector_name, + ObjCSelector_name, NULL, PyDoc_STR("The name of the Objective-C selector."), NULL, }, { "is_mapped", - (getter)ObjCSelector_is_mapped, + ObjCSelector_is_mapped, NULL, PyDoc_STR("Whether the Objective-C selector is mapped."), NULL, @@ -176,7 +222,7 @@ static PyType_Slot ObjCSelector_slots[] = { PyType_Spec ObjCSelector_spec = { .name = "objctypes.ObjCSelector", - .basicsize = sizeof(ObjCSelectorObject), + .basicsize = -(long)sizeof(ObjCSelectorData), .itemsize = 0, .flags = Py_TPFLAGS_DEFAULT, .slots = ObjCSelector_slots, @@ -185,11 +231,12 @@ PyType_Spec ObjCSelector_spec = { PyObject * ObjCSelector_FromSEL(PyObject *module, SEL sel) { + // Get the module state objctypes_state *state = PyModule_GetState(module); if (state->ObjCSelector_Type == NULL) { return NULL; } - return (PyObject *)_ObjCSelector_FromSEL( - (PyTypeObject *)state->ObjCSelector_Type, sel); + return _ObjCSelector_FromSEL((PyTypeObject *)state->ObjCSelector_Type, + sel); } diff --git a/csrc/objcselector.h b/csrc/objcselector.h deleted file mode 100644 index 174f683..0000000 --- a/csrc/objcselector.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file objcselector.h - * @brief Source declarations and definitions for objcselector.h. - */ - -#ifndef OBJCSELECTOR_H -#define OBJCSELECTOR_H - -#include - -#include - -/// ObjCSelector - -typedef struct { - PyObject_HEAD - SEL value; -} ObjCSelectorObject; - -extern PyType_Spec ObjCSelector_spec; - -#endif // OBJCSELECTOR_H diff --git a/csrc/objctypes.c b/csrc/objctypes.c index 786fd91..aa423a1 100644 --- a/csrc/objctypes.c +++ b/csrc/objctypes.c @@ -7,12 +7,6 @@ #include "objctypes.h" -#include "objcbool.h" -#include "objcclass.h" -#include "objcmetaclass.h" -#include "objcmethod.h" -#include "objcobject.h" -#include "objcselector.h" #include "objctypes_cache.h" #include "objctypes_module.h" diff --git a/csrc/objctypes_cache.cc b/csrc/objctypes_cache.cc index 94eb70a..48e70ba 100644 --- a/csrc/objctypes_cache.cc +++ b/csrc/objctypes_cache.cc @@ -7,9 +7,6 @@ #include "objctypes_cache.h" -#include "objcmethod.h" -#include "objcobject.h" -#include "objcselector.h" #include "objctypes_module.h" #include @@ -48,7 +45,7 @@ ObjCMetaClass_cache_set(PyObject *module, Class cls, PyObject *obj) { objctypes_state *state = (objctypes_state *)PyModule_GetState(module); cache_map *cache = (cache_map *)state->ObjCMetaClass_cache; - (*cache)[cls] = (PyObject *)obj; + (*cache)[cls] = obj; } void @@ -91,7 +88,7 @@ ObjCClass_cache_set(PyObject *module, Class cls, PyObject *obj) { objctypes_state *state = (objctypes_state *)PyModule_GetState(module); cache_map *cache = (cache_map *)state->ObjCClass_cache; - (*cache)[cls] = (PyObject *)obj; + (*cache)[cls] = obj; } void @@ -116,7 +113,7 @@ ObjCObject_cache_deinit(PyObject *module) delete (cache_map *)state->ObjCObject_cache; } -ObjCObjectObject * +PyObject * ObjCObject_cache_get(PyObject *module, id obj) { objctypes_state *state = (objctypes_state *)PyModule_GetState(module); @@ -124,17 +121,17 @@ ObjCObject_cache_get(PyObject *module, id obj) const auto it = cache->find(obj); if (it != cache->end()) { - return (ObjCObjectObject *)Py_NewRef(it->second); + return Py_NewRef(it->second); } return NULL; } void -ObjCObject_cache_set(PyObject *module, id obj, ObjCObjectObject *pyobj) +ObjCObject_cache_set(PyObject *module, id obj, PyObject *pyobj) { objctypes_state *state = (objctypes_state *)PyModule_GetState(module); cache_map *cache = (cache_map *)state->ObjCObject_cache; - (*cache)[obj] = (PyObject *)pyobj; + (*cache)[obj] = pyobj; } void @@ -159,7 +156,7 @@ ObjCMethod_cache_deinit(PyObject *module) delete (cache_map *)state->ObjCMethod_cache; } -ObjCMethodObject * +PyObject * ObjCMethod_cache_get(PyObject *module, Method method) { objctypes_state *state = (objctypes_state *)PyModule_GetState(module); @@ -167,17 +164,17 @@ ObjCMethod_cache_get(PyObject *module, Method method) const auto it = cache->find(method); if (it != cache->end()) { - return (ObjCMethodObject *)Py_NewRef(it->second); + return Py_NewRef(it->second); } return NULL; } void -ObjCMethod_cache_set(PyObject *module, Method method, ObjCMethodObject *obj) +ObjCMethod_cache_set(PyObject *module, Method method, PyObject *obj) { objctypes_state *state = (objctypes_state *)PyModule_GetState(module); cache_map *cache = (cache_map *)state->ObjCMethod_cache; - (*cache)[method] = (PyObject *)obj; + (*cache)[method] = obj; } void @@ -202,7 +199,7 @@ ObjCSelector_cache_deinit(PyObject *module) delete (cache_map *)state->ObjCSelector_cache; } -ObjCSelectorObject * +PyObject * ObjCSelector_cache_get(PyObject *module, SEL sel) { objctypes_state *state = (objctypes_state *)PyModule_GetState(module); @@ -210,17 +207,17 @@ ObjCSelector_cache_get(PyObject *module, SEL sel) const auto it = cache->find(sel); if (it != cache->end()) { - return (ObjCSelectorObject *)Py_NewRef(it->second); + return Py_NewRef(it->second); } return NULL; } void -ObjCSelector_cache_set(PyObject *module, SEL sel, ObjCSelectorObject *obj) +ObjCSelector_cache_set(PyObject *module, SEL sel, PyObject *obj) { objctypes_state *state = (objctypes_state *)PyModule_GetState(module); cache_map *cache = (cache_map *)state->ObjCSelector_cache; - (*cache)[sel] = (PyObject *)obj; + (*cache)[sel] = obj; } void diff --git a/csrc/objctypes_cache.h b/csrc/objctypes_cache.h index 0d42dc9..9b08319 100644 --- a/csrc/objctypes_cache.h +++ b/csrc/objctypes_cache.h @@ -8,11 +8,7 @@ #include -#include "objcmethod.h" -#include "objcobject.h" -#include "objcselector.h" - -#include +#include #ifdef __cplusplus extern "C" { @@ -124,7 +120,7 @@ ObjCObject_cache_deinit(PyObject *module); * @return A new reference to the cached `ObjCObject` object, or `NULL` if not * found. */ -ObjCObjectObject * +PyObject * ObjCObject_cache_get(PyObject *module, id obj); /** @@ -134,7 +130,7 @@ ObjCObject_cache_get(PyObject *module, id obj); * @param pyobj The `ObjCObject` to associate with the object. */ void -ObjCObject_cache_set(PyObject *module, id obj, ObjCObjectObject *pyobj); +ObjCObject_cache_set(PyObject *module, id obj, PyObject *pyobj); /** * @brief Delete an `ObjCObject` from the cache. @@ -166,7 +162,7 @@ ObjCMethod_cache_deinit(PyObject *module); * @return A new reference to the cached `ObjCMethod` object, or `NULL` if not * found. */ -ObjCMethodObject * +PyObject * ObjCMethod_cache_get(PyObject *module, Method method); /** @@ -176,7 +172,7 @@ ObjCMethod_cache_get(PyObject *module, Method method); * @param obj The `ObjCMethod` to associate with the method. */ void -ObjCMethod_cache_set(PyObject *module, Method method, ObjCMethodObject *obj); +ObjCMethod_cache_set(PyObject *module, Method method, PyObject *obj); /** * @brief Delete an `ObjCMethod` from the cache. @@ -219,7 +215,7 @@ ObjCSelector_cache_deinit(PyObject *module); * @return A new reference to the cached `ObjCSelector` object, or `NULL` if * not found. */ -ObjCSelectorObject * +PyObject * ObjCSelector_cache_get(PyObject *module, SEL sel); /** @@ -229,7 +225,7 @@ ObjCSelector_cache_get(PyObject *module, SEL sel); * @param obj The `ObjCSelector` to associate with the selector. */ void -ObjCSelector_cache_set(PyObject *module, SEL sel, ObjCSelectorObject *obj); +ObjCSelector_cache_set(PyObject *module, SEL sel, PyObject *obj); /** * @brief Delete an `ObjCSelector` from the cache. diff --git a/csrc/objctypes_module.h b/csrc/objctypes_module.h index c95a195..e949564 100644 --- a/csrc/objctypes_module.h +++ b/csrc/objctypes_module.h @@ -8,6 +8,8 @@ #include +#include + extern PyModuleDef objctypes_module; typedef struct { @@ -27,6 +29,23 @@ typedef struct { */ PyObject *ObjCBool_NO; + /// @brief The `ObjCClass` type. + PyTypeObject *ObjCClass_Type; + + /** + * @brief Cache for `ObjCClass` instances. + * @details This is a pointer to a C++ `std::map` that maps Objective-C + * class pointers to their corresponding `ObjCClass` Python objects. + * @warning Do not manipulate this field outside of the `ObjCClass` type. + */ + void *ObjCClass_cache; + + /** + * @brief Mutex for synchronizing access to the `ObjCClass` cache. + * @warning Do not manipulate this field outside of the `ObjCClass` type. + */ + PyMutex ObjCClass_cache_mutex; + /// @brief The `ObjCMetaClass` type. PyTypeObject *ObjCMetaClass_Type; @@ -47,23 +66,6 @@ typedef struct { */ PyMutex ObjCMetaClass_cache_mutex; - /// @brief The `ObjCClass` type. - PyTypeObject *ObjCClass_Type; - - /** - * @brief Cache for `ObjCClass` instances. - * @details This is a pointer to a C++ `std::map` that maps Objective-C - * class pointers to their corresponding `ObjCClass` Python objects. - * @warning Do not manipulate this field outside of the `ObjCClass` type. - */ - void *ObjCClass_cache; - - /** - * @brief Mutex for synchronizing access to the `ObjCClass` cache. - * @warning Do not manipulate this field outside of the `ObjCClass` type. - */ - PyMutex ObjCClass_cache_mutex; - /// @brief The `ObjCMethod` type. PyTypeObject *ObjCMethod_Type; @@ -119,4 +121,40 @@ typedef struct { } objctypes_state; +typedef struct { + BOOL value; +} ObjCBoolData; + +extern PyType_Spec ObjCBool_spec; + +typedef struct { + Class value; +} ObjCClassData; + +extern PyType_Spec ObjCClass_spec; + +typedef struct { + Class value; +} ObjCMetaClassData; + +extern PyType_Spec ObjCMetaClass_spec; + +typedef struct { + Method value; +} ObjCMethodData; + +extern PyType_Spec ObjCMethod_spec; + +typedef struct { + id value; +} ObjCObjectData; + +extern PyType_Spec ObjCObject_spec; + +typedef struct { + SEL value; +} ObjCSelectorData; + +extern PyType_Spec ObjCSelector_spec; + #endif // OBJCTYPES_MODULE_H From bc2f213daf7bc458dea56c3cf8641af9eb4d1180 Mon Sep 17 00:00:00 2001 From: qqfunc Date: Sat, 25 Apr 2026 21:06:04 +0900 Subject: [PATCH 2/8] fix: remove unnecessary format --- csrc/objcselector.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csrc/objcselector.c b/csrc/objcselector.c index 1c821e2..6e1541e 100644 --- a/csrc/objcselector.c +++ b/csrc/objcselector.c @@ -151,8 +151,8 @@ ObjCSelector_from_address(PyTypeObject *type, PyObject *address) SEL sel = PyLong_AsVoidPtr(address); if (sel == NULL) { - PyErr_Format(PyExc_TypeError, - "The specified address is a null selector", sel); + PyErr_SetString(PyExc_TypeError, + "The specified address is a null selector"); return NULL; } From d30d32467b247494d07001f0b6a03462b87caa47 Mon Sep 17 00:00:00 2001 From: qqfunc Date: Sat, 25 Apr 2026 21:08:52 +0900 Subject: [PATCH 3/8] fix: fix some errors --- csrc/objcobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/csrc/objcobject.c b/csrc/objcobject.c index 6025f10..e1bea67 100644 --- a/csrc/objcobject.c +++ b/csrc/objcobject.c @@ -122,12 +122,14 @@ ObjCObject_from_address(PyTypeObject *type, PyObject *address) "The Objective-C object at %p is a metaclass. Use " "ObjCMetaClass.from_address() instead.", obj); + return NULL; } else { PyErr_Format(PyExc_TypeError, "The Objective-C object at %p is a class. Use " "ObjCClass.from_address() instead.", obj); + return NULL; } } From 53d6eed24427430824f5a5fe7bc5c51b3c70d1fb Mon Sep 17 00:00:00 2001 From: qqfunc Date: Sat, 25 Apr 2026 21:28:46 +0900 Subject: [PATCH 4/8] fix: fix some `ObjCObject` behavior --- csrc/objcclass.c | 26 ++++++++++++++++++++++---- tests/test_objcclass.py | 33 +++++++++++++++++++++++++++++++++ tests/test_objcobject.py | 6 ------ 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/csrc/objcclass.c b/csrc/objcclass.c index 89dec2d..4b7c383 100644 --- a/csrc/objcclass.c +++ b/csrc/objcclass.c @@ -18,7 +18,7 @@ ObjCClass_dealloc(PyObject *self) objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); - if (data != NULL) { + if (data != NULL && data->value != NULL) { PyMutex_Lock(&state->ObjCClass_cache_mutex); ObjCClass_cache_del(module, data->value); PyMutex_Unlock(&state->ObjCClass_cache_mutex); @@ -58,6 +58,13 @@ ObjCClass_address(PyObject *self, void *Py_UNUSED(closure)) objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + // Make sure that the class is not ObjCObject class + if (data->value == NULL) { + PyErr_SetString(PyExc_TypeError, + "ObjCObject is not an actual Objective-C class"); + return NULL; + } + return PyLong_FromVoidPtr(data->value); } @@ -73,9 +80,13 @@ ObjCClass_name(PyObject *self, void *Py_UNUSED(closure)) objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + // Make sure that the class is not ObjCObject class if (data->value == NULL) { - return Py_GetConstant(Py_CONSTANT_EMPTY_STR); + PyErr_SetString(PyExc_TypeError, + "ObjCObject is not an actual Objective-C class"); + return NULL; } + return PyUnicode_FromString(class_getName(data->value)); } @@ -91,6 +102,12 @@ ObjCClass_load_methods(PyObject *self, PyObject *Py_UNUSED(args)) objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + if (data->value == NULL) { + PyErr_SetString(PyExc_TypeError, + "ObjCObject is not an actual Objective-C class"); + return NULL; + } + unsigned int outCount; Method *methods = class_copyMethodList(data->value, &outCount); for (unsigned int num = 0; num < outCount; num++) { @@ -178,9 +195,10 @@ ObjCClass_init(PyObject *self, PyObject *args, PyObject *kwds) objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + // This function is not called when the ObjCObject class is initialized data->value = NULL; - - return 0; + PyErr_SetNone(PyExc_NotImplementedError); + return -1; } /// @brief `ObjCClass.from_address()` diff --git a/tests/test_objcclass.py b/tests/test_objcclass.py index 2d23fd3..958dae1 100644 --- a/tests/test_objcclass.py +++ b/tests/test_objcclass.py @@ -70,6 +70,11 @@ def test_objcclass_repr() -> None: assert repr(NSNumber) == "" +def test_objcclass_repr_objcobject_class() -> None: + """Test ObjCClass.__repr__() for the ObjCObject class.""" + assert repr(ObjCObject) == "" + + def test_objcclass_from_address() -> None: """Test ObjCClass.from_address().""" NSObject = ObjCClass.from_name("NSObject") # noqa: N806 @@ -118,6 +123,16 @@ def test_objcclass_from_address_rejects_metaclass() -> None: ) +def test_objcclass_from_address_objcobject_class() -> None: + """Test ObjCClass.from_address() for the ObjCObject class.""" + with pytest.raises(TypeError) as excinfo: + ObjCClass.from_address(ObjCObject.address) + + assert ( + str(excinfo.value) == "ObjCObject is not an actual Objective-C class" + ) + + def test_objcclass_address() -> None: """Test ObjCClass.address.""" NSObject = ObjCClass.from_name("NSObject") # noqa: N806 @@ -133,6 +148,15 @@ def test_objcclass_address() -> None: assert NSNumber.address != NSObject.address +def test_objcclass_address_objcobject_class() -> None: + """Test ObjCClass.address for the ObjCObject class.""" + with pytest.raises(TypeError) as excinfo: + _ = ObjCObject.address + assert ( + str(excinfo.value) == "ObjCObject is not an actual Objective-C class" + ) + + def test_objcclass_name() -> None: """Test ObjCClass.name.""" NSObject = ObjCClass.from_name("NSObject") # noqa: N806 @@ -142,3 +166,12 @@ def test_objcclass_name() -> None: assert NSObject.name == "NSObject" assert NSString.name == "NSString" assert NSNumber.name == "NSNumber" + + +def test_objcclass_name_objcobject_class() -> None: + """Test ObjCClass.name for the ObjCObject class.""" + with pytest.raises(TypeError) as excinfo: + _ = ObjCObject.name + assert ( + str(excinfo.value) == "ObjCObject is not an actual Objective-C class" + ) diff --git a/tests/test_objcobject.py b/tests/test_objcobject.py index 25a80c1..158f828 100644 --- a/tests/test_objcobject.py +++ b/tests/test_objcobject.py @@ -12,12 +12,6 @@ def test_objcobject() -> None: assert str(excinfo.value) == "ObjCObject cannot be initialized directly" -def test_objcobject_type() -> None: - """Test ObjCObject type object.""" - assert repr(ObjCObject) == "" - assert ObjCObject.name == "" - - def test_objcobject_cache() -> None: """Test if ObjCObject objects are cached.""" From 64b4b341cebe9d791864b6c5fc8183b7587132c5 Mon Sep 17 00:00:00 2001 From: qqfunc Date: Sat, 25 Apr 2026 21:38:57 +0900 Subject: [PATCH 5/8] fix: fix some `ObjCClass` behaviors --- csrc/objcmetaclass.c | 17 ++++++++++++++--- tests/test_objcmetaclass.py | 25 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/csrc/objcmetaclass.c b/csrc/objcmetaclass.c index b6618ff..429310d 100644 --- a/csrc/objcmetaclass.c +++ b/csrc/objcmetaclass.c @@ -18,7 +18,7 @@ ObjCMetaClass_dealloc(PyObject *self) objctypes_state *state = PyModule_GetState(module); ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); - if (data != NULL) { + if (data != NULL && data->value != NULL) { PyMutex_Lock(&state->ObjCMetaClass_cache_mutex); ObjCMetaClass_cache_del(module, data->value); PyMutex_Unlock(&state->ObjCMetaClass_cache_mutex); @@ -41,7 +41,7 @@ ObjCMetaClass_repr(PyObject *self) PyObject_GetTypeData(self, state->ObjCMetaClass_Type); if (data->value == NULL) { - return PyUnicode_FromString(""); + return PyUnicode_FromString(""); } return PyUnicode_FromFormat("", class_getName(data->value)); @@ -60,6 +60,13 @@ ObjCMetaClass_address(PyObject *self, void *Py_UNUSED(closure)) ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); + // Make sure that the metaclass is not ObjCClass class + if (data->value == NULL) { + PyErr_SetString(PyExc_TypeError, + "ObjCClass is not an actual Objective-C metaclass"); + return NULL; + } + return PyLong_FromVoidPtr(data->value); } @@ -76,9 +83,13 @@ ObjCMetaClass_name(PyObject *self, void *Py_UNUSED(closure)) ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); + // Make sure that the metaclass is not ObjCClass class if (data->value == NULL) { - return Py_GetConstant(Py_CONSTANT_EMPTY_STR); + PyErr_SetString(PyExc_TypeError, + "ObjCClass is not an actual Objective-C metaclass"); + return NULL; } + return PyUnicode_FromString(class_getName(data->value)); } diff --git a/tests/test_objcmetaclass.py b/tests/test_objcmetaclass.py index 3b9398d..48b293e 100644 --- a/tests/test_objcmetaclass.py +++ b/tests/test_objcmetaclass.py @@ -36,6 +36,11 @@ def test_objcmetaclass_repr() -> None: assert repr(NSNumberMeta) == "" +def test_objcmetaclass_repr_objcclass_class() -> None: + """Test ObjCMetaClass.__repr__() for the ObjCClass class.""" + assert repr(ObjCClass) == "" + + def test_objcmetaclass_cache() -> None: """Test if ObjCMetaClass objects are cached.""" NSObjectMeta = ObjCMetaClass.from_name("NSObject") # noqa: N806 @@ -66,6 +71,16 @@ def test_objcmetaclass_address() -> None: assert NSNumberMeta.address != NSObjectMeta.address +def test_objcmetaclass_address_objcclass_class() -> None: + """Test ObjCMetaClass.address for the ObjCClass class.""" + with pytest.raises(TypeError) as excinfo: + _ = ObjCClass.address + assert ( + str(excinfo.value) + == "ObjCClass is not an actual Objective-C metaclass" + ) + + def test_objcmetaclass_name() -> None: """Test ObjCMetaClass.name.""" NSObjectMeta = ObjCMetaClass.from_name("NSObject") # noqa: N806 @@ -77,6 +92,16 @@ def test_objcmetaclass_name() -> None: assert NSNumberMeta.name == "NSNumber" +def test_objcmetaclass_name_objcclass_class() -> None: + """Test ObjCMetaClass.name for the ObjCClass class.""" + with pytest.raises(TypeError) as excinfo: + _ = ObjCClass.name + assert ( + str(excinfo.value) + == "ObjCClass is not an actual Objective-C metaclass" + ) + + def test_objcmetaclass_from_address() -> None: """Test ObjCMetaClass.from_address().""" NSObjectMeta = ObjCMetaClass.from_name("NSObject") # noqa: N806 From b2282a9d96d251f84988633837c3dfd9eaad853d Mon Sep 17 00:00:00 2001 From: qqfunc Date: Sat, 25 Apr 2026 22:16:27 +0900 Subject: [PATCH 6/8] fix: fix `ObjCMetaClass` --- csrc/objcclass.c | 4 +++- csrc/objcmetaclass.c | 28 ++++++++++++++++++----- csrc/objcobject.c | 2 +- src/objctypes/__init__.pyi | 9 ++++---- tests/test_objcmetaclass.py | 45 ++++++++++++++++++++++++++----------- 5 files changed, 63 insertions(+), 25 deletions(-) diff --git a/csrc/objcclass.c b/csrc/objcclass.c index 4b7c383..24fb924 100644 --- a/csrc/objcclass.c +++ b/csrc/objcclass.c @@ -156,6 +156,7 @@ _ObjCClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) // Create a new ObjCClass object PyObject *args = Py_BuildValue("(s(O){})", class_getName(cls), base); + Py_DECREF(base); PyObject *kwds = PyDict_New(); self = PyType_Type.tp_new(type, args, kwds); Py_XDECREF(args); @@ -319,7 +320,8 @@ PyType_Spec ObjCClass_spec = { .name = "objctypes.ObjCClass", .basicsize = -(long)sizeof(ObjCClassData), .itemsize = 0, - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_TYPE_SUBCLASS, + .flags = + Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_TYPE_SUBCLASS, .slots = ObjCClass_slots, }; diff --git a/csrc/objcmetaclass.c b/csrc/objcmetaclass.c index 429310d..51f371a 100644 --- a/csrc/objcmetaclass.c +++ b/csrc/objcmetaclass.c @@ -110,19 +110,37 @@ _ObjCMetaClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) } PyObject *self = ObjCMetaClass_cache_get(module, cls); - if (self == NULL) { - PyObject *args = - Py_BuildValue("(s(O){})", class_getName(cls), &PyBaseObject_Type); + PyObject *base; + + // Determine the Python base class of the ObjCMetaClass + Class super_cls = class_getSuperclass(cls); + if (super_cls == NULL) { + // The class is a root class + base = (PyObject *)state->ObjCClass_Type; + } + else { + // Retrieve the superclass of the Objective-C class + base = _ObjCMetaClass_FromClass(type, super_cls, 0); + if (base == NULL) { + if (lock_cache) { + PyMutex_Unlock(&state->ObjCMetaClass_cache_mutex); + } + return NULL; + } + } + + PyObject *args = Py_BuildValue("(s(O){})", class_getName(cls), base); + Py_DECREF(base); PyObject *kwds = PyDict_New(); self = PyType_Type.tp_new(type, args, kwds); Py_XDECREF(args); Py_XDECREF(kwds); if (self != NULL) { - ObjCMetaClassData *cls_state = + ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); - cls_state->value = cls; + data->value = cls; ObjCMetaClass_cache_set(module, cls, self); } } diff --git a/csrc/objcobject.c b/csrc/objcobject.c index e1bea67..31bcfc8 100644 --- a/csrc/objcobject.c +++ b/csrc/objcobject.c @@ -199,7 +199,7 @@ PyType_Spec ObjCObject_spec = { .name = "objctypes.ObjCObject", .basicsize = -(long)sizeof(ObjCObjectData), .itemsize = 0, - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .flags = Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DEFAULT, .slots = ObjCObject_slots, }; diff --git a/src/objctypes/__init__.pyi b/src/objctypes/__init__.pyi index a2c4ecf..a9fd0fe 100644 --- a/src/objctypes/__init__.pyi +++ b/src/objctypes/__init__.pyi @@ -10,7 +10,7 @@ class ObjCMetaClass(type): """ @classmethod - def from_address(cls, address: int, /) -> ObjCMetaClass: + def from_address(cls, address: int, /) -> type[ObjCClass]: """Retrieve an Objective-C metaclass from the specified address. :param address: The address of the Objective-C metaclass. @@ -23,7 +23,7 @@ class ObjCMetaClass(type): """ @classmethod - def from_name(cls, name: str, /) -> ObjCMetaClass: + def from_name(cls, name: str, /) -> type[ObjCClass]: """Retrieve an Objective-C metaclass by class name. :param name: The name of the Objective-C class whose metaclass @@ -41,8 +41,7 @@ class ObjCMetaClass(type): def name(cls) -> str: """The name of the Objective-C metaclass.""" -@final -class ObjCClass(type, metaclass=ObjCMetaClass): # NOTE: type? +class ObjCClass(type, metaclass=ObjCMetaClass): """A Python wrapper class for an Objective-C class. Equivalent to @@ -58,7 +57,7 @@ class ObjCClass(type, metaclass=ObjCMetaClass): # NOTE: type? /, ) -> None: ... @classmethod - def from_address(cls, address: int, /) -> ObjCClass: + def from_address(cls, address: int, /) -> type[ObjCObject]: """Retrieve an Objective-C class from the specified address. :param address: The address of the Objective-C class. diff --git a/tests/test_objcmetaclass.py b/tests/test_objcmetaclass.py index 48b293e..ddf6fc9 100644 --- a/tests/test_objcmetaclass.py +++ b/tests/test_objcmetaclass.py @@ -20,25 +20,23 @@ def test_objcmetaclass_from_name() -> None: ) -def test_objcmetaclass_doc() -> None: - """Test docstring of ObjCMetaClass.""" - assert ObjCMetaClass.__doc__ is not None - - -def test_objcmetaclass_repr() -> None: - """Test ObjCMetaClass.__repr__().""" +def test_objcmetaclass_inheritance() -> None: + """Test inheritance relationships of ObjCMetaClass.""" NSObjectMeta = ObjCMetaClass.from_name("NSObject") # noqa: N806 NSStringMeta = ObjCMetaClass.from_name("NSString") # noqa: N806 NSNumberMeta = ObjCMetaClass.from_name("NSNumber") # noqa: N806 - assert repr(NSObjectMeta) == "" - assert repr(NSStringMeta) == "" - assert repr(NSNumberMeta) == "" + assert issubclass(NSObjectMeta, ObjCClass) + assert issubclass(NSStringMeta, ObjCClass) + assert issubclass(NSNumberMeta, ObjCClass) + assert issubclass(NSStringMeta, NSObjectMeta) + assert issubclass(NSNumberMeta, NSObjectMeta) -def test_objcmetaclass_repr_objcclass_class() -> None: - """Test ObjCMetaClass.__repr__() for the ObjCClass class.""" - assert repr(ObjCClass) == "" + assert not issubclass(NSObjectMeta, NSStringMeta) + assert not issubclass(NSObjectMeta, NSNumberMeta) + assert not issubclass(NSNumberMeta, NSStringMeta) + assert not issubclass(NSStringMeta, NSNumberMeta) def test_objcmetaclass_cache() -> None: @@ -56,6 +54,27 @@ def test_objcmetaclass_cache() -> None: assert NSNumberMeta is not NSObjectMeta +def test_objcmetaclass_doc() -> None: + """Test docstring of ObjCMetaClass.""" + assert ObjCMetaClass.__doc__ is not None + + +def test_objcmetaclass_repr() -> None: + """Test ObjCMetaClass.__repr__().""" + NSObjectMeta = ObjCMetaClass.from_name("NSObject") # noqa: N806 + NSStringMeta = ObjCMetaClass.from_name("NSString") # noqa: N806 + NSNumberMeta = ObjCMetaClass.from_name("NSNumber") # noqa: N806 + + assert repr(NSObjectMeta) == "" + assert repr(NSStringMeta) == "" + assert repr(NSNumberMeta) == "" + + +def test_objcmetaclass_repr_objcclass_class() -> None: + """Test ObjCMetaClass.__repr__() for the ObjCClass class.""" + assert repr(ObjCClass) == "" + + def test_objcmetaclass_address() -> None: """Test ObjCMetaClass.address.""" NSObjectMeta = ObjCMetaClass.from_name("NSObject") # noqa: N806 From d6d3df9ab8ccfb30f0a3b5a09042cb4c929838a8 Mon Sep 17 00:00:00 2001 From: qqfunc Date: Sat, 25 Apr 2026 22:43:00 +0900 Subject: [PATCH 7/8] fix: add `NULL` check --- csrc/objcbool.c | 38 ++++++++++++++++++++++++++++++++++---- csrc/objcclass.c | 28 ++++++++++++++++++++++++++-- csrc/objcmetaclass.c | 19 +++++++++++++++++-- csrc/objcmethod.c | 22 +++++++++++++++++----- csrc/objcobject.c | 17 +++++++++++------ csrc/objcselector.c | 25 +++++++++++++++++++++++-- 6 files changed, 128 insertions(+), 21 deletions(-) diff --git a/csrc/objcbool.c b/csrc/objcbool.c index 25e843d..ec702a9 100644 --- a/csrc/objcbool.c +++ b/csrc/objcbool.c @@ -26,6 +26,9 @@ ObjCBool_repr(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromFormat("ObjCBool(%s)", data->value ? "True" : "False"); @@ -42,6 +45,9 @@ ObjCBool_str(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromString(data->value ? "YES" : "NO"); } @@ -64,10 +70,16 @@ _ObjCBool_FromLong(PyTypeObject *type, long v) if (state->ObjCBool_YES != NULL) { ObjCBoolData *data = PyObject_GetTypeData( state->ObjCBool_YES, state->ObjCBool_Type); - data->value = YES; + if (data != NULL) { + data->value = YES; + } + else { + Py_DECREF(state->ObjCBool_YES); + state->ObjCBool_YES = NULL; + } } } - return Py_NewRef(state->ObjCBool_YES); + return Py_XNewRef(state->ObjCBool_YES); } // Cache the NO value if it hasn't been cached yet @@ -76,10 +88,16 @@ _ObjCBool_FromLong(PyTypeObject *type, long v) if (state->ObjCBool_NO != NULL) { ObjCBoolData *data = PyObject_GetTypeData(state->ObjCBool_NO, state->ObjCBool_Type); - data->value = NO; + if (data == NULL) { + Py_DECREF(state->ObjCBool_NO); + state->ObjCBool_NO = NULL; + } + else { + data->value = NO; + } } } - return Py_NewRef(state->ObjCBool_NO); + return Py_XNewRef(state->ObjCBool_NO); } /// @brief `ObjCBool.__new__()` @@ -107,6 +125,9 @@ ObjCBool_bool(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + if (data == NULL) { + return -1; + } return data->value ? 1 : 0; } @@ -122,6 +143,9 @@ ObjCBool_invert(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + if (data == NULL) { + return NULL; + } return _ObjCBool_FromLong(Py_TYPE(self), !(data->value)); } @@ -137,6 +161,9 @@ ObjCBool_int(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + if (data == NULL) { + return NULL; + } return PyLong_FromLong(data->value ? 1 : 0); } @@ -152,6 +179,9 @@ ObjCBool_float(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCBoolData *data = PyObject_GetTypeData(self, state->ObjCBool_Type); + if (data == NULL) { + return NULL; + } return PyFloat_FromDouble(data->value ? 1 : 0); } diff --git a/csrc/objcclass.c b/csrc/objcclass.c index 24fb924..13e9450 100644 --- a/csrc/objcclass.c +++ b/csrc/objcclass.c @@ -38,6 +38,9 @@ ObjCClass_repr(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + if (data == NULL) { + return NULL; + } if (data->value == NULL) { return PyUnicode_FromString(""); @@ -57,6 +60,9 @@ ObjCClass_address(PyObject *self, void *Py_UNUSED(closure)) } objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + if (data == NULL) { + return NULL; + } // Make sure that the class is not ObjCObject class if (data->value == NULL) { @@ -79,6 +85,9 @@ ObjCClass_name(PyObject *self, void *Py_UNUSED(closure)) } objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + if (data == NULL) { + return NULL; + } // Make sure that the class is not ObjCObject class if (data->value == NULL) { @@ -101,6 +110,9 @@ ObjCClass_load_methods(PyObject *self, PyObject *Py_UNUSED(args)) } objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + if (data == NULL) { + return NULL; + } if (data->value == NULL) { PyErr_SetString(PyExc_TypeError, @@ -165,8 +177,14 @@ _ObjCClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) if (self != NULL) { ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); - data->value = cls; - ObjCClass_cache_set(module, cls, self); + if (data == NULL) { + Py_DECREF(self); + self = NULL; + } + else { + data->value = cls; + ObjCClass_cache_set(module, cls, self); + } } } @@ -193,8 +211,14 @@ ObjCClass_init(PyObject *self, PyObject *args, PyObject *kwds) // Get the type data of the ObjCClass object PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &objctypes_module); + if (module == NULL) { + return -1; + } objctypes_state *state = PyModule_GetState(module); ObjCClassData *data = PyObject_GetTypeData(self, state->ObjCClass_Type); + if (data == NULL) { + return -1; + } // This function is not called when the ObjCObject class is initialized data->value = NULL; diff --git a/csrc/objcmetaclass.c b/csrc/objcmetaclass.c index 51f371a..6f9a544 100644 --- a/csrc/objcmetaclass.c +++ b/csrc/objcmetaclass.c @@ -39,6 +39,9 @@ ObjCMetaClass_repr(PyObject *self) objctypes_state *state = PyModule_GetState(module); ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); + if (data == NULL) { + return NULL; + } if (data->value == NULL) { return PyUnicode_FromString(""); @@ -59,6 +62,9 @@ ObjCMetaClass_address(PyObject *self, void *Py_UNUSED(closure)) objctypes_state *state = PyModule_GetState(module); ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); + if (data == NULL) { + return NULL; + } // Make sure that the metaclass is not ObjCClass class if (data->value == NULL) { @@ -82,6 +88,9 @@ ObjCMetaClass_name(PyObject *self, void *Py_UNUSED(closure)) objctypes_state *state = PyModule_GetState(module); ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); + if (data == NULL) { + return NULL; + } // Make sure that the metaclass is not ObjCClass class if (data->value == NULL) { @@ -140,8 +149,14 @@ _ObjCMetaClass_FromClass(PyTypeObject *type, Class cls, int lock_cache) if (self != NULL) { ObjCMetaClassData *data = PyObject_GetTypeData(self, state->ObjCMetaClass_Type); - data->value = cls; - ObjCMetaClass_cache_set(module, cls, self); + if (data == NULL) { + Py_DECREF(self); + self = NULL; + } + else { + data->value = cls; + ObjCMetaClass_cache_set(module, cls, self); + } } } diff --git a/csrc/objcmethod.c b/csrc/objcmethod.c index 9a1de59..f93df8d 100644 --- a/csrc/objcmethod.c +++ b/csrc/objcmethod.c @@ -38,6 +38,9 @@ ObjCMethod_repr(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromFormat("", sel_getName(method_getName(data->value)), @@ -55,6 +58,9 @@ ObjCMethod_str(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromString(sel_getName(method_getName(data->value))); } @@ -70,6 +76,9 @@ ObjCMethod_name(PyObject *self, void *Py_UNUSED(closure)) } objctypes_state *state = PyModule_GetState(module); ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromString(sel_getName(method_getName(data->value))); } @@ -85,6 +94,9 @@ ObjCMethod_address(PyObject *self, void *Py_UNUSED(closure)) } objctypes_state *state = PyModule_GetState(module); ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); + if (data == NULL) { + return NULL; + } return PyLong_FromVoidPtr(data->value); } @@ -109,14 +121,14 @@ _ObjCMethod_FromMethod(PyTypeObject *type, Method method) if (self != NULL) { ObjCMethodData *data = PyObject_GetTypeData(self, state->ObjCMethod_Type); - if (data != NULL) { - data->value = method; - ObjCMethod_cache_set(module, method, self); - } - else { + if (data == NULL) { Py_DECREF(self); self = NULL; } + else { + data->value = method; + ObjCMethod_cache_set(module, method, self); + } } } diff --git a/csrc/objcobject.c b/csrc/objcobject.c index 31bcfc8..9d1d240 100644 --- a/csrc/objcobject.c +++ b/csrc/objcobject.c @@ -38,6 +38,9 @@ ObjCObject_repr(PyObject *self) } objctypes_state *state = PyModule_GetState(module); ObjCObjectData *data = PyObject_GetTypeData(self, state->ObjCObject_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromFormat("", object_getClassName(data->value), data->value); @@ -53,6 +56,9 @@ ObjCObject_address(PyObject *self, void *Py_UNUSED(closure)) } objctypes_state *state = PyModule_GetState(module); ObjCObjectData *data = PyObject_GetTypeData(self, state->ObjCObject_Type); + if (data == NULL) { + return NULL; + } return PyLong_FromVoidPtr(data->value); } @@ -71,20 +77,19 @@ _ObjCObject_FromId(PyTypeObject *type, id obj) PyMutex_Lock(&state->ObjCObject_cache_mutex); PyObject *self = ObjCObject_cache_get(module, obj); - if (self == NULL) { self = type->tp_alloc(type, 0); if (self != NULL) { ObjCObjectData *data = PyObject_GetTypeData(self, state->ObjCObject_Type); - if (data != NULL) { - data->value = obj; - ObjCObject_cache_set(module, obj, self); - } - else { + if (data == NULL) { Py_DECREF(self); self = NULL; } + else { + data->value = obj; + ObjCObject_cache_set(module, obj, self); + } } } diff --git a/csrc/objcselector.c b/csrc/objcselector.c index 6e1541e..9cb825e 100644 --- a/csrc/objcselector.c +++ b/csrc/objcselector.c @@ -39,6 +39,9 @@ ObjCSelector_repr(PyObject *self) objctypes_state *state = PyModule_GetState(module); ObjCSelectorData *data = PyObject_GetTypeData(self, state->ObjCSelector_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromFormat("ObjCSelector('%s')", sel_getName(data->value)); @@ -56,6 +59,9 @@ ObjCSelector_str(PyObject *self) objctypes_state *state = PyModule_GetState(module); ObjCSelectorData *data = PyObject_GetTypeData(self, state->ObjCSelector_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromString(sel_getName(data->value)); } @@ -72,6 +78,9 @@ ObjCSelector_address(PyObject *self, void *Py_UNUSED(closure)) objctypes_state *state = PyModule_GetState(module); ObjCSelectorData *data = PyObject_GetTypeData(self, state->ObjCSelector_Type); + if (data == NULL) { + return NULL; + } return PyLong_FromVoidPtr(data->value); } @@ -88,6 +97,9 @@ ObjCSelector_name(PyObject *self, void *Py_UNUSED(closure)) objctypes_state *state = PyModule_GetState(module); ObjCSelectorData *data = PyObject_GetTypeData(self, state->ObjCSelector_Type); + if (data == NULL) { + return NULL; + } return PyUnicode_FromString(sel_getName(data->value)); } @@ -104,6 +116,9 @@ ObjCSelector_is_mapped(PyObject *self, void *Py_UNUSED(closure)) objctypes_state *state = PyModule_GetState(module); ObjCSelectorData *data = PyObject_GetTypeData(self, state->ObjCSelector_Type); + if (data == NULL) { + return NULL; + } return sel_isMapped(data->value) ? Py_True : Py_False; } @@ -127,8 +142,14 @@ _ObjCSelector_FromSEL(PyTypeObject *type, SEL sel) if (self != NULL) { ObjCSelectorData *data = PyObject_GetTypeData(self, state->ObjCSelector_Type); - data->value = sel; - ObjCSelector_cache_set(module, sel, self); + if (data == NULL) { + Py_DECREF(self); + self = NULL; + } + else { + data->value = sel; + ObjCSelector_cache_set(module, sel, self); + } } } From 3b937b777c684761fbe1eea325415dee63682cac Mon Sep 17 00:00:00 2001 From: qqfunc Date: Sat, 25 Apr 2026 22:45:20 +0900 Subject: [PATCH 8/8] refactor: remove unnecessary header symbol --- csrc/objctypes_cache.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/csrc/objctypes_cache.h b/csrc/objctypes_cache.h index 9b08319..9f151bf 100644 --- a/csrc/objctypes_cache.h +++ b/csrc/objctypes_cache.h @@ -196,17 +196,6 @@ ObjCSelector_cache_init(PyObject *module); void ObjCSelector_cache_deinit(PyObject *module); -/** - * @brief Get an `ObjCSelector` from the cache if it exists, otherwise return - * `NULL`. - * @param module The Python module object. - * @param sel The Objective-C selector to look up. - * @return A new reference to the cached `ObjCSelector` object, or `NULL` if - * not found. - */ -void -ObjCSelector_cache_deinit(PyObject *module); - /** * @brief Get an `ObjCSelector` from the cache if it exists, otherwise return * `NULL`.