Skip to content

Commit

Permalink
Get all Plex/*.so modules to build in the Limited API (GH-5846)
Browse files Browse the repository at this point in the history
This PR (on top of #5845 and its dependencies)
is sufficient to compile all the built modules in the Plex folder of Cython.
  • Loading branch information
da-woods committed Dec 2, 2023
1 parent 9b2d0a6 commit 7d0d519
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 48 deletions.
55 changes: 42 additions & 13 deletions Cython/Compiler/ModuleNode.py
Expand Up @@ -1780,9 +1780,15 @@ def generate_dealloc_function(self, scope, code):
# cimported base type pointer directly interacts badly with
# the module cleanup, which may already have cleared it.
# In that case, fall back to traversing the type hierarchy.
code.putln("if (likely(%s)) __Pyx_PyType_GetSlot(%s, tp_dealloc, destructor)(o); "
"else __Pyx_call_next_tp_dealloc(o, %s);" % (
base_cname, base_cname, slot_func_cname))
# If we're using the module state then always go through the
# type hierarchy, because our access to the module state may
# have been lost (at least for the limited API version of
# using module state).
code.putln("#if !CYTHON_USE_MODULE_STATE")
code.putln("if (likely(%s)) __Pyx_PyType_GetSlot(%s, tp_dealloc, destructor)(o); else" % (
base_cname, base_cname))
code.putln("#endif")
code.putln("__Pyx_call_next_tp_dealloc(o, %s);" % slot_func_cname)
code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpDealloc", "ExtensionTypes.c"))
else:
Expand Down Expand Up @@ -1870,18 +1876,31 @@ def generate_traverse_function(self, scope, code, cclass_entry):
code.putln("e = %s(o, v, a); if (e) return e;" % static_call)
elif base_type.is_builtin_type:
base_cname = base_type.typeptr_cname
code.putln("if (!%s->tp_traverse); else { e = %s->tp_traverse(o,v,a); if (e) return e; }" % (
base_cname, base_cname))
code.putln("{")
code.putln(
f"traverseproc traverse = __Pyx_PyType_GetSlot({base_cname}, tp_traverse, traverseproc);")
code.putln("if (!traverse); else { e = traverse(o,v,a); if (e) return e; }")
code.putln("}")
else:
# This is an externally defined type. Calling through the
# cimported base type pointer directly interacts badly with
# the module cleanup, which may already have cleared it.
# In that case, fall back to traversing the type hierarchy.
# If we're using the module state then always go through the
# type hierarchy, because our access to the module state may
# have been lost (at least for the limited API version of
# using module state).
base_cname = base_type.typeptr_cname
code.putln("#if !CYTHON_USE_MODULE_STATE")
code.putln("e = 0;")
code.putln("if (likely(%s)) {" % base_cname)
code.putln(
"e = ((likely(%s)) ? ((%s->tp_traverse) ? %s->tp_traverse(o, v, a) : 0) : "
"__Pyx_call_next_tp_traverse(o, v, a, %s)); if (e) return e;" % (
base_cname, base_cname, base_cname, slot_func))
f"traverseproc traverse = __Pyx_PyType_GetSlot({base_cname}, tp_traverse, traverseproc);")
code.putln("if (traverse) { e = traverse(o, v, a); }")
code.putln("} else")
code.putln("#endif")
code.putln("{ e = __Pyx_call_next_tp_traverse(o, v, a, %s); }" % slot_func)
code.putln("if (e) return e;")
code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpTraverse", "ExtensionTypes.c"))

Expand Down Expand Up @@ -1936,17 +1955,27 @@ def generate_clear_function(self, scope, code, cclass_entry):
code.putln("%s(o);" % static_call)
elif base_type.is_builtin_type:
base_cname = base_type.typeptr_cname
code.putln("if (!%s->tp_clear); else %s->tp_clear(o);" % (
base_cname, base_cname))
code.putln("{")
code.putln(f"inquiry clear = __Pyx_PyType_GetSlot({base_cname}, tp_clear, inquiry);")
code.putln("if (clear) clear(o);")
code.putln("}")
else:
# This is an externally defined type. Calling through the
# cimported base type pointer directly interacts badly with
# the module cleanup, which may already have cleared it.
# In that case, fall back to traversing the type hierarchy.
# If we're using the module state then always go through the
# type hierarchy, because our access to the module state may
# have been lost (at least for the limited API version of
# using module state).
base_cname = base_type.typeptr_cname
code.putln(
"if (likely(%s)) { if (%s->tp_clear) %s->tp_clear(o); } else __Pyx_call_next_tp_clear(o, %s);" % (
base_cname, base_cname, base_cname, slot_func))
code.putln("#if !CYTHON_USE_MODULE_STATE")
code.putln("if (likely(%s)) {" % base_cname)
code.putln(f"inquiry clear = __Pyx_PyType_GetSlot({base_cname}, tp_clear, inquiry);")
code.putln("if (clear) clear(o);")
code.putln("} else")
code.putln("#endif")
code.putln("{ __Pyx_call_next_tp_clear(o, %s); }" % slot_func)
code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpClear", "ExtensionTypes.c"))

Expand Down
17 changes: 9 additions & 8 deletions Cython/Compiler/Nodes.py
Expand Up @@ -4926,9 +4926,15 @@ def generate_execution_code(self, code):
if self.py_func.is_module_scope:
code.putln("else {")
else:
code.putln("else if (unlikely((Py_TYPE(%s)->tp_dictoffset != 0) || "
"__Pyx_PyType_HasFeature(Py_TYPE(%s), (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)))) {" % (
self_arg, self_arg))
code.putln("else if (")
code.putln("#if CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY")
code.putln(f"unlikely(Py_TYPE({self_arg})->tp_dictoffset != 0)")
code.putln("#else")
dict_str_const = code.get_py_string_const(EncodedString("__dict__"))
code.putln(f'PyObject_HasAttr({self_arg}, {dict_str_const})')
code.putln("#endif")
code.putln(" || unlikely(__Pyx_PyType_HasFeature(Py_TYPE(%s), (Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HEAPTYPE)))) {" % (
self_arg))

code.putln("#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_PYTYPE_LOOKUP && CYTHON_USE_TYPE_SLOTS")
code.globalstate.use_utility_code(
Expand Down Expand Up @@ -5784,12 +5790,9 @@ def generate_type_ready_code(entry, code, bases_tuple_cname=None, check_heap_typ
typeptr_cname,
type.vtabptr_cname,
))
# TODO: find a way to make this work with the Limited API!
code.putln("#if !CYTHON_COMPILING_IN_LIMITED_API")
code.globalstate.use_utility_code(
UtilityCode.load_cached('MergeVTables', 'ImportExport.c'))
code.put_error_if_neg(entry.pos, "__Pyx_MergeVtables(%s)" % typeptr_cname)
code.putln("#endif")
if not type.scope.is_internal and not type.scope.directives.get('internal'):
# scope.is_internal is set for types defined by
# Cython (such as closures), the 'internal'
Expand Down Expand Up @@ -5822,9 +5825,7 @@ def generate_type_ready_code(entry, code, bases_tuple_cname=None, check_heap_typ
# do so at runtime.
code.globalstate.use_utility_code(
UtilityCode.load_cached('SetupReduce', 'ExtensionTypes.c'))
code.putln("#if !CYTHON_COMPILING_IN_LIMITED_API") # FIXME
code.put_error_if_neg(entry.pos, "__Pyx_setup_reduce((PyObject *) %s)" % typeptr_cname)
code.putln("#endif")

def annotate(self, code):
if self.type_init_args:
Expand Down
8 changes: 7 additions & 1 deletion Cython/Compiler/TypeSlots.py
Expand Up @@ -272,7 +272,13 @@ def generate_spec(self, scope, code):
preprocessor_guard = "#if defined(Py_%s)" % self.slot_name
if preprocessor_guard:
code.putln(preprocessor_guard)
if self.used_ifdef:
# different from preprocessor guard - this defines if we *want* to define it,
# rather than if the slot exists
code.putln(f"#if {self.used_ifdef}")
code.putln("{Py_%s, (void *)%s}," % (self.slot_name, value))
if self.used_ifdef:
code.putln("#endif")
if preprocessor_guard:
code.putln("#endif")

Expand Down Expand Up @@ -1096,7 +1102,7 @@ def __init__(self, old_binops):
EmptySlot("tp_weaklist"),
EmptySlot("tp_del"),
EmptySlot("tp_version_tag"),
SyntheticSlot("tp_finalize", ["__del__"], "0", ifdef="PY_VERSION_HEX >= 0x030400a1",
SyntheticSlot("tp_finalize", ["__del__"], "0",
used_ifdef="CYTHON_USE_TP_FINALIZE"),
EmptySlot("tp_vectorcall", ifdef="PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)"),
EmptySlot("tp_print", ifdef="__PYX_NEED_TP_PRINT_SLOT == 1"),
Expand Down
12 changes: 4 additions & 8 deletions Cython/Utility/ExtensionTypes.c
Expand Up @@ -416,16 +416,13 @@ static void __Pyx_call_next_tp_clear(PyObject* obj, inquiry current_tp_clear) {

/////////////// SetupReduce.proto ///////////////

#if !CYTHON_COMPILING_IN_LIMITED_API
static int __Pyx_setup_reduce(PyObject* type_obj);
#endif

/////////////// SetupReduce ///////////////
//@requires: ObjectHandling.c::PyObjectGetAttrStrNoError
//@requires: ObjectHandling.c::PyObjectGetAttrStr
//@substitute: naming

#if !CYTHON_COMPILING_IN_LIMITED_API
static int __Pyx_setup_reduce_is_named(PyObject* meth, PyObject* name) {
int ret;
PyObject *name_attr;
Expand Down Expand Up @@ -500,8 +497,8 @@ static int __Pyx_setup_reduce(PyObject* type_obj) {
if (reduce == object_reduce || __Pyx_setup_reduce_is_named(reduce, PYIDENT("__reduce_cython__"))) {
reduce_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__reduce_cython__"));
if (likely(reduce_cython)) {
ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce__"), reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD;
ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__reduce_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD;
ret = __Pyx_SetItemOnTypeDict((PyTypeObject*)type_obj, PYIDENT("__reduce__"), reduce_cython); if (unlikely(ret < 0)) goto __PYX_BAD;
ret = __Pyx_DelItemOnTypeDict((PyTypeObject*)type_obj, PYIDENT("__reduce_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD;
} else if (reduce == object_reduce || PyErr_Occurred()) {
// Ignore if we're done, i.e. if 'reduce' already has the right name and the original is gone.
// Otherwise: error.
Expand All @@ -513,8 +510,8 @@ static int __Pyx_setup_reduce(PyObject* type_obj) {
if (!setstate || __Pyx_setup_reduce_is_named(setstate, PYIDENT("__setstate_cython__"))) {
setstate_cython = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__setstate_cython__"));
if (likely(setstate_cython)) {
ret = PyDict_SetItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate__"), setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD;
ret = PyDict_DelItem(((PyTypeObject*)type_obj)->tp_dict, PYIDENT("__setstate_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD;
ret = __Pyx_SetItemOnTypeDict((PyTypeObject*)type_obj, PYIDENT("__setstate__"), setstate_cython); if (unlikely(ret < 0)) goto __PYX_BAD;
ret = __Pyx_DelItemOnTypeDict((PyTypeObject*)type_obj, PYIDENT("__setstate_cython__")); if (unlikely(ret < 0)) goto __PYX_BAD;
} else if (!setstate || PyErr_Occurred()) {
// Ignore if we're done, i.e. if 'setstate' already has the right name and the original is gone.
// Otherwise: error.
Expand Down Expand Up @@ -549,7 +546,6 @@ static int __Pyx_setup_reduce(PyObject* type_obj) {
Py_XDECREF(setstate_cython);
return ret;
}
#endif


/////////////// BinopSlot ///////////////
Expand Down
72 changes: 55 additions & 17 deletions Cython/Utility/ImportExport.c
Expand Up @@ -753,27 +753,24 @@ static void* __Pyx_GetVtable(PyTypeObject *type) {
/////////////// MergeVTables.proto ///////////////
//@requires: GetVTable

// TODO: find a way to make this work with the Limited API!
#if !CYTHON_COMPILING_IN_LIMITED_API
static int __Pyx_MergeVtables(PyTypeObject *type); /*proto*/
#endif

