]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-114314: ctypes: remove stgdict and switch to heap types (GH-116458)
authorPetr Viktorin <encukou@gmail.com>
Wed, 20 Mar 2024 16:33:08 +0000 (17:33 +0100)
committerGitHub <noreply@github.com>
Wed, 20 Mar 2024 16:33:08 +0000 (17:33 +0100)
Before this change, ctypes classes used a custom dict subclass, `StgDict`,
as their `tp_dict`. This acts like a regular dict but also includes extra information
about the type.

This replaces stgdict by `StgInfo`, a C struct on the type, accessed by
`PyObject_GetTypeData()` (PEP-697).
All usage of `StgDict` (mainly variables named `stgdict`, `dict`, `edict` etc.) is
converted to `StgInfo` (named `stginfo`, `info`, `einfo`, etc.).
Where the dict is actually used for class attributes (as a regular PyDict), it's now
called `attrdict`.

This change -- not overriding `tp_dict` -- is made to make me comfortable with
the next part of this PR: moving the initialization logic from `tp_new` to `tp_init`.

The `StgInfo` is set up in `__init__` of each class, with a guard that prevents
calling `__init__` more than once. Note that abstract classes (like `Array` or
`Structure`) are created using `PyType_FromMetaclass` and do not have
`__init__` called.
Previously, this was done in `__new__`, which also wasn't called for abstract
classes.
Since `__init__` can be called from Python code or skipped, there is a tested
guard to ensure `StgInfo` is initialized exactly once before it's used.

Co-authored-by: neonene <53406459+neonene@users.noreply.github.com>
Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
15 files changed:
Lib/test/test_ctypes/test_arrays.py
Lib/test/test_ctypes/test_callbacks.py
Lib/test/test_ctypes/test_funcptr.py
Lib/test/test_ctypes/test_pointers.py
Lib/test/test_ctypes/test_simplesubclasses.py
Lib/test/test_ctypes/test_struct_fields.py
Lib/test/test_ctypes/test_structures.py
Lib/test/test_ctypes/test_unions.py
Misc/NEWS.d/next/Library/2024-03-07-11-10-27.gh-issue-114314.iEhAMH.rst [new file with mode: 0644]
Modules/_ctypes/_ctypes.c
Modules/_ctypes/callbacks.c
Modules/_ctypes/callproc.c
Modules/_ctypes/cfield.c
Modules/_ctypes/ctypes.h
Modules/_ctypes/stgdict.c

index 774316e227ff73932a06b33f5a7e44111fd831cc..3568cf97f40b50c50f2f689b1edccdcdbc517916 100644 (file)
@@ -37,6 +37,22 @@ class ArrayTestCase(unittest.TestCase):
                 self.assertTrue(cls.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
                 self.assertFalse(cls.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
 
+    def test_metaclass_details(self):
+        # Abstract classes (whose metaclass __init__ was not called) can't be
+        # instantiated directly
+        NewArray = PyCArrayType.__new__(PyCArrayType, 'NewArray', (Array,), {})
+        for cls in Array, NewArray:
+            with self.subTest(cls=cls):
+                with self.assertRaisesRegex(TypeError, "abstract class"):
+                    obj = cls()
+
+        # Cannot call the metaclass __init__ more than once
+        class T(Array):
+            _type_ = c_int
+            _length_ = 13
+        with self.assertRaisesRegex(SystemError, "already initialized"):
+            PyCArrayType.__init__(T, 'ptr', (), {})
+
     def test_simple(self):
         # create classes holding simple numeric types, and check
         # various properties.
index 64f92ffdca6a3f880c307e0fe1b5216f1b8b95ed..8038169afe4304d5bb44ad51723b525e69c2be6f 100644 (file)
@@ -106,7 +106,7 @@ class Callbacks(unittest.TestCase):
 
     def test_unsupported_restype_1(self):
         # Only "fundamental" result types are supported for callback
-        # functions, the type must have a non-NULL stgdict->setfunc.
+        # functions, the type must have a non-NULL stginfo->setfunc.
         # POINTER(c_double), for example, is not supported.
 
         prototype = self.functype.__func__(POINTER(c_double))
index 0eed39484fb39ed9232d1f3fb73d587331add2ad..03cfddea6ea61a90078aa286c0ea0b8f1e4bc214 100644 (file)
@@ -29,6 +29,12 @@ class CFuncPtrTestCase(unittest.TestCase):
                 self.assertTrue(_CFuncPtr.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
                 self.assertFalse(_CFuncPtr.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
 
+    def test_metaclass_details(self):
+        # Cannot call the metaclass __init__ more than once
+        CdeclCallback = CFUNCTYPE(c_int, c_int, c_int)
+        with self.assertRaisesRegex(SystemError, "already initialized"):
+            PyCFuncPtrType.__init__(CdeclCallback, 'ptr', (), {})
+
     def test_basic(self):
         X = WINFUNCTYPE(c_int, c_int, c_int)
 
index 8cf2114c282cab2ea122a77e75755d67c475b498..3a5f3660dbbe239614b282e50496b169b43bf4b7 100644 (file)
@@ -33,6 +33,11 @@ class PointersTestCase(unittest.TestCase):
                 self.assertTrue(_Pointer.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
                 self.assertFalse(_Pointer.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
 
+    def test_metaclass_details(self):
+        # Cannot call the metaclass __init__ more than once
+        with self.assertRaisesRegex(SystemError, "already initialized"):
+            PyCPointerType.__init__(POINTER(c_byte), 'ptr', (), {})
+
     def test_pointer_crash(self):
 
         class A(POINTER(c_ulong)):
index c96798e67f23f7944426cea4d555d40f06396ecf..4e4bef3690f66a1290b0db17d0feaf7923cd72bd 100644 (file)
@@ -26,6 +26,29 @@ class Test(unittest.TestCase):
                 self.assertTrue(_SimpleCData.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
                 self.assertFalse(_SimpleCData.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
 
+    def test_metaclass_details(self):
+        # Abstract classes (whose metaclass __init__ was not called) can't be
+        # instantiated directly
+        NewT = PyCSimpleType.__new__(PyCSimpleType, 'NewT', (_SimpleCData,), {})
+        for cls in _SimpleCData, NewT:
+            with self.subTest(cls=cls):
+                with self.assertRaisesRegex(TypeError, "abstract class"):
+                    obj = cls()
+
+        # Cannot call the metaclass __init__ more than once
+        class T(_SimpleCData):
+            _type_ = "i"
+        with self.assertRaisesRegex(SystemError, "already initialized"):
+            PyCSimpleType.__init__(T, 'ptr', (), {})
+
+    def test_swapped_type_creation(self):
+        cls = PyCSimpleType.__new__(PyCSimpleType, '', (), {'_type_': 'i'})
+        with self.assertRaises(TypeError):
+            PyCSimpleType.__init__(cls)
+        PyCSimpleType.__init__(cls, '', (), {'_type_': 'i'})
+        self.assertEqual(cls.__ctype_le__.__dict__.get('_type_'), 'i')
+        self.assertEqual(cls.__ctype_be__.__dict__.get('_type_'), 'i')
+
     def test_compare(self):
         self.assertEqual(MyInt(3), MyInt(3))
         self.assertNotEqual(MyInt(42), MyInt(43))
index f474a02fa8db06e0c8ff496a50300560e1bb11ad..7adab794809def07cf84d9afd0dc0e74291b13cb 100644 (file)
@@ -69,7 +69,7 @@ class StructFieldsTestCase(unittest.TestCase):
     def test_gh99275(self):
         class BrokenStructure(Structure):
             def __init_subclass__(cls, **kwargs):
-                cls._fields_ = []  # This line will fail, `stgdict` is not ready
+                cls._fields_ = []  # This line will fail, `stginfo` is not ready
 
         with self.assertRaisesRegex(TypeError,
                                     'ctypes state is not initialized'):
index 98bc4bdcac9306d25b42b3b71519b0124ae98f06..8d83ce4f281b169cca0e3749429d86cb767d25fe 100644 (file)
@@ -85,6 +85,23 @@ class StructureTestCase(unittest.TestCase):
                 self.assertTrue(Structure.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
                 self.assertFalse(Structure.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
 
+    def test_metaclass_details(self):
+        # Abstract classes (whose metaclass __init__ was not called) can't be
+        # instantiated directly
+        NewStructure = PyCStructType.__new__(PyCStructType, 'NewStructure',
+                                             (Structure,), {})
+        for cls in Structure, NewStructure:
+            with self.subTest(cls=cls):
+                with self.assertRaisesRegex(TypeError, "abstract class"):
+                    obj = cls()
+
+        # Cannot call the metaclass __init__ more than once
+        class T(Structure):
+            _fields_ = [("x", c_char),
+                        ("y", c_char)]
+        with self.assertRaisesRegex(SystemError, "already initialized"):
+            PyCStructType.__init__(T, 'ptr', (), {})
+
     def test_simple_structs(self):
         for code, tp in self.formats.items():
             class X(Structure):
@@ -507,8 +524,8 @@ class StructureTestCase(unittest.TestCase):
     @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
     def test_issue18060_a(self):
         # This test case calls
-        # PyCStructUnionType_update_stgdict() for each
-        # _fields_ assignment, and PyCStgDict_clone()
+        # PyCStructUnionType_update_stginfo() for each
+        # _fields_ assignment, and PyCStgInfo_clone()
         # for the Mid and Vector class definitions.
         class Base(Structure):
             _fields_ = [('y', c_double),
@@ -523,7 +540,7 @@ class StructureTestCase(unittest.TestCase):
     @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
     def test_issue18060_b(self):
         # This test case calls
-        # PyCStructUnionType_update_stgdict() for each
+        # PyCStructUnionType_update_stginfo() for each
         # _fields_ assignment.
         class Base(Structure):
             _fields_ = [('y', c_double),
@@ -538,7 +555,7 @@ class StructureTestCase(unittest.TestCase):
     @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
     def test_issue18060_c(self):
         # This test case calls
-        # PyCStructUnionType_update_stgdict() for each
+        # PyCStructUnionType_update_stginfo() for each
         # _fields_ assignment.
         class Base(Structure):
             _fields_ = [('y', c_double)]
index cf5344bdf19165615bf57241fc96936b348e8f4a..e2dff0f22a9213f582b2dc0de6198d8f97f5d164 100644 (file)
@@ -1,5 +1,5 @@
 import unittest
-from ctypes import Union
+from ctypes import Union, c_char
 from ._support import (_CData, UnionType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
                        Py_TPFLAGS_IMMUTABLETYPE)
 
@@ -16,3 +16,20 @@ class ArrayTestCase(unittest.TestCase):
             with self.subTest(cls=Union):
                 self.assertTrue(Union.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
                 self.assertFalse(Union.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
+
+    def test_metaclass_details(self):
+        # Abstract classes (whose metaclass __init__ was not called) can't be
+        # instantiated directly
+        NewUnion = UnionType.__new__(UnionType, 'NewUnion',
+                                     (Union,), {})
+        for cls in Union, NewUnion:
+            with self.subTest(cls=cls):
+                with self.assertRaisesRegex(TypeError, "abstract class"):
+                    obj = cls()
+
+        # Cannot call the metaclass __init__ more than once
+        class T(Union):
+            _fields_ = [("x", c_char),
+                        ("y", c_char)]
+        with self.assertRaisesRegex(SystemError, "already initialized"):
+            UnionType.__init__(T, 'ptr', (), {})
diff --git a/Misc/NEWS.d/next/Library/2024-03-07-11-10-27.gh-issue-114314.iEhAMH.rst b/Misc/NEWS.d/next/Library/2024-03-07-11-10-27.gh-issue-114314.iEhAMH.rst
new file mode 100644 (file)
index 0000000..c241d96
--- /dev/null
@@ -0,0 +1,3 @@
+In :mod:`ctypes`, ctype data is now stored in type objects directly rather
+than in a dict subclass. This is an internal change that should not affect
+usage.
index 94245ae41afffc6600678ec11d0392db8ae13f2e..af094a0fb59e2764787a8f8e5aa3fe8ef626f6ca 100644 (file)
@@ -2,7 +2,7 @@
   ToDo:
 
   Get rid of the checker (and also the converters) field in PyCFuncPtrObject and
-  StgDictObject, and replace them by slot functions in StgDictObject.
+  StgInfo, and replace them by slot functions in StgInfo.
 
   think about a buffer-like object (memory? bytes?)
 
@@ -36,7 +36,6 @@ PyCData_Type
   Simple_Type                   __new__(), __init__(), _as_parameter_
 
 PyCField_Type
-PyCStgDict_Type
 
 ==============================================================================
 
@@ -82,7 +81,6 @@ bytes(cdata)
 */
 
 /*
- * PyCStgDict_Type
  * PyCStructType_Type
  * UnionType_Type
  * PyCPointerType_Type
@@ -128,20 +126,7 @@ bytes(cdata)
 
 #include "pycore_long.h"          // _PyLong_GetZero()
 
-static PyTypeObject Union_Type;
-static PyTypeObject Struct_Type;
-static PyTypeObject Simple_Type;
-
-ctypes_state global_state = {
-    .PyCStgDict_Type = &PyCStgDict_Type,
-    .PyCData_Type = &PyCData_Type,
-    .Struct_Type = &Struct_Type,
-    .Union_Type = &Union_Type,
-    .PyCArray_Type = &PyCArray_Type,
-    .Simple_Type = &Simple_Type,
-    .PyCPointer_Type = &PyCPointer_Type,
-    .PyCFuncPtr_Type = &PyCFuncPtr_Type,
-};
+ctypes_state global_state = {0};
 
 PyObject *PyExc_ArgError = NULL;
 
@@ -459,12 +444,121 @@ static PyType_Spec structparam_spec = {
     .slots = structparam_slots,
 };
 
+/*
+  CType_Type - a base metaclass. Its instances (classes) have a StgInfo.
+  */
+
+static int
+CType_Type_traverse(PyObject *self, visitproc visit, void *arg)
+{
+    Py_VISIT(Py_TYPE(self));
+    return 0;
+}
+
+static void
+_ctype_clear_stginfo(StgInfo *info)
+{
+    assert(info);
+    Py_CLEAR(info->proto);
+    Py_CLEAR(info->argtypes);
+    Py_CLEAR(info->converters);
+    Py_CLEAR(info->restype);
+    Py_CLEAR(info->checker);
+}
+
+static int
+CType_Type_clear(PyObject *self)
+{
+    ctypes_state *st = GLOBAL_STATE();
+    if (st && st->PyCType_Type) {
+        StgInfo *info;
+        if (PyStgInfo_FromType(st, self, &info) < 0) {
+            return -1;
+        }
+        if (info) {
+            _ctype_clear_stginfo(info);
+        }
+    }
+    return 0;
+}
+
+static void
+CType_Type_dealloc(PyObject *self)
+{
+    ctypes_state *st = GLOBAL_STATE();
+
+    if (st && st->PyCType_Type) {
+        StgInfo *info;
+        if (PyStgInfo_FromType(st, self, &info) < 0) {
+            PyErr_WriteUnraisable(self);
+        }
+        if (info) {
+            PyMem_Free(info->ffi_type_pointer.elements);
+            info->ffi_type_pointer.elements = NULL;
+            PyMem_Free(info->format);
+            info->format = NULL;
+            PyMem_Free(info->shape);
+            info->shape = NULL;
+            _ctype_clear_stginfo(info);
+        }
+    }
+
+    PyTypeObject *tp = Py_TYPE(self);
+    PyType_Type.tp_dealloc(self);
+    Py_DECREF(tp);
+}
+
+static PyObject *
+CType_Type_sizeof(PyObject *self)
+{
+    Py_ssize_t size = Py_TYPE(self)->tp_basicsize;
+    size += Py_TYPE(self)->tp_itemsize * Py_SIZE(self);
+
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, self, &info) < 0) {
+        return NULL;
+    }
+    if (info) {
+        if (info->format) {
+            size += strlen(info->format) + 1;
+        }
+        if (info->ffi_type_pointer.elements) {
+            size += (info->length + 1) * sizeof(ffi_type *);
+        }
+        size += info->ndim * sizeof(Py_ssize_t);
+    }
+
+    return PyLong_FromSsize_t(size);
+}
+
+static PyMethodDef ctype_methods[] = {
+    {"__sizeof__", _PyCFunction_CAST(CType_Type_sizeof),
+     METH_NOARGS, PyDoc_STR("Return memory consumption of the type object.")},
+    {0},
+};
+
+static PyType_Slot ctype_type_slots[] = {
+    {Py_tp_traverse, CType_Type_traverse},
+    {Py_tp_clear, CType_Type_clear},
+    {Py_tp_dealloc, CType_Type_dealloc},
+    {Py_tp_methods, ctype_methods},
+    {0, NULL},
+};
+
+static PyType_Spec pyctype_type_spec = {
+    .name = "_ctypes.CType_Type",
+    .basicsize = -(Py_ssize_t)sizeof(StgInfo),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE |
+              Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_BASETYPE ),
+    .slots = ctype_type_slots,
+};
 
 /*
   PyCStructType_Type - a meta type/class.  Creating a new class using this one as
-  __metaclass__ will call the constructor StructUnionType_new.  It replaces the
-  tp_dict member with a new instance of StgDict, and initializes the C
-  accessible fields somehow.
+  __metaclass__ will call the constructor StructUnionType_new.
+  It initializes the C accessible fields somehow.
 */
 
 static PyCArgObject *
@@ -472,7 +566,6 @@ StructUnionType_paramfunc(CDataObject *self)
 {
     PyCArgObject *parg;
     PyObject *obj;
-    StgDictObject *stgdict;
     void *ptr;
 
     if ((size_t)self->b_size > sizeof(void*)) {
@@ -507,104 +600,104 @@ StructUnionType_paramfunc(CDataObject *self)
         return NULL;
     }
 
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *stginfo;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+        Py_DECREF(obj);
+        return NULL;
+    }
+    assert(stginfo); /* Cannot be NULL for structure/union instances */
+
     parg->tag = 'V';
-    stgdict = PyObject_stgdict((PyObject *)self);
-    assert(stgdict); /* Cannot be NULL for structure/union instances */
-    parg->pffi_type = &stgdict->ffi_type_pointer;
+    parg->pffi_type = &stginfo->ffi_type_pointer;
     parg->value.p = ptr;
     parg->size = self->b_size;
     parg->obj = obj;
     return parg;
 }
 
-static PyObject *
-StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isStruct)
+static int
+StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruct)
 {
-    PyTypeObject *result;
     PyObject *fields;
-    StgDictObject *dict;
 
-    /* create the new instance (which is a class,
-       since we are a metatype!) */
-    result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
-    if (!result)
-        return NULL;
+    PyObject *attrdict = PyType_GetDict((PyTypeObject *)self);
+    if (!attrdict) {
+        return -1;
+    }
 
     /* keep this for bw compatibility */
-    int r = PyDict_Contains(result->tp_dict, &_Py_ID(_abstract_));
+    int r = PyDict_Contains(attrdict, &_Py_ID(_abstract_));
     if (r > 0) {
-        return (PyObject *)result;
+        Py_DECREF(attrdict);
+        return 0;
     }
     if (r < 0) {
-        Py_DECREF(result);
-        return NULL;
+        Py_DECREF(attrdict);
+        return -1;
     }
 
     ctypes_state *st = GLOBAL_STATE();
-    dict = (StgDictObject *)_PyObject_CallNoArgs((PyObject *)st->PyCStgDict_Type);
-    if (!dict) {
-        Py_DECREF(result);
-        return NULL;
+    StgInfo *info = PyStgInfo_Init(st, (PyTypeObject *)self);
+    if (!info) {
+        Py_DECREF(attrdict);
+        return -1;
     }
     if (!isStruct) {
-        dict->flags |= TYPEFLAG_HASUNION;
-    }
-    /* replace the class dict by our updated stgdict, which holds info
-       about storage requirements of the instances */
-    if (-1 == PyDict_Update((PyObject *)dict, result->tp_dict)) {
-        Py_DECREF(result);
-        Py_DECREF((PyObject *)dict);
-        return NULL;
+        info->flags |= TYPEFLAG_HASUNION;
     }
-    Py_SETREF(result->tp_dict, (PyObject *)dict);
-    dict->format = _ctypes_alloc_format_string(NULL, "B");
-    if (dict->format == NULL) {
-        Py_DECREF(result);
-        return NULL;
+
+    info->format = _ctypes_alloc_format_string(NULL, "B");
+    if (info->format == NULL) {
+        Py_DECREF(attrdict);
+        return -1;
     }
 
-    dict->paramfunc = StructUnionType_paramfunc;
+    info->paramfunc = StructUnionType_paramfunc;
 
-    if (PyDict_GetItemRef((PyObject *)dict, &_Py_ID(_fields_), &fields) < 0) {
-        Py_DECREF(result);
-        return NULL;
+    if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_fields_), &fields) < 0) {
+        Py_DECREF(attrdict);
+        return -1;
     }
+    Py_CLEAR(attrdict);
     if (fields) {
-        if (PyObject_SetAttr((PyObject *)result, &_Py_ID(_fields_), fields) < 0) {
-            Py_DECREF(result);
+        if (PyObject_SetAttr(self, &_Py_ID(_fields_), fields) < 0) {
             Py_DECREF(fields);
-            return NULL;
+            return -1;
         }
         Py_DECREF(fields);
-        return (PyObject *)result;
+        return 0;
     }
     else {
-        StgDictObject *basedict = PyType_stgdict((PyObject *)result->tp_base);
-
-        if (basedict == NULL) {
-            return (PyObject *)result;
+        StgInfo *baseinfo;
+        if (PyStgInfo_FromType(st, (PyObject *)((PyTypeObject *)self)->tp_base,
+                               &baseinfo) < 0) {
+            return -1;
         }
-        /* copy base dict */
-        if (-1 == PyCStgDict_clone(dict, basedict)) {
-            Py_DECREF(result);
-            return NULL;
+        if (baseinfo == NULL) {
+            return 0;
         }
-        dict->flags &= ~DICTFLAG_FINAL; /* clear the 'final' flag in the subclass dict */
-        basedict->flags |= DICTFLAG_FINAL; /* set the 'final' flag in the baseclass dict */
-        return (PyObject *)result;
+
+        /* copy base info */
+        if (PyCStgInfo_clone(info, baseinfo) < 0) {
+            return -1;
+        }
+        info->flags &= ~DICTFLAG_FINAL; /* clear the 'final' flag in the subclass info */
+        baseinfo->flags |= DICTFLAG_FINAL; /* set the 'final' flag in the baseclass info */
     }
+    return 0;
 }
 
-static PyObject *
-PyCStructType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int
+PyCStructType_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    return StructUnionType_new(type, args, kwds, 1);
+    return StructUnionType_init(self, args, kwds, 1);
 }
 
-static PyObject *
-UnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int
+UnionType_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    return StructUnionType_new(type, args, kwds, 0);
+    return StructUnionType_init(self, args, kwds, 0);
 }
 
 PyDoc_STRVAR(from_address_doc,
@@ -640,8 +733,12 @@ CDataType_from_buffer(PyObject *type, PyObject *args)
     Py_buffer *buffer;
     Py_ssize_t offset = 0;
 
-    StgDictObject *dict = PyType_stgdict(type);
-    if (!dict) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, type, &info) < 0) {
+        return NULL;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError, "abstract class");
         return NULL;
     }
@@ -676,11 +773,11 @@ CDataType_from_buffer(PyObject *type, PyObject *args)
         return NULL;
     }
 
-    if (dict->size > buffer->len - offset) {
+    if (info->size > buffer->len - offset) {
         PyErr_Format(PyExc_ValueError,
                      "Buffer size too small "
                      "(%zd instead of at least %zd bytes)",
-                     buffer->len, dict->size + offset);
+                     buffer->len, info->size + offset);
         Py_DECREF(mv);
         return NULL;
     }
@@ -717,8 +814,13 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args)
     Py_buffer buffer;
     Py_ssize_t offset = 0;
     PyObject *result;
-    StgDictObject *dict = PyType_stgdict(type);
-    if (!dict) {
+
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, type, &info) < 0) {
+        return NULL;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError, "abstract class");
         return NULL;
     }
@@ -733,10 +835,10 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args)
         return NULL;
     }
 
-    if (dict->size > buffer.len - offset) {
+    if (info->size > buffer.len - offset) {
         PyErr_Format(PyExc_ValueError,
                      "Buffer size too small (%zd instead of at least %zd bytes)",
-                     buffer.len, dict->size + offset);
+                     buffer.len, info->size + offset);
         PyBuffer_Release(&buffer);
         return NULL;
     }
@@ -750,7 +852,7 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args)
     result = GenericPyCData_new((PyTypeObject *)type, NULL, NULL);
     if (result != NULL) {
         memcpy(((CDataObject *)result)->b_ptr,
-               (char *)buffer.buf + offset, dict->size);
+               (char *)buffer.buf + offset, info->size);
     }
     PyBuffer_Release(&buffer);
     return result;
