Pyrex type conversion with overflow check for all int types.
authorRobert Bradshaw <robertwb@math.washington.edu>
Wed, 5 Dec 2007 22:38:25 +0000 (14:38 -0800)
committerRobert Bradshaw <robertwb@math.washington.edu>
Wed, 5 Dec 2007 22:38:25 +0000 (14:38 -0800)
Cython/Compiler/ModuleNode.py
Cython/Compiler/Nodes.py
Cython/Compiler/PyrexTypes.py

index 3fe0798918b1ea9223b6f204d9a3cd70e9193f4b..05befd1bba2df0066760019fbcf28e49b9b523e8 100644 (file)
@@ -279,6 +279,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         self.generate_includes(env, cimported_modules, code)
         code.putln('')
         code.put(Nodes.utility_function_predeclarations)
+        code.put(PyrexTypes.type_conversion_predeclarations)
         code.put(Nodes.branch_prediction_macros)
         code.putln('')
         code.putln('static PyObject *%s;' % env.module_cname)
@@ -1598,6 +1599,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         for utility_code in env.utility_code_used:
             code.h.put(utility_code[0])
             code.put(utility_code[1])
+        code.put(PyrexTypes.type_conversion_functions)
 
 #------------------------------------------------------------------------------------
 #
@@ -1745,11 +1747,11 @@ bad:
 
 register_cleanup_utility_code = [
 """
-static int __Pyx_RegisterCleanup(); /*proto*/
+static int __Pyx_RegisterCleanup(void); /*proto*/
 static PyObject* cleanup(PyObject *self, PyObject *unused); /*proto*/
 static PyMethodDef cleanup_def = {"__cleanup", (PyCFunction)&cleanup, METH_NOARGS, 0};
 ""","""
-static int __Pyx_RegisterCleanup() {
+static int __Pyx_RegisterCleanup(void) {
     /* Don't use Py_AtExit because that has a 32-call limit 
      * and is called after python finalization. 
      */
index 2cf5c38f65fc43cc1022cd683d670f9b560b222e..95f65222e235d7e3d6d8c9baa4b7d97a17e0d7dd 100644 (file)
@@ -3068,22 +3068,6 @@ utility_function_predeclarations = \
 typedef struct {PyObject **p; char *s;} __Pyx_InternTabEntry; /*proto*/
 typedef struct {PyObject **p; char *s; long n; int is_unicode;} __Pyx_StringTabEntry; /*proto*/
 
-static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b) {
-  Py_ssize_t ival;
-  PyObject* x = PyNumber_Index(b);
-  if (!x) return -1;
-  ival = PyInt_AsSsize_t(x);
-  Py_DECREF(x);
-  return ival;
-}
-
-#define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False))
-static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
-   if (x == Py_True) return 1;
-   else if (x == Py_False) return 0;
-   else return PyObject_IsTrue(x);
-}
-
 """ + """
 
 static int %(skip_dispatch_cname)s = 0;
index 2f8baf2bad4303277adeb6fa24f74882be680b9f..f37028dba2855f9ecd28b086110b765e0a0335ba 100644 (file)
@@ -359,19 +359,59 @@ class CNumericType(CType):
             for_display = 0, dll_linkage = None, pyrex = 0):
         base = public_decl(self.sign_and_name(), dll_linkage)
         return self.base_declaration_code(base,  entity_code)
-    
+
+
+int_conversion_list = {}
+type_conversion_functions = ""
+type_conversion_predeclarations = ""
 
 class CIntType(CNumericType):
     
     is_int = 1
     typedef_flag = 0
     to_py_function = "PyInt_FromLong"
-    from_py_function = "PyInt_AsLong"
+    from_py_function = "__pyx_PyInt_AsLong"
     exception_value = -1
 
     def __init__(self, rank, signed, pymemberdef_typecode = None, is_returncode = 0):
         CNumericType.__init__(self, rank, signed, pymemberdef_typecode)
         self.is_returncode = is_returncode
+        if self.from_py_function == '__pyx_PyInt_AsLong':
+            self.from_py_function = self.get_type_conversion()
+
+    def get_type_conversion(self):
+        # error on overflow
+        c_type = self.sign_and_name()
+        c_name = c_type.replace(' ', '_');
+        func_name = "__pyx_PyInt_%s" % c_name;
+        c_mask = "__pyx_%s_MASK" % c_name
+        if not int_conversion_list.has_key(func_name):
+            # no env to add utility code to
+            global type_conversion_predeclarations, type_conversion_functions
+            if self.signed:
+                neg_test = " && ((-val) & %s)" % c_mask
+            else:
+                neg_test = ""
+            type_conversion_predeclarations += """
+static INLINE %(c_type)s %(func_name)s(PyObject* x);""" % {'c_type': c_type, 'c_name': c_name, 'func_name': func_name }
+            type_conversion_functions +=  """
+#define %(c_mask)s ((long)(~((1LL << (8*sizeof(%(c_type)s))) - 1LL)))
+static INLINE %(c_type)s %(func_name)s(PyObject* x) {
+    if (sizeof(%(c_type)s) < sizeof(long)) {
+        long val = __pyx_PyInt_AsLong(x);
+        if (unlikely((val & %(c_mask)s) %(neg_test)s)) {
+            PyErr_SetString(PyExc_OverflowError, "value too large to convert to %(c_type)s");
+            return (%(c_type)s)-1;
+        }
+        return (%(c_type)s)val;
+    }
+    else {
+        return __pyx_PyInt_AsLong(x);
+    }
+}
+""" % {'c_type': c_type, 'c_name': c_name, 'func_name': func_name, 'c_mask': c_mask, 'neg_test': neg_test }
+            int_conversion_list[func_name] = True
+        return func_name
     
     def assignable_from_resolved_type(self, src_type):
         return src_type.is_int or src_type.is_enum or src_type is error_type
@@ -405,13 +445,13 @@ class CULongType(CUIntType):
 class CLongLongType(CUIntType):
 
     to_py_function = "PyLong_FromLongLong"
-    from_py_function = "PyInt_AsUnsignedLongLongMask"
+    from_py_function = "__pyx_PyInt_AsLongLong"
 
 
 class CULongLongType(CUIntType):
 
     to_py_function = "PyLong_FromUnsignedLongLong"
-    from_py_function = "PyInt_AsUnsignedLongLongMask"
+    from_py_function = "__pyx_PyInt_AsUnsignedLongLong"
 
 
 class CPySSizeTType(CIntType):
@@ -424,7 +464,7 @@ class CFloatType(CNumericType):
 
     is_float = 1
     to_py_function = "PyFloat_FromDouble"
-    from_py_function = "PyFloat_AsDouble"
+    from_py_function = "__pyx_PyFloat_AsDouble"
     
     def __init__(self, rank, pymemberdef_typecode = None):
         CNumericType.__init__(self, rank, 1, pymemberdef_typecode)
@@ -828,6 +868,18 @@ class ErrorType(PyrexType):
         return 1
 
 
+rank_to_type_name = (
+    "char",         # 0
+    "short",        # 1
+    "int",          # 2
+    "long",         # 3
+    "PY_LONG_LONG", # 4
+    "Py_ssize_t",   # 5
+    "float",        # 6
+    "double",       # 7
+    "long double",  # 8
+)
+
 py_object_type = PyObjectType()
 
 c_void_type =         CVoidType()
@@ -872,18 +924,6 @@ error_type =    ErrorType()
 
 lowest_float_rank = 6
 
-rank_to_type_name = (
-    "char",         # 0
-    "short",        # 1
-    "int",          # 2
-    "long",         # 3
-    "PY_LONG_LONG", # 4
-    "Py_ssize_t",   # 5
-    "float",        # 6
-    "double",       # 7
-    "long double",  # 8
-)
-
 sign_and_rank_to_type = {
     #(signed, rank)
     (0, 0, ): c_uchar_type, 
@@ -988,3 +1028,72 @@ def typecast(to_type, from_type, expr_code):
     else:
         #print "typecast: to", to_type, "from", from_type ###
         return to_type.cast_code(expr_code)
+
+
+type_conversion_predeclarations = """
+/* Type Conversion Predeclarations */
+
+#define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False))
+static INLINE int __Pyx_PyObject_IsTrue(PyObject* x);
+static INLINE PY_LONG_LONG __pyx_PyInt_AsLongLong(PyObject* x);
+static INLINE unsigned PY_LONG_LONG __pyx_PyInt_AsUnsignedLongLong(PyObject* x);
+static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b);
+
+#define __pyx_PyInt_AsLong(x) (PyInt_CheckExact(x) ? PyInt_AS_LONG(x) : PyInt_AsLong(x))
+#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x))
+""" + type_conversion_predeclarations
+
+type_conversion_functions = """
+/* Type Conversion Functions */
+
+static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b) {
+  Py_ssize_t ival;
+  PyObject* x = PyNumber_Index(b);
+  if (!x) return -1;
+  ival = PyInt_AsSsize_t(x);
+  Py_DECREF(x);
+  return ival;
+}
+
+static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
+   if (x == Py_True) return 1;
+   else if (x == Py_False) return 0;
+   else return PyObject_IsTrue(x);
+}
+
+static INLINE PY_LONG_LONG __pyx_PyInt_AsLongLong(PyObject* x) {
+    if (PyInt_CheckExact(x)) {
+        return PyInt_AS_LONG(x);
+    }
+    else if (PyLong_CheckExact(x)) {
+        return PyLong_AsLongLong(x);
+    }
+    else {
+        PyObject* tmp = PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1;
+        PY_LONG_LONG val = __pyx_PyInt_AsLongLong(tmp);
+        Py_DECREF(tmp);
+        return val;
+    }
+}
+
+static INLINE unsigned PY_LONG_LONG __pyx_PyInt_AsUnsignedLongLong(PyObject* x) {
+    if (PyInt_CheckExact(x)) {
+        long val = PyInt_AS_LONG(x);
+        if (unlikely(val < 0)) {
+            PyErr_SetString(PyExc_TypeError, "Negative assignment to unsigned type.");
+            return (unsigned PY_LONG_LONG)-1;
+        }
+        return val;
+    }
+    else if (PyLong_CheckExact(x)) {
+        return PyLong_AsUnsignedLongLong(x);
+    }
+    else {
+        PyObject* tmp = PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1;
+        PY_LONG_LONG val = __pyx_PyInt_AsUnsignedLongLong(tmp);
+        Py_DECREF(tmp);
+        return val;
+    }
+}
+
+""" + type_conversion_functions