aboutsummaryrefslogblamecommitdiffstats
path: root/contrib/python/zope.interface/py3/zope/interface/_zope_interface_coptimizations.c
blob: 45810ec617692edce6a264a1e1e392e5ba311825 (plain) (tree)
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
























                                                                               




                                                



                                                                                
     









                                                                        
 



                           






                                                                
                                                                     






                                                                       
                                        
      
 

























                                                                         
 
 
                       
 
                               
     





















                                                  
 
             
 


                                                                            
 











                                                                          
 






                                                       
 






                                                                          
 
                                                                        
 

                                                                             
 
                                                           
 
                                                                             
 
                                                           
 



                                                                    
     
                                   
 
                            
 
                              
 





                           
                 






                                                                              

                            







                                                                 
     




                                                        
                                                 
 





                                                            








                                
                  









                                
                    
 
                                         
                                         
                                     



                              
 



                                                       
 
                      
 


                             
 

                                               
 
                                                  
 
                   
 

                                            
 



                                                                  
 


                                           
 
                                                                            
 

                                  
 





                                                                         
 

                    
 

                                                                          
 
                                               
 


                                           
 
                                                                            
 






                                                              
 
                    
 














                                          
 









                                                                                  
  












                                                                              
                                           







                                                   
  
     
 










                                      
  

                                     
                                            
                                       
  
      
 




                                                        
 

                            
 







                                     
 









                                                            
 



                                                                            
     

                                      
 





















































                                                                    



                                                   
                                              





                                
                               



                      
                                         
                    






















                                                          

                                    





                                                                       
  










































                                                                               
  






















































                                                                                













                                          


                                                                    
 













                                           
 
                                                                            
 
                                                             
 


                                        
         











                                                                         
     

                       
     














                                                                               
         
                           
     
                    
 
                       
 



















                                                           
                                                            
 

                                                         
 

                                                         
 





                                                            
 

                           
 








                                                                            
 











                                                                                
 


                                                
 


                             
 





                                                                 
                
                    






















                                                              




                                                 
                                       





                                          






                                


                           





                                

         





                                                                    



                                                           
                                                         
                        
                                                              
                                      
                                                               















                                                                         
                             








                                                                          






                              
 






                                                                          
 

                                                                         
  


                                                                         
 
                    
 



















                                                      
 
     
 















                                                                             
 




                                         
 
      
 








                      
 












                                                            

          
                  
 


                            

           
                    
 





                                         






                                    
                                       
 

                       
 












                                         
                                         
 











                                                      
     
                    
 

                                                       
 
                    
 
                              
 

                                              
 
                                              
 
                 




















                                                                    











                                                                  
 




                                                                              
 

                                            
 


                                            
 







                                                                             
         



                                                    
         

                            
     


                                                
     
                  
 
 













                                                                                
 
                                                             
 










                                                                     




                            
 
                             
 
                                                                  
                    
     

















                                                              
         
                          
     
                  
 
                                                    
 










                                                                                
 
                                                              

















                                                                     




                                 
 


                       
 
                                        
 


                                                                  
 

                                          
 




















                                                                     
 
                                                                           
 
                        
 
                    
 
 













                                                                              
 
                                                                 
 
                                                         
 










                                                                              
 
                                                                 















                                                                 
                                                            
 
                             
 


                                                            
 
                               
 











                                                                          
         



                                                         
         

                            
     
                  
 

                                                      
 
                                                             
 

                                                                               
 
                                                















                                                                     
                                                                
 
                             
 


                                                            
 







                                               
 



                                                                              
         



                                                         
         

                            
     
                  
 

                                                          
 
                                                             
 

                                                          
 
                                                    
 






















                                                                           
  


















                                                                       
  
     
 









                                      
 




                                         
 






                                    
          
                                                 
 

                                              
 





                                        
           
                    
 





                                         






                                                                           













                                                                               
         
                                                     
     
                       
 
                                           
 
                     
 
                   
 

                                                     
 


                                    
 


                                                                      
 







                                                       
     
                          
 
                       







                                                    
                 
 
                             
 

                                                                        
 

                                                           
 



                                                         
 
                         
     


                                                                          
 
                              
 
                                                   
 
                                                                                
 

                                                                               
 
                          
 
                                                                  
 
                                                    
 
                                                                                
 

                                                                               
 
                          
 
                                                                   
 
                                                         
 
                                                                              
 

                                                                             
 
                          
 
                                                                      
 
                                                         
 
                                                                              
 

                                                                             
 
                          
 
                                                                      
 
                                                      
 
                                                             
 

                                                          
 
                          
 
                                                     
 
                                                          
 
                                                             
 

                                                          
 
                          
 
                                                         
 

























                                                           
  
                                                                          
 
                    
 














                                                    
 
     
 









                                                                             
  



                                         
  
 





                                                                        
 

















                                                               
 


                                                                
 



















                                                      
 


                                                                 
 





                                                         
 


                                      
 








































































































                                                                            
                





































































































































































































































































































































































































































































































                                                                              

              
                                           
                  
 


                            
/*###########################################################################
 #
 # Copyright (c) 2003 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
 ############################################################################*/

#include "Python.h"
#include "structmember.h"

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif

#define TYPE(O) ((PyTypeObject*)(O))
#define OBJECT(O) ((PyObject*)(O))
#define CLASSIC(O) ((PyClassObject*)(O))
#ifndef Py_TYPE
#define Py_TYPE(o) ((o)->ob_type)
#endif

#define PyNative_FromString PyUnicode_FromString

#define ASSURE_DICT(N)                                                         \
    if (N == NULL) {                                                           \
        N = PyDict_New();                                                      \
        if (N == NULL)                                                         \
            return NULL;                                                       \
    }

/*
 *  Don't use heap-allocated types for Python < 3.11:  the API needed
 *  to find the dynamic module, 'PyType_GetModuleByDef', was added then.
 */
#if PY_VERSION_HEX < 0x030b0000
#define USE_STATIC_TYPES 1
#define USE_HEAP_TYPES 0
#else
#define USE_STATIC_TYPES 0
#define USE_HEAP_TYPES 1
#endif

#define BASETYPE_FLAGS \
    Py_TPFLAGS_DEFAULT | \
    Py_TPFLAGS_BASETYPE | \
    Py_TPFLAGS_HAVE_GC

#if PY_VERSION_HEX >= 0x030c0000
/* Add MANAGED_WEAKREF flag for Python >= 3.12, and don't define
 * the '.tp_weaklistoffset' slot.
 *
 * See: https://docs.python.org/3/c-api/typeobj.html
 *      #c.PyTypeObject.tp_weaklistoffset
 */
#define USE_EXPLICIT_WEAKREFLIST 0
#define WEAKREFTYPE_FLAGS BASETYPE_FLAGS | Py_TPFLAGS_MANAGED_WEAKREF
#else
/* No MANAGED_WEAKREF flag for Python < 3.12, and therefore define
 * the '.tp_weaklistoffset' slot, and the member whose offset it holds.
 *
 * See: https://docs.python.org/3/c-api/typeobj.html
 *      #c.PyTypeObject.tp_weaklistoffset
 */
#define USE_EXPLICIT_WEAKREFLIST 1
#define WEAKREFTYPE_FLAGS BASETYPE_FLAGS
#endif

/* Static strings, used to invoke PyObject_GetAttr (only in hot paths) */
static PyObject *str__class__ = NULL;
static PyObject *str__conform__ = NULL;
static PyObject *str__dict__ = NULL;
static PyObject *str__module__ = NULL;
static PyObject *str__name__ = NULL;
static PyObject *str__providedBy__ = NULL;
static PyObject *str__provides__ = NULL;
static PyObject *str__self__ = NULL;
static PyObject *str_generation = NULL;
static PyObject *str_registry = NULL;
static PyObject *strro = NULL;

/* Static strings, used to invoke PyObject_CallMethodObjArgs */
static PyObject *str_call_conform = NULL;
static PyObject *str_uncached_lookup = NULL;
static PyObject *str_uncached_lookupAll = NULL;
static PyObject *str_uncached_subscriptions = NULL;
static PyObject *strchanged = NULL;
static PyObject *str__adapt__ = NULL;

/* Static strings, used to invoke PyObject_GetItem
 *
 * We cannot use PyDict_GetItemString, because the '__dict__' we get
 * from our types can be a 'types.mappingproxy', which causes a segfault.
 */
static PyObject* str__implemented__;