@@ -836,13 +938,14 @@ CDataType_from_param(PyObject *type, PyObject *value)
         PyCArgObject *p = (PyCArgObject *)value;
         PyObject *ob = p->obj;
         const char *ob_name;
-        StgDictObject *dict;
-        dict = PyType_stgdict(type);
-
+        StgInfo *info;
+        if (PyStgInfo_FromType(st, type, &info) < 0) {
+            return NULL;
+        }
         /* If we got a PyCArgObject, we must check if the object packed in it
-           is an instance of the type's dict->proto */
-        if(dict && ob) {
-            res = PyObject_IsInstance(ob, dict->proto);
+           is an instance of the type's info->proto */
+        if(info && ob) {
+            res = PyObject_IsInstance(ob, info->proto);
             if (res == -1)
                 return NULL;
             if (res) {
@@ -893,18 +996,27 @@ CDataType_repeat(PyObject *self, Py_ssize_t length)
 static int
 CDataType_clear(PyTypeObject *self)
 {
-    StgDictObject *dict = PyType_stgdict((PyObject *)self);
-    if (dict)
-        Py_CLEAR(dict->proto);
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)self, &info) < 0) {
+        return -1;
+    }
+    if (info) {
+        Py_CLEAR(info->proto);
+    }
     return PyType_Type.tp_clear((PyObject *)self);
 }
 
 static int
 CDataType_traverse(PyTypeObject *self, visitproc visit, void *arg)
 {
-    StgDictObject *dict = PyType_stgdict((PyObject *)self);
-    if (dict) {
-        Py_VISIT(dict->proto);
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)self, &info) < 0) {
+        return -1;
+    }
+    if (info) {
+        Py_VISIT(info->proto);
     }
     Py_VISIT(Py_TYPE(self));
     return PyType_Type.tp_traverse((PyObject *)self, visit, arg);
@@ -919,7 +1031,7 @@ PyCStructType_setattro(PyObject *self, PyObject *key, PyObject *value)
 
     if (value && PyUnicode_Check(key) &&
         _PyUnicode_EqualToASCIIString(key, "_fields_"))
-        return PyCStructUnionType_update_stgdict(self, value, 1);
+        return PyCStructUnionType_update_stginfo(self, value, 1);
     return 0;
 }
 
@@ -933,7 +1045,7 @@ UnionType_setattro(PyObject *self, PyObject *key, PyObject *value)
 
     if (PyUnicode_Check(key) &&
         _PyUnicode_EqualToASCIIString(key, "_fields_"))
-        return PyCStructUnionType_update_stgdict(self, value, 0);
+        return PyCStructUnionType_update_stginfo(self, value, 0);
     return 0;
 }
 
@@ -943,14 +1055,14 @@ static PyType_Slot pycstruct_type_slots[] = {
     {Py_tp_traverse, CDataType_traverse},
     {Py_tp_clear, CDataType_clear},
     {Py_tp_methods, CDataType_methods},
-    {Py_tp_new, PyCStructType_new},
+    {Py_tp_init, PyCStructType_init},
 
     // Sequence protocol.
     {Py_sq_repeat, CDataType_repeat},
     {0, NULL},
 };
 
-PyType_Spec pycstruct_type_spec = {
+static PyType_Spec pycstruct_type_spec = {
     .name = "_ctypes.PyCStructType",
     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
               Py_TPFLAGS_IMMUTABLETYPE),
@@ -963,7 +1075,7 @@ static PyType_Slot union_type_slots[] = {
     {Py_tp_traverse, CDataType_traverse},
     {Py_tp_clear, CDataType_clear},
     {Py_tp_methods, CDataType_methods},
-    {Py_tp_new, UnionType_new},
+    {Py_tp_init, UnionType_init},
 
     // Sequence protocol.
     {Py_sq_repeat, CDataType_repeat},
@@ -994,20 +1106,25 @@ size property/method, and the sequence protocol.
 */
 
 static int
-PyCPointerType_SetProto(StgDictObject *stgdict, PyObject *proto)
+PyCPointerType_SetProto(StgInfo *stginfo, PyObject *proto)
 {
+    ctypes_state *st = GLOBAL_STATE();
     if (!proto || !PyType_Check(proto)) {
         PyErr_SetString(PyExc_TypeError,
                         "_type_ must be a type");
         return -1;
     }
-    if (!PyType_stgdict(proto)) {
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, proto, &info) < 0) {
+        return -1;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError,
                         "_type_ must have storage info");
         return -1;
     }
     Py_INCREF(proto);
-    Py_XSETREF(stgdict->proto, proto);
+    Py_XSETREF(stginfo->proto, proto);
     return 0;
 }
 
@@ -1027,110 +1144,103 @@ PyCPointerType_paramfunc(CDataObject *self)
     return parg;
 }
 
-static PyObject *
-PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int
+PyCPointerType_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    PyTypeObject *result;
-    StgDictObject *stgdict;
     PyObject *proto;
     PyObject *typedict;
 
-
     typedict = PyTuple_GetItem(args, 2);
     if (!typedict) {
-        return NULL;
+        return -1;
     }
+
 /*
-  stgdict items size, align, length contain info about pointers itself,
-  stgdict->proto has info about the pointed to type!
+  stginfo items size, align, length contain info about pointers itself,
+  stginfo->proto has info about the pointed to type!
 */
     ctypes_state *st = GLOBAL_STATE();
-    stgdict = (StgDictObject *)_PyObject_CallNoArgs(
-        (PyObject *)st->PyCStgDict_Type);
-    if (!stgdict) {
-        return NULL;
+    StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject *)self);
+    if (!stginfo) {
+        return -1;
     }
-    stgdict->size = sizeof(void *);
-    stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment;
-    stgdict->length = 1;
-    stgdict->ffi_type_pointer = ffi_type_pointer;
-    stgdict->paramfunc = PyCPointerType_paramfunc;
-    stgdict->flags |= TYPEFLAG_ISPOINTER;
+    stginfo->size = sizeof(void *);
+    stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment;
+    stginfo->length = 1;
+    stginfo->ffi_type_pointer = ffi_type_pointer;
+    stginfo->paramfunc = PyCPointerType_paramfunc;
+    stginfo->flags |= TYPEFLAG_ISPOINTER;
 
     if (PyDict_GetItemRef(typedict, &_Py_ID(_type_), &proto) < 0) {
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
+        return -1;
     }
     if (proto) {
-        StgDictObject *itemdict;
         const char *current_format;
-        if (-1 == PyCPointerType_SetProto(stgdict, proto)) {
+        if (-1 == PyCPointerType_SetProto(stginfo, proto)) {
             Py_DECREF(proto);
-            Py_DECREF((PyObject *)stgdict);
-            return NULL;
+            return -1;
         }
-        itemdict = PyType_stgdict(proto);
-        /* PyCPointerType_SetProto has verified proto has a stgdict. */
-        assert(itemdict);
-        /* If itemdict->format is NULL, then this is a pointer to an
+        StgInfo *iteminfo;
+        if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) {
+            Py_DECREF(proto);
+            return -1;
+        }
+        /* PyCPointerType_SetProto has verified proto has a stginfo. */
+        assert(iteminfo);
+        /* If iteminfo->format is NULL, then this is a pointer to an
            incomplete type.  We create a generic format string
            'pointer to bytes' in this case.  XXX Better would be to
            fix the format string later...
         */
-        current_format = itemdict->format ? itemdict->format : "B";
-        if (itemdict->shape != NULL) {
+        current_format = iteminfo->format ? iteminfo->format : "B";
+        if (iteminfo->shape != NULL) {
             /* pointer to an array: the shape needs to be prefixed */
-            stgdict->format = _ctypes_alloc_format_string_with_shape(
-                itemdict->ndim, itemdict->shape, "&", current_format);
+            stginfo->format = _ctypes_alloc_format_string_with_shape(
+                iteminfo->ndim, iteminfo->shape, "&", current_format);
         } else {
-            stgdict->format = _ctypes_alloc_format_string("&", current_format);
+            stginfo->format = _ctypes_alloc_format_string("&", current_format);
         }
         Py_DECREF(proto);
-        if (stgdict->format == NULL) {
-            Py_DECREF((PyObject *)stgdict);
-            return NULL;
+        if (stginfo->format == NULL) {
+            return -1;
         }
     }
 
-    /* create the new instance (which is a class,
-       since we are a metatype!) */
-    result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
-    if (result == NULL) {
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
-    }
-
-    /* replace the class dict by our updated spam dict */
-    if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
-        Py_DECREF(result);
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
-    }
-    Py_SETREF(result->tp_dict, (PyObject *)stgdict);
-
-    return (PyObject *)result;
+    return 0;
 }
 
 
 static PyObject *
 PyCPointerType_set_type(PyTypeObject *self, PyObject *type)
 {
-    StgDictObject *dict;
-
-
-    dict = PyType_stgdict((PyObject *)self);
-    if (!dict) {
+    PyObject *attrdict = PyType_GetDict(self);
+    if (!attrdict) {
+        return NULL;
+    }
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)self, &info) < 0) {
+        Py_DECREF(attrdict);
+        return NULL;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError,
                         "abstract class");
+        Py_DECREF(attrdict);
         return NULL;
     }
 
-    if (-1 == PyCPointerType_SetProto(dict, type))
+    if (-1 == PyCPointerType_SetProto(info, type)) {
+        Py_DECREF(attrdict);
         return NULL;
+    }
 
-    if (-1 == PyDict_SetItem((PyObject *)dict, &_Py_ID(_type_), type))
+    if (-1 == PyDict_SetItem(attrdict, &_Py_ID(_type_), type)) {
+        Py_DECREF(attrdict);
         return NULL;
+    }
 
+    Py_DECREF(attrdict);
     Py_RETURN_NONE;
 }
 
