From d1a1d1ed802187cd1a9a8a95ac5d758c7acffee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 4 Dec 2007 22:10:37 +0000 Subject: [PATCH] Remove PyInt_CheckExact. Add PyLong_AsLongAndOverflow. --- Doc/c-api/concrete.rst | 15 ++++++++++----- Include/longobject.h | 2 +- Modules/_csv.c | 21 ++++++++++++++++++--- Modules/_cursesmodule.c | 9 +++++++-- Modules/_tkinter.c | 11 +++++++++-- Modules/datetimemodule.c | 9 ++++++--- Modules/socketmodule.c | 7 +++++-- Modules/timemodule.c | 4 ++-- Objects/exceptions.c | 10 +++++++--- Objects/frameobject.c | 18 ++++++++++++++++-- Objects/listobject.c | 6 +++++- Objects/longobject.c | 24 +++++++++++++++++++----- PC/_msi.c | 8 ++++++-- Python/bltinmodule.c | 35 ++++++++++++++++++++++------------- Python/ceval.c | 9 +-------- 15 files changed, 134 insertions(+), 54 deletions(-) diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst index eefd927f889..f90ab4f2ad1 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -211,10 +211,6 @@ All integers are implemented as "long" integer objects of arbitrary size. :ctype:`PyLongObject`. -.. XXX cfunction PyInt_CheckExact(PyObject *p) checks if argument is a long - object and fits into a C long - - .. cfunction:: PyObject* PyLong_FromLong(long v) Return a new :ctype:`PyLongObject` object from *v*, or *NULL* on failure. @@ -297,7 +293,16 @@ All integers are implemented as "long" integer objects of arbitrary size. single: OverflowError (built-in exception) Return a C :ctype:`long` representation of the contents of *pylong*. If - *pylong* is greater than :const:`LONG_MAX`, an :exc:`OverflowError` is raised. + *pylong* is greater than :const:`LONG_MAX`, raise an :exc:`OverflowError`, + and return -1. Convert non-long objects automatically to long first, + and return -1 if that raises exceptions. + +.. cfunction:: long PyLong_AsLongAndOverflow(PyObject *pylong, int* overflow) + + Return a C :ctype:`long` representation of the contents of *pylong*. If + *pylong* is greater than :const:`LONG_MAX`, return -1 and + set `*overflow` to 1 (for overflow) or -1 (for underflow). + If an exception is set because of type errors, also return -1. .. cfunction:: unsigned long PyLong_AsUnsignedLong(PyObject *pylong) diff --git a/Include/longobject.h b/Include/longobject.h index 5740f98deba..e1ee5eead9e 100644 --- a/Include/longobject.h +++ b/Include/longobject.h @@ -14,7 +14,6 @@ PyAPI_DATA(PyTypeObject) PyLong_Type; #define PyLong_Check(op) \ PyType_FastSubclass(Py_Type(op), Py_TPFLAGS_LONG_SUBCLASS) #define PyLong_CheckExact(op) (Py_Type(op) == &PyLong_Type) -#define PyInt_CheckExact(op) (PyLong_CheckExact(op) && _PyLong_FitsInLong(op)) PyAPI_FUNC(PyObject *) PyLong_FromLong(long); PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLong(unsigned long); @@ -22,6 +21,7 @@ PyAPI_FUNC(PyObject *) PyLong_FromSize_t(size_t); PyAPI_FUNC(PyObject *) PyLong_FromSsize_t(Py_ssize_t); PyAPI_FUNC(PyObject *) PyLong_FromDouble(double); PyAPI_FUNC(long) PyLong_AsLong(PyObject *); +PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *); PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *); PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *); diff --git a/Modules/_csv.c b/Modules/_csv.c index c30cea98d93..aee490ccd32 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -181,12 +181,23 @@ _set_int(const char *name, int *target, PyObject *src, int dflt) if (src == NULL) *target = dflt; else { - if (!PyInt_CheckExact(src)) { + long value; + if (!PyLong_CheckExact(src)) { PyErr_Format(PyExc_TypeError, "\"%s\" must be an integer", name); return -1; } - *target = PyLong_AsLong(src); + value = PyLong_AsLong(src); + if (value == -1 && PyErr_Occurred()) + return -1; +#if SIZEOF_LONG > SIZEOF_INT + if (value > INT_MAX || value < INT_MIN) { + PyErr_Format(PyExc_ValueError, + "integer out of range for \"%s\"", name); + return -1; + } +#endif + *target = (int)value; } return 0; } @@ -1385,12 +1396,16 @@ csv_field_size_limit(PyObject *module, PyObject *args) if (!PyArg_UnpackTuple(args, "field_size_limit", 0, 1, &new_limit)) return NULL; if (new_limit != NULL) { - if (!PyInt_CheckExact(new_limit)) { + if (!PyLong_CheckExact(new_limit)) { PyErr_Format(PyExc_TypeError, "limit must be an integer"); return NULL; } field_limit = PyLong_AsLong(new_limit); + if (field_limit == -1 && PyErr_Occurred()) { + field_limit = old_limit; + return NULL; + } } return PyLong_FromLong(old_limit); } diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 1fc7da7f1b3..d1cd15518c9 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -196,8 +196,13 @@ PyCursesCheckERR(int code, char *fname) static int PyCurses_ConvertToChtype(PyObject *obj, chtype *ch) { - if (PyInt_CheckExact(obj)) { - *ch = (chtype) PyLong_AsLong(obj); + if (PyLong_CheckExact(obj)) { + int overflow; + /* XXX should the truncation by the cast also be reported + as an error? */ + *ch = (chtype) PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) + return 0; } else if(PyString_Check(obj) && (PyString_Size(obj) == 1)) { *ch = (chtype) *PyString_AsString(obj); diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index c755f89ba32..2a341abb9dd 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -863,14 +863,21 @@ static Tcl_Obj* AsObj(PyObject *value) { Tcl_Obj *result; + long longVal; + int overflow; if (PyString_Check(value)) return Tcl_NewStringObj(PyString_AS_STRING(value), PyString_GET_SIZE(value)); else if (PyBool_Check(value)) return Tcl_NewBooleanObj(PyObject_IsTrue(value)); - else if (PyInt_CheckExact(value)) - return Tcl_NewLongObj(PyLong_AS_LONG(value)); + else if (PyLong_CheckExact(value) && + ((longVal = PyLong_AsLongAndOverflow(value, &overflow)), + !overflow)) { + /* If there is an overflow in the long conversion, + fall through to default object handling. */ + return Tcl_NewLongObj(longVal); + } else if (PyFloat_Check(value)) return Tcl_NewDoubleObj(PyFloat_AS_DOUBLE(value)); else if (PyTuple_Check(value)) { diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index 6955c784433..8eb7e045d9b 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -3827,7 +3827,7 @@ datetime_strptime(PyObject *cls, PyObject *args) Py_DECREF(module); if (obj != NULL) { - int i, good_timetuple = 1; + int i, good_timetuple = 1, overflow; long int ia[6]; if (PySequence_Check(obj) && PySequence_Size(obj) >= 6) for (i=0; i < 6; i++) { @@ -3836,8 +3836,11 @@ datetime_strptime(PyObject *cls, PyObject *args) Py_DECREF(obj); return NULL; } - if (PyInt_CheckExact(p)) - ia[i] = PyLong_AsLong(p); + if (PyLong_CheckExact(p)) { + ia[i] = PyLong_AsLongAndOverflow(p, &overflow); + if (overflow) + good_timetuple = 0; + } else good_timetuple = 0; Py_DECREF(p); diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 926f059ed54..c1fb5aa9e6c 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -3595,8 +3595,11 @@ socket_getaddrinfo(PyObject *self, PyObject *args) "getaddrinfo() argument 1 must be string or None"); return NULL; } - if (PyInt_CheckExact(pobj)) { - PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", PyLong_AsLong(pobj)); + if (PyLong_CheckExact(pobj)) { + long value = PyLong_AsLong(pobj); + if (value == -1 && PyErr_Occurred()) + goto err; + PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", value); pptr = pbuf; } else if (PyUnicode_Check(pobj)) { pptr = PyUnicode_AsString(pobj); diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 4196381baad..c64a3564713 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -392,8 +392,8 @@ gettmarg(PyObject *args, struct tm *p) if (y < 1900) { PyObject *accept = PyDict_GetItemString(moddict, "accept2dyear"); - if (accept == NULL || !PyInt_CheckExact(accept) || - PyLong_AsLong(accept) == 0) { + if (accept == NULL || !PyLong_CheckExact(accept) || + !PyObject_IsTrue(accept)) { PyErr_SetString(PyExc_ValueError, "year >= 1900 required"); return 0; diff --git a/Objects/exceptions.c b/Objects/exceptions.c index cbcda7b0660..96557333001 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -929,6 +929,10 @@ SyntaxError_str(PySyntaxErrorObject *self) { int have_lineno = 0; char *filename = 0; + /* Below, we always ignore overflow errors, just printing -1. + Still, we cannot allow an OverflowError to be raised, so + we need to call PyLong_AsLongAndOverflow. */ + int overflow; /* XXX -- do all the additional formatting with filename and lineno here */ @@ -936,7 +940,7 @@ SyntaxError_str(PySyntaxErrorObject *self) if (self->filename && PyUnicode_Check(self->filename)) { filename = PyUnicode_AsString(self->filename); } - have_lineno = (self->lineno != NULL) && PyInt_CheckExact(self->lineno); + have_lineno = (self->lineno != NULL) && PyLong_CheckExact(self->lineno); if (!filename && !have_lineno) return PyObject_Str(self->msg ? self->msg : Py_None); @@ -945,7 +949,7 @@ SyntaxError_str(PySyntaxErrorObject *self) return PyUnicode_FromFormat("%S (%s, line %ld)", self->msg ? self->msg : Py_None, my_basename(filename), - PyLong_AsLong(self->lineno)); + PyLong_AsLongAndOverflow(self->lineno, &overflow)); else if (filename) return PyUnicode_FromFormat("%S (%s)", self->msg ? self->msg : Py_None, @@ -953,7 +957,7 @@ SyntaxError_str(PySyntaxErrorObject *self) else /* only have_lineno */ return PyUnicode_FromFormat("%S (line %ld)", self->msg ? self->msg : Py_None, - PyLong_AsLong(self->lineno)); + PyLong_AsLongAndOverflow(self->lineno, &overflow)); } static PyMemberDef SyntaxError_members[] = { diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 0e6d9f8e5d5..266cbd2caa1 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -66,6 +66,8 @@ static int frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) { int new_lineno = 0; /* The new value of f_lineno */ + long l_new_lineno; + int overflow; int new_lasti = 0; /* The new value of f_lasti */ int new_iblock = 0; /* The new value of f_iblock */ unsigned char *code = NULL; /* The bytecode for the frame... */ @@ -88,7 +90,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) unsigned char setup_op = 0; /* (ditto) */ /* f_lineno must be an integer. */ - if (!PyInt_CheckExact(p_new_lineno)) { + if (!PyLong_CheckExact(p_new_lineno)) { PyErr_SetString(PyExc_ValueError, "lineno must be an integer"); return -1; @@ -104,7 +106,19 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) } /* Fail if the line comes before the start of the code block. */ - new_lineno = (int) PyLong_AsLong(p_new_lineno); + l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow); + if (overflow +#if SIZEOF_LONG > SIZEOF_INT + || l_new_lineno > INT_MAX + || l_new_lineno < INT_MIN +#endif + ) { + PyErr_SetString(PyExc_ValueError, + "lineno out of range"); + return -1; + } + new_lineno = (int)l_new_lineno; + if (new_lineno < f->f_code->co_firstlineno) { PyErr_Format(PyExc_ValueError, "line %d comes before the current code block", diff --git a/Objects/listobject.c b/Objects/listobject.c index 59674bf0ee5..18d3b901a27 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -925,7 +925,7 @@ islt(PyObject *x, PyObject *y, PyObject *compare) Py_DECREF(args); if (res == NULL) return -1; - if (!PyInt_CheckExact(res)) { + if (!PyLong_CheckExact(res)) { PyErr_Format(PyExc_TypeError, "comparison function must return int, not %.200s", res->ob_type->tp_name); @@ -934,6 +934,10 @@ islt(PyObject *x, PyObject *y, PyObject *compare) } i = PyLong_AsLong(res); Py_DECREF(res); + if (i == -1 && PyErr_Occurred()) { + /* Overflow in long conversion. */ + return -1; + } return i < 0; } diff --git a/Objects/longobject.c b/Objects/longobject.c index 1e20485f16c..cf7cb4713b1 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -299,7 +299,7 @@ PyLong_FromDouble(double dval) Returns -1 and sets an error condition if overflow occurs. */ long -PyLong_AsLong(PyObject *vv) +PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) { /* This version by Tim Peters */ register PyLongObject *v; @@ -309,6 +309,7 @@ PyLong_AsLong(PyObject *vv) int sign; int do_decref = 0; /* if nb_int was called */ + *overflow = 0; if (vv == NULL) { PyErr_BadInternalCall(); return -1; @@ -358,8 +359,7 @@ PyLong_AsLong(PyObject *vv) prev = x; x = (x << PyLong_SHIFT) + v->ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { - PyErr_SetString(PyExc_OverflowError, - "Python int too large to convert to C long"); + *overflow = Py_Size(v) > 0 ? 1 : -1; goto exit; } } @@ -373,8 +373,8 @@ PyLong_AsLong(PyObject *vv) res = LONG_MIN; } else { - PyErr_SetString(PyExc_OverflowError, - "Python int too large to convert to C long"); + *overflow = Py_Size(v) > 0 ? 1 : -1; + /* res is already set to -1 */ } } exit: @@ -384,6 +384,20 @@ PyLong_AsLong(PyObject *vv) return res; } +long +PyLong_AsLong(PyObject *obj) +{ + int overflow; + long result = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) { + /* XXX: could be cute and give a different + message for overflow == -1 */ + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C long"); + } + return result; +} + int _PyLong_FitsInLong(PyObject *vv) { diff --git a/PC/_msi.c b/PC/_msi.c index 8dc77344c4b..80c3cae9a4f 100644 --- a/PC/_msi.c +++ b/PC/_msi.c @@ -542,9 +542,13 @@ summary_setproperty(msiobj* si, PyObject *args) if (PyString_Check(data)) { status = MsiSummaryInfoSetProperty(si->h, field, VT_LPSTR, 0, NULL, PyString_AsString(data)); - } else if (PyInt_CheckExact(data)) { + } else if (PyLong_CheckExact(data)) { + long value = PyLong_AsLong(data); + if (value == -1 && PyErr_Occurred()) { + return NULL; + } status = MsiSummaryInfoSetProperty(si->h, field, VT_I4, - PyLong_AsLong(data), NULL, NULL); + value, NULL, NULL); } else { PyErr_SetString(PyExc_TypeError, "unsupported type"); return NULL; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index b57083b4550..56ec738f30a 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1623,10 +1623,14 @@ builtin_sum(PyObject *self, PyObject *args) Assumes all inputs are the same type. If the assumption fails, default to the more general routine. */ - if (PyInt_CheckExact(result)) { - long i_result = PyLong_AS_LONG(result); - Py_DECREF(result); - result = NULL; + if (PyLong_CheckExact(result)) { + int overflow; + long i_result = PyLong_AsLongAndOverflow(result, &overflow); + /* If this already overflowed, don't even enter the loop. */ + if (overflow == 0) { + Py_DECREF(result); + result = NULL; + } while(result == NULL) { item = PyIter_Next(iter); if (item == NULL) { @@ -1635,10 +1639,10 @@ builtin_sum(PyObject *self, PyObject *args) return NULL; return PyLong_FromLong(i_result); } - if (PyInt_CheckExact(item)) { - long b = PyLong_AS_LONG(item); + if (PyLong_CheckExact(item)) { + long b = PyLong_AsLongAndOverflow(item, &overflow); long x = i_result + b; - if ((x^i_result) >= 0 || (x^b) >= 0) { + if (overflow == 0 && ((x^i_result) >= 0 || (x^b) >= 0)) { i_result = x; Py_DECREF(item); continue; @@ -1676,12 +1680,17 @@ builtin_sum(PyObject *self, PyObject *args) Py_DECREF(item); continue; } - if (PyInt_CheckExact(item)) { - PyFPE_START_PROTECT("add", return 0) - f_result += (double)PyLong_AS_LONG(item); - PyFPE_END_PROTECT(f_result) - Py_DECREF(item); - continue; + if (PyLong_CheckExact(item)) { + long value; + int overflow; + value = PyLong_AsLongAndOverflow(item, &overflow); + if (!overflow) { + PyFPE_START_PROTECT("add", return 0) + f_result += (double)value; + PyFPE_END_PROTECT(f_result) + Py_DECREF(item); + continue; + } } result = PyFloat_FromDouble(f_result); temp = PyNumber_Add(result, item); diff --git a/Python/ceval.c b/Python/ceval.c index 9aa83c74e78..813f6f636a9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3711,14 +3711,7 @@ _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi) { if (v != NULL) { Py_ssize_t x; - if (PyInt_CheckExact(v)) { - /* XXX(nnorwitz): I think PyLong_AS_LONG is correct, - however, it looks like it should be AsSsize_t. - There should be a comment here explaining why. - */ - x = PyLong_AS_LONG(v); - } - else if (PyIndex_Check(v)) { + if (PyIndex_Check(v)) { x = PyNumber_AsSsize_t(v, NULL); if (x == -1 && PyErr_Occurred()) return 0;