static int
define_static_strings()
{
    if (str__class__ != NULL) {
        return 0;
    }

#define DEFINE_STATIC_STRING(S)                  \
    if (!(str##S = PyUnicode_FromString(#S)))    \
    return -1

    DEFINE_STATIC_STRING(__class__);
    DEFINE_STATIC_STRING(__conform__);
    DEFINE_STATIC_STRING(__dict__);
    DEFINE_STATIC_STRING(__module__);
    DEFINE_STATIC_STRING(__name__);
    DEFINE_STATIC_STRING(__providedBy__);
    DEFINE_STATIC_STRING(__provides__);
    DEFINE_STATIC_STRING(__self__);
    DEFINE_STATIC_STRING(_generation);
    DEFINE_STATIC_STRING(_registry);
    DEFINE_STATIC_STRING(ro);
    DEFINE_STATIC_STRING(__implemented__);
    DEFINE_STATIC_STRING(_call_conform);
    DEFINE_STATIC_STRING(_uncached_lookup);
    DEFINE_STATIC_STRING(_uncached_lookupAll);
    DEFINE_STATIC_STRING(_uncached_subscriptions);
    DEFINE_STATIC_STRING(changed);
    DEFINE_STATIC_STRING(__adapt__);
#undef DEFINE_STATIC_STRING

    return 0;
}

/* Public module-scope functions, forward-declared here for type methods. */
static PyObject *implementedBy(PyObject* module, PyObject *cls);
static PyObject *getObjectSpecification(PyObject *module, PyObject *ob);
static PyObject *providedBy(PyObject *module, PyObject *ob);

/*
 * Utility functions, forward-declared here for type methods.
 */
static PyObject* _get_module(PyTypeObject *typeobj);
static PyObject* _get_adapter_hooks(PyTypeObject *typeobj);
static PyTypeObject* _get_specification_base_class(PyTypeObject *typeobj);
static PyTypeObject* _get_interface_base_class(PyTypeObject *typeobj);

#if USE_STATIC_TYPES
/*
 *  Global used by static IB__adapt
 */
static PyObject*       adapter_hooks = NULL;

/*
 *  Globals imported from 'zope.interface.declarations'
 */
static int imported_declarations = 0;
static PyObject* BuiltinImplementationSpecifications;
static PyObject* empty;
static PyObject* fallback;
static PyTypeObject *Implements;

/* Import zope.interface.declarations and store results in global statics.
 *
 * Static alternative to '_zic_state_load_declarations' below.
 */
static int
import_declarations(void)
{
    PyObject *declarations, *i;

    declarations = PyImport_ImportModule("zope.interface.declarations");
    if (declarations == NULL) { return -1; }

    BuiltinImplementationSpecifications = PyObject_GetAttrString(
                        declarations, "BuiltinImplementationSpecifications");
    if (BuiltinImplementationSpecifications == NULL) { return -1; }

    empty = PyObject_GetAttrString(declarations, "_empty");
    if (empty == NULL) { return -1; }

    fallback = PyObject_GetAttrString(declarations, "implementedByFallback");
    if (fallback == NULL) { return -1;}

    i = PyObject_GetAttrString(declarations, "Implements");
    if (i == NULL) { return -1; }

    if (! PyType_Check(i)) {
        PyErr_SetString(
            PyExc_TypeError,
            "zope.interface.declarations.Implements is not a type");
        return -1;
    }

    Implements = (PyTypeObject *)i;

    Py_DECREF(declarations);

    imported_declarations = 1;
    return 0;
}

#endif

/*
 *  SpecificationBase class
 */
typedef struct
{
    PyObject_HEAD
    /*
      In the past, these fields were stored in the __dict__
      and were technically allowed to contain any Python object, though
      other type checks would fail or fall back to generic code paths if
      they didn't have the expected type. We preserve that behaviour and don't
      make any assumptions about contents.
    */
    PyObject* _implied;
#if USE_EXPLICIT_WEAKREFLIST
    PyObject* weakreflist;
#endif
    /*
      The remainder aren't used in C code but must be stored here
      to prevent instance layout conflicts.
    */
    PyObject* _dependents;
    PyObject* _bases;
    PyObject* _v_attrs;
    PyObject* __iro__;
    PyObject* __sro__;
} SB;

/*
  We know what the fields are *supposed* to define, but
  they could have anything, so we need to traverse them.
*/
static int
SB_traverse(SB* self, visitproc visit, void* arg)
{
/* Visit our 'tp_type' only on Python >= 3.9, per
 * https://docs.python.org/3/howto/isolating-extensions.html
 * #tp-traverse-in-python-3-8-and-lower
 */
#if USE_HEAP_TYPES && PY_VERSION_HEX > 0x03090000
    Py_VISIT(Py_TYPE(self));
#endif
    Py_VISIT(self->_implied);
    Py_VISIT(self->_dependents);
    Py_VISIT(self->_bases);
    Py_VISIT(self->_v_attrs);
    Py_VISIT(self->__iro__);
    Py_VISIT(self->__sro__);
    return 0;
}

static int
SB_clear(SB* self)
{
    Py_CLEAR(self->_implied);
    Py_CLEAR(self->_dependents);
    Py_CLEAR(self->_bases);
    Py_CLEAR(self->_v_attrs);
    Py_CLEAR(self->__iro__);
    Py_CLEAR(self->__sro__);
    return 0;
}

static void
SB_dealloc(SB* self)
{
    PyObject_GC_UnTrack((PyObject*)self);
    PyObject_ClearWeakRefs(OBJECT(self));
    PyTypeObject* tp = Py_TYPE(self);
    SB_clear(self);
    tp->tp_free(OBJECT(self));
#if USE_HEAP_TYPES
    Py_DECREF(tp);
#endif
}

static char SB_extends__doc__[] =
  "Test whether a specification is or extends another";

static PyObject*
SB_extends(SB* self, PyObject* other)
{
    PyObject* implied;

    implied = self->_implied;
    if (implied == NULL) {
        return NULL;
    }

    if (PyDict_GetItem(implied, other) != NULL)
        Py_RETURN_TRUE;
    Py_RETURN_FALSE;
}

static PyObject*
SB__call__(SB* self, PyObject* args, PyObject* kw)
{
    PyObject* spec;

    if (!PyArg_ParseTuple(args, "O", &spec))
        return NULL;
    return SB_extends(self, spec);
}

static char SB_providedBy__doc__[] =
  "Test whether an interface is implemented by the specification";

static PyObject*
SB_providedBy(PyObject* self, PyObject* ob)
{
    PyObject *decl;
    PyObject *item;
    PyObject *module;
    PyTypeObject *specification_base_class;

    module = _get_module(Py_TYPE(self));
    specification_base_class = _get_specification_base_class(Py_TYPE(self));

    decl = providedBy(module, ob);
    if (decl == NULL)
        return NULL;

    if (PyObject_TypeCheck(decl, specification_base_class))
        item = SB_extends((SB*)decl, self);
    else
        /* decl is probably a security proxy.  We have to go the long way
           around.
        */
        item = PyObject_CallFunctionObjArgs(decl, self, NULL);

    Py_DECREF(decl);
    return item;
}

static char SB_implementedBy__doc__[] =
  "Test whether the specification is implemented by a class or factory.\n"
  "Raise TypeError if argument is neither a class nor a callable.";

static PyObject*
SB_implementedBy(PyObject* self, PyObject* cls)
{
    PyObject *decl;
    PyObject *item;
    PyObject *module;
    PyTypeObject *specification_base_class;

    module = _get_module(Py_TYPE(self));
    specification_base_class = _get_specification_base_class(Py_TYPE(self));

    decl = implementedBy(module, cls);
    if (decl == NULL)
        return NULL;

    if (PyObject_TypeCheck(decl, specification_base_class))
        item = SB_extends((SB*)decl, self);
    else
        item = PyObject_CallFunctionObjArgs(decl, self, NULL);

    Py_DECREF(decl);
    return item;
}

static struct PyMethodDef SB_methods[] = {
    { "providedBy",
      (PyCFunction)SB_providedBy,
      METH_O,
      SB_providedBy__doc__ },
    { "implementedBy",
      (PyCFunction)SB_implementedBy,
      METH_O,
      SB_implementedBy__doc__ },
    { "isOrExtends",
      (PyCFunction)SB_extends,
      METH_O,
      SB_extends__doc__ },

    { NULL, NULL } /* sentinel */
};

static PyMemberDef SB_members[] = {
    { "_implied", T_OBJECT_EX, offsetof(SB, _implied), 0, "" },
    { "_dependents", T_OBJECT_EX, offsetof(SB, _dependents), 0, "" },
    { "_bases", T_OBJECT_EX, offsetof(SB, _bases), 0, "" },
    { "_v_attrs", T_OBJECT_EX, offsetof(SB, _v_attrs), 0, "" },
    { "__iro__", T_OBJECT_EX, offsetof(SB, __iro__), 0, "" },
    { "__sro__", T_OBJECT_EX, offsetof(SB, __sro__), 0, "" },
#if USE_EXPLICIT_WEAKREFLIST
    { "__weaklistoffset__", T_PYSSIZET, offsetof(SB, weakreflist), READONLY, "" },
#endif
    { NULL },
};

static char SB__name__[] = "_zope_interface_coptimizations.SpecificationBase";
static char SB__doc__[] = "Base type for Specification objects";

#if USE_STATIC_TYPES

/*
 * Static type: SpecificationBase
 */

static PyTypeObject SB_type_def = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name           = SB__name__,
    .tp_doc            = SB__doc__,
    .tp_basicsize      = sizeof(SB),
    .tp_flags          = WEAKREFTYPE_FLAGS,
    .tp_call           = (ternaryfunc)SB__call__,
    .tp_traverse       = (traverseproc)SB_traverse,
    .tp_clear          = (inquiry)SB_clear,
    .tp_dealloc        = (destructor)SB_dealloc,
#if USE_EXPLICIT_WEAKREFLIST
    .tp_weaklistoffset = offsetof(SB, weakreflist),
#endif
    .tp_methods        = SB_methods,
    .tp_members        = SB_members,
};

#else

/*
 * Heap-based type: SpecificationBase
 */
static PyType_Slot SB_type_slots[] = {
    {Py_tp_doc,         SB__doc__},
    {Py_tp_call,        SB__call__},
    {Py_tp_traverse,    SB_traverse},
    {Py_tp_clear,       SB_clear},
    {Py_tp_dealloc,     SB_dealloc},
    {Py_tp_methods,     SB_methods},
    {Py_tp_members,     SB_members},
    {0,                 NULL}
};

static PyType_Spec SB_type_spec = {
    .name               = SB__name__,
    .basicsize          = sizeof(SB),
    .flags              = WEAKREFTYPE_FLAGS,
    .slots              = SB_type_slots
};

#endif

/*
 *  ObjectSpecificationDescriptor class
 */
#if USE_HEAP_TYPES
static int
OSD_traverse(PyObject* self, visitproc visit, void* arg)
{
    Py_VISIT(Py_TYPE(self));
    return 0;
}

static void
OSD_dealloc(PyObject* self)
{
    PyObject_GC_UnTrack(self);
    PyTypeObject *tp = Py_TYPE(self);
    tp->tp_free(OBJECT(self));
    Py_DECREF(tp);
}
#endif

static PyObject*
OSD_descr_get(PyObject* self, PyObject* inst, PyObject* cls)
{
    PyObject* provides;
    PyObject *module;

    module = _get_module(Py_TYPE(self));

    if (inst == NULL) {
        return getObjectSpecification(module, cls);
    }

    provides = PyObject_GetAttr(inst, str__provides__);
    /* Return __provides__ if we got it, or return NULL and propagate
     * non-AttributeError. */
    if (provides != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) {
        return provides;
    }

    PyErr_Clear();

    return implementedBy(module, cls);
}

static char OSD__name__[] = (
    "_zope_interface_coptimizations.ObjectSpecificationDescriptor");
static char OSD__doc__[] = "Object Specification Descriptor";

#if USE_STATIC_TYPES

/*
 * Static type: ObjectSpecificationDescriptor
 */

static PyTypeObject OSD_type_def = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name            = OSD__name__,
    .tp_doc             = OSD__doc__,
    /* No GC for the static version */
    .tp_flags           = Py_TPFLAGS_DEFAULT |
                          Py_TPFLAGS_BASETYPE,
    .tp_descr_get       = (descrgetfunc)OSD_descr_get,
  /*.tp_traverse,       = OSD_traverse},     not reqd for static */
  /*.tp_dealloc,        = OSD_dealloc},      not reqd for static */
};