@@ -1139,15 +1249,17 @@ static PyObject *_byref(PyObject *);
 static PyObject *
 PyCPointerType_from_param(PyObject *type, PyObject *value)
 {
-    StgDictObject *typedict;
-
     if (value == Py_None) {
         /* ConvParam will convert to a NULL pointer later */
         return Py_NewRef(value);
     }
 
-    typedict = PyType_stgdict(type);
-    if (!typedict) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *typeinfo;
+    if (PyStgInfo_FromType(st, type, &typeinfo) < 0) {
+        return NULL;
+    }
+    if (!typeinfo) {
         PyErr_SetString(PyExc_TypeError,
                         "abstract class");
         return NULL;
@@ -1156,7 +1268,8 @@ PyCPointerType_from_param(PyObject *type, PyObject *value)
     /* If we expect POINTER(<type>), but receive a <type> instance, accept
        it by calling byref(<type>).
     */
-    switch (PyObject_IsInstance(value, typedict->proto)) {
+    assert(typeinfo->proto);
+    switch (PyObject_IsInstance(value, typeinfo->proto)) {
     case 1:
         Py_INCREF(value); /* _byref steals a refcount */
         return _byref(value);
@@ -1166,14 +1279,16 @@ PyCPointerType_from_param(PyObject *type, PyObject *value)
         break;
     }
 
-    ctypes_state *st = GLOBAL_STATE();
     if (PointerObject_Check(st, value) || ArrayObject_Check(st, value)) {
         /* Array instances are also pointers when
            the item types are the same.
         */
-        StgDictObject *v = PyObject_stgdict(value);
+        StgInfo *v;
+        if (PyStgInfo_FromObject(st, value, &v) < 0) {
+            return NULL;
+        }
         assert(v); /* Cannot be NULL for pointer or array objects */
-        int ret = PyObject_IsSubclass(v->proto, typedict->proto);
+        int ret = PyObject_IsSubclass(v->proto, typeinfo->proto);
         if (ret < 0) {
             return NULL;
         }
@@ -1199,7 +1314,7 @@ static PyType_Slot pycpointer_type_slots[] = {
     {Py_tp_traverse, CDataType_traverse},
     {Py_tp_clear, CDataType_clear},
     {Py_tp_methods, PyCPointerType_methods},
-    {Py_tp_new, PyCPointerType_new},
+    {Py_tp_init, PyCPointerType_init},
 
     // Sequence protocol.
     {Py_sq_repeat, CDataType_repeat},
@@ -1219,7 +1334,7 @@ static PyType_Spec pycpointer_type_spec = {
   PyCArrayType_Type
 */
 /*
-  PyCArrayType_new ensures that the new Array subclass created has a _length_
+  PyCArrayType_init ensures that the new Array subclass created has a _length_
   attribute, and a _type_ attribute.
 */
 
@@ -1403,28 +1518,18 @@ PyCArrayType_paramfunc(CDataObject *self)
     return p;
 }
 
-static PyObject *
-PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int
+PyCArrayType_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    PyTypeObject *result;
-    StgDictObject *stgdict;
-    StgDictObject *itemdict;
     PyObject *length_attr, *type_attr;
     Py_ssize_t length;
     Py_ssize_t itemsize, itemalign;
 
-    /* create the new instance (which is a class,
-       since we are a metatype!) */
-    result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
-    if (result == NULL)
-        return NULL;
-
     /* Initialize these variables to NULL so that we can simplify error
        handling by using Py_XDECREF.  */
-    stgdict = NULL;
     type_attr = NULL;
 
-    if (PyObject_GetOptionalAttr((PyObject *)result, &_Py_ID(_length_), &length_attr) < 0) {
+    if (PyObject_GetOptionalAttr(self, &_Py_ID(_length_), &length_attr) < 0) {
         goto error;
     }
     if (!length_attr) {
@@ -1457,7 +1562,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
         goto error;
     }
 
-    if (PyObject_GetOptionalAttr((PyObject *)result, &_Py_ID(_type_), &type_attr) < 0) {
+    if (PyObject_GetOptionalAttr(self, &_Py_ID(_type_), &type_attr) < 0) {
         goto error;
     }
     if (!type_attr) {
@@ -1467,88 +1572,83 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     }
 
     ctypes_state *st = GLOBAL_STATE();
-    stgdict = (StgDictObject *)_PyObject_CallNoArgs(
-        (PyObject *)st->PyCStgDict_Type);
-    if (!stgdict) {
+    StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject*)self);
+    if (!stginfo) {
+        goto error;
+    }
+
+    StgInfo *iteminfo;
+    if (PyStgInfo_FromType(st, type_attr, &iteminfo) < 0) {
         goto error;
     }
-    itemdict = PyType_stgdict(type_attr);
-    if (!itemdict) {
+    if (!iteminfo) {
         PyErr_SetString(PyExc_TypeError,
                         "_type_ must have storage info");
         goto error;
     }
 
-    assert(itemdict->format);
-    stgdict->format = _ctypes_alloc_format_string(NULL, itemdict->format);
-    if (stgdict->format == NULL)
+    assert(iteminfo->format);
+    stginfo->format = _ctypes_alloc_format_string(NULL, iteminfo->format);
+    if (stginfo->format == NULL)
         goto error;
-    stgdict->ndim = itemdict->ndim + 1;
-    stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stgdict->ndim);
-    if (stgdict->shape == NULL) {
+    stginfo->ndim = iteminfo->ndim + 1;
+    stginfo->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stginfo->ndim);
+    if (stginfo->shape == NULL) {
         PyErr_NoMemory();
         goto error;
     }
-    stgdict->shape[0] = length;
-    if (stgdict->ndim > 1) {
-        memmove(&stgdict->shape[1], itemdict->shape,
-            sizeof(Py_ssize_t) * (stgdict->ndim - 1));
+    stginfo->shape[0] = length;
+    if (stginfo->ndim > 1) {
+        memmove(&stginfo->shape[1], iteminfo->shape,
+            sizeof(Py_ssize_t) * (stginfo->ndim - 1));
     }
 
-    itemsize = itemdict->size;
+    itemsize = iteminfo->size;
     if (itemsize != 0 && length > PY_SSIZE_T_MAX / itemsize) {
         PyErr_SetString(PyExc_OverflowError,
                         "array too large");
         goto error;
     }
 
-    itemalign = itemdict->align;
+    itemalign = iteminfo->align;
 
-    if (itemdict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
-        stgdict->flags |= TYPEFLAG_HASPOINTER;
+    if (iteminfo->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
+        stginfo->flags |= TYPEFLAG_HASPOINTER;
 
-    stgdict->size = itemsize * length;
-    stgdict->align = itemalign;
-    stgdict->length = length;
-    stgdict->proto = type_attr;
+    stginfo->size = itemsize * length;
+    stginfo->align = itemalign;
+    stginfo->length = length;
+    stginfo->proto = type_attr;
     type_attr = NULL;
 
-    stgdict->paramfunc = &PyCArrayType_paramfunc;
+    stginfo->paramfunc = &PyCArrayType_paramfunc;
 
     /* Arrays are passed as pointers to function calls. */
-    stgdict->ffi_type_pointer = ffi_type_pointer;
-
-    /* replace the class dict by our updated spam dict */
-    if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict))
-        goto error;
-    Py_SETREF(result->tp_dict, (PyObject *)stgdict);  /* steal the reference */
-    stgdict = NULL;
+    stginfo->ffi_type_pointer = ffi_type_pointer;
 
     /* Special case for character arrays.
        A permanent annoyance: char arrays are also strings!
     */
-    if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
-        if (-1 == add_getset(result, CharArray_getsets))
+    if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
+        if (-1 == add_getset((PyTypeObject*)self, CharArray_getsets))
             goto error;
     }
-    else if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
-        if (-1 == add_getset(result, WCharArray_getsets))
+    else if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
+        if (-1 == add_getset((PyTypeObject*)self, WCharArray_getsets))
             goto error;
     }
 
-    return (PyObject *)result;
+    return 0;
 error:
-    Py_XDECREF((PyObject*)stgdict);
     Py_XDECREF(type_attr);
-    Py_DECREF(result);
-    return NULL;
+    return -1;
 }
 
 static PyType_Slot pycarray_type_slots[] = {
     {Py_tp_doc, PyDoc_STR("metatype for the Array Objects")},
     {Py_tp_traverse, CDataType_traverse},
     {Py_tp_methods, CDataType_methods},
-    {Py_tp_new, PyCArrayType_new},
+    {Py_tp_init, PyCArrayType_init},
     {Py_tp_clear, CDataType_clear},
 
     // Sequence protocol.
@@ -1569,7 +1669,7 @@ static PyType_Spec pycarray_type_spec = {
 */
 /*
 
-PyCSimpleType_new ensures that the new Simple_Type subclass created has a valid
+PyCSimpleType_init ensures that the new Simple_Type subclass created has a valid
 _type_ attribute.
 
 */
@@ -1609,19 +1709,29 @@ c_wchar_p_from_param(PyObject *type, PyObject *value)
     ctypes_state *st = GLOBAL_STATE();
     if (ArrayObject_Check(st, value) || PointerObject_Check(st, value)) {
         /* c_wchar array instance or pointer(c_wchar(...)) */
-        StgDictObject *dt = PyObject_stgdict(value);
-        StgDictObject *dict;
-        assert(dt); /* Cannot be NULL for pointer or array objects */
-        dict = dt && dt->proto ? PyType_stgdict(dt->proto) : NULL;
-        if (dict && (dict->setfunc == _ctypes_get_fielddesc("u")->setfunc)) {
+        StgInfo *it;
+        if (PyStgInfo_FromObject(st, value, &it) < 0) {
+            return NULL;
+        }
+        assert(it); /* Cannot be NULL for pointer or array objects */
+        StgInfo *info = NULL;
+        if (it && it->proto) {
+            if (PyStgInfo_FromType(st, it->proto, &info) < 0) {
+                return NULL;
+            }
+        }
+        if (info && (info->setfunc == _ctypes_get_fielddesc("u")->setfunc)) {
             return Py_NewRef(value);
         }
     }
     if (PyCArg_CheckExact(st, value)) {
         /* byref(c_char(...)) */
         PyCArgObject *a = (PyCArgObject *)value;
-        StgDictObject *dict = PyObject_stgdict(a->obj);
-        if (dict && (dict->setfunc == _ctypes_get_fielddesc("u")->setfunc)) {
+        StgInfo *info;
+        if (PyStgInfo_FromObject(st, a->obj, &info) < 0) {
+            return NULL;
+        }
+        if (info && (info->setfunc == _ctypes_get_fielddesc("u")->setfunc)) {
             return Py_NewRef(value);
         }
     }
@@ -1673,19 +1783,29 @@ c_char_p_from_param(PyObject *type, PyObject *value)
     ctypes_state *st = GLOBAL_STATE();
     if (ArrayObject_Check(st, value) || PointerObject_Check(st, value)) {
         /* c_char array instance or pointer(c_char(...)) */
-        StgDictObject *dt = PyObject_stgdict(value);
-        StgDictObject *dict;
-        assert(dt); /* Cannot be NULL for pointer or array objects */
-        dict = dt && dt->proto ? PyType_stgdict(dt->proto) : NULL;
-        if (dict && (dict->setfunc == _ctypes_get_fielddesc("c")->setfunc)) {
+        StgInfo *it;
+        if (PyStgInfo_FromObject(st, value, &it) < 0) {
+            return NULL;
+        }
+        assert(it); /* Cannot be NULL for pointer or array objects */
+        StgInfo *info = NULL;
+        if (it && it->proto) {
+            if (PyStgInfo_FromType(st, it->proto, &info) < 0) {
+                return NULL;
+            }
+        }
+        if (info && (info->setfunc == _ctypes_get_fielddesc("c")->setfunc)) {
             return Py_NewRef(value);
         }
     }
     if (PyCArg_CheckExact(st, value)) {
         /* byref(c_char(...)) */
         PyCArgObject *a = (PyCArgObject *)value;
-        StgDictObject *dict = PyObject_stgdict(a->obj);
-        if (dict && (dict->setfunc == _ctypes_get_fielddesc("c")->setfunc)) {
+        StgInfo *info;
+        if (PyStgInfo_FromObject(st, a->obj, &info) < 0) {
+            return NULL;
+        }
+        if (info && (info->setfunc == _ctypes_get_fielddesc("c")->setfunc)) {
             return Py_NewRef(value);
         }
     }
@@ -1707,7 +1827,6 @@ c_char_p_from_param(PyObject *type, PyObject *value)
 static PyObject *
 c_void_p_from_param(PyObject *type, PyObject *value)
 {
-    StgDictObject *stgd;
     PyObject *as_parameter;
     int res;
 
@@ -1806,15 +1925,18 @@ c_void_p_from_param(PyObject *type, PyObject *value)
         return (PyObject *)parg;
     }
 /* c_char_p, c_wchar_p */
-    stgd = PyObject_stgdict(value);
-    if (stgd
+    StgInfo *stgi;
+    if (PyStgInfo_FromObject(st, value, &stgi) < 0) {
+        return NULL;
+    }
+    if (stgi
         && CDataObject_Check(st, value)
-        && stgd->proto
-        && PyUnicode_Check(stgd->proto))
+        && stgi->proto
+        && PyUnicode_Check(stgi->proto))
     {
         PyCArgObject *parg;
 
-        switch (PyUnicode_AsUTF8(stgd->proto)[0]) {
+        switch (PyUnicode_AsUTF8(stgi->proto)[0]) {
         case 'z': /* c_char_p */
         case 'Z': /* c_wchar_p */
             parg = PyCArgObject_new();
@@ -1851,7 +1973,6 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject
                                    PyObject *proto, struct fielddesc *fmt)
 {
     PyTypeObject *result;
-    StgDictObject *stgdict;
     PyObject *name = PyTuple_GET_ITEM(args, 0);
     PyObject *newname;
     PyObject *swapped_args;
@@ -1894,29 +2015,21 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject
         return NULL;
 
     ctypes_state *st = GLOBAL_STATE();
-    stgdict = (StgDictObject *)_PyObject_CallNoArgs(
-        (PyObject *)st->PyCStgDict_Type);
-    if (!stgdict) {
+
+    StgInfo *stginfo = PyStgInfo_Init(st, result);
+    if (!stginfo) {
         Py_DECREF(result);
         return NULL;
     }
 
-    stgdict->ffi_type_pointer = *fmt->pffi_type;
-    stgdict->align = fmt->pffi_type->alignment;
-    stgdict->length = 0;
-    stgdict->size = fmt->pffi_type->size;
-    stgdict->setfunc = fmt->setfunc_swapped;
-    stgdict->getfunc = fmt->getfunc_swapped;
+    stginfo->ffi_type_pointer = *fmt->pffi_type;
+    stginfo->align = fmt->pffi_type->alignment;
+    stginfo->length = 0;
+    stginfo->size = fmt->pffi_type->size;
+    stginfo->setfunc = fmt->setfunc_swapped;
+    stginfo->getfunc = fmt->getfunc_swapped;
 
-    stgdict->proto = Py_NewRef(proto);
-
-    /* replace the class dict by our updated spam dict */
-    if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
-        Py_DECREF(result);
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
-    }
-    Py_SETREF(result->tp_dict, (PyObject *)stgdict);
+    stginfo->proto = Py_NewRef(proto);
 
     return (PyObject *)result;
 }
@@ -1924,14 +2037,17 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject
 static PyCArgObject *
 PyCSimpleType_paramfunc(CDataObject *self)
 {
-    StgDictObject *dict;
     const char *fmt;
     PyCArgObject *parg;
     struct fielddesc *fd;
 
-    dict = PyObject_stgdict((PyObject *)self);
-    assert(dict); /* Cannot be NULL for CDataObject instances */
-    fmt = PyUnicode_AsUTF8(dict->proto);
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) {
+        return NULL;
+    }
+    assert(info); /* Cannot be NULL for CDataObject instances */
+    fmt = PyUnicode_AsUTF8(info->proto);
     assert(fmt);
 
     fd = _ctypes_get_fielddesc(fmt);
@@ -1948,33 +2064,27 @@ PyCSimpleType_paramfunc(CDataObject *self)
     return parg;
 }
 
-static PyObject *
-PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int
+PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    PyTypeObject *result;
-    StgDictObject *stgdict;
     PyObject *proto;
     const char *proto_str;
     Py_ssize_t proto_len;
     PyMethodDef *ml;
     struct fielddesc *fmt;
 
-    /* create the new instance (which is a class,
-       since we are a metatype!) */
-    result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
-    if (result == NULL)
-        return NULL;
-
-    if (PyObject_GetOptionalAttr((PyObject *)result, &_Py_ID(_type_), &proto) < 0) {
-        return NULL;
+    if (PyType_Type.tp_init(self, args, kwds) < 0) {
+        return -1;
+    }
+    if (PyObject_GetOptionalAttr(self, &_Py_ID(_type_), &proto) < 0) {
+        return -1;
     }
     if (!proto) {
         PyErr_SetString(PyExc_AttributeError,
                         "class must define a '_type_' attribute");
   error:
         Py_XDECREF(proto);
-        Py_DECREF(result);
-        return NULL;
+        return -1;
     }
     if (PyUnicode_Check(proto)) {
         proto_str = PyUnicode_AsUTF8AndSize(proto, &proto_len);
@@ -2006,70 +2116,60 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     }
 
     ctypes_state *st = GLOBAL_STATE();
-    stgdict = (StgDictObject *)_PyObject_CallNoArgs(
-        (PyObject *)st->PyCStgDict_Type);
-    if (!stgdict) {
+    StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject *)self);
+    if (!stginfo) {
         goto error;
     }
-    stgdict->ffi_type_pointer = *fmt->pffi_type;
-    stgdict->align = fmt->pffi_type->alignment;
-    stgdict->length = 0;
-    stgdict->size = fmt->pffi_type->size;
-    stgdict->setfunc = fmt->setfunc;
-    stgdict->getfunc = fmt->getfunc;
+
+    stginfo->ffi_type_pointer = *fmt->pffi_type;
+    stginfo->align = fmt->pffi_type->alignment;
+    stginfo->length = 0;
+    stginfo->size = fmt->pffi_type->size;
+    stginfo->setfunc = fmt->setfunc;
+    stginfo->getfunc = fmt->getfunc;
 #ifdef WORDS_BIGENDIAN
-    stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1);
+    stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1);
 #else
-    stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0);
+    stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0);
 #endif
-    if (stgdict->format == NULL) {
-        Py_DECREF(result);
+    if (stginfo->format == NULL) {
         Py_DECREF(proto);
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
+        return -1;
     }
 
-    stgdict->paramfunc = PyCSimpleType_paramfunc;
+    stginfo->paramfunc = PyCSimpleType_paramfunc;
 /*
-    if (result->tp_base != st->Simple_Type) {
-        stgdict->setfunc = NULL;
-        stgdict->getfunc = NULL;
+    if (self->tp_base != st->Simple_Type) {
+        stginfo->setfunc = NULL;
+        stginfo->getfunc = NULL;
     }
 */
 
     /* This consumes the refcount on proto which we have */
-    stgdict->proto = proto;
-
-    /* replace the class dict by our updated spam dict */
-    if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
-        Py_DECREF(result);
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
-    }
-    Py_SETREF(result->tp_dict, (PyObject *)stgdict);
+    stginfo->proto = proto;
 
     /* Install from_param class methods in ctypes base classes.
        Overrides the PyCSimpleType_from_param generic method.
      */
-    if (result->tp_base == st->Simple_Type) {
+    if (((PyTypeObject *)self)->tp_base == st->Simple_Type) {
         switch (*proto_str) {
         case 'z': /* c_char_p */
             ml = &c_char_p_method;
-            stgdict->flags |= TYPEFLAG_ISPOINTER;
+            stginfo->flags |= TYPEFLAG_ISPOINTER;
             break;
         case 'Z': /* c_wchar_p */
             ml = &c_wchar_p_method;
-            stgdict->flags |= TYPEFLAG_ISPOINTER;
+            stginfo->flags |= TYPEFLAG_ISPOINTER;
             break;
         case 'P': /* c_void_p */
             ml = &c_void_p_method;
-            stgdict->flags |= TYPEFLAG_ISPOINTER;
+            stginfo->flags |= TYPEFLAG_ISPOINTER;
             break;
         case 's':
         case 'X':
         case 'O':
             ml = NULL;
-            stgdict->flags |= TYPEFLAG_ISPOINTER;
+            stginfo->flags |= TYPEFLAG_ISPOINTER;
             break;
         default:
             ml = NULL;
@@ -2079,57 +2179,57 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
         if (ml) {
             PyObject *meth;
             int x;
-            meth = PyDescr_NewClassMethod(result, ml);
+            meth = PyDescr_NewClassMethod((PyTypeObject*)self, ml);
             if (!meth) {
-                Py_DECREF(result);
-                return NULL;
+                return -1;
             }
-            x = PyDict_SetItemString(result->tp_dict,
+            x = PyDict_SetItemString(((PyTypeObject*)self)->tp_dict,
                                      ml->ml_name,
                                      meth);
             Py_DECREF(meth);
             if (x == -1) {
-                Py_DECREF(result);
-                return NULL;
+                return -1;
             }
         }
     }
 
+    PyTypeObject *type = Py_TYPE(self);
     if (type == st->PyCSimpleType_Type
         && fmt->setfunc_swapped
         && fmt->getfunc_swapped)
     {
         PyObject *swapped = CreateSwappedType(type, args, kwds,
                                               proto, fmt);
-        StgDictObject *sw_dict;
         if (swapped == NULL) {
-            Py_DECREF(result);
-            return NULL;
+            return -1;
         }
-        sw_dict = PyType_stgdict(swapped);
+        StgInfo *sw_info;
+        if (PyStgInfo_FromType(st, swapped, &sw_info) < 0) {
+            return -1;
+        }
+        assert(sw_info);
 #ifdef WORDS_BIGENDIAN
-        PyObject_SetAttrString((PyObject *)result, "__ctype_le__", swapped);
-        PyObject_SetAttrString((PyObject *)result, "__ctype_be__", (PyObject *)result);
-        PyObject_SetAttrString(swapped, "__ctype_be__", (PyObject *)result);
+        PyObject_SetAttrString(self, "__ctype_le__", swapped);
+        PyObject_SetAttrString(self, "__ctype_be__", self);
+        PyObject_SetAttrString(swapped, "__ctype_be__", self);
         PyObject_SetAttrString(swapped, "__ctype_le__", swapped);
         /* We are creating the type for the OTHER endian */
-        sw_dict->format = _ctypes_alloc_format_string("<", stgdict->format+1);
+        sw_info->format = _ctypes_alloc_format_string("<", stginfo->format+1);
 #else
-        PyObject_SetAttrString((PyObject *)result, "__ctype_be__", swapped);
-        PyObject_SetAttrString((PyObject *)result, "__ctype_le__", (PyObject *)result);
-        PyObject_SetAttrString(swapped, "__ctype_le__", (PyObject *)result);
+        PyObject_SetAttrString(self, "__ctype_be__", swapped);
+        PyObject_SetAttrString(self, "__ctype_le__", self);
+        PyObject_SetAttrString(swapped, "__ctype_le__", self);
         PyObject_SetAttrString(swapped, "__ctype_be__", swapped);
         /* We are creating the type for the OTHER endian */
-        sw_dict->format = _ctypes_alloc_format_string(">", stgdict->format+1);
+        sw_info->format = _ctypes_alloc_format_string(">", stginfo->format+1);
 #endif
         Py_DECREF(swapped);
         if (PyErr_Occurred()) {
-            Py_DECREF(result);
-            return NULL;
+            return -1;
         }
     };
 
-    return (PyObject *)result;
+    return 0;
 }
 
 /*
@@ -2139,7 +2239,6 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 static PyObject *
 PyCSimpleType_from_param(PyObject *type, PyObject *value)
 {
-    StgDictObject *dict;
     const char *fmt;
     PyCArgObject *parg;
     struct fielddesc *fd;
@@ -2155,15 +2254,19 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
         return Py_NewRef(value);
     }
 
-    dict = PyType_stgdict(type);
-    if (!dict) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, type, &info) < 0) {
+        return NULL;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError,
                         "abstract class");
         return NULL;
     }
 
     /* I think we can rely on this being a one-character string */
-    fmt = PyUnicode_AsUTF8(dict->proto);
+    fmt = PyUnicode_AsUTF8(info->proto);
     assert(fmt);
 
     fd = _ctypes_get_fielddesc(fmt);
@@ -2218,7 +2321,7 @@ static PyMethodDef PyCSimpleType_methods[] = {
 static PyType_Slot pycsimple_type_slots[] = {
     {Py_tp_doc, PyDoc_STR("metatype for the PyCSimpleType Objects")},
     {Py_tp_methods, PyCSimpleType_methods},
-    {Py_tp_new, PyCSimpleType_new},
+    {Py_tp_init, PyCSimpleType_init},
     {Py_tp_traverse, CDataType_traverse},
     {Py_tp_clear, CDataType_clear},
 
@@ -2227,7 +2330,7 @@ static PyType_Slot pycsimple_type_slots[] = {
     {0, NULL},
 };
 
-PyType_Spec pycsimple_type_spec = {
+static PyType_Spec pycsimple_type_spec = {
     .name = "_ctypes.PyCSimpleType",
     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
               Py_TPFLAGS_IMMUTABLETYPE),
@@ -2292,10 +2395,13 @@ converters_from_argtypes(PyObject *ob)
  *      not bitfields, the bitfields check is also being disabled as a
  *      precaution.
 
-        StgDictObject *stgdict = PyType_stgdict(tp);
+        StgInfo *stginfo;
+        if (PyStgInfo_FromType(st, tp, &stginfo) < 0) {
+            return -1;
+        }
 
-        if (stgdict != NULL) {
-            if (stgdict->flags & TYPEFLAG_HASUNION) {
+        if (stginfo != NULL) {
+            if (stginfo->flags & TYPEFLAG_HASUNION) {
                 Py_DECREF(converters);
                 Py_DECREF(ob);
                 if (!PyErr_Occurred()) {
@@ -2306,7 +2412,7 @@ converters_from_argtypes(PyObject *ob)
                 }
                 return NULL;
             }
-            if (stgdict->flags & TYPEFLAG_HASBITFIELD) {
+            if (stginfo->flags & TYPEFLAG_HASBITFIELD) {
                 Py_DECREF(converters);
                 Py_DECREF(ob);
                 if (!PyErr_Occurred()) {
@@ -2338,19 +2444,19 @@ converters_from_argtypes(PyObject *ob)
 }
 
 static int
-make_funcptrtype_dict(StgDictObject *stgdict)
+make_funcptrtype_dict(PyObject *attrdict, StgInfo *stginfo)
 {
     PyObject *ob;
     PyObject *converters = NULL;
 
-    stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment;
-    stgdict->length = 1;
-    stgdict->size = sizeof(void *);
-    stgdict->setfunc = NULL;
-    stgdict->getfunc = NULL;
-    stgdict->ffi_type_pointer = ffi_type_pointer;
+    stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment;
+    stginfo->length = 1;
+    stginfo->size = sizeof(void *);
+    stginfo->setfunc = NULL;
+    stginfo->getfunc = NULL;
+    stginfo->ffi_type_pointer = ffi_type_pointer;
 
-    if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_flags_), &ob) < 0) {
+    if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_flags_), &ob) < 0) {
         return -1;
     }
     if (!ob || !PyLong_Check(ob)) {
@@ -2359,11 +2465,11 @@ make_funcptrtype_dict(StgDictObject *stgdict)
         Py_XDECREF(ob);
         return -1;
     }
-    stgdict->flags = PyLong_AsUnsignedLongMask(ob) | TYPEFLAG_ISPOINTER;
+    stginfo->flags = PyLong_AsUnsignedLongMask(ob) | TYPEFLAG_ISPOINTER;
     Py_DECREF(ob);
 
     /* _argtypes_ is optional... */
-    if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_argtypes_), &ob) < 0) {
+    if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_argtypes_), &ob) < 0) {
         return -1;
     }
     if (ob) {
@@ -2372,29 +2478,34 @@ make_funcptrtype_dict(StgDictObject *stgdict)
             Py_DECREF(ob);
             return -1;
         }
-        stgdict->argtypes = ob;
-        stgdict->converters = converters;
+        stginfo->argtypes = ob;
+        stginfo->converters = converters;
     }
 
-    if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_restype_), &ob) < 0) {
+    if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_restype_), &ob) < 0) {
         return -1;
     }
     if (ob) {
-        if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) {
+        StgInfo *info;
+        ctypes_state *st = GLOBAL_STATE();
+        if (PyStgInfo_FromType(st, ob, &info) < 0) {
+            return -1;
+        }
+        if (ob != Py_None && !info && !PyCallable_Check(ob)) {
             PyErr_SetString(PyExc_TypeError,
                 "_restype_ must be a type, a callable, or None");
             Py_DECREF(ob);
             return -1;
         }
-        stgdict->restype = ob;
+        stginfo->restype = ob;
         if (PyObject_GetOptionalAttr(ob, &_Py_ID(_check_retval_),
-                                   &stgdict->checker) < 0)
+                                   &stginfo->checker) < 0)
         {
             return -1;
         }
     }
 /* XXX later, maybe.
-    if (PyDict_GetItemRef((PyObject *)stgdict, &_Py _ID(_errcheck_), &ob) < 0) {
+    if (PyDict_GetItemRef((PyObject *)attrdict, &_Py _ID(_errcheck_), &ob) < 0) {
         return -1;
     }
     if (ob) {
@@ -2404,7 +2515,7 @@ make_funcptrtype_dict(StgDictObject *stgdict)
             Py_DECREF(ob);
             return -1;
         }
-        stgdict->errcheck = ob;
+        stginfo->errcheck = ob;
     }
 */
     return 0;
@@ -2426,54 +2537,43 @@ PyCFuncPtrType_paramfunc(CDataObject *self)
     return parg;
 }
 
-static PyObject *
-PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int
+PyCFuncPtrType_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    PyTypeObject *result;
-    StgDictObject *stgdict;
+    PyObject *attrdict = PyType_GetDict((PyTypeObject *)self);
+    if (!attrdict) {
+        return -1;
+    }
 
     ctypes_state *st = GLOBAL_STATE();
-    stgdict = (StgDictObject *)_PyObject_CallNoArgs(
-        (PyObject *)st->PyCStgDict_Type);
-    if (!stgdict) {
-        return NULL;
+    StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject *)self);
+    if (!stginfo) {
+        Py_DECREF(attrdict);
+        return -1;
     }
-    stgdict->paramfunc = PyCFuncPtrType_paramfunc;
+
+    stginfo->paramfunc = PyCFuncPtrType_paramfunc;
+
     /* We do NOT expose the function signature in the format string.  It
        is impossible, generally, because the only requirement for the
        argtypes items is that they have a .from_param method - we do not
        know the types of the arguments (although, in practice, most
        argtypes would be a ctypes type).
     */
