Commits

Martin von Löwis  committed 81e0c0e

Remove PyInt_CheckExact. Add PyLong_AsLongAndOverflow.

  • Participants
  • Parent commits 565012d

Comments (0)

Files changed (15)

File Doc/c-api/concrete.rst

    :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.
       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)

File Include/longobject.h

 #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);
 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 *);

File Modules/_csv.c

 	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;
 }
 	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);
 }

File Modules/_cursesmodule.c

 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);

File Modules/_tkinter.c

 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)) {

File Modules/datetimemodule.c

 	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++) {
 					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);

File Modules/socketmodule.c

 				"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);

File Modules/timemodule.c

 	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;

File Objects/exceptions.c

 {
     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 */
     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);
         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,
     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[] = {

File Objects/frameobject.c

 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... */
 	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;
 	}
 
 	/* 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",

File Objects/listobject.c

 	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);
 	}
 	i = PyLong_AsLong(res);
 	Py_DECREF(res);
+	if (i == -1 && PyErr_Occurred()) {
+		/* Overflow in long conversion. */
+		return -1;
+	}
 	return i < 0;
 }
 

File Objects/longobject.c

    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;
 	int sign;
 	int do_decref = 0; /* if nb_int was called */
 
+	*overflow = 0;
 	if (vv == NULL) {
 		PyErr_BadInternalCall();
 		return -1;
 			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;
 			}
 		}
 			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:
 	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)
 {
     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;

File Python/bltinmodule.c

            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) {
 					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;
 				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);

File Python/ceval.c

 {
 	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;