#else

/*
 * Heap type: ObjectSpecificationDescriptor
 */
static PyType_Slot OSD_type_slots[] = {
    {Py_tp_doc,         OSD__doc__},
    {Py_tp_descr_get,   OSD_descr_get},
    {Py_tp_traverse,    OSD_traverse},
    {Py_tp_dealloc,     OSD_dealloc},
    {0,                 NULL}
};

static PyType_Spec OSD_type_spec = {
    .name               = OSD__name__,
    .basicsize          = 0,
    .flags              = BASETYPE_FLAGS,
    .slots              = OSD_type_slots
};

#endif

/*
 *  ClassProvidesBase class
 */
typedef struct
{
    SB spec;
    /* These members are handled generically, as for SB members. */
    PyObject* _cls;
    PyObject* _implements;
} CPB;

static int
CPB_traverse(CPB* self, visitproc visit, void* arg)
{
    Py_VISIT(self->_cls);
    Py_VISIT(self->_implements);
    return SB_traverse((SB*)self, visit, arg);
}

static int
CPB_clear(CPB* self)
{
    Py_CLEAR(self->_cls);
    Py_CLEAR(self->_implements);
    return SB_clear((SB*)self);
}

static void
CPB_dealloc(CPB* self)
{
    PyObject_GC_UnTrack((PyObject*)self);
    CPB_clear(self);
    SB_dealloc((SB*)self); /* handles decrefing tp */
}

static PyObject*
CPB_descr_get(CPB* self, PyObject* inst, PyObject* cls)
{
    PyObject* implements;

    if (self->_cls == NULL)
        return NULL;

    if (cls == self->_cls) {
        if (inst == NULL) {
            Py_INCREF(self);
            return OBJECT(self);
        }

        implements = self->_implements;
        Py_XINCREF(implements);
        return implements;
    }

    PyErr_SetString(PyExc_AttributeError, "__provides__");
    return NULL;
}

static PyMemberDef CPB_members[] = {
    { "_cls", T_OBJECT_EX, offsetof(CPB, _cls), 0, "Defining class." },
    { "_implements",
      T_OBJECT_EX,
      offsetof(CPB, _implements),
      0,
      "Result of implementedBy." },
    { NULL }
};

static char CPB__name__[] = "_zope_interface_coptimizations.ClassProvidesBase";
static char CPB__doc__[] = "C Base class for ClassProvides";

#if USE_STATIC_TYPES

/*
 * Static type: ClassProvidesBase
 */

static PyTypeObject CPB_type_def = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name            = CPB__name__,
    .tp_doc             = CPB__doc__,
    .tp_base            = &SB_type_def,
    .tp_basicsize       = sizeof(CPB),
    .tp_flags           = BASETYPE_FLAGS,
    .tp_descr_get       = (descrgetfunc)CPB_descr_get,
    .tp_traverse        = (traverseproc)CPB_traverse,
    .tp_clear           = (inquiry)CPB_clear,
    .tp_dealloc         = (destructor)CPB_dealloc,
    .tp_members         = CPB_members,
};

#else

/*
 * Heap type: ClassProvidesBase
 */
static PyType_Slot CPB_type_slots[] = {
    {Py_tp_doc,         CPB__doc__},
    {Py_tp_descr_get,   CPB_descr_get},
    {Py_tp_traverse,    CPB_traverse},
    {Py_tp_clear,       CPB_clear},
    {Py_tp_dealloc,     CPB_dealloc},
    {Py_tp_members,     CPB_members},
    /* tp_base cannot be set as a slot -- pass to PyType_FromModuleAndSpec */
    {0,                 NULL}
};

static PyType_Spec CPB_type_spec = {
    .name               = CPB__name__,
    .basicsize          = sizeof(CPB),
    .flags              = BASETYPE_FLAGS,
    .slots              = CPB_type_slots
};

#endif

/*
 *  InterfaceBase class
 */

typedef struct
{
    SB spec;
    PyObject* __name__;
    PyObject* __module__;
    Py_hash_t _v_cached_hash;
} IB;

static int
IB_traverse(IB* self, visitproc visit, void* arg)
{
    Py_VISIT(self->__name__);
    Py_VISIT(self->__module__);
    return SB_traverse((SB*)self, visit, arg);
}

static int
IB_clear(IB* self)
{
    Py_CLEAR(self->__name__);
    Py_CLEAR(self->__module__);
    return SB_clear((SB*)self);
}

static void
IB_dealloc(IB* self)
{
    PyObject_GC_UnTrack((PyObject*)self);
    IB_clear(self);
    SB_dealloc((SB*)self); /* handles decrefing tp */
}

static int
IB__init__(IB* self, PyObject* args, PyObject* kwargs)
{
    static char* kwlist[] = { "__name__", "__module__", NULL };
    PyObject* module = NULL;
    PyObject* name = NULL;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwargs, "|OO:InterfaceBase.__init__", kwlist, &name, &module)) {
        return -1;
    }
    IB_clear(self);
    self->__module__ = module ? module : Py_None;
    Py_INCREF(self->__module__);
    self->__name__ = name ? name : Py_None;
    Py_INCREF(self->__name__);
    return 0;
}

/*
    def __adapt__(self, obj):
        """Adapt an object to the receiver
        """
        if self.providedBy(obj):
            return obj

        for hook in adapter_hooks:
            adapter = hook(self, obj)
            if adapter is not None:
                return adapter


*/
const char IB__adapt____doc__[] = "Adapt an object to the receiver";

static PyObject*
IB__adapt__(PyObject* self, PyObject* obj)
{
    PyObject *decl;
    PyObject *args;
    PyObject *adapter;
    PyObject *module;
    PyObject *adapter_hooks;
    PyTypeObject *specification_base_class;
    int implements;
    int i;
    int l;

    module = _get_module(Py_TYPE(self));

    decl = providedBy(module, obj);
    if (decl == NULL)
        return NULL;

    specification_base_class = _get_specification_base_class(Py_TYPE(self));

    if (PyObject_TypeCheck(decl, specification_base_class)) {
        PyObject* implied;

        implied = ((SB*)decl)->_implied;
        if (implied == NULL) {
            Py_DECREF(decl);
            return NULL;
        }

        implements = PyDict_GetItem(implied, self) != NULL;
        Py_DECREF(decl);
    } else {
        /* decl is probably a security proxy.  We have to go the long way
           around.
        */
        PyObject* r;
        r = PyObject_CallFunctionObjArgs(decl, self, NULL);
        Py_DECREF(decl);
        if (r == NULL)
            return NULL;
        implements = PyObject_IsTrue(r);
        Py_DECREF(r);
    }

    if (implements) {
        Py_INCREF(obj);
        return obj;
    }

    args = PyTuple_New(2);
    if (args == NULL) { return NULL; }

    Py_INCREF(self);
    PyTuple_SET_ITEM(args, 0, self);

    Py_INCREF(obj);
    PyTuple_SET_ITEM(args, 1, obj);

    adapter_hooks = _get_adapter_hooks(Py_TYPE(self));
    l = PyList_GET_SIZE(adapter_hooks);
    for (i = 0; i < l; i++) {
        adapter = PyObject_CallObject(PyList_GET_ITEM(adapter_hooks, i), args);
        if (adapter == NULL || adapter != Py_None) {
            Py_DECREF(args);
            return adapter;
        }
        Py_DECREF(adapter);
    }

    Py_DECREF(args);

    Py_INCREF(Py_None);
    return Py_None;
}