-    stgdict->format = _ctypes_alloc_format_string(NULL, "X{}");
-    if (stgdict->format == NULL) {
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
-    }
-    stgdict->flags |= TYPEFLAG_ISPOINTER;
-
-    /* create the new instance (which is a class,
-       since we are a metatype!) */
-    result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
-    if (result == NULL) {
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
-    }
-
-    /* replace the class dict by our updated storage dict */
-    if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
-        Py_DECREF(result);
-        Py_DECREF((PyObject *)stgdict);
-        return NULL;
+    stginfo->format = _ctypes_alloc_format_string(NULL, "X{}");
+    if (stginfo->format == NULL) {
+        Py_DECREF(attrdict);
+        return -1;
     }
-    Py_SETREF(result->tp_dict, (PyObject *)stgdict);
+    stginfo->flags |= TYPEFLAG_ISPOINTER;
 
-    if (-1 == make_funcptrtype_dict(stgdict)) {
-        Py_DECREF(result);
-        return NULL;
+    if (-1 == make_funcptrtype_dict(attrdict, stginfo)) {
+        Py_DECREF(attrdict);
+        return -1;
     }
 
-    return (PyObject *)result;
+    Py_DECREF(attrdict);
+    return 0;
 }
 
 static PyType_Slot pycfuncptr_type_slots[] = {
@@ -2481,7 +2581,7 @@ static PyType_Slot pycfuncptr_type_slots[] = {
     {Py_tp_traverse, CDataType_traverse},
     {Py_tp_clear, CDataType_clear},
     {Py_tp_methods, CDataType_methods},
-    {Py_tp_new, PyCFuncPtrType_new},
+    {Py_tp_init, PyCFuncPtrType_init},
 
     // Sequence protocol.
     {Py_sq_repeat, CDataType_repeat},
@@ -2609,6 +2709,8 @@ PyCData_traverse(CDataObject *self, visitproc visit, void *arg)
 {
     Py_VISIT(self->b_objects);
     Py_VISIT((PyObject *)self->b_base);
+    PyTypeObject *type = Py_TYPE(self);
+    Py_VISIT(type);
     return 0;
 }
 
@@ -2627,8 +2729,11 @@ PyCData_clear(CDataObject *self)
 static void
 PyCData_dealloc(PyObject *self)
 {
+    PyTypeObject *type = Py_TYPE(self);
+    PyObject_GC_UnTrack(self);
     PyCData_clear((CDataObject *)self);
-    Py_TYPE(self)->tp_free(self);
+    type->tp_free(self);
+    Py_DECREF(type);
 }
 
 static PyMemberDef PyCData_members[] = {
@@ -2650,13 +2755,15 @@ PyCData_item_type(PyObject *type)
 {
     ctypes_state *st = GLOBAL_STATE();
     if (PyCArrayTypeObject_Check(st, type)) {
-        StgDictObject *stg_dict;
         PyObject *elem_type;
 
         /* asserts used here as these are all guaranteed by construction */
-        stg_dict = PyType_stgdict(type);
-        assert(stg_dict);
-        elem_type = stg_dict->proto;
+        StgInfo *stg_info;
+        if (PyStgInfo_FromType(st, type, &stg_info) < 0) {
+            return NULL;
+        }
+        assert(stg_info);
+        elem_type = stg_info->proto;
         assert(elem_type);
         return PyCData_item_type(elem_type);
     }
@@ -2669,32 +2776,42 @@ static int
 PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags)
 {
     CDataObject *self = (CDataObject *)myself;
-    StgDictObject *dict = PyObject_stgdict(myself);
+
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromObject(st, myself, &info) < 0) {
+        return -1;
+    }
+    assert(info);
+
     PyObject *item_type = PyCData_item_type((PyObject*)Py_TYPE(myself));
-    StgDictObject *item_dict = PyType_stgdict(item_type);
+    if (item_type == NULL) {
+        return 0;
+    }
 
     if (view == NULL) return 0;
 
+    StgInfo *item_info;
+    if (PyStgInfo_FromType(st, item_type, &item_info) < 0) {
+        return -1;
+    }
+    assert(item_info);
+
     view->buf = self->b_ptr;
     view->obj = Py_NewRef(myself);
     view->len = self->b_size;
     view->readonly = 0;
     /* use default format character if not set */
-    view->format = dict->format ? dict->format : "B";
-    view->ndim = dict->ndim;
-    view->shape = dict->shape;
-    view->itemsize = item_dict->size;
+    view->format = info->format ? info->format : "B";
+    view->ndim = info->ndim;
+    view->shape = info->shape;
+    view->itemsize = item_info->size;
     view->strides = NULL;
     view->suboffsets = NULL;
     view->internal = NULL;
     return 0;
 }
 
-static PyBufferProcs PyCData_as_buffer = {
-    PyCData_NewGetBuffer,
-    NULL,
-};
-
 /*
  * CData objects are mutable, so they cannot be hashable!
  */
@@ -2710,7 +2827,14 @@ PyCData_reduce(PyObject *myself, PyObject *args)
 {
     CDataObject *self = (CDataObject *)myself;
 
-    if (PyObject_stgdict(myself)->flags & (TYPEFLAG_ISPOINTER|TYPEFLAG_HASPOINTER)) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromObject(st, myself, &info) < 0) {
+        return NULL;
+    }
+    assert(info);
+
+    if (info->flags & (TYPEFLAG_ISPOINTER|TYPEFLAG_HASPOINTER)) {
         PyErr_SetString(PyExc_ValueError,
                         "ctypes objects containing pointers cannot be pickled");
         return NULL;
@@ -2773,51 +2897,30 @@ static PyMethodDef PyCData_methods[] = {
     { NULL, NULL },
 };
 
-PyTypeObject PyCData_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_ctypes._CData",
-    sizeof(CDataObject),                        /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    PyCData_dealloc,                                    /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    0,                                          /* tp_repr */
-    0,                                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    PyCData_nohash,                             /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    &PyCData_as_buffer,                         /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    PyDoc_STR("XXX to be provided"),            /* tp_doc */
-    (traverseproc)PyCData_traverse,             /* tp_traverse */
-    (inquiry)PyCData_clear,                     /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    PyCData_methods,                                    /* tp_methods */
-    PyCData_members,                                    /* tp_members */
-    0,                                          /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    0,                                          /* tp_init */
-    0,                                          /* tp_alloc */
-    0,                                          /* tp_new */
-    0,                                          /* tp_free */
+static PyType_Slot pycdata_slots[] = {
+    {Py_tp_dealloc, PyCData_dealloc},
+    {Py_tp_hash, PyCData_nohash},
+    {Py_tp_doc, PyDoc_STR("XXX to be provided")},
+    {Py_tp_traverse, PyCData_traverse},
+    {Py_tp_clear, PyCData_clear},
+    {Py_tp_methods, PyCData_methods},
+    {Py_tp_members, PyCData_members},
+    {Py_bf_getbuffer, PyCData_NewGetBuffer},
+    {0, NULL},
 };
 
-static int PyCData_MallocBuffer(CDataObject *obj, StgDictObject *dict)
+static PyType_Spec pycdata_spec = {
+    .name = "_ctypes._CData",
+    .basicsize = sizeof(CDataObject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION),
+    .slots = pycdata_slots,
+};
+
+static int
+PyCData_MallocBuffer(CDataObject *obj, StgInfo *info)
 {
-    if ((size_t)dict->size <= sizeof(obj->b_value)) {
+    if ((size_t)info->size <= sizeof(obj->b_value)) {
         /* No need to call malloc, can use the default buffer */
         obj->b_ptr = (char *)&obj->b_value;
         /* The b_needsfree flag does not mean that we actually did
@@ -2831,15 +2934,15 @@ static int PyCData_MallocBuffer(CDataObject *obj, StgDictObject *dict)
         /* In python 2.4, and ctypes 0.9.6, the malloc call took about
            33% of the creation time for c_int().
         */
-        obj->b_ptr = (char *)PyMem_Malloc(dict->size);
+        obj->b_ptr = (char *)PyMem_Malloc(info->size);
         if (obj->b_ptr == NULL) {
             PyErr_NoMemory();
             return -1;
         }
         obj->b_needsfree = 1;
-        memset(obj->b_ptr, 0, dict->size);
+        memset(obj->b_ptr, 0, info->size);
     }
-    obj->b_size = dict->size;
+    obj->b_size = info->size;
     return 0;
 }
 
@@ -2847,23 +2950,28 @@ PyObject *
 PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr)
 {
     CDataObject *cmem;
-    StgDictObject *dict;
 
     assert(PyType_Check(type));
-    dict = PyType_stgdict(type);
-    if (!dict) {
+
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, type, &info) < 0) {
+        return NULL;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError,
                         "abstract class");
         return NULL;
     }
-    dict->flags |= DICTFLAG_FINAL;
+
+    info->flags |= DICTFLAG_FINAL;
     cmem = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0);
     if (cmem == NULL) {
         return NULL;
     }
     assert(CDataObject_Check(GLOBAL_STATE(), cmem));
-    cmem->b_length = dict->length;
-    cmem->b_size = dict->size;
+    cmem->b_length = info->length;
+    cmem->b_size = info->size;
     if (base) { /* use base's buffer */
         assert(CDataObject_Check(GLOBAL_STATE(), base));
         cmem->b_ptr = adr;
@@ -2871,11 +2979,11 @@ PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr)
         cmem->b_base = (CDataObject *)Py_NewRef(base);
         cmem->b_index = index;
     } else { /* copy contents of adr */
-        if (-1 == PyCData_MallocBuffer(cmem, dict)) {
+        if (-1 == PyCData_MallocBuffer(cmem, info)) {
             Py_DECREF(cmem);
             return NULL;
         }
-        memcpy(cmem->b_ptr, adr, dict->size);
+        memcpy(cmem->b_ptr, adr, info->size);
         cmem->b_index = index;
     }
     return (PyObject *)cmem;
@@ -2888,20 +2996,25 @@ PyObject *
 PyCData_AtAddress(PyObject *type, void *buf)
 {
     CDataObject *pd;
-    StgDictObject *dict;
 
     if (PySys_Audit("ctypes.cdata", "n", (Py_ssize_t)buf) < 0) {
         return NULL;
     }
 
     assert(PyType_Check(type));
-    dict = PyType_stgdict(type);
-    if (!dict) {
+
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, type, &info) < 0) {
+        return NULL;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError,
                         "abstract class");
         return NULL;
     }
-    dict->flags |= DICTFLAG_FINAL;
+
+    info->flags |= DICTFLAG_FINAL;
 
     pd = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0);
     if (!pd) {
@@ -2909,8 +3022,8 @@ PyCData_AtAddress(PyObject *type, void *buf)
     }
     assert(CDataObject_Check(GLOBAL_STATE(), pd));
     pd->b_ptr = (char *)buf;
-    pd->b_length = dict->length;
-    pd->b_size = dict->size;
+    pd->b_length = info->length;
+    pd->b_size = info->size;
     return (PyObject *)pd;
 }
 
@@ -2934,13 +3047,16 @@ PyObject *
 PyCData_get(PyObject *type, GETFUNC getfunc, PyObject *src,
           Py_ssize_t index, Py_ssize_t size, char *adr)
 {
-    StgDictObject *dict;
     if (getfunc)
         return getfunc(adr, size);
     assert(type);
-    dict = PyType_stgdict(type);
-    if (dict && dict->getfunc && !_ctypes_simple_instance(type))
-        return dict->getfunc(adr, size);
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, type, &info) < 0) {
+        return NULL;
+    }
+    if (info && info->getfunc && !_ctypes_simple_instance(type))
+        return info->getfunc(adr, size);
     return PyCData_FromBaseObj(type, src, index, adr);
 }
 
@@ -2959,9 +3075,12 @@ _PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value,
     }
     ctypes_state *st = GLOBAL_STATE();
     if (!CDataObject_Check(st, value)) {
-        StgDictObject *dict = PyType_stgdict(type);
-        if (dict && dict->setfunc)
-            return dict->setfunc(ptr, value, size);
+        StgInfo *info;
+        if (PyStgInfo_FromType(st, type, &info) < 0) {
+            return NULL;
+        }
+        if (info && info->setfunc)
+            return info->setfunc(ptr, value, size);
         /*
            If value is a tuple, we try to call the type with the tuple
            and use the result!
@@ -3014,11 +3133,16 @@ _PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value,
 
     if (PyCPointerTypeObject_Check(st, type)
         && ArrayObject_Check(st, value)) {
-        StgDictObject *p1, *p2;
         PyObject *keep;
-        p1 = PyObject_stgdict(value);
+
+        StgInfo *p1, *p2;
+        if (PyStgInfo_FromObject(st, value, &p1) < 0) {
+            return NULL;
+        }
         assert(p1); /* Cannot be NULL for array instances */
-        p2 = PyType_stgdict(type);
+        if (PyStgInfo_FromType(st, type, &p2) < 0) {
+            return NULL;
+        }
         assert(p2); /* Cannot be NULL for pointer types */
 
         if (p1->proto != p2->proto) {
@@ -3086,15 +3210,19 @@ static PyObject *
 GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
     CDataObject *obj;
-    StgDictObject *dict;
 
-    dict = PyType_stgdict((PyObject *)type);
-    if (!dict) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
+        return NULL;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError,
                         "abstract class");
         return NULL;
     }
-    dict->flags |= DICTFLAG_FINAL;
+
+    info->flags |= DICTFLAG_FINAL;
 
     obj = (CDataObject *)type->tp_alloc(type, 0);
     if (!obj)
@@ -3103,9 +3231,9 @@ GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     obj->b_base = NULL;
     obj->b_index = 0;
     obj->b_objects = NULL;
-    obj->b_length = dict->length;
+    obj->b_length = info->length;
 
-    if (-1 == PyCData_MallocBuffer(obj, dict)) {
+    if (-1 == PyCData_MallocBuffer(obj, info)) {
         Py_DECREF(obj);
         return NULL;
     }
@@ -3149,7 +3277,12 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ign
         Py_XDECREF(oldchecker);
         return 0;
     }
-    if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, ob, &info) < 0) {
+        return -1;
+    }
+    if (ob != Py_None && !info && !PyCallable_Check(ob)) {
         PyErr_SetString(PyExc_TypeError,
                         "restype must be a type, a callable, or None");
         return -1;
@@ -3168,14 +3301,17 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ign
 static PyObject *
 PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
 {
-    StgDictObject *dict;
     if (self->restype) {
         return Py_NewRef(self->restype);
     }
-    dict = PyObject_stgdict((PyObject *)self);
-    assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */
-    if (dict->restype) {
-        return Py_NewRef(dict->restype);
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) {
+        return NULL;
+    }
+    assert(info); /* Cannot be NULL for PyCFuncPtrObject instances */
+    if (info->restype) {
+        return Py_NewRef(info->restype);
     } else {
         Py_RETURN_NONE;
     }
@@ -3203,14 +3339,17 @@ PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ig
 static PyObject *
 PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
 {
-    StgDictObject *dict;
     if (self->argtypes) {
         return Py_NewRef(self->argtypes);
     }
-    dict = PyObject_stgdict((PyObject *)self);
-    assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */
-    if (dict->argtypes) {
-        return Py_NewRef(dict->argtypes);
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) {
+        return NULL;
+    }
+    assert(info); /* Cannot be NULL for PyCFuncPtrObject instances */
+    if (info->argtypes) {
+        return Py_NewRef(info->argtypes);
     } else {
         Py_RETURN_NONE;
     }
@@ -3242,7 +3381,6 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type)
 #else
     char *mangled_name;
     int i;
-    StgDictObject *dict;
 
     Py_BEGIN_ALLOW_THREADS
     address = (PPROC)GetProcAddress(handle, name);
@@ -3253,9 +3391,13 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type)
         return NULL;
     }
 
-    dict = PyType_stgdict((PyObject *)type);
-    /* It should not happen that dict is NULL, but better be safe */
-    if (dict==NULL || dict->flags & FUNCFLAG_CDECL)
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
+        return NULL;
+    }
+    /* It should not happen that info is NULL, but better be safe */
+    if (info==NULL || info->flags & FUNCFLAG_CDECL)
         return address;
 
     /* for stdcall, try mangled names:
@@ -3282,7 +3424,6 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type)
 static int
 _check_outarg_type(PyObject *arg, Py_ssize_t index)
 {
-    StgDictObject *dict;
     ctypes_state *st = GLOBAL_STATE();
 
     if (PyCPointerTypeObject_Check(st, arg)) {
@@ -3291,12 +3432,15 @@ _check_outarg_type(PyObject *arg, Py_ssize_t index)
     if (PyCArrayTypeObject_Check(st, arg)) {
         return 1;
     }
-    dict = PyType_stgdict(arg);
-    if (dict
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, arg, &info) < 0) {
+        return -1;
+    }
+    if (info
         /* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */
-        && PyUnicode_Check(dict->proto)
+        && PyUnicode_Check(info->proto)
 /* We only allow c_void_p, c_char_p and c_wchar_p as a simple output parameter type */
-        && (strchr("PzZ", PyUnicode_AsUTF8(dict->proto)[0]))) {
+        && (strchr("PzZ", PyUnicode_AsUTF8(info->proto)[0]))) {
         return 1;
     }
 
@@ -3314,18 +3458,21 @@ static int
 _validate_paramflags(PyTypeObject *type, PyObject *paramflags)
 {
     Py_ssize_t i, len;
-    StgDictObject *dict;
     PyObject *argtypes;
 
-    dict = PyType_stgdict((PyObject *)type);
-    if (!dict) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
+        return -1;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError,
                         "abstract class");
         return 0;
     }
-    argtypes = dict->argtypes;
+    argtypes = info->argtypes;
 
-    if (paramflags == NULL || dict->argtypes == NULL)
+    if (paramflags == NULL || info->argtypes == NULL)
         return 1;
 
     if (!PyTuple_Check(paramflags)) {
@@ -3335,7 +3482,7 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags)
     }
 
     len = PyTuple_GET_SIZE(paramflags);
-    if (len != PyTuple_GET_SIZE(dict->argtypes)) {
+    if (len != PyTuple_GET_SIZE(info->argtypes)) {
         PyErr_SetString(PyExc_ValueError,
                         "paramflags must have the same length as argtypes");
         return 0;
@@ -3565,7 +3712,6 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
     PyCFuncPtrObject *self;
     PyObject *callable;
-    StgDictObject *dict;
     CThunkObject *thunk;
 
     if (PyTuple_GET_SIZE(args) == 0)
@@ -3616,9 +3762,13 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     }
 */
 
-    dict = PyType_stgdict((PyObject *)type);
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
+        return NULL;
+    }
     /* XXXX Fails if we do: 'PyCFuncPtr(lambda x: x)' */
-    if (!dict || !dict->argtypes) {
+    if (!info || !info->argtypes) {
         PyErr_SetString(PyExc_TypeError,
                "cannot construct instance of this class:"
             " no argtypes");
@@ -3626,9 +3776,9 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     }
 
     thunk = _ctypes_alloc_callback(callable,
-                                  dict->argtypes,
-                                  dict->restype,
-                                  dict->flags);
+                                  info->argtypes,
+                                  info->restype,
+                                  info->flags);
     if (!thunk)
         return NULL;
 
@@ -3737,7 +3887,6 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes,
 {
     PyObject *paramflags = self->paramflags;
     PyObject *callargs;
-    StgDictObject *dict;
     Py_ssize_t i, len;
     int inargs_index = 0;
     /* It's a little bit difficult to determine how many arguments the
@@ -3826,15 +3975,18 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes,
                 break;
             }
             ob = PyTuple_GET_ITEM(argtypes, i);
-            dict = PyType_stgdict(ob);
-            if (dict == NULL) {
+            StgInfo *info;
+            if (PyStgInfo_FromType(st, ob, &info) < 0) {
+                goto error;
+            }
+            if (info == NULL) {
                 /* Cannot happen: _validate_paramflags()
                   would not accept such an object */
                 PyErr_Format(PyExc_RuntimeError,
-                             "NULL stgdict unexpected");
+                             "NULL stginfo unexpected");
                 goto error;
             }
-            if (PyUnicode_Check(dict->proto)) {
+            if (PyUnicode_Check(info->proto)) {
                 PyErr_Format(
                     PyExc_TypeError,
                     "%s 'out' parameter must be passed as default value",
@@ -3846,7 +3998,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes,
             }
             else {
                 /* Create an instance of the pointed-to type */
-                ob = _PyObject_CallNoArgs(dict->proto);
+                ob = _PyObject_CallNoArgs(info->proto);
             }
             /*
                XXX Is the following correct any longer?
@@ -3966,7 +4118,6 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds)
     PyObject *converters;
     PyObject *checker;
     PyObject *argtypes;
-    StgDictObject *dict = PyObject_stgdict((PyObject *)self);
     PyObject *result;
     PyObject *callargs;
     PyObject *errcheck;
@@ -3979,13 +4130,19 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds)
     int outmask;
     unsigned int numretvals;
 
-    assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */
-    restype = self->restype ? self->restype : dict->restype;
-    converters = self->converters ? self->converters : dict->converters;
-    checker = self->checker ? self->checker : dict->checker;
-    argtypes = self->argtypes ? self->argtypes : dict->argtypes;
-/* later, we probably want to have an errcheck field in stgdict */
-    errcheck = self->errcheck /* ? self->errcheck : dict->errcheck */;
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) {
+        return NULL;
+    }
+    assert(info); /* Cannot be NULL for PyCFuncPtrObject instances */
+
+    restype = self->restype ? self->restype : info->restype;
+    converters = self->converters ? self->converters : info->converters;
+    checker = self->checker ? self->checker : info->checker;
+    argtypes = self->argtypes ? self->argtypes : info->argtypes;
+/* later, we probably want to have an errcheck field in stginfo */
+    errcheck = self->errcheck /* ? self->errcheck : info->errcheck */;
 
 
     pProc = *(void **)self->b_ptr;
@@ -4033,7 +4190,7 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds)
         int actual = Py_SAFE_DOWNCAST(PyTuple_GET_SIZE(callargs),
                                       Py_ssize_t, int);
 