/////////////// MergeVTables ///////////////

#if !CYTHON_COMPILING_IN_LIMITED_API
static int __Pyx_MergeVtables(PyTypeObject *type) {
int i;
int i=0;
Py_ssize_t size;
void** base_vtables;
__Pyx_TypeName tp_base_name;
__Pyx_TypeName base_name;
__Pyx_TypeName tp_base_name = NULL;
__Pyx_TypeName base_name = NULL;
void* unknown = (void*)-1;
PyObject* bases = type->tp_bases;
PyObject* bases = __Pyx_PyType_GetSlot(type, tp_bases, PyObject*);
int base_depth = 0;
{
PyTypeObject* base = type->tp_base;
PyTypeObject* base = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
while (base) {
base_depth += 1;
base = base->tp_base;
base = __Pyx_PyType_GetSlot(base, tp_base, PyTypeObject*);
}
}
base_vtables = (void**) malloc(sizeof(void*) * (size_t)(base_depth + 1));
Expand All @@ -784,11 +781,31 @@ static int __Pyx_MergeVtables(PyTypeObject *type) {
// resolution isn't possible and we must reject it just as when the
// instance struct is so extended. (It would be good to also do this
// check when a multiple-base class is created in pure Python as well.)
for (i = 1; i < PyTuple_GET_SIZE(bases); i++) {
void* base_vtable = __Pyx_GetVtable(((PyTypeObject*)PyTuple_GET_ITEM(bases, i)));
#if CYTHON_COMPILING_IN_LIMITED_API
size = PyTuple_Size(bases);
if (size < 0) goto other_failure;
#else
size = PyTuple_GET_SIZE(bases);
#endif
for (i = 1; i < size; i++) {
PyObject *basei;
void* base_vtable;
#if CYTHON_AVOID_BORROWED_REFS
basei = PySequence_GetItem(bases, i);
if (unlikely(!basei)) goto other_failure;
#elif !CYTHON_ASSUME_SAFE_MACROS
basei = PyTuple_GetItem(bases, i);
if (unlikely(!basei)) goto other_failure;
#else
basei = PyTuple_GET_ITEM(bases, i);
#endif
base_vtable = __Pyx_GetVtable((PyTypeObject*)basei);
#if CYTHON_AVOID_BORROWED_REFS
Py_DECREF(basei);
#endif
if (base_vtable != NULL) {
int j;
PyTypeObject* base = type->tp_base;
PyTypeObject* base = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
for (j = 0; j < base_depth; j++) {
if (base_vtables[j] == unknown) {
base_vtables[j] = __Pyx_GetVtable(base);
Expand All @@ -800,24 +817,45 @@ static int __Pyx_MergeVtables(PyTypeObject *type) {
// No more potential matching bases (with vtables).
goto bad;
}
base = base->tp_base;
base = __Pyx_PyType_GetSlot(base, tp_base, PyTypeObject*);
}
}
}
PyErr_Clear();
free(base_vtables);
return 0;
bad:
tp_base_name = __Pyx_PyType_GetName(type->tp_base);
base_name = __Pyx_PyType_GetName((PyTypeObject*)PyTuple_GET_ITEM(bases, i));
{
PyTypeObject* basei = NULL;
PyTypeObject* tp_base = __Pyx_PyType_GetSlot(type, tp_base, PyTypeObject*);
tp_base_name = __Pyx_PyType_GetName(tp_base);
#if CYTHON_AVOID_BORROWED_REFS
basei = (PyTypeObject*)PySequence_GetItem(bases, i);
if (unlikely(!basei)) goto really_bad;
#elif !CYTHON_ASSUME_SAFE_MACROS
basei = (PyTypeObject*)PyTuple_GetItem(bases, i);
if (unlikely(!basei)) goto really_bad;
#else
basei = (PyTypeObject*)PyTuple_GET_ITEM(bases, i);
#endif
base_name = __Pyx_PyType_GetName(basei);
#if CYTHON_AVOID_BORROWED_REFS
Py_DECREF(basei);
#endif
}
PyErr_Format(PyExc_TypeError,
"multiple bases have vtable conflict: '" __Pyx_FMT_TYPENAME "' and '" __Pyx_FMT_TYPENAME "'", tp_base_name, base_name);
#if CYTHON_AVOID_BORROWED_REFS || !CYTHON_ASSUME_SAFE_MACROS
really_bad: // bad has failed!
#endif
__Pyx_DECREF_TypeName(tp_base_name);
__Pyx_DECREF_TypeName(base_name);
#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_AVOID_BORROWED_REFS || !CYTHON_ASSUME_SAFE_MACROS
other_failure:
#endif
free(base_vtables);
return -1;
}
#endif


/////////////// ImportNumPyArray.proto ///////////////
Expand Down
4 changes: 3 additions & 1 deletion Cython/Utility/ModuleSetupCode.c
Expand Up @@ -1040,8 +1040,10 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict,
// a little hacky, but it does work in the limited API .
// (It doesn't work on PyPy but that probably isn't a bug.)
#define __Pyx_SetItemOnTypeDict(tp, k, v) PyObject_GenericSetAttr((PyObject*)tp, k, v)
#define __Pyx_DelItemOnTypeDict(tp, k) PyObject_GenericSetAttr((PyObject*)tp, k, NULL)
#else
#define __Pyx_SetItemOnTypeDict(tp, k, v) PyDict_SetItem(tp->tp_dict, k, v)
#define __Pyx_SetItemOnTypeDict(tp, k, v) PyDict_SetItem(((PyTypeObject*)(tp))->tp_dict, k, v)
#define __Pyx_DelItemOnTypeDict(tp, k) PyDict_DelItem(((PyTypeObject*)(tp))->tp_dict, k)
#endif

#if CYTHON_USE_TYPE_SPECS && PY_VERSION_HEX >= 0x03080000
Expand Down

0 comments on commit 7d0d519

Please sign in to comment.