/*
    def __call__(self, obj, alternate=_marker):
        try:
            conform = obj.__conform__
        except AttributeError: # pylint:disable=bare-except
            conform = None

        if conform is not None:
            adapter = self._call_conform(conform)
            if adapter is not None:
                return adapter

        adapter = self.__adapt__(obj)

        if adapter is not None:
            return adapter
        if alternate is not _marker:
            return alternate
        raise TypeError("Could not adapt", obj, self)

*/
static PyObject*
IB__call__(PyObject* self, PyObject* args, PyObject* kwargs)
{
    PyObject *conform, *obj, *alternate, *adapter;
    static char* kwlist[] = { "obj", "alternate", NULL };
    conform = obj = alternate = adapter = NULL;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwargs, "O|O", kwlist, &obj, &alternate))
        return NULL;

    conform = PyObject_GetAttr(obj, str__conform__);
    if (conform == NULL) {
        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
            /* Propagate non-AttributeErrors */
            return NULL;
        }
        PyErr_Clear();

        Py_INCREF(Py_None);
        conform = Py_None;
    }

    if (conform != Py_None) {
        adapter =
          PyObject_CallMethodObjArgs(self, str_call_conform, conform, NULL);
        Py_DECREF(conform);
        if (adapter == NULL || adapter != Py_None)
            return adapter;
        Py_DECREF(adapter);
    } else {
        Py_DECREF(conform);
    }

    /* We differ from the Python code here. For speed, instead of always calling
       self.__adapt__(), we check to see if the type has defined it. Checking in
       the dict for __adapt__ isn't sufficient because there's no cheap way to
       tell if it's the __adapt__ that InterfaceBase itself defines (our type
       will *never* be InterfaceBase, we're always subclassed by
       InterfaceClass). Instead, we cooperate with InterfaceClass in Python to
       set a flag in a new subclass when this is necessary. */
    if (PyDict_GetItemString(self->ob_type->tp_dict, "_CALL_CUSTOM_ADAPT")) {
        /* Doesn't matter what the value is. Simply being present is enough. */
        adapter = PyObject_CallMethodObjArgs(self, str__adapt__, obj, NULL);
    } else {
        adapter = IB__adapt__(self, obj);
    }

    if (adapter == NULL || adapter != Py_None) {
        return adapter;
    }
    Py_DECREF(adapter);

    if (alternate != NULL) {
        Py_INCREF(alternate);
        return alternate;
    }

    adapter = Py_BuildValue("sOO", "Could not adapt", obj, self);
    if (adapter != NULL) {
        PyErr_SetObject(PyExc_TypeError, adapter);
        Py_DECREF(adapter);
    }
    return NULL;
}

static Py_hash_t
IB__hash__(IB* self)
{
    PyObject* tuple;
    if (!self->__module__) {
        PyErr_SetString(PyExc_AttributeError, "__module__");
        return -1;
    }
    if (!self->__name__) {
        PyErr_SetString(PyExc_AttributeError, "__name__");
        return -1;
    }

    if (self->_v_cached_hash) {
        return self->_v_cached_hash;
    }

    tuple = PyTuple_Pack(2, self->__name__, self->__module__);
    if (!tuple) {
        return -1;
    }
    self->_v_cached_hash = PyObject_Hash(tuple);
    Py_CLEAR(tuple);
    return self->_v_cached_hash;
}

static PyObject*
IB_richcompare(IB* self, PyObject* other, int op)
{
    PyObject* othername;
    PyObject* othermod;
    PyObject* oresult;
    PyTypeObject* interface_base_class;
    IB* otherib;
    int result;

    otherib = NULL;
    oresult = othername = othermod = NULL;

    if (OBJECT(self) == other) {
        switch (op) {
            case Py_EQ:
            case Py_LE:
            case Py_GE:
                Py_RETURN_TRUE;
                break;
            case Py_NE:
                Py_RETURN_FALSE;
        }
    }

    if (other == Py_None) {
        switch (op) {
            case Py_LT:
            case Py_LE:
            case Py_NE:
                Py_RETURN_TRUE;
            default:
                Py_RETURN_FALSE;
        }
    }

    interface_base_class = _get_interface_base_class(Py_TYPE(self));
    if (interface_base_class == NULL) {
        oresult = Py_NotImplemented;
        goto cleanup;
    }

    if (PyObject_TypeCheck(other, interface_base_class)) {
        // This branch borrows references. No need to clean
        // up if otherib is not null.
        otherib = (IB*)other;
        othername = otherib->__name__;
        othermod = otherib->__module__;
    } else {
        othername = PyObject_GetAttr(other, str__name__);
        if (othername) {
            othermod = PyObject_GetAttr(other, str__module__);
        }
        if (!othername || !othermod) {
            if (PyErr_Occurred() &&
                PyErr_ExceptionMatches(PyExc_AttributeError)) {
                PyErr_Clear();
                oresult = Py_NotImplemented;
            }
            goto cleanup;
        }
    }
#if 0
// This is the simple, straightforward version of what Python does.
    PyObject* pt1 = PyTuple_Pack(2, self->__name__, self->__module__);
    PyObject* pt2 = PyTuple_Pack(2, othername, othermod);
    oresult = PyObject_RichCompare(pt1, pt2, op);
#endif

    // tuple comparison is decided by the first non-equal element.
    result = PyObject_RichCompareBool(self->__name__, othername, Py_EQ);
    if (result == 0) {
        result = PyObject_RichCompareBool(self->__name__, othername, op);
    } else if (result == 1) {
        result = PyObject_RichCompareBool(self->__module__, othermod, op);
    }
    // If either comparison failed, we have an error set.
    // Leave oresult NULL so we raise it.
    if (result == -1) {
        goto cleanup;
    }

    oresult = result ? Py_True : Py_False;

cleanup:
    Py_XINCREF(oresult);

    if (!otherib) {
        Py_XDECREF(othername);
        Py_XDECREF(othermod);
    }
    return oresult;
}

static PyMemberDef IB_members[] = {
    { "__name__", T_OBJECT_EX, offsetof(IB, __name__), 0, "" },
    // The redundancy between __module__ and __ibmodule__ is because
    // __module__ is often shadowed by subclasses.
    { "__module__", T_OBJECT_EX, offsetof(IB, __module__), READONLY, "" },
    { "__ibmodule__", T_OBJECT_EX, offsetof(IB, __module__), 0, "" },
    { NULL }
};

static struct PyMethodDef IB_methods[] = {
    { "__adapt__", (PyCFunction)IB__adapt__, METH_O, IB__adapt____doc__},
    { NULL, NULL } /* sentinel */
};

static char IB__name__[] ="_zope_interface_coptimizations.InterfaceBase";
static char IB__doc__[] = (
    "Interface base type providing __call__ and __adapt__"
);

#if USE_STATIC_TYPES

/*
 * Static type: InterfaceBase
 */

static PyTypeObject IB_type_def = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name            = IB__name__,
    .tp_doc             = IB__doc__,
    .tp_base            = &SB_type_def,
    .tp_basicsize       = sizeof(IB),
    .tp_flags           = BASETYPE_FLAGS,
    .tp_init            = (initproc)IB__init__,
    .tp_hash            = (hashfunc)IB__hash__,
    .tp_richcompare     = (richcmpfunc)IB_richcompare,
    .tp_call            = (ternaryfunc)IB__call__,
    .tp_traverse        = (traverseproc)IB_traverse,
    .tp_clear           = (inquiry)IB_clear,
    .tp_dealloc         = (destructor)IB_dealloc,
    .tp_methods         = IB_methods,
    .tp_members         = IB_members,
};

#else

/*
 * Heap type: InterfaceBase
 */
static PyType_Slot IB_type_slots[] = {
    {Py_tp_doc,         IB__doc__},
    {Py_tp_init,        IB__init__},
    {Py_tp_hash,        IB__hash__},
    {Py_tp_richcompare, IB_richcompare},
    {Py_tp_call,        IB__call__},
    {Py_tp_traverse,    IB_traverse},
    {Py_tp_clear,       IB_clear},
    {Py_tp_dealloc,     IB_dealloc},
    {Py_tp_methods,     IB_methods},
    {Py_tp_members,     IB_members},
    /* tp_base cannot be set as a slot -- pass to PyType_FromModuleAndSpec */
    {0,                   NULL}
};

static PyType_Spec IB_type_spec = {
    .name               = IB__name__,
    .basicsize          = sizeof(IB),
    .flags              = BASETYPE_FLAGS,
    .slots              = IB_type_slots
};

#endif

/*
 *  LookupBase class
 */
typedef struct
{
    PyObject_HEAD
    PyObject* _cache;
    PyObject* _mcache;
    PyObject* _scache;
} LB;

static int
LB_traverse(LB* self, visitproc visit, void* arg)
{
/* Visit our 'tp_type' only on Python >= 3.9, per
 * https://docs.python.org/3/howto/isolating-extensions.html
 * #tp-traverse-in-python-3-8-and-lower
 */
#if USE_HEAP_TYPES && PY_VERSION_HEX > 0x03090000
    Py_VISIT(Py_TYPE(self));
#endif
    Py_VISIT(self->_cache);
    Py_VISIT(self->_mcache);
    Py_VISIT(self->_scache);
    return 0;
}

static int
LB_clear(LB* self)
{
    Py_CLEAR(self->_cache);
    Py_CLEAR(self->_mcache);
    Py_CLEAR(self->_scache);
    return 0;
}

static void
LB_dealloc(LB* self)
{
    PyObject_GC_UnTrack((PyObject*)self);
    PyTypeObject* tp = Py_TYPE(self);
    LB_clear(self);
    tp->tp_free((PyObject*)self);
#if USE_HEAP_TYPES
    Py_DECREF(tp);
#endif
}

/*
    def changed(self, ignored=None):
        self._cache.clear()
        self._mcache.clear()
        self._scache.clear()
*/
static PyObject*
LB_changed(LB* self, PyObject* ignored)
{
    LB_clear(self);
    Py_INCREF(Py_None);
    return Py_None;
}

/*
    def _getcache(self, provided, name):
        cache = self._cache.get(provided)
        if cache is None:
            cache = {}
            self._cache[provided] = cache
        if name:
            c = cache.get(name)
            if c is None:
                c = {}
                cache[name] = c
            cache = c
        return cache
*/
static PyObject*
_subcache(PyObject* cache, PyObject* key)
{
    PyObject* subcache;

    subcache = PyDict_GetItem(cache, key);
    if (subcache == NULL) {
        int status;

        subcache = PyDict_New();
        if (subcache == NULL)
            return NULL;
        status = PyDict_SetItem(cache, key, subcache);
        Py_DECREF(subcache);
        if (status < 0)
            return NULL;
    }

    return subcache;
}