-        if ((dict->flags & FUNCFLAG_CDECL) == FUNCFLAG_CDECL) {
+        if ((info->flags & FUNCFLAG_CDECL) == FUNCFLAG_CDECL) {
             /* For cdecl functions, we allow more actual arguments
                than the length of the argtypes tuple.
             */
@@ -4063,7 +4220,7 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds)
                        piunk,
                        self->iid,
 #endif
-                       dict->flags,
+                       info->flags,
                        converters,
                        restype,
                        checker);
@@ -4123,8 +4280,11 @@ PyCFuncPtr_clear(PyCFuncPtrObject *self)
 static void
 PyCFuncPtr_dealloc(PyCFuncPtrObject *self)
 {
+    PyObject_GC_UnTrack(self);
     PyCFuncPtr_clear(self);
-    Py_TYPE(self)->tp_free((PyObject *)self);
+    PyTypeObject *type = Py_TYPE(self);
+    type->tp_free((PyObject *)self);
+    Py_DECREF(type);
 }
 
 static PyObject *
@@ -4152,59 +4312,26 @@ PyCFuncPtr_bool(PyCFuncPtrObject *self)
         );
 }
 
-static PyNumberMethods PyCFuncPtr_as_number = {
-    0, /* nb_add */
-    0, /* nb_subtract */
-    0, /* nb_multiply */
-    0, /* nb_remainder */
-    0, /* nb_divmod */
-    0, /* nb_power */
-    0, /* nb_negative */
-    0, /* nb_positive */
-    0, /* nb_absolute */
-    (inquiry)PyCFuncPtr_bool, /* nb_bool */
+static PyType_Slot pycfuncptr_slots[] = {
+    {Py_tp_dealloc, PyCFuncPtr_dealloc},
+    {Py_tp_repr, PyCFuncPtr_repr},
+    {Py_tp_call, PyCFuncPtr_call},
+    {Py_tp_doc, PyDoc_STR("Function Pointer")},
+    {Py_tp_traverse, PyCFuncPtr_traverse},
+    {Py_tp_clear, PyCFuncPtr_clear},
+    {Py_tp_getset, PyCFuncPtr_getsets},
+    {Py_tp_new, PyCFuncPtr_new},
+    {Py_bf_getbuffer, PyCData_NewGetBuffer},
+    {Py_nb_bool, PyCFuncPtr_bool},
+    {0, NULL},
 };
 
-PyTypeObject PyCFuncPtr_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_ctypes.CFuncPtr",
-    sizeof(PyCFuncPtrObject),                           /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    (destructor)PyCFuncPtr_dealloc,             /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    (reprfunc)PyCFuncPtr_repr,                  /* tp_repr */
-    &PyCFuncPtr_as_number,                      /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    (ternaryfunc)PyCFuncPtr_call,               /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    &PyCData_as_buffer,                         /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    PyDoc_STR("Function Pointer"),              /* tp_doc */
-    (traverseproc)PyCFuncPtr_traverse,          /* tp_traverse */
-    (inquiry)PyCFuncPtr_clear,                  /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    0,                                          /* tp_methods */
-    0,                                          /* tp_members */
-    PyCFuncPtr_getsets,                         /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    0,                                          /* tp_init */
-    0,                                          /* tp_alloc */
-    PyCFuncPtr_new,                             /* tp_new */
-    0,                                          /* tp_free */
+static PyType_Spec pycfuncptr_spec = {
+    .name = "_ctypes.CFuncPtr",
+    .basicsize = sizeof(PyCFuncPtrObject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = pycfuncptr_slots,
 };
 
 /*****************************************************************/
@@ -4224,11 +4351,15 @@ _init_pos_args(PyObject *self, PyTypeObject *type,
                PyObject *args, PyObject *kwds,
                Py_ssize_t index)
 {
-    StgDictObject *dict;
     PyObject *fields;
     Py_ssize_t i;
 
-    if (PyType_stgdict((PyObject *)type->tp_base)) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *baseinfo;
+    if (PyStgInfo_FromType(st, (PyObject *)type->tp_base, &baseinfo) < 0) {
+        return -1;
+    }
+    if (baseinfo) {
         index = _init_pos_args(self, type->tp_base,
                                args, kwds,
                                index);
@@ -4236,8 +4367,17 @@ _init_pos_args(PyObject *self, PyTypeObject *type,
             return -1;
     }
 
-    dict = PyType_stgdict((PyObject *)type);
-    fields = PyDict_GetItemWithError((PyObject *)dict, &_Py_ID(_fields_));
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
+        return -1;
+    }
+    assert(info);
+
+    PyObject *attrdict = PyType_GetDict(type);
+    assert(attrdict);
+
+    fields = PyDict_GetItemWithError((PyObject *)attrdict, &_Py_ID(_fields_));
+    Py_CLEAR(attrdict);
     if (fields == NULL) {
         if (PyErr_Occurred()) {
             return -1;
@@ -4246,7 +4386,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type,
     }
 
     for (i = index;
-         i < dict->length && i < PyTuple_GET_SIZE(args);
+         i < info->length && i < PyTuple_GET_SIZE(args);
          ++i) {
         PyObject *pair = PySequence_GetItem(fields, i - index);
         PyObject *name, *val;
@@ -4279,7 +4419,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type,
         if (res == -1)
             return -1;
     }
-    return dict->length;
+    return info->length;
 }
 
 static int
@@ -4316,88 +4456,34 @@ Struct_init(PyObject *self, PyObject *args, PyObject *kwds)
     return 0;
 }
 
-static PyTypeObject Struct_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_ctypes.Structure",
-    sizeof(CDataObject),                        /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    0,                                          /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    0,                                          /* tp_repr */
-    0,                                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    &PyCData_as_buffer,                         /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    PyDoc_STR("Structure base class"),          /* tp_doc */
-    (traverseproc)PyCData_traverse,             /* tp_traverse */
-    (inquiry)PyCData_clear,                     /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    0,                                          /* tp_methods */
-    0,                                          /* tp_members */
-    0,                                          /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    Struct_init,                                /* tp_init */
-    0,                                          /* tp_alloc */
-    GenericPyCData_new,                         /* tp_new */
-    0,                                          /* tp_free */
+static PyType_Slot pycstruct_slots[] = {
+    {Py_tp_doc, PyDoc_STR("Structure base class")},
+    {Py_tp_init, Struct_init},
+    {Py_tp_new, GenericPyCData_new},
+    {Py_bf_getbuffer, PyCData_NewGetBuffer},
+    {0, NULL},
+};
+
+static PyType_Spec pycstruct_spec = {
+    .name = "_ctypes.Structure",
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = pycstruct_slots,
+};
+
+static PyType_Slot pycunion_slots[] = {
+    {Py_tp_doc, PyDoc_STR("Union base class")},
+    {Py_tp_init, Struct_init},
+    {Py_tp_new, GenericPyCData_new},
+    {Py_bf_getbuffer, PyCData_NewGetBuffer},
+    {0, NULL},
 };
 
-static PyTypeObject Union_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_ctypes.Union",
-    sizeof(CDataObject),                        /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    0,                                          /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    0,                                          /* tp_repr */
-    0,                                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    &PyCData_as_buffer,                         /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    PyDoc_STR("Union base class"),              /* tp_doc */
-    (traverseproc)PyCData_traverse,             /* tp_traverse */
-    (inquiry)PyCData_clear,                     /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    0,                                          /* tp_methods */
-    0,                                          /* tp_members */
-    0,                                          /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    Struct_init,                                /* tp_init */
-    0,                                          /* tp_alloc */
-    GenericPyCData_new,                         /* tp_new */
-    0,                                          /* tp_free */
+static PyType_Spec pycunion_spec = {
+    .name = "_ctypes.Union",
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = pycunion_slots,
 };
 
 
@@ -4431,8 +4517,6 @@ Array_item(PyObject *myself, Py_ssize_t index)
 {
     CDataObject *self = (CDataObject *)myself;
     Py_ssize_t offset, size;
-    StgDictObject *stgdict;
-
 
     if (index < 0 || index >= self->b_length) {
         PyErr_SetString(PyExc_IndexError,
@@ -4440,15 +4524,19 @@ Array_item(PyObject *myself, Py_ssize_t index)
         return NULL;
     }
 
-    stgdict = PyObject_stgdict((PyObject *)self);
-    assert(stgdict); /* Cannot be NULL for array instances */
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *stginfo;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+        return NULL;
+    }
+
     /* Would it be clearer if we got the item size from
-       stgdict->proto's stgdict?
+       stginfo->proto's stginfo?
     */
-    size = stgdict->size / stgdict->length;
+    size = stginfo->size / stginfo->length;
     offset = index * size;
 
-    return PyCData_get(stgdict->proto, stgdict->getfunc, (PyObject *)self,
+    return PyCData_get(stginfo->proto, stginfo->getfunc, (PyObject *)self,
                      index, size, self->b_ptr + offset);
 }
 
@@ -4467,7 +4555,6 @@ Array_subscript(PyObject *myself, PyObject *item)
         return Array_item(myself, i);
     }
     else if (PySlice_Check(item)) {
-        StgDictObject *stgdict, *itemdict;
         PyObject *proto;
         PyObject *np;
         Py_ssize_t start, stop, step, slicelen, i;
@@ -4478,14 +4565,21 @@ Array_subscript(PyObject *myself, PyObject *item)
         }
         slicelen = PySlice_AdjustIndices(self->b_length, &start, &stop, step);
 
-        stgdict = PyObject_stgdict((PyObject *)self);
-        assert(stgdict); /* Cannot be NULL for array object instances */
-        proto = stgdict->proto;
-        itemdict = PyType_stgdict(proto);
-        assert(itemdict); /* proto is the item type of the array, a
+        ctypes_state *st = GLOBAL_STATE();
+        StgInfo *stginfo;
+        if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+            return NULL;
+        }
+        assert(stginfo); /* Cannot be NULL for array object instances */
+        proto = stginfo->proto;
+        StgInfo *iteminfo;
+        if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) {
+            return NULL;
+        }
+        assert(iteminfo); /* proto is the item type of the array, a
                              ctypes type, so this cannot be NULL */
 
-        if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
+        if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
             char *ptr = (char *)self->b_ptr;
             char *dest;
 
@@ -4509,7 +4603,7 @@ Array_subscript(PyObject *myself, PyObject *item)
             PyMem_Free(dest);
             return np;
         }
-        if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
+        if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
             wchar_t *ptr = (wchar_t *)self->b_ptr;
             wchar_t *dest;
 
@@ -4564,7 +4658,6 @@ Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
 {
     CDataObject *self = (CDataObject *)myself;
     Py_ssize_t size, offset;
-    StgDictObject *stgdict;
     char *ptr;
 
     if (value == NULL) {
@@ -4573,18 +4666,23 @@ Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
         return -1;
     }
 
-    stgdict = PyObject_stgdict((PyObject *)self);
-    assert(stgdict); /* Cannot be NULL for array object instances */
-    if (index < 0 || index >= stgdict->length) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *stginfo;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+        return -1;
+    }
+    assert(stginfo); /* Cannot be NULL for array object instances */
+
+    if (index < 0 || index >= stginfo->length) {
         PyErr_SetString(PyExc_IndexError,
                         "invalid index");
         return -1;
     }
-    size = stgdict->size / stgdict->length;
+    size = stginfo->size / stginfo->length;
     offset = index * size;
     ptr = self->b_ptr + offset;
 
-    return PyCData_set((PyObject *)self, stgdict->proto, stgdict->setfunc, value,
+    return PyCData_set((PyObject *)self, stginfo->proto, stginfo->setfunc, value,
                      index, size, ptr);
 }
 
@@ -4658,26 +4756,6 @@ static PyMethodDef Array_methods[] = {
     { NULL, NULL }
 };
 
-static PySequenceMethods Array_as_sequence = {
-    Array_length,                               /* sq_length; */
-    0,                                          /* sq_concat; */
-    0,                                          /* sq_repeat; */
-    Array_item,                                 /* sq_item; */
-    0,                                          /* sq_slice; */
-    Array_ass_item,                             /* sq_ass_item; */
-    0,                                          /* sq_ass_slice; */
-    0,                                          /* sq_contains; */
-
-    0,                                          /* sq_inplace_concat; */
-    0,                                          /* sq_inplace_repeat; */
-};
-
-static PyMappingMethods Array_as_mapping = {
-    Array_length,
-    Array_subscript,
-    Array_ass_subscript,
-};
-
 PyDoc_STRVAR(array_doc,
 "Abstract base class for arrays.\n"
 "\n"
@@ -4688,46 +4766,26 @@ PyDoc_STRVAR(array_doc,
 "reads, the resulting object is not itself an Array."
 );
 
-PyTypeObject PyCArray_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_ctypes.Array",
-    sizeof(CDataObject),                        /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    0,                                          /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    0,                                          /* tp_repr */
-    0,                                          /* tp_as_number */
-    &Array_as_sequence,                         /* tp_as_sequence */
-    &Array_as_mapping,                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    &PyCData_as_buffer,                         /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
-    array_doc,                                  /* tp_doc */
-    (traverseproc)PyCData_traverse,             /* tp_traverse */
-    (inquiry)PyCData_clear,                     /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    Array_methods,                              /* tp_methods */
-    0,                                          /* tp_members */
-    0,                                          /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    (initproc)Array_init,                       /* tp_init */
-    0,                                          /* tp_alloc */
-    GenericPyCData_new,                         /* tp_new */
-    0,                                          /* tp_free */
+static PyType_Slot pycarray_slots[] = {
+    {Py_tp_doc, (char*)array_doc},
+    {Py_tp_methods, Array_methods},
+    {Py_tp_init, Array_init},
+    {Py_tp_new, GenericPyCData_new},
+    {Py_bf_getbuffer, PyCData_NewGetBuffer},
+    {Py_sq_length, Array_length},
+    {Py_sq_item, Array_item},
+    {Py_sq_ass_item, Array_ass_item},
+    {Py_mp_length, Array_length},
+    {Py_mp_subscript, Array_subscript},
+    {Py_mp_ass_subscript, Array_ass_subscript},
+    {0, NULL},
+};
+
+static PyType_Spec pycarray_spec = {
+    .name = "_ctypes.Array",
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = pycarray_slots,
 };
 
 PyObject *
@@ -4804,16 +4862,22 @@ static int
 Simple_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
     PyObject *result;
-    StgDictObject *dict = PyObject_stgdict((PyObject *)self);
 
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError,
                         "can't delete attribute");
         return -1;
     }
-    assert(dict); /* Cannot be NULL for CDataObject instances */
-    assert(dict->setfunc);
-    result = dict->setfunc(self->b_ptr, value, dict->size);
+
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) {
+        return -1;
+    }
+    assert(info); /* Cannot be NULL for CDataObject instances */
+    assert(info->setfunc);
+
+    result = info->setfunc(self->b_ptr, value, info->size);
     if (!result)
         return -1;
 
@@ -4835,11 +4899,14 @@ Simple_init(CDataObject *self, PyObject *args, PyObject *kw)
 static PyObject *
 Simple_get_value(CDataObject *self, void *Py_UNUSED(ignored))
 {
-    StgDictObject *dict;
-    dict = PyObject_stgdict((PyObject *)self);
-    assert(dict); /* Cannot be NULL for CDataObject instances */
-    assert(dict->getfunc);
-    return dict->getfunc(self->b_ptr, self->b_size);
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) {
+        return NULL;
+    }
+    assert(info); /* Cannot be NULL for CDataObject instances */
+    assert(info->getfunc);
+    return info->getfunc(self->b_ptr, self->b_size);
 }
 
 static PyGetSetDef Simple_getsets[] = {
@@ -4854,7 +4921,7 @@ Simple_from_outparm(PyObject *self, PyObject *args)
     if (_ctypes_simple_instance((PyObject *)Py_TYPE(self))) {
         return Py_NewRef(self);
     }
-    /* call stgdict->getfunc */
+    /* call stginfo->getfunc */
     return Simple_get_value((CDataObject *)self, NULL);
 }
 
@@ -4868,19 +4935,6 @@ static int Simple_bool(CDataObject *self)
     return memcmp(self->b_ptr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", self->b_size);
 }
 
-static PyNumberMethods Simple_as_number = {
-    0, /* nb_add */
-    0, /* nb_subtract */
-    0, /* nb_multiply */
-    0, /* nb_remainder */
-    0, /* nb_divmod */
-    0, /* nb_power */
-    0, /* nb_negative */
-    0, /* nb_positive */
-    0, /* nb_absolute */
-    (inquiry)Simple_bool, /* nb_bool */
-};
-
 /* "%s(%s)" % (self.__class__.__name__, self.value) */
 static PyObject *
 Simple_repr(CDataObject *self)
@@ -4903,48 +4957,26 @@ Simple_repr(CDataObject *self)
     return result;
 }
 
-static PyTypeObject Simple_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_ctypes._SimpleCData",
-    sizeof(CDataObject),                        /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    0,                                          /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    (reprfunc)&Simple_repr,                     /* tp_repr */
-    &Simple_as_number,                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    &PyCData_as_buffer,                         /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    PyDoc_STR("XXX to be provided"),            /* tp_doc */
-    (traverseproc)PyCData_traverse,             /* tp_traverse */
-    (inquiry)PyCData_clear,                     /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    Simple_methods,                             /* tp_methods */
-    0,                                          /* tp_members */
-    Simple_getsets,                             /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    (initproc)Simple_init,                      /* tp_init */
-    0,                                          /* tp_alloc */
-    GenericPyCData_new,                         /* tp_new */
-    0,                                          /* tp_free */
+static PyType_Slot pycsimple_slots[] = {
+    {Py_tp_repr, &Simple_repr},
+    {Py_tp_doc, PyDoc_STR("XXX to be provided")},
+    {Py_tp_methods, Simple_methods},
+    {Py_tp_getset, Simple_getsets},
+    {Py_tp_init, Simple_init},
+    {Py_tp_new, GenericPyCData_new},
+    {Py_bf_getbuffer, PyCData_NewGetBuffer},
+    {Py_nb_bool, Simple_bool},
+    {0, NULL},
+};
+
+static PyType_Spec pycsimple_spec = {
+    .name = "_ctypes._SimpleCData",
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = pycsimple_slots,
 };
 
+
 /******************************************************************/
 /*
   PyCPointer_Type
@@ -4955,7 +4987,6 @@ Pointer_item(PyObject *myself, Py_ssize_t index)
     CDataObject *self = (CDataObject *)myself;
     Py_ssize_t size;
     Py_ssize_t offset;
-    StgDictObject *stgdict, *itemdict;
     PyObject *proto;
 
     if (*(void **)self->b_ptr == NULL) {
@@ -4964,19 +4995,27 @@ Pointer_item(PyObject *myself, Py_ssize_t index)
         return NULL;
     }
 
-    stgdict = PyObject_stgdict((PyObject *)self);
-    assert(stgdict); /* Cannot be NULL for pointer object instances */
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *stginfo;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+        return NULL;
+    }
+    assert(stginfo); /* Cannot be NULL for pointer object instances */
 
-    proto = stgdict->proto;
+    proto = stginfo->proto;
     assert(proto);
-    itemdict = PyType_stgdict(proto);
-    assert(itemdict); /* proto is the item type of the pointer, a ctypes
+
+    StgInfo *iteminfo;
+    if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) {
+        return NULL;
+    }
+    assert(iteminfo); /* proto is the item type of the pointer, a ctypes
                          type, so this cannot be NULL */
 
-    size = itemdict->size;
-    offset = index * itemdict->size;
+    size = iteminfo->size;
+    offset = index * iteminfo->size;
 
-    return PyCData_get(proto, stgdict->getfunc, (PyObject *)self,
+    return PyCData_get(proto, stginfo->getfunc, (PyObject *)self,
                      index, size, (*(char **)self->b_ptr) + offset);
 }
 
@@ -4986,7 +5025,6 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
     CDataObject *self = (CDataObject *)myself;
     Py_ssize_t size;
     Py_ssize_t offset;
-    StgDictObject *stgdict, *itemdict;
     PyObject *proto;
 
     if (value == NULL) {
@@ -5001,37 +5039,47 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
         return -1;
     }
 
-    stgdict = PyObject_stgdict((PyObject *)self);
-    assert(stgdict); /* Cannot be NULL for pointer instances */
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *stginfo;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+        return -1;
+    }
+    assert(stginfo); /* Cannot be NULL for pointer instances */
 
-    proto = stgdict->proto;
+    proto = stginfo->proto;
     assert(proto);
 
-    itemdict = PyType_stgdict(proto);
-    assert(itemdict); /* Cannot be NULL because the itemtype of a pointer
+    StgInfo *iteminfo;
+    if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) {
+        return -1;
+    }
+    assert(iteminfo); /* Cannot be NULL because the itemtype of a pointer
                          is always a ctypes type */
 
-    size = itemdict->size;
-    offset = index * itemdict->size;
+    size = iteminfo->size;
+    offset = index * iteminfo->size;
 
-    return PyCData_set((PyObject *)self, proto, stgdict->setfunc, value,
+    return PyCData_set((PyObject *)self, proto, stginfo->setfunc, value,
                      index, size, (*(char **)self->b_ptr) + offset);
 }
 
 static PyObject *
 Pointer_get_contents(CDataObject *self, void *closure)
 {
-    StgDictObject *stgdict;
-
     if (*(void **)self->b_ptr == NULL) {
         PyErr_SetString(PyExc_ValueError,
                         "NULL pointer access");
         return NULL;
     }
 
-    stgdict = PyObject_stgdict((PyObject *)self);
-    assert(stgdict); /* Cannot be NULL for pointer instances */
-    return PyCData_FromBaseObj(stgdict->proto,
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *stginfo;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+        return NULL;
+    }
+    assert(stginfo); /* Cannot be NULL for pointer instances */
+
+    return PyCData_FromBaseObj(stginfo->proto,
                              (PyObject *)self, 0,
                              *(void **)self->b_ptr);
 }
@@ -5039,7 +5087,6 @@ Pointer_get_contents(CDataObject *self, void *closure)
 static int
 Pointer_set_contents(CDataObject *self, PyObject *value, void *closure)
 {
-    StgDictObject *stgdict;
     CDataObject *dst;
     PyObject *keep;
 
@@ -5048,18 +5095,21 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure)
                         "Pointer does not support item deletion");
         return -1;
     }
