fix ticket 636: build the correct slice objects on 2-value object slicing
authorStefan Behnel <stefan_ml@behnel.de>
Sat, 16 Mar 2013 13:18:05 +0000 (14:18 +0100)
committerStefan Behnel <stefan_ml@behnel.de>
Sat, 16 Mar 2013 13:18:05 +0000 (14:18 +0100)
Cython/Compiler/ExprNodes.py
Cython/Utility/ObjectHandling.c

index 119373d..550da92 100755 (executable)
@@ -3478,8 +3478,11 @@ class SliceIndexNode(ExprNode):
     #  base      ExprNode
     #  start     ExprNode or None
     #  stop      ExprNode or None
+    #  slice     ExprNode or None   constant slice object
 
-    subexprs = ['base', 'start', 'stop']
+    subexprs = ['base', 'start', 'stop', 'slice']
+
+    slice = None
 
     def infer_type(self, env):
         base_type = self.base.infer_type(env)
@@ -3568,11 +3571,23 @@ class SliceIndexNode(ExprNode):
             self.type = base_type
             self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
 
-        c_int = PyrexTypes.c_py_ssize_t_type
-        if self.start:
-            self.start = self.start.coerce_to(c_int, env)
-        if self.stop:
-            self.stop = self.stop.coerce_to(c_int, env)
+        if self.type is py_object_type:
+            if (not self.start or self.start.is_literal) and \
+                    (not self.stop or self.stop.is_literal):
+                # cache the constant slice object, in case we need it
+                none_node = NoneNode(self.pos)
+                self.slice = SliceNode(
+                    self.pos,
+                    start=copy.deepcopy(self.start or none_node),
+                    stop=copy.deepcopy(self.stop or none_node),
+                    step=none_node
+                ).analyse_types(env)
+        else:
+            c_int = PyrexTypes.c_py_ssize_t_type
+            if self.start:
+                self.start = self.start.coerce_to(c_int, env)
+            if self.stop:
+                self.stop = self.stop.coerce_to(c_int, env)
         self.is_temp = 1
         return self
 
@@ -3652,6 +3667,32 @@ class SliceIndexNode(ExprNode):
                     start_code,
                     stop_code,
                     code.error_goto_if_null(result, self.pos)))
+        elif self.type is py_object_type:
+            code.globalstate.use_utility_code(
+                UtilityCode.load_cached("GetObjectSlice", "ObjectHandling.c"))
+            has_c_start, c_start, py_start = False, '0', 'NULL'
+            if self.start:
+                has_c_start = not self.start.type.is_pyobject
+                if has_c_start:
+                    c_start = self.start.result()
+                else:
+                    py_start = '&%s' % self.start.py_result()
+            has_c_stop, c_stop, py_stop = False, '0', 'NULL'
+            if self.stop:
+                has_c_stop = not self.stop.type.is_pyobject
+                if has_c_stop:
+                    c_stop = self.stop.result()
+                else:
+                    py_stop = '&%s' % self.stop.py_result()
+            py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL'
+            code.putln(
+                "%s = __Pyx_PySequence_GetObjectSlice(%s, %s, %s, %s, %s, %s, %d, %d); %s" % (
+                    result,
+                    self.base.py_result(),
+                    c_start, c_stop,
+                    py_start, py_stop, py_slice,
+                    has_c_start, has_c_stop,
+                    code.error_goto_if_null(result, self.pos)))
         else:
             if self.base.type is list_type:
                 code.globalstate.use_utility_code(
index c97d749..9f34f7e 100644 (file)
@@ -444,6 +444,86 @@ static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i,
 }
 
 
+/////////////// GetObjectSlice.proto ///////////////
+
+// we pass pointer addresses to show the C compiler what is NULL and what isn't
+static CYTHON_INLINE PyObject* __Pyx_PySequence_GetObjectSlice(
+        PyObject* obj, Py_ssize_t cstart, Py_ssize_t cstop,
+        PyObject** py_start, PyObject** py_stop, PyObject** py_slice,
+        int has_cstart, int has_cstop);
+
+/////////////// GetObjectSlice ///////////////
+
+static CYTHON_INLINE PyObject* __Pyx_PySequence_GetObjectSlice(
+        PyObject* obj, Py_ssize_t cstart, Py_ssize_t cstop,
+        PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice,
+        int has_cstart, int has_cstop) {
+    PyMappingMethods* mp;
+#if PY_MAJOR_VERSION < 3
+    PySequenceMethods* ms = Py_TYPE(obj)->tp_as_sequence;
+    if (likely(ms && ms->sq_slice)) {
+        if (!has_cstart) {
+            if (_py_start) {
+                cstart = __Pyx_PyIndex_AsSsize_t(*_py_start);
+                if ((cstart == (Py_ssize_t)-1) && PyErr_Occurred()) return NULL;
+            } else
+                cstart = 0;
+        }
+        if (!has_cstop) {
+            if (_py_stop) {
+                cstop = __Pyx_PyIndex_AsSsize_t(*_py_stop);
+                if ((cstop == (Py_ssize_t)-1) && PyErr_Occurred()) return NULL;
+            } else
+                cstop = PY_SSIZE_T_MAX;
+        }
+        return ms->sq_slice(obj, cstart, cstop);
+    }
+#endif
+    mp = Py_TYPE(obj)->tp_as_mapping;
+    if (likely(mp && mp->mp_subscript)) {
+        PyObject *result, *py_slice, *py_start, *py_stop;
+        if (_py_slice) {
+            py_slice = *_py_slice;
+        } else {
+            PyObject* owned_start = NULL;
+            PyObject* owned_stop = NULL;
+            if (_py_start) {
+                py_start = *_py_start;
+            } else {
+                if (has_cstart) {
+                    owned_start = py_start = PyInt_FromSsize_t(cstart);
+                    if (unlikely(!py_start)) return NULL;
+                } else
+                    py_start = Py_None;
+            }
+            if (_py_stop) {
+                py_stop = *_py_stop;
+            } else {
+                if (has_cstop) {
+                    owned_stop = py_stop = PyInt_FromSsize_t(cstop);
+                    if (unlikely(!py_stop)) {
+                        Py_XDECREF(owned_start);
+                        return NULL;
+                    }
+                } else
+                    py_stop = Py_None;
+            }
+            py_slice = PySlice_New(py_start, py_stop, Py_None);
+            Py_XDECREF(owned_start);
+            Py_XDECREF(owned_stop);
+            if (unlikely(!py_slice)) return NULL;
+        }
+        result = mp->mp_subscript(obj, py_slice);
+        if (!_py_slice) {
+            Py_DECREF(py_slice);
+        }
+        return result;
+    }
+    PyErr_Format(PyExc_TypeError,
+        "'%.200s' object is unsliceable", Py_TYPE(obj)->tp_name);
+    return NULL;
+}
+
 /////////////// SliceTupleAndList.proto ///////////////
 
 #if CYTHON_COMPILING_IN_CPYTHON