static PyObject*
_getcache(LB* self, PyObject* provided, PyObject* name)
{
    PyObject* cache;

    ASSURE_DICT(self->_cache);

    cache = _subcache(self->_cache, provided);
    if (cache == NULL)
        return NULL;

    if (name != NULL && PyObject_IsTrue(name))
        cache = _subcache(cache, name);

    return cache;
}

/*
    def lookup(self, required, provided, name=u'', default=None):
        cache = self._getcache(provided, name)
        if len(required) == 1:
            result = cache.get(required[0], _not_in_mapping)
        else:
            result = cache.get(tuple(required), _not_in_mapping)

        if result is _not_in_mapping:
            result = self._uncached_lookup(required, provided, name)
            if len(required) == 1:
                cache[required[0]] = result
            else:
                cache[tuple(required)] = result

        if result is None:
            return default

        return result
*/

static PyObject*
_lookup(LB* self,
        PyObject* required,
        PyObject* provided,
        PyObject* name,
        PyObject* default_)
{
    PyObject *result, *key, *cache;
    result = key = cache = NULL;
    if (name && !PyUnicode_Check(name)) {
        PyErr_SetString(PyExc_ValueError, "name is not a string");
        return NULL;
    }

    /* If `required` is a lazy sequence, it could have arbitrary side-effects,
       such as clearing our caches. So we must not retrieve the cache until
       after resolving it. */
    required = PySequence_Tuple(required);
    if (required == NULL)
        return NULL;

    cache = _getcache(self, provided, name);
    if (cache == NULL)
        return NULL;

    if (PyTuple_GET_SIZE(required) == 1)
        key = PyTuple_GET_ITEM(required, 0);
    else
        key = required;

    result = PyDict_GetItem(cache, key);
    if (result == NULL) {
        int status;

        result = PyObject_CallMethodObjArgs(
          OBJECT(self), str_uncached_lookup, required, provided, name, NULL);
        if (result == NULL) {
            Py_DECREF(required);
            return NULL;
        }
        status = PyDict_SetItem(cache, key, result);
        Py_DECREF(required);
        if (status < 0) {
            Py_DECREF(result);
            return NULL;
        }
    } else {
        Py_INCREF(result);
        Py_DECREF(required);
    }

    if (result == Py_None && default_ != NULL) {
        Py_DECREF(Py_None);
        Py_INCREF(default_);
        return default_;
    }

    return result;
}

static PyObject*
LB_lookup(LB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "required", "provided", "name", "default", NULL };
    PyObject *required, *provided, *name = NULL, *default_ = NULL;

    if (!PyArg_ParseTupleAndKeywords(args,
                                     kwds,
                                     "OO|OO:LookupBase.lookup",
                                     kwlist,
                                     &required,
                                     &provided,
                                     &name,
                                     &default_))
        return NULL;

    return _lookup(self, required, provided, name, default_);
}

/*
    def lookup1(self, required, provided, name=u'', default=None):
        cache = self._getcache(provided, name)
        result = cache.get(required, _not_in_mapping)
        if result is _not_in_mapping:
            return self.lookup((required, ), provided, name, default)

        if result is None:
            return default

        return result
*/
static PyObject*
_lookup1(LB* self,
         PyObject* required,
         PyObject* provided,
         PyObject* name,
         PyObject* default_)
{
    PyObject *result, *cache;

    if (name && !PyUnicode_Check(name)) {
        PyErr_SetString(PyExc_ValueError, "name is not a string");
        return NULL;
    }

    cache = _getcache(self, provided, name);
    if (cache == NULL)
        return NULL;

    result = PyDict_GetItem(cache, required);
    if (result == NULL) {
        PyObject* tup;

        tup = PyTuple_New(1);
        if (tup == NULL)
            return NULL;
        Py_INCREF(required);
        PyTuple_SET_ITEM(tup, 0, required);
        result = _lookup(self, tup, provided, name, default_);
        Py_DECREF(tup);
    } else {
        if (result == Py_None && default_ != NULL) {
            result = default_;
        }
        Py_INCREF(result);
    }

    return result;
}
static PyObject*
LB_lookup1(LB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "required", "provided", "name", "default", NULL };
    PyObject *required, *provided, *name = NULL, *default_ = NULL;

    if (!PyArg_ParseTupleAndKeywords(args,
                                     kwds,
                                     "OO|OO:LookupBase.lookup1",
                                     kwlist,
                                     &required,
                                     &provided,
                                     &name,
                                     &default_))
        return NULL;

    return _lookup1(self, required, provided, name, default_);
}

/*
    def adapter_hook(self, provided, object, name=u'', default=None):
        required = providedBy(object)
        cache = self._getcache(provided, name)
        factory = cache.get(required, _not_in_mapping)
        if factory is _not_in_mapping:
            factory = self.lookup((required, ), provided, name)

        if factory is not None:
            if isinstance(object, super):
                object = object.__self__
            result = factory(object)
            if result is not None:
                return result

        return default
*/
static PyObject*
_adapter_hook(LB* self,
              PyObject* provided,
              PyObject* object,
              PyObject* name,
              PyObject* default_)
{
    PyObject *required;
    PyObject *factory;
    PyObject *result;
    PyObject *module;

    module = _get_module(Py_TYPE(self));

    if (name && !PyUnicode_Check(name)) {
        PyErr_SetString(PyExc_ValueError, "name is not a string");
        return NULL;
    }

    required = providedBy(module, object);
    if (required == NULL)
        return NULL;

    factory = _lookup1(self, required, provided, name, Py_None);
    Py_DECREF(required);
    if (factory == NULL)
        return NULL;

    if (factory != Py_None) {
        if (PyObject_TypeCheck(object, &PySuper_Type)) {
            PyObject* self = PyObject_GetAttr(object, str__self__);
            if (self == NULL) {
                Py_DECREF(factory);
                return NULL;
            }
            // Borrow the reference to self
            Py_DECREF(self);
            object = self;
        }
        result = PyObject_CallFunctionObjArgs(factory, object, NULL);
        Py_DECREF(factory);
        if (result == NULL || result != Py_None)
            return result;
    } else
        result = factory; /* None */

    if (default_ == NULL || default_ == result) /* No default specified, */
        return result; /* Return None.  result is owned None */

    Py_DECREF(result);
    Py_INCREF(default_);

    return default_;
}

static PyObject*
LB_adapter_hook(LB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "provided", "object", "name", "default", NULL };
    PyObject *object, *provided, *name = NULL, *default_ = NULL;

    if (!PyArg_ParseTupleAndKeywords(args,
                                     kwds,
                                     "OO|OO:LookupBase.adapter_hook",
                                     kwlist,
                                     &provided,
                                     &object,
                                     &name,
                                     &default_))
        return NULL;

    return _adapter_hook(self, provided, object, name, default_);
}

static PyObject*
LB_queryAdapter(LB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "object", "provided", "name", "default", NULL };
    PyObject *object, *provided, *name = NULL, *default_ = NULL;

    if (!PyArg_ParseTupleAndKeywords(args,
                                     kwds,
                                     "OO|OO:LookupBase.queryAdapter",
                                     kwlist,
                                     &object,
                                     &provided,
                                     &name,
                                     &default_))
        return NULL;

    return _adapter_hook(self, provided, object, name, default_);
}

/*
    def lookupAll(self, required, provided):
        cache = self._mcache.get(provided)
        if cache is None:
            cache = {}
            self._mcache[provided] = cache

        required = tuple(required)
        result = cache.get(required, _not_in_mapping)
        if result is _not_in_mapping:
            result = self._uncached_lookupAll(required, provided)
            cache[required] = result

        return result
*/
static PyObject*
_lookupAll(LB* self, PyObject* required, PyObject* provided)
{
    PyObject *cache, *result;

    /* resolve before getting cache. See note in _lookup. */
    required = PySequence_Tuple(required);
    if (required == NULL)
        return NULL;

    ASSURE_DICT(self->_mcache);

    cache = _subcache(self->_mcache, provided);
    if (cache == NULL)
        return NULL;

    result = PyDict_GetItem(cache, required);
    if (result == NULL) {
        int status;

        result = PyObject_CallMethodObjArgs(
          OBJECT(self), str_uncached_lookupAll, required, provided, NULL);
        if (result == NULL) {
            Py_DECREF(required);
            return NULL;
        }
        status = PyDict_SetItem(cache, required, result);
        Py_DECREF(required);
        if (status < 0) {
            Py_DECREF(result);
            return NULL;
        }
    } else {
        Py_INCREF(result);
        Py_DECREF(required);
    }

    return result;
}

static PyObject*
LB_lookupAll(LB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "required", "provided", NULL };
    PyObject *required, *provided;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwds, "OO:LookupBase.lookupAll", kwlist, &required, &provided))
        return NULL;

    return _lookupAll(self, required, provided);
}

/*
    def subscriptions(self, required, provided):
        cache = self._scache.get(provided)
        if cache is None:
            cache = {}
            self._scache[provided] = cache

        required = tuple(required)
        result = cache.get(required, _not_in_mapping)
        if result is _not_in_mapping:
            result = self._uncached_subscriptions(required, provided)
            cache[required] = result

        return result
*/
static PyObject*
_subscriptions(LB* self, PyObject* required, PyObject* provided)
{
    PyObject *cache, *result;

    /* resolve before getting cache. See note in _lookup. */
    required = PySequence_Tuple(required);
    if (required == NULL)
        return NULL;

    ASSURE_DICT(self->_scache);

    cache = _subcache(self->_scache, provided);
    if (cache == NULL)
        return NULL;

    result = PyDict_GetItem(cache, required);
    if (result == NULL) {
        int status;

        result = PyObject_CallMethodObjArgs(
          OBJECT(self), str_uncached_subscriptions, required, provided, NULL);
        if (result == NULL) {
            Py_DECREF(required);
            return NULL;
        }
        status = PyDict_SetItem(cache, required, result);
        Py_DECREF(required);
        if (status < 0) {
            Py_DECREF(result);
            return NULL;
        }
    } else {
        Py_INCREF(result);
        Py_DECREF(required);
    }

    return result;
}