-    stgdict = PyObject_stgdict((PyObject *)self);
-    assert(stgdict); /* Cannot be NULL for pointer instances */
-    assert(stgdict->proto);
     ctypes_state *st = GLOBAL_STATE();
+    StgInfo *stginfo;
+    if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+        return -1;
+    }
+    assert(stginfo); /* Cannot be NULL for pointer instances */
+    assert(stginfo->proto);
     if (!CDataObject_Check(st, value)) {
-        int res = PyObject_IsInstance(value, stgdict->proto);
+        int res = PyObject_IsInstance(value, stginfo->proto);
         if (res == -1)
             return -1;
         if (!res) {
             PyErr_Format(PyExc_TypeError,
                          "expected %s instead of %s",
-                         ((PyTypeObject *)(stgdict->proto))->tp_name,
+                         ((PyTypeObject *)(stginfo->proto))->tp_name,
                          Py_TYPE(value)->tp_name);
             return -1;
         }
@@ -5107,8 +5157,12 @@ Pointer_init(CDataObject *self, PyObject *args, PyObject *kw)
 static PyObject *
 Pointer_new(PyTypeObject *type, PyObject *args, PyObject *kw)
 {
-    StgDictObject *dict = PyType_stgdict((PyObject *)type);
-    if (!dict || !dict->proto) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
+        return NULL;
+    }
+    if (!info || !info->proto) {
         PyErr_SetString(PyExc_TypeError,
                         "Cannot create instance: has no _type_");
         return NULL;
@@ -5130,7 +5184,6 @@ Pointer_subscript(PyObject *myself, PyObject *item)
         PySliceObject *slice = (PySliceObject *)item;
         Py_ssize_t start, stop, step;
         PyObject *np;
-        StgDictObject *stgdict, *itemdict;
         PyObject *proto;
         Py_ssize_t i, len;
         size_t cur;
@@ -5184,13 +5237,20 @@ Pointer_subscript(PyObject *myself, PyObject *item)
         else
             len = (stop - start + 1) / step + 1;
 
-        stgdict = PyObject_stgdict((PyObject *)self);
-        assert(stgdict); /* Cannot be NULL for pointer instances */
-        proto = stgdict->proto;
+        ctypes_state *st = GLOBAL_STATE();
+        StgInfo *stginfo;
+        if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) {
+            return NULL;
+        }
+        assert(stginfo); /* Cannot be NULL for pointer instances */
+        proto = stginfo->proto;
         assert(proto);
-        itemdict = PyType_stgdict(proto);
-        assert(itemdict);
-        if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
+        StgInfo *iteminfo;
+        if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) {
+            return NULL;
+        }
+        assert(iteminfo);
+        if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
             char *ptr = *(char **)self->b_ptr;
             char *dest;
 
@@ -5210,7 +5270,7 @@ Pointer_subscript(PyObject *myself, PyObject *item)
             PyMem_Free(dest);
             return np;
         }
-        if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
+        if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
             wchar_t *ptr = *(wchar_t **)self->b_ptr;
             wchar_t *dest;
 
@@ -5248,87 +5308,32 @@ Pointer_subscript(PyObject *myself, PyObject *item)
     }
 }
 
-static PySequenceMethods Pointer_as_sequence = {
-    0,                                          /* inquiry sq_length; */
-    0,                                          /* binaryfunc sq_concat; */
-    0,                                          /* intargfunc sq_repeat; */
-    Pointer_item,                               /* intargfunc sq_item; */
-    0,                                          /* intintargfunc sq_slice; */
-    Pointer_ass_item,                           /* intobjargproc sq_ass_item; */
-    0,                                          /* intintobjargproc sq_ass_slice; */
-    0,                                          /* objobjproc sq_contains; */
-    /* Added in release 2.0 */
-    0,                                          /* binaryfunc sq_inplace_concat; */
-    0,                                          /* intargfunc sq_inplace_repeat; */
-};
-
-static PyMappingMethods Pointer_as_mapping = {
-    0,
-    Pointer_subscript,
-};
-
 static int
 Pointer_bool(CDataObject *self)
 {
     return (*(void **)self->b_ptr != NULL);
 }
 
-static PyNumberMethods Pointer_as_number = {
-    0, /* nb_add */
-    0, /* nb_subtract */
-    0, /* nb_multiply */
-    0, /* nb_remainder */
-    0, /* nb_divmod */
-    0, /* nb_power */
-    0, /* nb_negative */
-    0, /* nb_positive */
-    0, /* nb_absolute */
-    (inquiry)Pointer_bool, /* nb_bool */
+static PyType_Slot pycpointer_slots[] = {
+    {Py_tp_doc, PyDoc_STR("XXX to be provided")},
+    {Py_tp_getset, Pointer_getsets},
+    {Py_tp_init, Pointer_init},
+    {Py_tp_new, Pointer_new},
+    {Py_bf_getbuffer, PyCData_NewGetBuffer},
+    {Py_nb_bool, Pointer_bool},
+    {Py_mp_subscript, Pointer_subscript},
+    {Py_sq_item, Pointer_item},
+    {Py_sq_ass_item, Pointer_ass_item},
+    {0, NULL},
 };
 
-PyTypeObject PyCPointer_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_ctypes._Pointer",
-    sizeof(CDataObject),                        /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    0,                                          /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    0,                                          /* tp_repr */
-    &Pointer_as_number,                         /* tp_as_number */
-    &Pointer_as_sequence,                       /* tp_as_sequence */
-    &Pointer_as_mapping,                        /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    &PyCData_as_buffer,                         /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    PyDoc_STR("XXX to be provided"),            /* tp_doc */
-    (traverseproc)PyCData_traverse,             /* tp_traverse */
-    (inquiry)PyCData_clear,                     /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    0,                                          /* tp_methods */
-    0,                                          /* tp_members */
-    Pointer_getsets,                            /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    (initproc)Pointer_init,                     /* tp_init */
-    0,                                          /* tp_alloc */
-    Pointer_new,                                /* tp_new */
-    0,                                          /* tp_free */
+static PyType_Spec pycpointer_spec = {
+    .name = "_ctypes._Pointer",
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = pycpointer_slots,
 };
 
-
 /******************************************************************/
 /*
  *  Module initialization.
@@ -5433,7 +5438,6 @@ string_at(const char *ptr, int size)
 static int
 cast_check_pointertype(PyObject *arg)
 {
-    StgDictObject *dict;
     ctypes_state *st = GLOBAL_STATE();
 
     if (PyCPointerTypeObject_Check(st, arg)) {
@@ -5442,10 +5446,13 @@ cast_check_pointertype(PyObject *arg)
     if (PyCFuncPtrTypeObject_Check(st, arg)) {
         return 1;
     }
-    dict = PyType_stgdict(arg);
-    if (dict != NULL && dict->proto != NULL) {
-        if (PyUnicode_Check(dict->proto)
-            && (strchr("sPzUZXO", PyUnicode_AsUTF8(dict->proto)[0]))) {
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, arg, &info) < 0) {
+        return 0;
+    }
+    if (info != NULL && info->proto != NULL) {
+        if (PyUnicode_Check(info->proto)
+            && (strchr("sPzUZXO", PyUnicode_AsUTF8(info->proto)[0]))) {
             /* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */
             return 1;
         }
@@ -5544,26 +5551,8 @@ _ctypes_add_types(PyObject *mod)
     if (PyType_Ready(TYPE) < 0) { \
         return -1; \
     }
-
-#define TYPE_READY_BASE(TYPE_EXPR, TP_BASE) \
-    do { \
-        PyTypeObject *type = (TYPE_EXPR); \
-        type->tp_base = (TP_BASE); \
-        TYPE_READY(type); \
-    } while (0)
-
-#define MOD_ADD_TYPE(TYPE_EXPR, TP_TYPE, TP_BASE) \
-    do { \
-        PyTypeObject *type = (TYPE_EXPR); \
-        Py_SET_TYPE(type, TP_TYPE); \
-        type->tp_base = TP_BASE; \
-        if (PyModule_AddType(mod, type) < 0) { \
-            return -1; \
-        } \
-    } while (0)
-
-#define CREATE_TYPE(MOD, TP, SPEC, BASE) do {                       \
-    PyObject *type = PyType_FromMetaclass(NULL, MOD, SPEC,          \
+#define CREATE_TYPE(TP, SPEC, META, BASE) do {                      \
+    PyObject *type = PyType_FromMetaclass(META, mod, SPEC,          \
                                           (PyObject *)BASE);        \
     if (type == NULL) {                                             \
         return -1;                                                  \
@@ -5571,67 +5560,82 @@ _ctypes_add_types(PyObject *mod)
     TP = (PyTypeObject *)type;                                      \
 } while (0)
 
+#define MOD_ADD_TYPE(TP, SPEC, META, BASE) do {                     \
+    CREATE_TYPE(TP, SPEC, META, BASE);                              \
+    if (PyModule_AddType(mod, (PyTypeObject *)(TP)) < 0) {          \
+        return -1;                                                  \
+    }                                                               \
+} while (0)
+
     ctypes_state *st = GLOBAL_STATE();
 
     /* Note:
        ob_type is the metatype (the 'type'), defaults to PyType_Type,
        tp_base is the base type, defaults to 'object' aka PyBaseObject_Type.
     */
-    CREATE_TYPE(mod, st->PyCArg_Type, &carg_spec, NULL);
-    CREATE_TYPE(mod, st->PyCThunk_Type, &cthunk_spec, NULL);
-    TYPE_READY(st->PyCData_Type);
-    /* StgDict is derived from PyDict_Type */
-    TYPE_READY_BASE(st->PyCStgDict_Type, &PyDict_Type);
+    CREATE_TYPE(st->PyCArg_Type, &carg_spec, NULL, NULL);
+    CREATE_TYPE(st->PyCThunk_Type, &cthunk_spec, NULL, NULL);
+    CREATE_TYPE(st->PyCData_Type, &pycdata_spec, NULL, NULL);
+
+    // Common Metaclass
+    CREATE_TYPE(st->PyCType_Type, &pyctype_type_spec,
+                NULL, &PyType_Type);
 
     /*************************************************
      *
      * Metaclasses
      */
-    CREATE_TYPE(mod, st->PyCStructType_Type, &pycstruct_type_spec,
-                &PyType_Type);
-    CREATE_TYPE(mod, st->UnionType_Type, &union_type_spec, &PyType_Type);
-    CREATE_TYPE(mod, st->PyCPointerType_Type, &pycpointer_type_spec,
-                &PyType_Type);
-    CREATE_TYPE(mod, st->PyCArrayType_Type, &pycarray_type_spec,
-                &PyType_Type);
-    CREATE_TYPE(mod, st->PyCSimpleType_Type, &pycsimple_type_spec,
-                &PyType_Type);
-    CREATE_TYPE(mod, st->PyCFuncPtrType_Type, &pycfuncptr_type_spec,
-                &PyType_Type);
+    CREATE_TYPE(st->PyCStructType_Type, &pycstruct_type_spec,
+                NULL, st->PyCType_Type);
+    CREATE_TYPE(st->UnionType_Type, &union_type_spec,
+                NULL, st->PyCType_Type);
+    CREATE_TYPE(st->PyCPointerType_Type, &pycpointer_type_spec,
+                NULL, st->PyCType_Type);
+    CREATE_TYPE(st->PyCArrayType_Type, &pycarray_type_spec,
+                NULL, st->PyCType_Type);
+    CREATE_TYPE(st->PyCSimpleType_Type, &pycsimple_type_spec,
+                NULL, st->PyCType_Type);
+    CREATE_TYPE(st->PyCFuncPtrType_Type, &pycfuncptr_type_spec,
+                NULL, st->PyCType_Type);
 
     /*************************************************
      *
      * Classes using a custom metaclass
      */
 
-    MOD_ADD_TYPE(st->Struct_Type, st->PyCStructType_Type, st->PyCData_Type);
-    MOD_ADD_TYPE(st->Union_Type, st->UnionType_Type, st->PyCData_Type);
-    MOD_ADD_TYPE(st->PyCPointer_Type, st->PyCPointerType_Type, st->PyCData_Type);
-    MOD_ADD_TYPE(st->PyCArray_Type, st->PyCArrayType_Type, st->PyCData_Type);
-    MOD_ADD_TYPE(st->Simple_Type, st->PyCSimpleType_Type, st->PyCData_Type);
-    MOD_ADD_TYPE(st->PyCFuncPtr_Type, st->PyCFuncPtrType_Type, st->PyCData_Type);
+    MOD_ADD_TYPE(st->Struct_Type, &pycstruct_spec,
+                 st->PyCStructType_Type, st->PyCData_Type);
+    MOD_ADD_TYPE(st->Union_Type, &pycunion_spec,
+                 st->UnionType_Type, st->PyCData_Type);
+    MOD_ADD_TYPE(st->PyCPointer_Type, &pycpointer_spec,
+                 st->PyCPointerType_Type, st->PyCData_Type);
+    MOD_ADD_TYPE(st->PyCArray_Type, &pycarray_spec,
+                 st->PyCArrayType_Type, st->PyCData_Type);
+    MOD_ADD_TYPE(st->Simple_Type, &pycsimple_spec,
+                 st->PyCSimpleType_Type, st->PyCData_Type);
+    MOD_ADD_TYPE(st->PyCFuncPtr_Type, &pycfuncptr_spec,
+                 st->PyCFuncPtrType_Type, st->PyCData_Type);
 
     /*************************************************
      *
      * Simple classes
      */
 
-    CREATE_TYPE(mod, st->PyCField_Type, &cfield_spec, NULL);
+    CREATE_TYPE(st->PyCField_Type, &cfield_spec, NULL, NULL);
 
     /*************************************************
      *
      * Other stuff
      */
 
-    CREATE_TYPE(mod, st->DictRemover_Type, &dictremover_spec, NULL);
-    CREATE_TYPE(mod, st->StructParam_Type, &structparam_spec, NULL);
+    CREATE_TYPE(st->DictRemover_Type, &dictremover_spec, NULL, NULL);
+    CREATE_TYPE(st->StructParam_Type, &structparam_spec, NULL, NULL);
 
 #ifdef MS_WIN32
-    CREATE_TYPE(mod, st->PyComError_Type, &comerror_spec, PyExc_Exception);
+    CREATE_TYPE(st->PyComError_Type, &comerror_spec, NULL, PyExc_Exception);
 #endif
 
 #undef TYPE_READY
-#undef TYPE_READY_BASE
 #undef MOD_ADD_TYPE
 #undef CREATE_TYPE
     return 0;
index f70479435915ff0abf217818da7c91f803249502..08d068e47ee2bfb73b2cacad8873e6698c7d2f02 100644 (file)
@@ -109,10 +109,14 @@ PrintError(const char *msg, ...)
  * slower.
  */
 static void
