diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index fbc3eb1ffae..9edac7d2fea 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -14179,6 +14179,7 @@ class CoerceToBooleanNode(CoercionNode): type = PyrexTypes.c_bint_type + # Note that all of these need a check if CYTHON_ASSUME_SAFE_MACROS is false _special_builtins = { Builtin.list_type: '__Pyx_PyList_GET_SIZE', Builtin.tuple_type: '__Pyx_PyTuple_GET_SIZE', @@ -14217,6 +14218,8 @@ def generate_result_code(self, code): checks = ["(%s != Py_None)" % self.arg.py_result()] if self.arg.may_be_none() else [] checks.append("(%s(%s) != 0)" % (test_func, self.arg.py_result())) code.putln("%s = %s;" % (self.result(), '&&'.join(checks))) + code.putln(code.error_goto_if( + "((!CYTHON_ASSUME_SAFE_MACROS) && %s < 0)" % self.result(), self.pos)) else: code.putln( "%s = __Pyx_PyObject_IsTrue(%s); %s" % ( diff --git a/Cython/Utility/Builtins.c b/Cython/Utility/Builtins.c index cde493d4c10..7b91bde5e6a 100644 --- a/Cython/Utility/Builtins.c +++ b/Cython/Utility/Builtins.c @@ -333,14 +333,30 @@ static long __Pyx__PyObject_Ord(PyObject* c) { if (PyBytes_Check(c)) { size = __Pyx_PyBytes_GET_SIZE(c); if (likely(size == 1)) { +#if CYTHON_ASSUME_SAFE_MACROS return (unsigned char) PyBytes_AS_STRING(c)[0]; +#else + char *data = PyBytes_AsString(c); + if (unlikely(!data)) return -1; + return (unsigned char) data[0]; +#endif } -#if (!CYTHON_COMPILING_IN_PYPY) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)) +#if !CYTHON_ASSUME_SAFE_SIZE + else if (unlikely(size < 0)) return -1; +#endif } else if (PyByteArray_Check(c)) { size = __Pyx_PyByteArray_GET_SIZE(c); if (likely(size == 1)) { +#if CYTHON_ASSUME_SAFE_MACROS return (unsigned char) PyByteArray_AS_STRING(c)[0]; +#else + char *data = PyByteArray_AsString(c); + if (unlikely(!data)) return -1; + return (unsigned char) data[0]; +#endif } +#if !CYTHON_ASSUME_SAFE_SIZE + else if (unlikely(size < 0)) return -1; #endif } else { // FIXME: support character buffers - but CPython doesn't support them either @@ -475,8 +491,23 @@ static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it) { result = PyFrozenSet_New(it); if (unlikely(!result)) return NULL; - if ((PY_VERSION_HEX >= 0x030A00A1) || likely(__Pyx_PySet_GET_SIZE(result))) + if ((__PYX_LIMITED_VERSION_HEX >= 0x030A00A1) +#if CYTHON_COMPILING_IN_LIMITED_API + || __Pyx_get_runtime_version() >= 0x030A00A1 +#endif + ) return result; + { + Py_ssize_t size = __Pyx_PySet_GET_SIZE(result); + if (likely(size > 0)) + return result; +#if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(size < 0)) { + Py_DECREF(result); + return NULL; + } +#endif + } // empty frozenset is a singleton (on Python <3.10) // seems wasteful, but CPython does the same Py_DECREF(result); diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 80188388d21..d9397fc6a11 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -54,6 +54,9 @@ static void __Pyx_UnpackTupleError(PyObject *t, Py_ssize_t index) { __Pyx_RaiseNoneNotIterableError(); } else { Py_ssize_t size = __Pyx_PyTuple_GET_SIZE(t); + #if !CYTHON_ASSUME_SAFE_SIZE + if (unlikely(size < 0)) return; + #endif if (size < index) { __Pyx_RaiseNeedMoreValuesError(size); } else { @@ -82,12 +85,8 @@ static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected) { /////////////// UnpackTuple2.proto /////////////// -#define __Pyx_unpack_tuple2(tuple, value1, value2, is_tuple, has_known_size, decref_tuple) \ - (likely(is_tuple || PyTuple_Check(tuple)) ? \ - (likely(has_known_size || PyTuple_GET_SIZE(tuple) == 2) ? \ - __Pyx_unpack_tuple2_exact(tuple, value1, value2, decref_tuple) : \ - (__Pyx_UnpackTupleError(tuple, 2), -1)) : \ - __Pyx_unpack_tuple2_generic(tuple, value1, value2, has_known_size, decref_tuple)) +static CYTHON_INLINE int __Pyx_unpack_tuple2( + PyObject* tuple, PyObject** value1, PyObject** value2, int is_tuple, int has_known_size, int decref_tuple); static CYTHON_INLINE int __Pyx_unpack_tuple2_exact( PyObject* tuple, PyObject** value1, PyObject** value2, int decref_tuple); @@ -99,6 +98,27 @@ static int __Pyx_unpack_tuple2_generic( //@requires: UnpackTupleError //@requires: RaiseNeedMoreValuesToUnpack +static CYTHON_INLINE int __Pyx_unpack_tuple2( + PyObject* tuple, PyObject** value1, PyObject** value2, int is_tuple, int has_known_size, int decref_tuple) { + if (likely(is_tuple || PyTuple_Check(tuple))) { + Py_ssize_t size; + if (has_known_size) { + return __Pyx_unpack_tuple2_exact(tuple, value1, value2, decref_tuple); + } + size = __Pyx_PyTuple_GET_SIZE(tuple); + if (likely(size == 2)) { + return __Pyx_unpack_tuple2_exact(tuple, value1, value2, decref_tuple); + } + if (size >= 0) { + // "size == -1" indicates an error already. + __Pyx_UnpackTupleError(tuple, 2); + } + return -1; + } else { + return __Pyx_unpack_tuple2_generic(tuple, value1, value2, has_known_size, decref_tuple); + } +} + static CYTHON_INLINE int __Pyx_unpack_tuple2_exact( PyObject* tuple, PyObject** pvalue1, PyObject** pvalue2, int decref_tuple) { PyObject *value1 = NULL, *value2 = NULL; diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c index 9a7eb356fbe..b0124544f57 100644 --- a/Cython/Utility/Optimize.c +++ b/Cython/Utility/Optimize.c @@ -361,8 +361,22 @@ static CYTHON_INLINE int __Pyx_dict_iter_next( } Py_INCREF(key); Py_INCREF(value); + #if CYTHON_ASSUME_SAFE_MACROS PyTuple_SET_ITEM(tuple, 0, key); PyTuple_SET_ITEM(tuple, 1, value); + #else + if (unlikely(PyTuple_SetItem(tuple, 0, key) < 0)) { + // decref value; PyTuple_SetItem decrefs key on failure + Py_DECREF(value); + Py_DECREF(tuple); + return -1; + } + if (unlikely(PyTuple_SetItem(tuple, 1, value) < 0)) { + // PyTuple_SetItem decrefs value on failure + Py_DECREF(tuple); + return -1; + } + #endif *pitem = tuple; } else { if (pkey) { diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c index 867f99b8f43..8187af29b87 100644 --- a/Cython/Utility/StringTools.c +++ b/Cython/Utility/StringTools.c @@ -75,75 +75,13 @@ static CYTHON_INLINE int __Pyx_UnicodeContainsUCS4(PyObject* unicode, Py_UCS4 ch //////////////////// PyUCS4InUnicode //////////////////// -#if PY_VERSION_HEX < 0x03090000 || (defined(PyUnicode_WCHAR_KIND) && defined(PyUnicode_AS_UNICODE)) - -#if PY_VERSION_HEX < 0x03090000 -#define __Pyx_PyUnicode_AS_UNICODE(op) PyUnicode_AS_UNICODE(op) -#define __Pyx_PyUnicode_GET_SIZE(op) PyUnicode_GET_SIZE(op) -#else -// Avoid calling deprecated C-API functions in Py3.9+ that PEP-623 schedules for removal in Py3.12. -// https://www.python.org/dev/peps/pep-0623/ -#define __Pyx_PyUnicode_AS_UNICODE(op) (((PyASCIIObject *)(op))->wstr) -#define __Pyx_PyUnicode_GET_SIZE(op) ((PyCompactUnicodeObject *)(op))->wstr_length -#endif - -#if !defined(Py_UNICODE_SIZE) || Py_UNICODE_SIZE == 2 -static int __Pyx_PyUnicodeBufferContainsUCS4_SP(Py_UNICODE* buffer, Py_ssize_t length, Py_UCS4 character) { - /* handle surrogate pairs for Py_UNICODE buffers in 16bit Unicode builds */ - Py_UNICODE high_val, low_val; - Py_UNICODE* pos; - high_val = (Py_UNICODE) (0xD800 | (((character - 0x10000) >> 10) & ((1<<10)-1))); - low_val = (Py_UNICODE) (0xDC00 | ( (character - 0x10000) & ((1<<10)-1))); - for (pos=buffer; pos < buffer+length-1; pos++) { - if (unlikely((high_val == pos[0]) & (low_val == pos[1]))) return 1; - } - return 0; -} -#endif - -static int __Pyx_PyUnicodeBufferContainsUCS4_BMP(Py_UNICODE* buffer, Py_ssize_t length, Py_UCS4 character) { - Py_UNICODE uchar; - Py_UNICODE* pos; - uchar = (Py_UNICODE) character; - for (pos=buffer; pos < buffer+length; pos++) { - if (unlikely(uchar == pos[0])) return 1; - } - return 0; -} -#endif - static CYTHON_INLINE int __Pyx_UnicodeContainsUCS4(PyObject* unicode, Py_UCS4 character) { - const int kind = PyUnicode_KIND(unicode); - #ifdef PyUnicode_WCHAR_KIND - if (likely(kind != PyUnicode_WCHAR_KIND)) - #endif - { - Py_ssize_t i; - const void* udata = PyUnicode_DATA(unicode); - const Py_ssize_t length = PyUnicode_GET_LENGTH(unicode); - for (i=0; i < length; i++) { - if (unlikely(character == PyUnicode_READ(kind, udata, i))) return 1; - } - return 0; - } - -#if PY_VERSION_HEX < 0x03090000 || (defined(PyUnicode_WCHAR_KIND) && defined(PyUnicode_AS_UNICODE)) -#if !defined(Py_UNICODE_SIZE) || Py_UNICODE_SIZE == 2 - if ((sizeof(Py_UNICODE) == 2) && unlikely(character > 65535)) { - return __Pyx_PyUnicodeBufferContainsUCS4_SP( - __Pyx_PyUnicode_AS_UNICODE(unicode), - __Pyx_PyUnicode_GET_SIZE(unicode), - character); - } else -#endif - { - return __Pyx_PyUnicodeBufferContainsUCS4_BMP( - __Pyx_PyUnicode_AS_UNICODE(unicode), - __Pyx_PyUnicode_GET_SIZE(unicode), - character); - - } -#endif + // Note that from Python 3.7, the indices of FindChar are adjusted to match the bounds + // so need to check the length + Py_ssize_t idx = PyUnicode_FindChar(unicode, character, 0, PY_SSIZE_T_MAX, 1); + if (unlikely(idx == -2)) return -1; + // >= 0: found the index, == -1: not found + return idx >= 0; } @@ -597,8 +535,13 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Substring( return __Pyx_NewRef($empty_unicode); if (start == 0 && stop == length) return __Pyx_NewRef(text); +#if CYTHON_COMPILING_IN_LIMITED_API + // PyUnicode_Substring() does not support negative indexing but is otherwise fine to use. + return PyUnicode_Substring(text, start, stop); +#else return PyUnicode_FromKindAndData(PyUnicode_KIND(text), PyUnicode_1BYTE_DATA(text) + start*PyUnicode_KIND(text), stop-start); +#endif } diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c index 4ac109ccd2c..5fbfff879b7 100644 --- a/Cython/Utility/TypeConversion.c +++ b/Cython/Utility/TypeConversion.c @@ -584,15 +584,16 @@ static CYTHON_INLINE Py_UCS4 __Pyx_PyUnicode_AsPy_UCS4(PyObject*); /////////////// UnicodeAsUCS4 /////////////// static CYTHON_INLINE Py_UCS4 __Pyx_PyUnicode_AsPy_UCS4(PyObject* x) { - Py_ssize_t length; - length = PyUnicode_GET_LENGTH(x); - if (likely(length == 1)) { - return PyUnicode_READ_CHAR(x, 0); - } - PyErr_Format(PyExc_ValueError, - "only single character unicode strings can be converted to Py_UCS4, " - "got length %" CYTHON_FORMAT_SSIZE_T "d", length); - return (Py_UCS4)-1; + Py_ssize_t length = __Pyx_PyUnicode_GET_LENGTH(x); + if (likely(length == 1)) { + return __Pyx_PyUnicode_READ_CHAR(x, 0); + } else if (likely(length >= 0)) { + // "length == -1" indicates an error already. + PyErr_Format(PyExc_ValueError, + "only single character unicode strings can be converted to Py_UCS4, " + "got length %" CYTHON_FORMAT_SSIZE_T "d", length); + } + return (Py_UCS4)-1; }