static PyObject*
LB_subscriptions(LB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "required", "provided", NULL };
    PyObject *required, *provided;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwds, "OO", kwlist, &required, &provided))
        return NULL;

    return _subscriptions(self, required, provided);
}

static struct PyMethodDef LB_methods[] = {
    { "changed", (PyCFunction)LB_changed, METH_O, "" },
    { "lookup", (PyCFunction)LB_lookup, METH_KEYWORDS | METH_VARARGS, "" },
    { "lookup1",
      (PyCFunction)LB_lookup1,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "queryAdapter",
      (PyCFunction)LB_queryAdapter,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "adapter_hook",
      (PyCFunction)LB_adapter_hook,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "lookupAll",
      (PyCFunction)LB_lookupAll,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "subscriptions",
      (PyCFunction)LB_subscriptions,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { NULL, NULL } /* sentinel */
};

static char LB__name__[] = "_zope_interface_coptimizations.LookupBase";
static char LB__doc__[] = "Base class for adapter registries";


#if USE_STATIC_TYPES

/*
 * Static type: LookupBase
 */

static PyTypeObject LB_type_def = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name            = LB__name__,
    .tp_doc             = LB__doc__,
    .tp_basicsize       = sizeof(LB),
    .tp_flags           = BASETYPE_FLAGS,
    .tp_traverse        = (traverseproc)LB_traverse,
    .tp_clear           = (inquiry)LB_clear,
    .tp_dealloc         = (destructor)&LB_dealloc,
    .tp_methods         = LB_methods,
};

#else

/*
 * Heap type: LookupBase
 */
static PyType_Slot LB_type_slots[] = {
    {Py_tp_doc,         LB__doc__},
    {Py_tp_traverse,    LB_traverse},
    {Py_tp_clear,       LB_clear},
    {Py_tp_dealloc,     LB_dealloc},
    {Py_tp_methods,     LB_methods},
    {0,                 NULL}
};

static PyType_Spec LB_type_spec = {
    .name               = LB__name__,
    .basicsize          = sizeof(LB),
    .flags              = BASETYPE_FLAGS,
    .slots              = LB_type_slots
};

#endif

typedef struct
{
    LB          lookup;
    PyObject*   _verify_ro;
    PyObject*   _verify_generations;
} VB;

static int
VB_traverse(VB* self, visitproc visit, void* arg)
{
    Py_VISIT(self->_verify_ro);
    Py_VISIT(self->_verify_generations);
    return LB_traverse((LB*)self, visit, arg);
}

static int
VB_clear(VB* self)
{
    Py_CLEAR(self->_verify_generations);
    Py_CLEAR(self->_verify_ro);
    return LB_clear((LB*)self);
}

static void
VB_dealloc(VB* self)
{
    PyObject_GC_UnTrack((PyObject*)self);
    PyTypeObject *tp = Py_TYPE(self);
    VB_clear(self);
    tp->tp_free((PyObject*)self);
#if USE_HEAP_TYPES
    Py_DECREF(tp);
#endif
}

/*
    def changed(self, originally_changed):
        super(VerifyingBasePy, self).changed(originally_changed)
        self._verify_ro = self._registry.ro[1:]
        self._verify_generations = [r._generation for r in self._verify_ro]
*/
static PyObject*
_generations_tuple(PyObject* ro)
{
    int i, l;
    PyObject* generations;

    l = PyTuple_GET_SIZE(ro);
    generations = PyTuple_New(l);
    for (i = 0; i < l; i++) {
        PyObject* generation;

        generation = PyObject_GetAttr(PyTuple_GET_ITEM(ro, i), str_generation);
        if (generation == NULL) {
            Py_DECREF(generations);
            return NULL;
        }
        PyTuple_SET_ITEM(generations, i, generation);
    }

    return generations;
}
static PyObject*
verify_changed(VB* self, PyObject* ignored)
{
    PyObject *t, *ro;

    VB_clear(self);

    t = PyObject_GetAttr(OBJECT(self), str_registry);
    if (t == NULL)
        return NULL;

    ro = PyObject_GetAttr(t, strro);
    Py_DECREF(t);
    if (ro == NULL)
        return NULL;

    t = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), ro, NULL);
    Py_DECREF(ro);
    if (t == NULL)
        return NULL;

    ro = PyTuple_GetSlice(t, 1, PyTuple_GET_SIZE(t));
    Py_DECREF(t);
    if (ro == NULL)
        return NULL;

    self->_verify_generations = _generations_tuple(ro);
    if (self->_verify_generations == NULL) {
        Py_DECREF(ro);
        return NULL;
    }

    self->_verify_ro = ro;

    Py_INCREF(Py_None);
    return Py_None;
}

/*
    def _verify(self):
        if ([r._generation for r in self._verify_ro]
            != self._verify_generations):
            self.changed(None)
*/
static int
_verify(VB* self)
{
    PyObject* changed_result;

    if (self->_verify_ro != NULL && self->_verify_generations != NULL) {
        PyObject* generations;
        int changed;

        generations = _generations_tuple(self->_verify_ro);
        if (generations == NULL)
            return -1;

        changed = PyObject_RichCompareBool(
          self->_verify_generations, generations, Py_NE);
        Py_DECREF(generations);
        if (changed == -1)
            return -1;

        if (changed == 0)
            return 0;
    }

    changed_result =
      PyObject_CallMethodObjArgs(OBJECT(self), strchanged, Py_None, NULL);
    if (changed_result == NULL)
        return -1;

    Py_DECREF(changed_result);
    return 0;
}

static PyObject*
VB_lookup(VB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "required", "provided", "name", "default", NULL };
    PyObject *required, *provided, *name = NULL, *default_ = NULL;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwds, "OO|OO", kwlist, &required, &provided, &name, &default_))
        return NULL;

    if (_verify(self) < 0)
        return NULL;

    return _lookup((LB*)self, required, provided, name, default_);
}

static PyObject*
VB_lookup1(VB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "required", "provided", "name", "default", NULL };
    PyObject *required, *provided, *name = NULL, *default_ = NULL;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwds, "OO|OO", kwlist, &required, &provided, &name, &default_))
        return NULL;

    if (_verify(self) < 0)
        return NULL;

    return _lookup1((LB*)self, required, provided, name, default_);
}

static PyObject*
VB_adapter_hook(VB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "provided", "object", "name", "default", NULL };
    PyObject *object, *provided, *name = NULL, *default_ = NULL;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwds, "OO|OO", kwlist, &provided, &object, &name, &default_))
        return NULL;

    if (_verify(self) < 0)
        return NULL;

    return _adapter_hook((LB*)self, provided, object, name, default_);
}

static PyObject*
VB_queryAdapter(VB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "object", "provided", "name", "default", NULL };
    PyObject *object, *provided, *name = NULL, *default_ = NULL;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwds, "OO|OO", kwlist, &object, &provided, &name, &default_))
        return NULL;

    if (_verify(self) < 0)
        return NULL;

    return _adapter_hook((LB*)self, provided, object, name, default_);
}

static PyObject*
VB_lookupAll(VB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "required", "provided", NULL };
    PyObject *required, *provided;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwds, "OO", kwlist, &required, &provided))
        return NULL;

    if (_verify(self) < 0)
        return NULL;

    return _lookupAll((LB*)self, required, provided);
}

static PyObject*
VB_subscriptions(VB* self, PyObject* args, PyObject* kwds)
{
    static char* kwlist[] = { "required", "provided", NULL };
    PyObject *required, *provided;

    if (!PyArg_ParseTupleAndKeywords(
          args, kwds, "OO", kwlist, &required, &provided))
        return NULL;

    if (_verify(self) < 0)
        return NULL;

    return _subscriptions((LB*)self, required, provided);
}

static struct PyMethodDef VB_methods[] = {
    { "changed", (PyCFunction)verify_changed, METH_O, "" },
    { "lookup",
      (PyCFunction)VB_lookup,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "lookup1",
      (PyCFunction)VB_lookup1,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "queryAdapter",
      (PyCFunction)VB_queryAdapter,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "adapter_hook",
      (PyCFunction)VB_adapter_hook,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "lookupAll",
      (PyCFunction)VB_lookupAll,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { "subscriptions",
      (PyCFunction)VB_subscriptions,
      METH_KEYWORDS | METH_VARARGS,
      "" },
    { NULL, NULL } /* sentinel */
};

static char VB__name__[] = "_zope_interface_coptimizations.VerifyingBase";
static char VB__doc__[] = "Base class for verifying adapter registries.";

#if USE_STATIC_TYPES

/*
 * Static type: VerifyingBase
 */

static PyTypeObject VB_type_def = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name            = VB__name__,
    .tp_doc             = VB__doc__,
    .tp_base            = &LB_type_def,
    .tp_basicsize       = sizeof(VB),
    .tp_flags           = BASETYPE_FLAGS,
    .tp_traverse        = (traverseproc)VB_traverse,
    .tp_clear           = (inquiry)VB_clear,
    .tp_dealloc         = (destructor)&VB_dealloc,
    .tp_methods         = VB_methods,
};


#else

/*
 * Heap type: VerifyingBase
 */
static PyType_Slot VB_type_slots[] = {
    {Py_tp_doc,         VB__doc__},
    {Py_tp_traverse,    VB_traverse},
    {Py_tp_clear,       VB_clear},
    {Py_tp_dealloc,     VB_dealloc},
    {Py_tp_methods,     VB_methods},
    /* tp_base cannot be set as a slot -- pass to PyType_FromModuleAndSpec */
    {0,                 NULL}
};