-TryAddRef(StgDictObject *dict, CDataObject *obj)
+TryAddRef(PyObject *cnv, CDataObject *obj)
 {
     IUnknown *punk;
-    int r = PyDict_Contains((PyObject *)dict, &_Py_ID(_needs_com_addref_));
+    PyObject *attrdict = _PyType_GetDict((PyTypeObject *)cnv);
+    if (!attrdict) {
+        return;
+    }
+    int r = PyDict_Contains(attrdict, &_Py_ID(_needs_com_addref_));
     if (r <= 0) {
         if (r < 0) {
             PrintError("getting _needs_com_addref_");
@@ -154,22 +158,26 @@ static void _CallPythonObject(void *mem,
     ctypes_state *st = GLOBAL_STATE();
     for (i = 0; i < nargs; i++) {
         PyObject *cnv = cnvs[i]; // borrowed ref
-        StgDictObject *dict;
-        dict = PyType_stgdict(cnv);
 
-        if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) {
-            PyObject *v = dict->getfunc(*pArgs, dict->size);
+        StgInfo *info;
+        if (PyStgInfo_FromType(st, cnv, &info) < 0) {
+            goto Done;
+        }
+
+        if (info && info->getfunc && !_ctypes_simple_instance(cnv)) {
+            PyObject *v = info->getfunc(*pArgs, info->size);
             if (!v) {
                 PrintError("create argument %zd:\n", i);
                 goto Done;
             }
             args[i] = v;
             /* XXX XXX XX
-               We have the problem that c_byte or c_short have dict->size of
+               We have the problem that c_byte or c_short have info->size of
                1 resp. 4, but these parameters are pushed as sizeof(int) bytes.
                BTW, the same problem occurs when they are pushed as parameters
             */
-        } else if (dict) {
+        }
+        else if (info) {
             /* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */
             CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv);
             if (!obj) {
@@ -181,10 +189,10 @@ static void _CallPythonObject(void *mem,
                 PrintError("unexpected result of create argument %zd:\n", i);
                 goto Done;
             }
-            memcpy(obj->b_ptr, *pArgs, dict->size);
+            memcpy(obj->b_ptr, *pArgs, info->size);
             args[i] = (PyObject *)obj;
 #ifdef MS_WIN32
-            TryAddRef(dict, obj);
+            TryAddRef(cnv, obj);
 #endif
         } else {
             PyErr_SetString(PyExc_TypeError,
@@ -348,10 +356,8 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
     if (p == NULL)
         return NULL;
 
-#ifdef Py_DEBUG
     ctypes_state *st = GLOBAL_STATE();
     assert(CThunk_CheckExact(st, (PyObject *)p));
-#endif
 
     p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec);
     if (p->pcl_write == NULL) {
@@ -372,14 +378,18 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
         p->setfunc = NULL;
         p->ffi_restype = &ffi_type_void;
     } else {
-        StgDictObject *dict = PyType_stgdict(restype);
-        if (dict == NULL || dict->setfunc == NULL) {
+        StgInfo *info;
+        if (PyStgInfo_FromType(st, restype, &info) < 0) {
+            goto error;
+        }
+
+        if (info == NULL || info->setfunc == NULL) {
           PyErr_SetString(PyExc_TypeError,
                           "invalid result type for callback function");
           goto error;
         }
-        p->setfunc = dict->setfunc;
-        p->ffi_restype = &dict->ffi_type_pointer;
+        p->setfunc = info->setfunc;
+        p->ffi_restype = &info->ffi_type_pointer;
     }
 
     cc = FFI_DEFAULT_ABI;
index 97d1dbaae03d4f8e594606d07d92fa897bbd4367..6ebbb64d61b07a3045ea3318f8b9ec5bc23dab74 100644 (file)
@@ -664,15 +664,20 @@ struct argument {
  */
 static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa)
 {
-    StgDictObject *dict;
     pa->keep = NULL; /* so we cannot forget it later */
+    ctypes_state *st = GLOBAL_STATE();
 
-    dict = PyObject_stgdict(obj);
-    if (dict) {
+    StgInfo *info;
+    int result = PyStgInfo_FromObject(st, obj, &info);
+    if (result < 0) {
+        return -1;
+    }
+    if (info) {
+        assert(info);
         PyCArgObject *carg;
-        assert(dict->paramfunc);
-        /* If it has an stgdict, it is a CDataObject */
-        carg = dict->paramfunc((CDataObject *)obj);
+        assert(info->paramfunc);
+        /* If it has an stginfo, it is a CDataObject */
+        carg = info->paramfunc((CDataObject *)obj);
         if (carg == NULL)
             return -1;
         pa->ffi_type = carg->pffi_type;
@@ -681,7 +686,6 @@ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa)
         return 0;
     }
 
-    ctypes_state *st = GLOBAL_STATE();
     if (PyCArg_CheckExact(st, obj)) {
         PyCArgObject *carg = (PyCArgObject *)obj;
         pa->ffi_type = carg->pffi_type;
@@ -778,26 +782,34 @@ int can_return_struct_as_sint64(size_t s)
 #endif
 
 
+// returns NULL with exception set on error
 ffi_type *_ctypes_get_ffi_type(PyObject *obj)
 {
-    StgDictObject *dict;
-    if (obj == NULL)
+    if (obj == NULL) {
         return &ffi_type_sint;
-    dict = PyType_stgdict(obj);
-    if (dict == NULL)
+    }
+
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, obj, &info) < 0) {
+        return NULL;
+    }
+
+    if (info == NULL) {
         return &ffi_type_sint;
+    }
 #if defined(MS_WIN32) && !defined(_WIN32_WCE)
     /* This little trick works correctly with MSVC.
        It returns small structures in registers
     */
-    if (dict->ffi_type_pointer.type == FFI_TYPE_STRUCT) {
-        if (can_return_struct_as_int(dict->ffi_type_pointer.size))
+    if (info->ffi_type_pointer.type == FFI_TYPE_STRUCT) {
+        if (can_return_struct_as_int(info->ffi_type_pointer.size))
             return &ffi_type_sint32;
-        else if (can_return_struct_as_sint64 (dict->ffi_type_pointer.size))
+        else if (can_return_struct_as_sint64 (info->ffi_type_pointer.size))
             return &ffi_type_sint64;
     }
 #endif
-    return &dict->ffi_type_pointer;
+    return &info->ffi_type_pointer;
 }
 
 
@@ -983,7 +995,6 @@ static int _call_function_pointer(int flags,
  */
 static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker)
 {
-    StgDictObject *dict;
     PyObject *retval, *v;
 
     if (restype == NULL)
@@ -993,17 +1004,22 @@ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker)
         Py_RETURN_NONE;
     }
 
-    dict = PyType_stgdict(restype);
-    if (dict == NULL)
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, restype, &info) < 0) {
+        return NULL;
+    }
+    if (info == NULL) {
         return PyObject_CallFunction(restype, "i", *(int *)result);
+    }
 
-    if (dict->getfunc && !_ctypes_simple_instance(restype)) {
-        retval = dict->getfunc(result, dict->size);
+    if (info->getfunc && !_ctypes_simple_instance(restype)) {
+        retval = info->getfunc(result, info->size);
         /* If restype is py_object (detected by comparing getfunc with
            O_get), we have to call Py_DECREF because O_get has already
            called Py_INCREF.
         */
-        if (dict->getfunc == _ctypes_get_fielddesc("O")->getfunc) {
+        if (info->getfunc == _ctypes_get_fielddesc("O")->getfunc) {
             Py_DECREF(retval);
         }
     } else
@@ -1240,6 +1256,9 @@ PyObject *_ctypes_callproc(PPROC pProc,
     } else {
         rtype = _ctypes_get_ffi_type(restype);
     }
+    if (!rtype) {
+        goto cleanup;
+    }
 
     resbuf = alloca(max(rtype->size, sizeof(ffi_arg)));
 
@@ -1683,13 +1702,16 @@ PyDoc_STRVAR(sizeof_doc,
 static PyObject *
 sizeof_func(PyObject *self, PyObject *obj)
 {
-    StgDictObject *dict;
+    ctypes_state *st = GLOBAL_STATE();
 
-    dict = PyType_stgdict(obj);
-    if (dict) {
-        return PyLong_FromSsize_t(dict->size);
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, obj, &info) < 0) {
+        return NULL;
     }
-    ctypes_state *st = GLOBAL_STATE();
+    if (info) {
+        return PyLong_FromSsize_t(info->size);
+    }
+
     if (CDataObject_Check(st, obj)) {
         return PyLong_FromSsize_t(((CDataObject *)obj)->b_size);
     }
@@ -1706,16 +1728,14 @@ PyDoc_STRVAR(alignment_doc,
 static PyObject *
 align_func(PyObject *self, PyObject *obj)
 {
-    StgDictObject *dict;
-
-    dict = PyType_stgdict(obj);
-    if (dict)
-        return PyLong_FromSsize_t(dict->align);
-
-    dict = PyObject_stgdict(obj);
-    if (dict)
-        return PyLong_FromSsize_t(dict->align);
-
+   ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromAny(st, obj, &info) < 0) {
+        return NULL;
+    }
+    if (info) {
+        return PyLong_FromSsize_t(info->align);
+    }
     PyErr_SetString(PyExc_TypeError,
                     "no alignment info");
     return NULL;
@@ -1824,7 +1844,6 @@ static PyObject *
 resize(PyObject *self, PyObject *args)
 {
     CDataObject *obj;
-    StgDictObject *dict;
     Py_ssize_t size;
 
     if (!PyArg_ParseTuple(args,
@@ -1832,16 +1851,21 @@ resize(PyObject *self, PyObject *args)
                           &obj, &size))
         return NULL;
 
-    dict = PyObject_stgdict((PyObject *)obj);
-    if (dict == NULL) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    int result = PyStgInfo_FromObject(st, (PyObject *)obj, &info);
+    if (result < 0) {
+        return NULL;
+    }
+    if (info == NULL) {
         PyErr_SetString(PyExc_TypeError,
                         "expected ctypes instance");
         return NULL;
     }
-    if (size < dict->size) {
+    if (size < info->size) {
         PyErr_Format(PyExc_ValueError,
                      "minimum size is %zd",
-                     dict->size);
+                     info->size);
         return NULL;
     }
     if (obj->b_needsfree == 0) {
@@ -2004,28 +2028,30 @@ create_pointer_inst(PyObject *module, PyObject *arg)
 static PyObject *
 buffer_info(PyObject *self, PyObject *arg)
 {
-    StgDictObject *dict = PyType_stgdict(arg);
     PyObject *shape;
     Py_ssize_t i;
 
-    if (dict == NULL)
-        dict = PyObject_stgdict(arg);
-    if (dict == NULL) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *info;
+    if (PyStgInfo_FromAny(st, arg, &info) < 0) {
+        return NULL;
+    }
+    if (info == NULL) {
         PyErr_SetString(PyExc_TypeError,
                         "not a ctypes type or object");
         return NULL;
     }
-    shape = PyTuple_New(dict->ndim);
+    shape = PyTuple_New(info->ndim);
     if (shape == NULL)
         return NULL;
-    for (i = 0; i < (int)dict->ndim; ++i)
-        PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(dict->shape[i]));
+    for (i = 0; i < (int)info->ndim; ++i)
+        PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(info->shape[i]));
 
     if (PyErr_Occurred()) {
         Py_DECREF(shape);
         return NULL;
     }
-    return Py_BuildValue("siN", dict->format, dict->ndim, shape);
+    return Py_BuildValue("siN", info->format, info->ndim, shape);
 }
 
 
index 1d5b0b14bc39e546d393038af297fbb5b329183b..16b66382bfe33f5c366c55c98c05fe51de9673b4 100644 (file)
@@ -54,7 +54,6 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index,
     Py_ssize_t size, align;
     SETFUNC setfunc = NULL;
     GETFUNC getfunc = NULL;
-    StgDictObject *dict;
     int fieldtype;
 #define NO_BITFIELD 0
 #define NEW_BITFIELD 1
@@ -66,21 +65,27 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index,
     self = (CFieldObject *)tp->tp_alloc(tp, 0);
     if (self == NULL)
         return NULL;
-    dict = PyType_stgdict(desc);
-    if (!dict) {
+
+    StgInfo *info;
+    if (PyStgInfo_FromType(st, desc, &info) < 0) {
+        Py_DECREF(self);
+        return NULL;
+    }
+    if (!info) {
         PyErr_SetString(PyExc_TypeError,
                         "has no _stginfo_");
         Py_DECREF(self);
         return NULL;
     }
+
     if (bitsize /* this is a bitfield request */
         && *pfield_size /* we have a bitfield open */
 #ifdef MS_WIN32
         /* MSVC, GCC with -mms-bitfields */
-        && dict->size * 8 == *pfield_size
+        && info->size * 8 == *pfield_size
 #else
         /* GCC */
-        && dict->size * 8 <= *pfield_size
+        && info->size * 8 <= *pfield_size
 #endif
         && (*pbitofs + bitsize) <= *pfield_size) {
         /* continue bit field */
@@ -88,8 +93,8 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index,
 #ifndef MS_WIN32
     } else if (bitsize /* this is a bitfield request */
         && *pfield_size /* we have a bitfield open */
-        && dict->size * 8 >= *pfield_size
-        && (*pbitofs + bitsize) <= dict->size * 8) {
+        && info->size * 8 >= *pfield_size
+        && (*pbitofs + bitsize) <= info->size * 8) {
         /* expand bit field */
         fieldtype = EXPAND_BITFIELD;
 #endif
@@ -97,7 +102,7 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index,
         /* start new bitfield */
         fieldtype = NEW_BITFIELD;
         *pbitofs = 0;
-        *pfield_size = dict->size * 8;
+        *pfield_size = info->size * 8;
     } else {
         /* not a bit field */
         fieldtype = NO_BITFIELD;
@@ -105,29 +110,37 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index,
         *pfield_size = 0;
     }
 
-    size = dict->size;
+    size = info->size;
     proto = desc;
 
     /*  Field descriptors for 'c_char * n' are be scpecial cased to
         return a Python string instead of an Array object instance...
     */
     if (PyCArrayTypeObject_Check(st, proto)) {
-        StgDictObject *adict = PyType_stgdict(proto);
-        StgDictObject *idict;
-        if (adict && adict->proto) {
-            idict = PyType_stgdict(adict->proto);
-            if (!idict) {
+        StgInfo *ainfo;
+        if (PyStgInfo_FromType(st, proto, &ainfo) < 0) {
+            Py_DECREF(self);
+            return NULL;
+        }
+
+        if (ainfo && ainfo->proto) {
+            StgInfo *iinfo;
+            if (PyStgInfo_FromType(st, ainfo->proto, &iinfo) < 0) {
+                Py_DECREF(self);
+                return NULL;
+            }
+            if (!iinfo) {
                 PyErr_SetString(PyExc_TypeError,
                                 "has no _stginfo_");
                 Py_DECREF(self);
                 return NULL;
             }
-            if (idict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
+            if (iinfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
                 struct fielddesc *fd = _ctypes_get_fielddesc("s");
                 getfunc = fd->getfunc;
                 setfunc = fd->setfunc;
             }
-            if (idict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
+            if (iinfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
                 struct fielddesc *fd = _ctypes_get_fielddesc("U");
                 getfunc = fd->getfunc;
                 setfunc = fd->setfunc;
@@ -151,9 +164,9 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index,
         /* fall through */
     case NO_BITFIELD:
         if (pack)
-            align = min(pack, dict->align);
+            align = min(pack, info->align);
         else
-            align = dict->align;
+            align = info->align;
         if (align && *poffset % align) {
             Py_ssize_t delta = align - (*poffset % align);
             *psize += delta;
@@ -171,10 +184,10 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index,
         break;
 
     case EXPAND_BITFIELD:
-        *poffset += dict->size - *pfield_size/8;
-        *psize += dict->size - *pfield_size/8;
+        *poffset += info->size - *pfield_size/8;
+        *psize += info->size - *pfield_size/8;
 
-        *pfield_size = dict->size * 8;
+        *pfield_size = info->size * 8;
 
         if (big_endian)
             self->size = (bitsize << 16) + *pfield_size - *pbitofs - bitsize;
index 02f48a9ed55843cf974a64f65c4232f2752c9b91..d7d725a4fdf669c1f635a51d5d2f90a43624cc9e 100644 (file)
@@ -41,7 +41,6 @@ typedef struct {
     PyTypeObject *PyCArg_Type;
     PyTypeObject *PyCField_Type;
     PyTypeObject *PyCThunk_Type;
-    PyTypeObject *PyCStgDict_Type;
     PyTypeObject *StructParam_Type;
     PyTypeObject *PyCStructType_Type;
     PyTypeObject *UnionType_Type;
@@ -59,6 +58,7 @@ typedef struct {
 #ifdef MS_WIN32
     PyTypeObject *PyComError_Type;
 #endif
+    PyTypeObject *PyCType_Type;
 } ctypes_state;
 
 extern ctypes_state global_state;
@@ -144,7 +144,7 @@ typedef struct {
     CThunkObject *thunk;
     PyObject *callable;
 
-    /* These two fields will override the ones in the type's stgdict if
+    /* These two fields will override the ones in the type's stginfo if
        they are set */
     PyObject *converters;
     PyObject *argtypes;
@@ -158,17 +158,12 @@ typedef struct {
     PyObject *paramflags;
 } PyCFuncPtrObject;
 
-extern PyTypeObject PyCStgDict_Type;
-#define PyCStgDict_CheckExact(st, v)        Py_IS_TYPE((v), (st)->PyCStgDict_Type)
-#define PyCStgDict_Check(st, v)     PyObject_TypeCheck((v), (st)->PyCStgDict_Type)
-
-extern int PyCStructUnionType_update_stgdict(PyObject *fields, PyObject *type, int isStruct);
+extern int PyCStructUnionType_update_stginfo(PyObject *fields, PyObject *type, int isStruct);
 extern int PyType_stginfo(PyTypeObject *self, Py_ssize_t *psize, Py_ssize_t *palign, Py_ssize_t *plength);
 extern int PyObject_stginfo(PyObject *self, Py_ssize_t *psize, Py_ssize_t *palign, Py_ssize_t *plength);
 
 
 
-extern PyTypeObject PyCData_Type;
 #define CDataObject_CheckExact(st, v)  Py_IS_TYPE((v), (st)->PyCData_Type)
 #define CDataObject_Check(st, v)       PyObject_TypeCheck((v), (st)->PyCData_Type)
 #define _CDataObject_HasExternalBuffer(v)  ((v)->b_ptr != (char *)&(v)->b_value)
@@ -188,10 +183,6 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index,
 extern PyObject *PyCData_AtAddress(PyObject *type, void *buf);
 extern PyObject *PyCData_FromBytes(PyObject *type, char *data, Py_ssize_t length);
 
-extern PyTypeObject PyCArray_Type;
-extern PyTypeObject PyCPointer_Type;
-extern PyTypeObject PyCFuncPtr_Type;
-
 #define PyCArrayTypeObject_Check(st, v)   PyObject_TypeCheck((v), (st)->PyCArrayType_Type)
 #define ArrayObject_Check(st, v)          PyObject_TypeCheck((v), (st)->PyCArray_Type)
 #define PointerObject_Check(st, v)        PyObject_TypeCheck((v), (st)->PyCPointer_Type)
@@ -231,45 +222,22 @@ typedef struct {
     int anonymous;
 } CFieldObject;
 
-/* A subclass of PyDictObject, used as the instance dictionary of ctypes
-   metatypes */
-typedef struct {
-    PyDictObject dict;          /* first part identical to PyDictObject */
-/* The size and align fields are unneeded, they are in ffi_type as well.  As
-   an experiment shows, it's trivial to get rid of them, the only thing to
-   remember is that in PyCArrayType_new the ffi_type fields must be filled in -
-   so far it was unneeded because libffi doesn't support arrays at all
-   (because they are passed as pointers to function calls anyway).  But it's
-   too much risk to change that now, and there are other fields which doesn't
-   belong into this structure anyway.  Maybe in ctypes 2.0... (ctypes 2000?)
-*/
-    Py_ssize_t size;            /* number of bytes */
-    Py_ssize_t align;           /* alignment requirements */
-    Py_ssize_t length;          /* number of fields */
-    ffi_type ffi_type_pointer;
-    PyObject *proto;            /* Only for Pointer/ArrayObject */
-    SETFUNC setfunc;            /* Only for simple objects */
-    GETFUNC getfunc;            /* Only for simple objects */
-    PARAMFUNC paramfunc;
+/****************************************************************
+ StgInfo
 
-    /* Following fields only used by PyCFuncPtrType_Type instances */
-    PyObject *argtypes;         /* tuple of CDataObjects */
-    PyObject *converters;       /* tuple([t.from_param for t in argtypes]) */
-    PyObject *restype;          /* CDataObject or NULL */
-    PyObject *checker;
-    int flags;                  /* calling convention and such */
+ Since Python 3.13, ctypes-specific type information is stored in the
+ corresponding type object, in a `StgInfo` struct accessed by the helpers
+ below.
+ Before that, each type's `tp_dict` was set to a dict *subclass* that included
+ the fields that are now in StgInfo. The mechanism was called "StgDict"; a few
+ references to that name might remain.
 
-    /* pep3118 fields, pointers need PyMem_Free */
-    char *format;
-    int ndim;
-    Py_ssize_t *shape;
-/*      Py_ssize_t *strides;    */ /* unused in ctypes */
-/*      Py_ssize_t *suboffsets; */ /* unused in ctypes */
+ Functions for accessing StgInfo are `static inline` for performance;
+ see later in this file.
 
-} StgDictObject;
+ ****************************************************************
 
-/****************************************************************
- StgDictObject fields
+ StgInfo fields
 
  setfunc and getfunc is only set for simple data types, it is copied from the
  corresponding fielddesc entry.  These are functions to set and get the value
@@ -280,11 +248,11 @@ typedef struct {
  object.
 
  Probably all the magic ctypes methods (like from_param) should have C
- callable wrappers in the StgDictObject.  For simple data type, for example,
+ callable wrappers in the StgInfo.  For simple data type, for example,
  the fielddesc table could have entries for C codec from_param functions or
  other methods as well, if a subtype overrides this method in Python at
  construction time, or assigns to it later, tp_setattro should update the
- StgDictObject function to a generic one.
+ StgInfo function to a generic one.
 
  Currently, PyCFuncPtr types have 'converters' and 'checker' entries in their
  type dict.  They are only used to cache attributes from other entries, which
@@ -308,13 +276,33 @@ typedef struct {
 
 *****************************************************************/
 
-/* May return NULL, but does not set an exception! */
-extern StgDictObject *PyType_stgdict(PyObject *obj);
+typedef struct {
+    int initialized;
+    Py_ssize_t size;            /* number of bytes */
+    Py_ssize_t align;           /* alignment requirements */
+    Py_ssize_t length;          /* number of fields */
+    ffi_type ffi_type_pointer;
+    PyObject *proto;            /* Only for Pointer/ArrayObject */
+    SETFUNC setfunc;            /* Only for simple objects */
+    GETFUNC getfunc;            /* Only for simple objects */
+    PARAMFUNC paramfunc;
+
+    /* Following fields only used by PyCFuncPtrType_Type instances */
+    PyObject *argtypes;         /* tuple of CDataObjects */
+    PyObject *converters;       /* tuple([t.from_param for t in argtypes]) */
+    PyObject *restype;          /* CDataObject or NULL */
+    PyObject *checker;
+    int flags;                  /* calling convention and such */
 
-/* May return NULL, but does not set an exception! */
-extern StgDictObject *PyObject_stgdict(PyObject *self);
+    /* pep3118 fields, pointers need PyMem_Free */
+    char *format;
+    int ndim;
+    Py_ssize_t *shape;
+/*      Py_ssize_t *strides;    */ /* unused in ctypes */
+/*      Py_ssize_t *suboffsets; */ /* unused in ctypes */
+} StgInfo;
 
-extern int PyCStgDict_clone(StgDictObject *src, StgDictObject *dst);
+extern int PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info);
 
 typedef int(* PPROC)(void);
 
@@ -416,8 +404,74 @@ void *Py_ffi_closure_alloc(size_t size, void** codeloc);
 #define Py_ffi_closure_alloc ffi_closure_alloc
 #endif
 
-/*
- Local Variables:
- compile-command: "python setup.py -q build install --home ~"
- End:
-*/
+
+/****************************************************************
+ * Accessing StgInfo -- these are inlined for performance reasons.
+ */
+
+// `PyStgInfo_From**` functions get a PyCTypeDataObject.
+// These return -1 on error, 0 if "not found", 1 on OK.
+// (Currently, these do not return -1 in practice. This might change
+// in the future.)
+
+//
+// Common helper:
+static inline int
+_stginfo_from_type(ctypes_state *state, PyTypeObject *type, StgInfo **result)
+{
+    *result = NULL;
+    if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) {
+        // not a ctypes class.
+        return 0;
+    }
+    StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type);
+    assert(info != NULL);
+    if (!info->initialized) {
+        // StgInfo is not initialized. This happens in abstract classes.
+        return 0;
+    }
+    *result = info;
+    return 1;
+}
+// from a type:
+static inline int
+PyStgInfo_FromType(ctypes_state *state, PyObject *type, StgInfo **result)
+{
+    return _stginfo_from_type(state, (PyTypeObject *)type, result);
+}
+// from an instance:
+static inline int
+PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result)
+{
+    return _stginfo_from_type(state, Py_TYPE(obj), result);
+}
+// from either a type or an instance:
+static inline int
+PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result)
+{
+    if (PyType_Check(obj)) {
+        return _stginfo_from_type(state, (PyTypeObject *)obj, result);
+    }
+    return _stginfo_from_type(state, Py_TYPE(obj), result);
+}
+
+// Initialize StgInfo on a newly created type
+static inline StgInfo *
+PyStgInfo_Init(ctypes_state *state, PyTypeObject *type)
+{
+    if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) {
+        PyErr_Format(PyExc_SystemError,
+                     "'%s' is not a ctypes class.",
+                     type->tp_name);
+        return NULL;
+    }
+    StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type);
+    if (info->initialized) {
+        PyErr_Format(PyExc_SystemError,
+                     "StgInfo of '%s' is already initialized.",
+                     type->tp_name);
+        return NULL;
+    }
+    info->initialized = 1;
+    return info;
+}
index 32ee414a7a0cddd008844f6b22101e3d7236ce55..8666ded5c2b3f21cd870e4944cab30bfe2d1fab2 100644 (file)
 #endif
 #include "ctypes.h"
 
-/******************************************************************/
-/*
-  StdDict - a dictionary subclass, containing additional C accessible fields
-
-  XXX blabla more
-*/
-
-/* Seems we need this, otherwise we get problems when calling
- * PyDict_SetItem() (ma_lookup is NULL)
+/* This file relates to StgInfo -- type-specific information for ctypes.
+ * See ctypes.h for details.
  */
-static int
-PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds)
-{
-    if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0)
-        return -1;
-    self->format = NULL;
-    self->ndim = 0;
-    self->shape = NULL;
-    return 0;
-}
 
-static int
-PyCStgDict_clear(StgDictObject *self)
+int
+PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info)
 {
-    Py_CLEAR(self->proto);
-    Py_CLEAR(self->argtypes);
-    Py_CLEAR(self->converters);
-    Py_CLEAR(self->restype);
-    Py_CLEAR(self->checker);
-    return 0;
-}
+    Py_ssize_t size;
 
-static void
-PyCStgDict_dealloc(StgDictObject *self)
-{
-    PyCStgDict_clear(self);
-    PyMem_Free(self->format);
-    PyMem_Free(self->shape);
-    PyMem_Free(self->ffi_type_pointer.elements);
-    PyDict_Type.tp_dealloc((PyObject *)self);
-}
+    PyMem_Free(dst_info->ffi_type_pointer.elements);
+    PyMem_Free(dst_info->format);
+    dst_info->format = NULL;
+    PyMem_Free(dst_info->shape);
+    dst_info->shape = NULL;
+    dst_info->ffi_type_pointer.elements = NULL;
 
-static PyObject *
-PyCStgDict_sizeof(StgDictObject *self, void *unused)
-{
-    Py_ssize_t res;
-
-    res = _PyDict_SizeOf((PyDictObject *)self);
-    res += sizeof(StgDictObject) - sizeof(PyDictObject);
-    if (self->format)
-        res += strlen(self->format) + 1;
-    res += self->ndim * sizeof(Py_ssize_t);
-    if (self->ffi_type_pointer.elements)
-        res += (self->length + 1) * sizeof(ffi_type *);
-    return PyLong_FromSsize_t(res);
-}
+    memcpy(dst_info, src_info, sizeof(StgInfo));
 
-int
-PyCStgDict_clone(StgDictObject *dst, StgDictObject *src)
-{
-    char *d, *s;
-    Py_ssize_t size;
+    Py_XINCREF(dst_info->proto);
+    Py_XINCREF(dst_info->argtypes);
+    Py_XINCREF(dst_info->converters);
+    Py_XINCREF(dst_info->restype);
+    Py_XINCREF(dst_info->checker);
 
-    PyCStgDict_clear(dst);
-    PyMem_Free(dst->ffi_type_pointer.elements);
-    PyMem_Free(dst->format);
-    dst->format = NULL;
-    PyMem_Free(dst->shape);
-    dst->shape = NULL;
-    dst->ffi_type_pointer.elements = NULL;
-
-    d = (char *)dst;
-    s = (char *)src;
-    memcpy(d + sizeof(PyDictObject),
-           s + sizeof(PyDictObject),
-           sizeof(StgDictObject) - sizeof(PyDictObject));
-
-    Py_XINCREF(dst->proto);
-    Py_XINCREF(dst->argtypes);
-    Py_XINCREF(dst->converters);
-    Py_XINCREF(dst->restype);
-    Py_XINCREF(dst->checker);
-
-    if (src->format) {
-        dst->format = PyMem_Malloc(strlen(src->format) + 1);
-        if (dst->format == NULL) {
+    if (src_info->format) {
+        dst_info->format = PyMem_Malloc(strlen(src_info->format) + 1);
+        if (dst_info->format == NULL) {
             PyErr_NoMemory();
             return -1;
         }
-        strcpy(dst->format, src->format);
+        strcpy(dst_info->format, src_info->format);
     }
-    if (src->shape) {
-        dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src->ndim);
-        if (dst->shape == NULL) {
+    if (src_info->shape) {
+        dst_info->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src_info->ndim);
+        if (dst_info->shape == NULL) {
             PyErr_NoMemory();
             return -1;
         }
-        memcpy(dst->shape, src->shape,
-               sizeof(Py_ssize_t) * src->ndim);
+        memcpy(dst_info->shape, src_info->shape,
+               sizeof(Py_ssize_t) * src_info->ndim);
     }
 
-    if (src->ffi_type_pointer.elements == NULL)
+    if (src_info->ffi_type_pointer.elements == NULL)
         return 0;
-    size = sizeof(ffi_type *) * (src->length + 1);
-    dst->ffi_type_pointer.elements = PyMem_Malloc(size);
-    if (dst->ffi_type_pointer.elements == NULL) {
+    size = sizeof(ffi_type *) * (src_info->length + 1);
+    dst_info->ffi_type_pointer.elements = PyMem_Malloc(size);
+    if (dst_info->ffi_type_pointer.elements == NULL) {
         PyErr_NoMemory();
         return -1;
     }
-    memcpy(dst->ffi_type_pointer.elements,
-           src->ffi_type_pointer.elements,
+    memcpy(dst_info->ffi_type_pointer.elements,
+           src_info->ffi_type_pointer.elements,
            size);
     return 0;
 }
 
-static struct PyMethodDef PyCStgDict_methods[] = {
-    {"__sizeof__", (PyCFunction)PyCStgDict_sizeof, METH_NOARGS},
-    {NULL, NULL}                /* sentinel */
-};
-
-PyTypeObject PyCStgDict_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "StgDict",
-    sizeof(StgDictObject),
-    0,
-    (destructor)PyCStgDict_dealloc,             /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    0,                                          /* tp_repr */
-    0,                                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    0,                                          /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
-    0,                                          /* tp_doc */
-    0,                                          /* tp_traverse */
-    0,                                          /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    PyCStgDict_methods,                         /* tp_methods */
-    0,                                          /* tp_members */
-    0,                                          /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    (initproc)PyCStgDict_init,                          /* tp_init */
-    0,                                          /* tp_alloc */
-    0,                                          /* tp_new */
-    0,                                          /* tp_free */
-};
-
-/* May return NULL, but does not set an exception! */
-StgDictObject *
-PyType_stgdict(PyObject *obj)
-{
-    PyTypeObject *type;
-
-    if (!PyType_Check(obj)) {
-        return NULL;
-    }
-    ctypes_state *st = GLOBAL_STATE();
-    type = (PyTypeObject *)obj;
-    if (!type->tp_dict || !PyCStgDict_CheckExact(st, type->tp_dict)) {
-        return NULL;
-    }
-    return (StgDictObject *)type->tp_dict;
-}
-
-/* May return NULL, but does not set an exception! */
-/*
-  This function should be as fast as possible, so we don't call PyType_stgdict
-  above but inline the code, and avoid the PyType_Check().
-*/
-StgDictObject *
-PyObject_stgdict(PyObject *self)
-{
-    PyTypeObject *type = Py_TYPE(self);
-    ctypes_state *st = GLOBAL_STATE();
-    if (!type->tp_dict || !PyCStgDict_CheckExact(st, type->tp_dict)) {
-        return NULL;
-    }
-    return (StgDictObject *)type->tp_dict;
-}
-
 /* descr is the descriptor for a field marked as anonymous.  Get all the
  _fields_ descriptors from descr->proto, create new descriptors with offset
  and index adjusted, and stuff them into type.
@@ -372,12 +233,11 @@ _ctypes_alloc_format_padding(const char *prefix, Py_ssize_t padding)
 
 /*
   Retrieve the (optional) _pack_ attribute from a type, the _fields_ attribute,
-  and create an StgDictObject.  Used for Structure and Union subclasses.
+  and initialize StgInfo.  Used for Structure and Union subclasses.
 */
 int
-PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct)
+PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct)
 {
-    StgDictObject *stgdict, *basedict;
     Py_ssize_t len, offset, size, align, i;
     Py_ssize_t union_size, total_align, aligned_size;
     Py_ssize_t field_size = 0;
@@ -456,90 +316,97 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
         return -1;
     }
 
-    stgdict = PyType_stgdict(type);
-    if (!stgdict) {
+    ctypes_state *st = GLOBAL_STATE();
+    StgInfo *stginfo;
+    if (PyStgInfo_FromType(st, type, &stginfo) < 0) {
+        return -1;
+    }
+    if (!stginfo) {
         PyErr_SetString(PyExc_TypeError,
                         "ctypes state is not initialized");
         return -1;
     }
+
     /* If this structure/union is already marked final we cannot assign
        _fields_ anymore. */
 
-    if (stgdict->flags & DICTFLAG_FINAL) {/* is final ? */
+    if (stginfo->flags & DICTFLAG_FINAL) {/* is final ? */
         PyErr_SetString(PyExc_AttributeError,
                         "_fields_ is final");
         return -1;
     }
 
-    if (stgdict->format) {
-        PyMem_Free(stgdict->format);
-        stgdict->format = NULL;
+    if (stginfo->format) {
+        PyMem_Free(stginfo->format);
+        stginfo->format = NULL;
     }
 
-    if (stgdict->ffi_type_pointer.elements)
-        PyMem_Free(stgdict->ffi_type_pointer.elements);
+    if (stginfo->ffi_type_pointer.elements)
+        PyMem_Free(stginfo->ffi_type_pointer.elements);
 
-    basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base);
-    if (basedict) {
-        stgdict->flags |= (basedict->flags &
+    StgInfo *baseinfo;
+    if (PyStgInfo_FromType(st, (PyObject *)((PyTypeObject *)type)->tp_base,
+                           &baseinfo) < 0) {
+        return -1;
+    }
+    if (baseinfo) {
+        stginfo->flags |= (baseinfo->flags &
                            (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD));
     }
     if (!isStruct) {
-        stgdict->flags |= TYPEFLAG_HASUNION;
+        stginfo->flags |= TYPEFLAG_HASUNION;
     }
-    if (basedict) {
-        size = offset = basedict->size;
-        align = basedict->align;
+    if (baseinfo) {
+        size = offset = baseinfo->size;
+        align = baseinfo->align;
         union_size = 0;
         total_align = align ? align : 1;
         total_align = max(total_align, forced_alignment);
-        stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT;
-        stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, basedict->length + len + 1);
-        if (stgdict->ffi_type_pointer.elements == NULL) {
+        stginfo->ffi_type_pointer.type = FFI_TYPE_STRUCT;
+        stginfo->ffi_type_pointer.elements = PyMem_New(ffi_type *, baseinfo->length + len + 1);
+        if (stginfo->ffi_type_pointer.elements == NULL) {
             PyErr_NoMemory();
             return -1;
         }
-        memset(stgdict->ffi_type_pointer.elements, 0,
-               sizeof(ffi_type *) * (basedict->length + len + 1));
-        if (basedict->length > 0) {
-            memcpy(stgdict->ffi_type_pointer.elements,
-                   basedict->ffi_type_pointer.elements,
-                   sizeof(ffi_type *) * (basedict->length));
+        memset(stginfo->ffi_type_pointer.elements, 0,
+               sizeof(ffi_type *) * (baseinfo->length + len + 1));
+        if (baseinfo->length > 0) {
+            memcpy(stginfo->ffi_type_pointer.elements,
+                   baseinfo->ffi_type_pointer.elements,
+                   sizeof(ffi_type *) * (baseinfo->length));
         }
-        ffi_ofs = basedict->length;
+        ffi_ofs = baseinfo->length;
     } else {
         offset = 0;
         size = 0;
         align = 0;
         union_size = 0;
         total_align = forced_alignment;
-        stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT;
-        stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, len + 1);
-        if (stgdict->ffi_type_pointer.elements == NULL) {
+        stginfo->ffi_type_pointer.type = FFI_TYPE_STRUCT;
+        stginfo->ffi_type_pointer.elements = PyMem_New(ffi_type *, len + 1);
+        if (stginfo->ffi_type_pointer.elements == NULL) {
             PyErr_NoMemory();
             return -1;
         }
-        memset(stgdict->ffi_type_pointer.elements, 0,
+        memset(stginfo->ffi_type_pointer.elements, 0,
                sizeof(ffi_type *) * (len + 1));
         ffi_ofs = 0;
     }
 
-    assert(stgdict->format == NULL);
+    assert(stginfo->format == NULL);
     if (isStruct) {
-        stgdict->format = _ctypes_alloc_format_string(NULL, "T{");
+        stginfo->format = _ctypes_alloc_format_string(NULL, "T{");
     } else {
         /* PEP3118 doesn't support union. Use 'B' for bytes. */
-        stgdict->format = _ctypes_alloc_format_string(NULL, "B");
+        stginfo->format = _ctypes_alloc_format_string(NULL, "B");
     }
-    if (stgdict->format == NULL)
+    if (stginfo->format == NULL)
         return -1;
 
-    ctypes_state *st = GLOBAL_STATE();
     for (i = 0; i < len; ++i) {
         PyObject *name = NULL, *desc = NULL;
         PyObject *pair = PySequence_GetItem(fields, i);
         PyObject *prop;
-        StgDictObject *dict;
         int bitsize = 0;
 
         if (!pair || !PyArg_ParseTuple(pair, "UO|i", &name, &desc, &bitsize)) {
@@ -551,22 +418,28 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
         if (PyCArrayTypeObject_Check(st, desc)) {
             arrays_seen = 1;
         }
-        dict = PyType_stgdict(desc);
-        if (dict == NULL) {
+
+        StgInfo *info;
+        if (PyStgInfo_FromType(st, desc, &info) < 0) {
+            Py_DECREF(pair);
+            return -1;
+        }
+        if (info == NULL) {
             Py_DECREF(pair);
             PyErr_Format(PyExc_TypeError,
                          "second item in _fields_ tuple (index %zd) must be a C type",
                          i);
             return -1;
         }
-        stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer;
-        if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
-            stgdict->flags |= TYPEFLAG_HASPOINTER;
-        stgdict->flags |= dict->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD);
-        dict->flags |= DICTFLAG_FINAL; /* mark field type final */
+
+        stginfo->ffi_type_pointer.elements[ffi_ofs + i] = &info->ffi_type_pointer;
+        if (info->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
+            stginfo->flags |= TYPEFLAG_HASPOINTER;
+        stginfo->flags |= info->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD);
+        info->flags |= DICTFLAG_FINAL; /* mark field type final */
         if (PyTuple_Size(pair) == 3) { /* bits specified */
-            stgdict->flags |= TYPEFLAG_HASBITFIELD;
-            switch(dict->ffi_type_pointer.type) {
+            stginfo->flags |= TYPEFLAG_HASBITFIELD;
+            switch(info->ffi_type_pointer.type) {
             case FFI_TYPE_UINT8:
             case FFI_TYPE_UINT16:
             case FFI_TYPE_UINT32:
@@ -577,8 +450,8 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
             case FFI_TYPE_SINT8:
             case FFI_TYPE_SINT16:
             case FFI_TYPE_SINT32:
-                if (dict->getfunc != _ctypes_get_fielddesc("c")->getfunc
-                    && dict->getfunc != _ctypes_get_fielddesc("u")->getfunc
+                if (info->getfunc != _ctypes_get_fielddesc("c")->getfunc
+                    && info->getfunc != _ctypes_get_fielddesc("u")->getfunc
                     )
                     break;
                 /* else fall through */
@@ -589,7 +462,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
                 Py_DECREF(pair);
                 return -1;
             }
-            if (bitsize <= 0 || bitsize > dict->size * 8) {
+            if (bitsize <= 0 || bitsize > info->size * 8) {
                 PyErr_SetString(PyExc_ValueError,
                                 "number of bits invalid for bit field");
                 Py_DECREF(pair);
@@ -599,7 +472,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
             bitsize = 0;
 
         if (isStruct) {
-            const char *fieldfmt = dict->format ? dict->format : "B";
+            const char *fieldfmt = info->format ? info->format : "B";
             const char *fieldname = PyUnicode_AsUTF8(name);
             char *ptr;
             Py_ssize_t len;
@@ -629,10 +502,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
             padding = ((CFieldObject *)prop)->offset - last_size;
 
             if (padding > 0) {
-                ptr = stgdict->format;
-                stgdict->format = _ctypes_alloc_format_padding(ptr, padding);
+                ptr = stginfo->format;
+                stginfo->format = _ctypes_alloc_format_padding(ptr, padding);
                 PyMem_Free(ptr);
-                if (stgdict->format == NULL) {
+                if (stginfo->format == NULL) {
                     Py_DECREF(pair);
                     Py_DECREF(prop);
                     return -1;
@@ -650,17 +523,17 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
             }
             sprintf(buf, "%s:%s:", fieldfmt, fieldname);
 
-            ptr = stgdict->format;
-            if (dict->shape != NULL) {
-                stgdict->format = _ctypes_alloc_format_string_with_shape(
-                    dict->ndim, dict->shape, stgdict->format, buf);
+            ptr = stginfo->format;
+            if (info->shape != NULL) {
+                stginfo->format = _ctypes_alloc_format_string_with_shape(
+                    info->ndim, info->shape, stginfo->format, buf);
             } else {
-                stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf);
+                stginfo->format = _ctypes_alloc_format_string(stginfo->format, buf);
             }
             PyMem_Free(ptr);
             PyMem_Free(buf);
 
-            if (stgdict->format == NULL) {
+            if (stginfo->format == NULL) {
                 Py_DECREF(pair);
                 Py_DECREF(prop);
                 return -1;
@@ -704,29 +577,29 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
         /* Pad up to the full size of the struct */
         padding = aligned_size - size;
         if (padding > 0) {
-            ptr = stgdict->format;
-            stgdict->format = _ctypes_alloc_format_padding(ptr, padding);
+            ptr = stginfo->format;
+            stginfo->format = _ctypes_alloc_format_padding(ptr, padding);
             PyMem_Free(ptr);
-            if (stgdict->format == NULL) {
+            if (stginfo->format == NULL) {
                 return -1;
             }
         }
 
-        ptr = stgdict->format;
-        stgdict->format = _ctypes_alloc_format_string(stgdict->format, "}");
+        ptr = stginfo->format;
+        stginfo->format = _ctypes_alloc_format_string(stginfo->format, "}");
         PyMem_Free(ptr);
-        if (stgdict->format == NULL)
+        if (stginfo->format == NULL)
             return -1;
     }
 
-    stgdict->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align,
+    stginfo->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align,
                                                            Py_ssize_t,
                                                            unsigned short);
-    stgdict->ffi_type_pointer.size = aligned_size;
+    stginfo->ffi_type_pointer.size = aligned_size;
 
-    stgdict->size = aligned_size;
-    stgdict->align = total_align;
-    stgdict->length = ffi_ofs + len;
+    stginfo->size = aligned_size;
+    stginfo->align = total_align;
+    stginfo->length = ffi_ofs + len;
 
 /*
  * The value of MAX_STRUCT_SIZE depends on the platform Python is running on.
@@ -817,7 +690,6 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
         for (i = 0; i < len; ++i) {
             PyObject *name, *desc;
             PyObject *pair = PySequence_GetItem(fields, i);
-            StgDictObject *dict;
             int bitsize = 0;
 
             if (pair == NULL) {
@@ -829,25 +701,34 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
                 Py_DECREF(pair);
                 return -1;
             }
-            dict = PyType_stgdict(desc);
-            if (dict == NULL) {
+
+            StgInfo *info;
+            if (PyStgInfo_FromType(st, desc, &info) < 0) {
+                Py_DECREF(pair);
+                return -1;
+            }
+            if (info == NULL) {
                 Py_DECREF(pair);
                 PyErr_Format(PyExc_TypeError,
                     "second item in _fields_ tuple (index %zd) must be a C type",
                     i);
                 return -1;
             }
+
             if (!PyCArrayTypeObject_Check(st, desc)) {
                 /* Not an array. Just need an ffi_type pointer. */
                 num_ffi_type_pointers++;
             }
             else {
                 /* It's an array. */
-                Py_ssize_t length = dict->length;
-                StgDictObject *edict;
+                Py_ssize_t length = info->length;
 
-                edict = PyType_stgdict(dict->proto);
-                if (edict == NULL) {
+                StgInfo *einfo;
+                if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) {
+                    Py_DECREF(pair);
+                    return -1;
+                }
+                if (einfo == NULL) {
                     Py_DECREF(pair);
                     PyErr_Format(PyExc_TypeError,
                         "second item in _fields_ tuple (index %zd) must be a C type",
@@ -895,9 +776,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
         if (num_ffi_types > 0) {
             memset(structs, 0, num_ffi_types * sizeof(ffi_type));
         }
-        if (ffi_ofs && (basedict != NULL)) {
+        if (ffi_ofs && (baseinfo != NULL)) {
             memcpy(element_types,
-                basedict->ffi_type_pointer.elements,
+                baseinfo->ffi_type_pointer.elements,
                 ffi_ofs * sizeof(ffi_type *));
         }
         element_index = ffi_ofs;
@@ -906,7 +787,6 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
         for (i = 0; i < len; ++i) {
             PyObject *name, *desc;
             PyObject *pair = PySequence_GetItem(fields, i);
-            StgDictObject *dict;
             int bitsize = 0;
 
             if (pair == NULL) {
@@ -926,9 +806,16 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
                 PyMem_Free(type_block);
                 return -1;
             }
-            dict = PyType_stgdict(desc);
+
+            StgInfo *info;
+            if (PyStgInfo_FromType(st, desc, &info) < 0) {
+                Py_DECREF(pair);
+                PyMem_Free(type_block);
+                return -1;
+            }
+
             /* Possibly this check could be avoided, but see above comment. */
-            if (dict == NULL) {
+            if (info == NULL) {
                 Py_DECREF(pair);
                 PyMem_Free(type_block);
                 PyErr_Format(PyExc_TypeError,
@@ -936,17 +823,21 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
                              i);
                 return -1;
             }
+
             assert(element_index < (ffi_ofs + len)); /* will be used below */
             if (!PyCArrayTypeObject_Check(st, desc)) {
                 /* Not an array. Just copy over the element ffi_type. */
-                element_types[element_index++] = &dict->ffi_type_pointer;
+                element_types[element_index++] = &info->ffi_type_pointer;
             }
             else {
-                Py_ssize_t length = dict->length;
-                StgDictObject *edict;
-
-                edict = PyType_stgdict(dict->proto);
-                if (edict == NULL) {
+                Py_ssize_t length = info->length;
+                StgInfo *einfo;
+                if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) {
+                    Py_DECREF(pair);
+                    PyMem_Free(type_block);
+                    return -1;
+                }
+                if (einfo == NULL) {
                     Py_DECREF(pair);
                     PyMem_Free(type_block);
                     PyErr_Format(PyExc_TypeError,
@@ -955,15 +846,15 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
                     return -1;
                 }
                 element_types[element_index++] = &structs[struct_index];
-                structs[struct_index].size = length * edict->ffi_type_pointer.size;
-                structs[struct_index].alignment = edict->ffi_type_pointer.alignment;
+                structs[struct_index].size = length * einfo->ffi_type_pointer.size;
+                structs[struct_index].alignment = einfo->ffi_type_pointer.alignment;
                 structs[struct_index].type = FFI_TYPE_STRUCT;
                 structs[struct_index].elements = &dummy_types[dummy_index];
                 ++struct_index;
                 /* Copy over the element's type, length times. */
                 while (length > 0) {
                     assert(dummy_index < (num_ffi_type_pointers));
-                    dummy_types[dummy_index++] = &edict->ffi_type_pointer;
+                    dummy_types[dummy_index++] = &einfo->ffi_type_pointer;
                     length--;
                 }
                 assert(dummy_index < (num_ffi_type_pointers));
@@ -977,19 +868,19 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
          * Replace the old elements with the new, taking into account
          * base class elements where necessary.
          */
-        assert(stgdict->ffi_type_pointer.elements);
-        PyMem_Free(stgdict->ffi_type_pointer.elements);
-        stgdict->ffi_type_pointer.elements = element_types;
+        assert(stginfo->ffi_type_pointer.elements);
+        PyMem_Free(stginfo->ffi_type_pointer.elements);
+        stginfo->ffi_type_pointer.elements = element_types;
     }
 
     /* We did check that this flag was NOT set above, it must not
        have been set until now. */
-    if (stgdict->flags & DICTFLAG_FINAL) {
+    if (stginfo->flags & DICTFLAG_FINAL) {
         PyErr_SetString(PyExc_AttributeError,
                         "Structure or union cannot contain itself");
         return -1;
     }
-    stgdict->flags |= DICTFLAG_FINAL;
+    stginfo->flags |= DICTFLAG_FINAL;
 
     return MakeAnonFields(type);
 }