From 306c3a71c7982bc76acbb68ac2081a630825997f Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sat, 16 Mar 2013 14:18:05 +0100 Subject: [PATCH] fix ticket 636: build the correct slice objects on 2-value object slicing --- Cython/Compiler/ExprNodes.py | 53 +++++++++++++++++++++++---- Cython/Utility/ObjectHandling.c | 80 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 119373d..550da92 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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( diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index c97d749..9f34f7e 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -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 -- 2.7.4