static PyType_Spec VB_type_spec = {
    .name               = VB__name__,
    .basicsize          = sizeof(VB),
    .flags              = BASETYPE_FLAGS,
    .slots              = VB_type_slots
};

#endif


/*
 * Module state struct:  holds all data formerly kept as static globals.
 */
typedef struct
{
    /* our globals (exposed to Python) */
    PyTypeObject*   specification_base_class;
    PyTypeObject*   object_specification_descriptor_class;
    PyTypeObject*   class_provides_base_class;
    PyTypeObject*   interface_base_class;
    PyTypeObject*   lookup_base_class;
    PyTypeObject*   verifying_base_class;
    PyObject*       adapter_hooks;
    /* members imported from 'zope.interface.declarations'
     */
    PyObject*       empty;
    PyObject*       fallback;
    PyObject*       builtin_impl_specs;
    PyTypeObject*   implements_class;
    /* flag:  have we imported the next set of members yet from
     * 'zope.interface.declarations?
     */
    int             decl_imported;
} _zic_module_state;

/*
 *  Macro to speed lookup of state members
 */
#define _zic_state(o) ((_zic_module_state*)PyModule_GetState(o))

static _zic_module_state*
_zic_state_init(PyObject* module)
{
    _zic_module_state* rec = _zic_state(module);

    rec->specification_base_class = NULL;
    rec->object_specification_descriptor_class = NULL;
    rec->class_provides_base_class = NULL;
    rec->interface_base_class = NULL;
    rec->lookup_base_class = NULL;
    rec->verifying_base_class = NULL;
    rec->adapter_hooks = NULL;

    rec->builtin_impl_specs = NULL;
    rec->empty = NULL;
    rec->fallback = NULL;
    rec->implements_class = NULL;
    rec->decl_imported = 0;

    return rec;
}

static int
_zic_state_traverse(PyObject* module, visitproc visit, void* arg)
{
    _zic_module_state* rec = _zic_state(module);

    Py_VISIT(rec->specification_base_class);
    Py_VISIT(rec->object_specification_descriptor_class);
    Py_VISIT(rec->class_provides_base_class);
    Py_VISIT(rec->interface_base_class);
    Py_VISIT(rec->lookup_base_class);
    Py_VISIT(rec->verifying_base_class);
    Py_VISIT(rec->adapter_hooks);

    Py_VISIT(rec->builtin_impl_specs);
    Py_VISIT(rec->empty);
    Py_VISIT(rec->fallback);
    Py_VISIT(rec->implements_class);

    return 0;
}

static int
_zic_state_clear(PyObject* module)
{
    _zic_module_state* rec = _zic_state(module);

    Py_CLEAR(rec->specification_base_class);
    Py_CLEAR(rec->object_specification_descriptor_class);
    Py_CLEAR(rec->class_provides_base_class);
    Py_CLEAR(rec->interface_base_class);
    Py_CLEAR(rec->lookup_base_class);
    Py_CLEAR(rec->verifying_base_class);
    Py_CLEAR(rec->adapter_hooks);

    Py_CLEAR(rec->builtin_impl_specs);
    Py_CLEAR(rec->empty);
    Py_CLEAR(rec->fallback);
    Py_CLEAR(rec->implements_class);

    return 0;
}

#if USE_HEAP_TYPES
/* Import zope.interface.declarations and store results in module state.
 *
 * Dynamic alternative to 'import_declarations' above.
 */
static _zic_module_state*
_zic_state_load_declarations(PyObject* module)
{
    PyObject* declarations;
    PyObject* builtin_impl_specs;
    PyObject* empty;
    PyObject* fallback;
    PyObject* implements;

    _zic_module_state* rec = _zic_state(module);

    if (!rec->decl_imported) {
        declarations = PyImport_ImportModule("zope.interface.declarations");
        if (declarations == NULL) {
            return NULL;
        }

        builtin_impl_specs = PyObject_GetAttrString(
          declarations, "BuiltinImplementationSpecifications");
        if (builtin_impl_specs == NULL) {
            return NULL;
        }

        empty = PyObject_GetAttrString(declarations, "_empty");
        if (empty == NULL) {
            return NULL;
        }

        fallback =
          PyObject_GetAttrString(declarations, "implementedByFallback");
        if (fallback == NULL) {
            return NULL;
        }

        implements = PyObject_GetAttrString(declarations, "Implements");
        if (implements == NULL) {
            return NULL;
        }

        if (!PyType_Check(implements)) {
            PyErr_SetString(
              PyExc_TypeError,
              "zope.interface.declarations.Implements is not a type");
            return NULL;
        }

        Py_DECREF(declarations);

        rec->builtin_impl_specs = builtin_impl_specs;
        rec->empty = empty;
        rec->fallback = fallback;
        rec->implements_class = (PyTypeObject*)implements;
        rec->decl_imported = 1;
    }
    return rec;
}

#endif

/*
 *  Provide access to the current module given the type.
 */

static struct PyModuleDef _zic_module_def;

static PyObject*
_get_module(PyTypeObject *typeobj)
{
#if USE_STATIC_TYPES
    return (PyObject*)&_zic_module_def;
#else
    if (PyType_Check(typeobj)) {
        /* Only added in Python 3.11 */
        return PyType_GetModuleByDef(typeobj, &_zic_module_def);
    }

    PyErr_SetString(PyExc_TypeError, "_get_module: called w/ non-type");
    return NULL;
#endif
}

/*
 * Fetch the adapter hooks for the current type's module.
 */
static PyObject*
_get_adapter_hooks(PyTypeObject *typeobj)
{
#if USE_STATIC_TYPES
    return adapter_hooks;
#else
    PyObject* module;
    _zic_module_state* rec;

    module = _get_module(typeobj);
    if (module == NULL) { return NULL; }

    rec = _zic_state(module);
    return rec->adapter_hooks;
#endif
}

/*
 * Fetch the 'SpecificationBase' class for the current type's module.
 */
static PyTypeObject*
_get_specification_base_class(PyTypeObject *typeobj)
{
#if USE_STATIC_TYPES
    return &SB_type_def;
#else
    PyObject* module;
    _zic_module_state* rec;

    module = _get_module(typeobj);
    if (module == NULL) { return NULL; }

    rec = _zic_state(module);
    return rec->specification_base_class;
#endif
}

/*
 * Fetch the 'InterfaceBase' class for the current type's module.
 */
static PyTypeObject*
_get_interface_base_class(PyTypeObject *typeobj)
{
#if USE_STATIC_TYPES
    return &IB_type_def;
#else
    PyObject* module;
    _zic_module_state* rec;

    module = _get_module(typeobj);
    if (module == NULL) { return NULL; }

    rec = _zic_state(module);
    return rec->interface_base_class;
#endif
}

static PyObject*
implementedByFallback(PyObject* module, PyObject* cls)
{
#if USE_STATIC_TYPES
    if (imported_declarations == 0 && import_declarations() < 0) {
        return NULL;
    }
    /* now use static 'fallback' */
#else
    PyObject* fallback;

    _zic_module_state* rec = _zic_state_load_declarations(module);
    if (rec == NULL) { return NULL; }

    fallback = rec->fallback;
#endif

    return PyObject_CallFunctionObjArgs(fallback, cls, NULL);
}

static char implementedBy___doc__[] =
  ("Interfaces implemented by a class or factory.\n"
   "Raises TypeError if argument is neither a class nor a callable.");

static PyObject*
implementedBy(PyObject* module, PyObject* cls)
{
    /* Fast retrieval of implements spec, if possible, to optimize
       common case.  Use fallback code if we get stuck.
    */
    PyObject *dict = NULL;
    PyObject *spec;
    PyTypeObject *implements_class;
    PyObject *builtin_impl_specs;

#if USE_STATIC_TYPES
    if (imported_declarations == 0 && import_declarations() < 0) {
        return NULL;
    }

    implements_class = Implements;
    builtin_impl_specs = BuiltinImplementationSpecifications;
#else
    _zic_module_state* rec = _zic_state_load_declarations(module);
    if (rec == NULL) { return NULL; }

    implements_class = rec->implements_class;
    builtin_impl_specs = rec->builtin_impl_specs;
#endif

    if (PyObject_TypeCheck(cls, &PySuper_Type)) {
        // Let merging be handled by Python.
        return implementedByFallback(module, cls);
    }

    if (PyType_Check(cls)) {
        dict = TYPE(cls)->tp_dict;
        Py_XINCREF(dict);
    }

    if (dict == NULL)
        dict = PyObject_GetAttr(cls, str__dict__);

    if (dict == NULL) {
        /* Probably a security proxied class, use more expensive fallback code
         */
        PyErr_Clear();
        return implementedByFallback(module, cls);
    }

    spec = PyObject_GetItem(dict, str__implemented__);
    Py_DECREF(dict);
    if (spec) {

        if (PyObject_TypeCheck(spec, implements_class))
            return spec;

        /* Old-style declaration, use more expensive fallback code */
        Py_DECREF(spec);
        return implementedByFallback(module, cls);
    }

    PyErr_Clear();

    /* Maybe we have a builtin */
    spec = PyDict_GetItem(builtin_impl_specs, cls);
    if (spec != NULL) {
        Py_INCREF(spec);
        return spec;
    }

    /* We're stuck, use fallback */
    return implementedByFallback(module, cls);
}

static char getObjectSpecification___doc__[] =
  ("Get an object's interfaces (internal api)");

static PyObject*
getObjectSpecification(PyObject* module, PyObject* ob)
{
    PyObject *cls;
    PyObject *result;
    PyTypeObject *specification_base_class;
    PyObject *empty_;

#if USE_STATIC_TYPES
    specification_base_class = &SB_type_def;

    if (imported_declarations == 0 && import_declarations() < 0) {
        return NULL;
    }
    empty_ = empty;  /* global from import */

#else
    _zic_module_state* rec = _zic_state_load_declarations(module);
    if (rec == NULL) { return NULL; }

    specification_base_class = rec->specification_base_class;
    empty_ = rec->empty;
#endif

    result = PyObject_GetAttr(ob, str__provides__);
    if (!result) {
        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
            /* Propagate non AttributeError exceptions. */
            return NULL;
        }
        PyErr_Clear();
    } else {
        int is_instance = -1;
        is_instance =
          PyObject_IsInstance(result, OBJECT(specification_base_class));
        if (is_instance < 0) {
            /* Propagate all errors */
            return NULL;
        }
        if (is_instance) {
            return result;
        }
    }

    /* We do a getattr here so as not to be defeated by proxies */
    cls = PyObject_GetAttr(ob, str__class__);
    if (cls == NULL) {
        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
            /* Propagate non-AttributeErrors */
            return NULL;
        }
        PyErr_Clear();

        Py_INCREF(empty_);
        return empty_;
    }
    result = implementedBy(module, cls);
    Py_DECREF(cls);

    return result;
}

static char providedBy___doc__[] = ("Get an object's interfaces");

static PyObject*
providedBy(PyObject* module, PyObject* ob)
{
    PyObject *result = NULL;
    PyObject *cls;
    PyObject *cp;
    PyTypeObject *specification_base_class;
    int is_instance = -1;

    is_instance = PyObject_IsInstance(ob, (PyObject*)&PySuper_Type);
    if (is_instance < 0) {
        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
            /* Propagate non-AttributeErrors */
            return NULL;
        }
        PyErr_Clear();
    }
    if (is_instance) {
        return implementedBy(module, ob);
    }

    result = PyObject_GetAttr(ob, str__providedBy__);

    if (result == NULL) {
        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
            return NULL;
        }

        PyErr_Clear();
        return getObjectSpecification(module, ob);
    }

    /* We want to make sure we have a spec. We can't do a type check
       because we may have a proxy, so we'll just try to get the
       only attribute.
    */
#if USE_STATIC_TYPES
    specification_base_class = &SB_type_def;
#else
    _zic_module_state* rec = _zic_state(module);
    specification_base_class = rec->specification_base_class;
#endif
    if (PyObject_TypeCheck(result, specification_base_class) ||
        PyObject_HasAttrString(result, "extends"))
        return result;

    /*
      The object's class doesn't understand descriptors.
      Sigh. We need to get an object descriptor, but we have to be
      careful.  We want to use the instance's __provides__,l if
      there is one, but only if it didn't come from the class.
    */
    Py_DECREF(result);

    cls = PyObject_GetAttr(ob, str__class__);
    if (cls == NULL)
        return NULL;

    result = PyObject_GetAttr(ob, str__provides__);
    if (result == NULL) {
        /* No __provides__, so just fall back to implementedBy */
        PyErr_Clear();
        result = implementedBy(module, cls);
        Py_DECREF(cls);
        return result;
    }

    cp = PyObject_GetAttr(cls, str__provides__);
    if (cp == NULL) {
        /* The the class has no provides, assume we're done: */
        PyErr_Clear();
        Py_DECREF(cls);
        return result;
    }

    if (cp == result) {
        /*
          Oops, we got the provides from the class. This means
          the object doesn't have it's own. We should use implementedBy
        */
        Py_DECREF(result);
        result = implementedBy(module, cls);
    }

    Py_DECREF(cls);
    Py_DECREF(cp);

    return result;
}

static struct PyMethodDef _zic_module_methods[] = {
    { "implementedBy",
      (PyCFunction)implementedBy,
      METH_O,
      implementedBy___doc__ },
    { "getObjectSpecification",
      (PyCFunction)getObjectSpecification,
      METH_O,
      getObjectSpecification___doc__ },
    { "providedBy", (PyCFunction)providedBy, METH_O, providedBy___doc__ },

    { NULL, (PyCFunction)NULL, 0, NULL } /* sentinel */
};


/* Handler for the 'execute' phase of multi-phase initialization
 *
 * See: https://docs.python.org/3/c-api/module.html#multi-phase-initialization
 * and: https://peps.python.org/pep-0489/#module-execution-phase
 */
static int
_zic_module_exec(PyObject* module)
{
    _zic_module_state* rec = _zic_state_init(module);

    rec->adapter_hooks = PyList_New(0);
    if (rec->adapter_hooks == NULL)
        return -1;
    Py_INCREF(rec->adapter_hooks);

#if USE_STATIC_TYPES

    /* Initialize static global */
    adapter_hooks = rec->adapter_hooks;

    /* Initialize types: */
    SB_type_def.tp_new = PyBaseObject_Type.tp_new;
    if (PyType_Ready(&SB_type_def) < 0) { return -1; }
    Py_INCREF(&SB_type_def);
    rec->specification_base_class = &SB_type_def;

    OSD_type_def.tp_new = PyBaseObject_Type.tp_new;
    if (PyType_Ready(&OSD_type_def) < 0) { return -1; }
    Py_INCREF(&OSD_type_def);
    rec->object_specification_descriptor_class = &OSD_type_def;

    CPB_type_def.tp_new = PyBaseObject_Type.tp_new;
    if (PyType_Ready(&CPB_type_def) < 0) { return -1; }
    Py_INCREF(&CPB_type_def);
    rec->class_provides_base_class = &CPB_type_def;

    IB_type_def.tp_new = PyBaseObject_Type.tp_new;
    if (PyType_Ready(&IB_type_def) < 0) { return -1; }
    Py_INCREF(&IB_type_def);
    rec->interface_base_class = &IB_type_def;

    LB_type_def.tp_new = PyBaseObject_Type.tp_new;
    if (PyType_Ready(&LB_type_def) < 0) { return -1; }
    Py_INCREF(&LB_type_def);
    rec->lookup_base_class = &LB_type_def;

    VB_type_def.tp_new = PyBaseObject_Type.tp_new;
    if (PyType_Ready(&VB_type_def) < 0) { return -1; }
    Py_INCREF(&VB_type_def);
    rec->verifying_base_class = &VB_type_def;

#else

    PyObject *sb_class;
    PyObject *osd_class;
    PyObject *cpb_class;
    PyObject *ib_class;
    PyObject *lb_class;
    PyObject *vb_class;

    /* Initialize types:
     */
    sb_class = PyType_FromModuleAndSpec(module, &SB_type_spec, NULL);
    if (sb_class == NULL) { return -1; }
    Py_INCREF(sb_class);
    rec->specification_base_class = TYPE(sb_class);

    osd_class = PyType_FromModuleAndSpec(module, &OSD_type_spec, NULL);
    if (osd_class == NULL) { return -1; }
    Py_INCREF(osd_class);
    rec->object_specification_descriptor_class = TYPE(osd_class);

    cpb_class = PyType_FromModuleAndSpec(module, &CPB_type_spec, sb_class);
    if (cpb_class == NULL) { return -1; }
    Py_INCREF(cpb_class);
    rec->class_provides_base_class = TYPE(cpb_class);

    ib_class = PyType_FromModuleAndSpec(module, &IB_type_spec, sb_class);
    if (ib_class == NULL) { return -1; }
    Py_INCREF(ib_class);
    rec->interface_base_class = TYPE(ib_class);

    lb_class = PyType_FromModuleAndSpec(module, &LB_type_spec, NULL);
    if (lb_class == NULL) { return -1; }
    Py_INCREF(lb_class);
    rec->lookup_base_class = TYPE(lb_class);

    vb_class = PyType_FromModuleAndSpec(module, &VB_type_spec, lb_class);
    if (vb_class == NULL) { return -1; }
    Py_INCREF(vb_class);
    rec->verifying_base_class = TYPE(vb_class);

#endif

    /* Add types to our dict FBO python;  also the adapter hooks */
    if (PyModule_AddObject(module,
        "SpecificationBase", OBJECT(rec->specification_base_class)) < 0)
        return -1;

    if (PyModule_AddObject(module,
        "ObjectSpecificationDescriptor",
        OBJECT(rec->object_specification_descriptor_class)) <
        0)
        return -1;

    if (PyModule_AddObject(module,
        "ClassProvidesBase", OBJECT(rec->class_provides_base_class)) < 0)
        return -1;

    if (PyModule_AddObject(module,
        "InterfaceBase", OBJECT(rec->interface_base_class)) < 0)
        return -1;

    if (PyModule_AddObject(module,
        "LookupBase", OBJECT(rec->lookup_base_class)) < 0)
        return -1;

    if (PyModule_AddObject(module,
        "VerifyingBase", OBJECT(rec->verifying_base_class)) < 0)
        return -1;

    if (PyModule_AddObject(module, "adapter_hooks", rec->adapter_hooks) < 0)
        return -1;

    return 0;
}


/* Slot definitions for multi-phase initialization
 *
 * See: https://docs.python.org/3/c-api/module.html#multi-phase-initialization
 * and: https://peps.python.org/pep-0489
 */
static PyModuleDef_Slot _zic_module_slots[] = {
    {Py_mod_exec,       _zic_module_exec},
    {0,                 NULL}
};

static char _zic_module__doc__[] = "C optimizations for zope.interface\n\n";

static struct PyModuleDef _zic_module_def = {
    PyModuleDef_HEAD_INIT,
    .m_name = "_zope_interface_coptimizations",
    .m_doc = _zic_module__doc__,
    .m_size = sizeof(_zic_module_state),
    .m_methods = _zic_module_methods,
    .m_slots=_zic_module_slots,
    .m_traverse = _zic_state_traverse,
    .m_clear = _zic_state_clear,
};

static PyObject*
init(void)
{
    if (define_static_strings() < 0) { return NULL; }

    return PyModuleDef_Init(&_zic_module_def);
}

PyMODINIT_FUNC
PyInit__zope_interface_coptimizations(void)
{
    return init();
}

#ifdef __clang__
#pragma clang diagnostic pop
#endif