Commit 01b003b1 authored by Stefan Behnel's avatar Stefan Behnel

rewrite of dict iteration code at a more C-ish level in order to make the...

rewrite of dict iteration code at a more C-ish level in order to make the actual code C compile time dependent

--HG--
extra : transplant_source : R%BCF%17%5D%7E%FA%B3%C4q%D2%28%CF%0Di%8D%FC%0B%E4%7B
parent 131ab175
......@@ -9752,25 +9752,9 @@ static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void) {
#------------------------------------------------------------------------------------
raise_noneattr_error_utility_code = UtilityCode(
proto = """
static CYTHON_INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname);
""",
impl = '''
static CYTHON_INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname) {
PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", attrname);
}
''')
raise_noneindex_error_utility_code = UtilityCode(
proto = """
static CYTHON_INLINE void __Pyx_RaiseNoneIndexingError(void);
""",
impl = '''
static CYTHON_INLINE void __Pyx_RaiseNoneIndexingError(void) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is unsubscriptable");
}
''')
raise_noneattr_error_utility_code = UtilityCode.load_cached("RaiseNoneAttrError", "ObjectHandling.c")
raise_noneindex_error_utility_code = UtilityCode.load_cached("RaiseNoneIndexingError", "ObjectHandling.c")
raise_none_iter_error_utility_code = UtilityCode.load_cached("RaiseNoneIterError", "ObjectHandling.c")
raise_noneindex_memview_error_utility_code = UtilityCode(
proto = """
......@@ -9782,16 +9766,6 @@ static CYTHON_INLINE void __Pyx_RaiseNoneMemviewIndexingError(void) {
}
''')
raise_none_iter_error_utility_code = UtilityCode(
proto = """
static CYTHON_INLINE void __Pyx_RaiseNoneNotIterableError(void);
""",
impl = '''
static CYTHON_INLINE void __Pyx_RaiseNoneNotIterableError(void) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is not iterable");
}
''')
raise_unbound_local_error_utility_code = UtilityCode(
proto = """
static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname);
......@@ -10029,92 +10003,14 @@ impl = """
#------------------------------------------------------------------------------------
raise_too_many_values_to_unpack = UtilityCode(
proto = """
static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected);
""",
impl = '''
static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected) {
PyErr_Format(PyExc_ValueError,
"too many values to unpack (expected %"PY_FORMAT_SIZE_T"d)", expected);
}
''')
raise_need_more_values_to_unpack = UtilityCode(
proto = """
static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index);
""",
impl = '''
static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) {
PyErr_Format(PyExc_ValueError,
"need more than %"PY_FORMAT_SIZE_T"d value%s to unpack",
index, (index == 1) ? "" : "s");
}
''')
raise_too_many_values_to_unpack = UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c")
raise_need_more_values_to_unpack = UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c")
#------------------------------------------------------------------------------------
tuple_unpacking_error_code = UtilityCode(
proto = """
static void __Pyx_UnpackTupleError(PyObject *, Py_ssize_t index); /*proto*/
""",
impl = """
static void __Pyx_UnpackTupleError(PyObject *t, Py_ssize_t index) {
if (t == Py_None) {
__Pyx_RaiseNoneNotIterableError();
} else if (PyTuple_GET_SIZE(t) < index) {
__Pyx_RaiseNeedMoreValuesError(PyTuple_GET_SIZE(t));
} else {
__Pyx_RaiseTooManyValuesError(index);
}
}
""",
requires = [raise_none_iter_error_utility_code,
raise_need_more_values_to_unpack,
raise_too_many_values_to_unpack]
)
unpacking_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_UnpackItem(PyObject *, Py_ssize_t index); /*proto*/
""",
impl = """
static PyObject *__Pyx_UnpackItem(PyObject *iter, Py_ssize_t index) {
PyObject *item;
if (!(item = PyIter_Next(iter))) {
if (!PyErr_Occurred()) {
__Pyx_RaiseNeedMoreValuesError(index);
}
}
return item;
}
""",
requires = [raise_need_more_values_to_unpack]
)
iternext_unpacking_end_utility_code = UtilityCode(
proto = """
static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected); /*proto*/
""",
impl = """
static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected) {
if (unlikely(retval)) {
Py_DECREF(retval);
__Pyx_RaiseTooManyValuesError(expected);
return -1;
} else if (PyErr_Occurred()) {
if (likely(PyErr_ExceptionMatches(PyExc_StopIteration))) {
PyErr_Clear();
return 0;
} else {
return -1;
}
}
return 0;
}
""",
requires = [raise_too_many_values_to_unpack]
)
tuple_unpacking_error_code = UtilityCode.load_cached("UnpackTupleError", "ObjectHandling.c")
unpacking_utility_code = UtilityCode.load_cached("UnpackItem", "ObjectHandling.c")
iternext_unpacking_end_utility_code = UtilityCode.load_cached("UnpackItemEndCheck", "ObjectHandling.c")
#------------------------------------------------------------------------------------
......
......@@ -5772,45 +5772,98 @@ class DictIterationNextNode(Node):
# Helper node for calling PyDict_Next() inside of a WhileStatNode
# and checking the dictionary size for changes. Created in
# Optimize.py.
child_attrs = ['dict_obj', 'expected_size', 'pos_index_addr', 'key_addr', 'value_addr']
child_attrs = ['dict_obj', 'expected_size', 'pos_index_var',
'coerced_key_var', 'coerced_value_var', 'coerced_tuple_var',
'key_target', 'value_target', 'tuple_target', 'is_dict_flag']
def __init__(self, dict_obj, expected_size, pos_index_addr, key_addr, value_addr):
coerced_key_var = key_ref = None
coerced_value_var = value_ref = None
coerced_tuple_var = tuple_ref = None
def __init__(self, dict_obj, expected_size, pos_index_var,
key_target, value_target, tuple_target, is_dict_flag):
Node.__init__(
self, dict_obj.pos,
dict_obj = dict_obj,
expected_size = expected_size,
pos_index_addr = pos_index_addr,
key_addr = key_addr,
value_addr = value_addr,
pos_index_var = pos_index_var,
key_target = key_target,
value_target = value_target,
tuple_target = tuple_target,
is_dict_flag = is_dict_flag,
is_temp = True,
type = PyrexTypes.c_bint_type)
def analyse_expressions(self, env):
import UtilNodes
self.dict_obj.analyse_types(env)
self.expected_size.analyse_types(env)
self.pos_index_addr.analyse_types(env)
self.key_addr.analyse_types(env)
self.value_addr.analyse_types(env)
if self.pos_index_var: self.pos_index_var.analyse_types(env)
if self.key_target:
self.key_target.analyse_target_types(env)
self.key_ref = UtilNodes.ResultRefNode(pos=self.key_target.pos, is_temp=True,
type=PyrexTypes.py_object_type)
self.coerced_key_var = self.key_ref.coerce_to(self.key_target.type, env)
if self.value_target:
self.value_target.analyse_target_types(env)
self.value_ref = UtilNodes.ResultRefNode(pos=self.value_target.pos, is_temp=True,
type=PyrexTypes.py_object_type)
self.coerced_value_var = self.value_ref.coerce_to(self.value_target.type, env)
if self.tuple_target:
self.tuple_target.analyse_target_types(env)
self.tuple_ref = UtilNodes.ResultRefNode(pos=self.tuple_target.pos, is_temp=True,
type=PyrexTypes.py_object_type)
self.coerced_tuple_var = self.tuple_ref.coerce_to(self.tuple_target.type, env)
self.is_dict_flag.analyse_types(env)
def generate_function_definitions(self, env, code):
self.dict_obj.generate_function_definitions(env, code)
def generate_execution_code(self, code):
code.globalstate.use_utility_code(UtilityCode.load_cached("dict_iter", "Optimize.c"))
self.dict_obj.generate_evaluation_code(code)
code.putln("if (unlikely(%s != PyDict_Size(%s))) {" % (
self.expected_size.result(),
self.dict_obj.py_result(),
))
code.putln('PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); %s' % (
code.error_goto(self.pos)))
code.putln("}")
self.pos_index_addr.generate_evaluation_code(code)
code.putln("if (!PyDict_Next(%s, %s, %s, %s)) break;" % (
assignments = []
temp_addresses = []
for var, result, target in [(self.key_ref, self.coerced_key_var, self.key_target),
(self.value_ref, self.coerced_value_var, self.value_target),
(self.tuple_ref, self.coerced_tuple_var, self.tuple_target)]:
if target is None:
addr = 'NULL'
else:
temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, True)
var.result_code = temp
assignments.append((temp, result, target))
addr = '&%s' % temp
temp_addresses.append(addr)
result_temp = code.funcstate.allocate_temp(PyrexTypes.c_int_type, False)
code.putln("%s = __Pyx_dict_iter_next(%s, %s, &%s, %s, %s, %s, %s);" % (
result_temp,
self.dict_obj.py_result(),
self.pos_index_addr.result(),
self.key_addr.result(),
self.value_addr.result()))
self.expected_size.result(),
self.pos_index_var.result(),
temp_addresses[0],
temp_addresses[1],
temp_addresses[2],
self.is_dict_flag.result()
))
code.putln("if (unlikely(%s == 0)) break;" % result_temp)
code.putln(code.error_goto_if("%s == -1" % result_temp, self.pos))
code.funcstate.release_temp(result_temp)
# evaluate all coercions before the assignments
for temp, result, target in assignments:
code.put_gotref(temp)
result.generate_evaluation_code(code)
if not result.type.is_pyobject:
code.put_decref_clear(temp, PyrexTypes.py_object_type)
code.funcstate.release_temp(temp)
for temp, result, target in assignments:
target.generate_assignment_code(result, code)
if result.type.is_pyobject:
code.funcstate.release_temp(temp)
def ForStatNode(pos, **kw):
if 'iterator' in kw:
......
This diff is collapsed.
......@@ -120,7 +120,7 @@ class ResultRefNode(AtomicExprNode):
subexprs = []
lhs_of_first_assignment = False
def __init__(self, expression=None, pos=None, type=None, may_hold_none=True):
def __init__(self, expression=None, pos=None, type=None, may_hold_none=True, is_temp=False):
self.expression = expression
self.pos = None
self.may_hold_none = may_hold_none
......@@ -132,6 +132,8 @@ class ResultRefNode(AtomicExprNode):
self.pos = pos
if type is not None:
self.type = type
if is_temp:
self.is_temp = True
assert self.pos is not None
def clone_node(self):
......
/////////////// RaiseNoneAttrError.proto ///////////////
static CYTHON_INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname);
/////////////// RaiseNoneAttrError ///////////////
static CYTHON_INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname) {
PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", attrname);
}
/////////////// RaiseNoneIndexingError.proto ///////////////
static CYTHON_INLINE void __Pyx_RaiseNoneIndexingError(void);
/////////////// RaiseNoneIndexingError ///////////////
static CYTHON_INLINE void __Pyx_RaiseNoneIndexingError(void) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is unsubscriptable");
}
/////////////// RaiseNoneIterError.proto ///////////////
static CYTHON_INLINE void __Pyx_RaiseNoneNotIterableError(void);
/////////////// RaiseNoneIterError ///////////////
static CYTHON_INLINE void __Pyx_RaiseNoneNotIterableError(void) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is not iterable");
}
/////////////// RaiseTooManyValuesToUnpack.proto ///////////////
static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected);
/////////////// RaiseTooManyValuesToUnpack ///////////////
static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected) {
PyErr_Format(PyExc_ValueError,
"too many values to unpack (expected %"PY_FORMAT_SIZE_T"d)", expected);
}
/////////////// RaiseNeedMoreValuesToUnpack.proto ///////////////
static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index);
/////////////// RaiseNeedMoreValuesToUnpack ///////////////
static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) {
PyErr_Format(PyExc_ValueError,
"need more than %"PY_FORMAT_SIZE_T"d value%s to unpack",
index, (index == 1) ? "" : "s");
}
/////////////// UnpackTupleError.proto ///////////////
static void __Pyx_UnpackTupleError(PyObject *, Py_ssize_t index); /*proto*/
/////////////// UnpackTupleError ///////////////
//@requires: RaiseNoneIterError
//@requires: RaiseNeedMoreValuesToUnpack
//@requires: RaiseTooManyValuesToUnpack
static void __Pyx_UnpackTupleError(PyObject *t, Py_ssize_t index) {
if (t == Py_None) {
__Pyx_RaiseNoneNotIterableError();
} else if (PyTuple_GET_SIZE(t) < index) {
__Pyx_RaiseNeedMoreValuesError(PyTuple_GET_SIZE(t));
} else {
__Pyx_RaiseTooManyValuesError(index);
}
}
/////////////// UnpackItem.proto ///////////////
static PyObject *__Pyx_UnpackItem(PyObject *, Py_ssize_t index); /*proto*/
/////////////// UnpackItem ///////////////
//@requires: RaiseNeedMoreValuesToUnpack
static PyObject *__Pyx_UnpackItem(PyObject *iter, Py_ssize_t index) {
PyObject *item;
if (!(item = PyIter_Next(iter))) {
if (!PyErr_Occurred()) {
__Pyx_RaiseNeedMoreValuesError(index);
}
}
return item;
}
/////////////// UnpackItemEndCheck.proto ///////////////
static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected); /*proto*/
/////////////// UnpackItemEndCheck ///////////////
//@requires: RaiseTooManyValuesToUnpack
static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected) {
if (unlikely(retval)) {
Py_DECREF(retval);
__Pyx_RaiseTooManyValuesError(expected);
return -1;
} else if (PyErr_Occurred()) {
if (likely(PyErr_ExceptionMatches(PyExc_StopIteration))) {
PyErr_Clear();
return 0;
} else {
return -1;
}
}
return 0;
}
/////////////// UnpackTuple2.proto ///////////////
static CYTHON_INLINE int __Pyx_unpack_tuple2(PyObject* tuple, PyObject** value1, PyObject** value2,
int is_tuple, int has_known_size, int decref_tuple);
/////////////// UnpackTuple2 ///////////////
//@requires: UnpackItem
//@requires: UnpackItemEndCheck
//@requires: UnpackTupleError
static CYTHON_INLINE int __Pyx_unpack_tuple2(PyObject* tuple, PyObject** pvalue1, PyObject** pvalue2,
int is_tuple, int has_known_size, int decref_tuple) {
PyObject *value1 = NULL, *value2 = NULL, *iter = NULL;
if (!is_tuple && unlikely(!PyTuple_Check(tuple))) {
iter = PyObject_GetIter(tuple);
if (unlikely(!iter)) goto bad;
if (decref_tuple)
Py_DECREF(tuple);
value1 = __Pyx_UnpackItem(iter, 0);
if (unlikely(!value1)) goto bad;
value2 = __Pyx_UnpackItem(iter, 1);
if (unlikely(!value2)) goto bad;
if (!has_known_size && unlikely(__Pyx_IternextUnpackEndCheck(PyIter_Next(iter), 2))) goto bad;
Py_DECREF(iter);
} else {
if (!has_known_size && unlikely(PyTuple_GET_SIZE(tuple) != 2)) {
__Pyx_UnpackTupleError(tuple, 2);
goto bad;
}
value1 = PyTuple_GET_ITEM(tuple, 0);
value2 = PyTuple_GET_ITEM(tuple, 1);
#if CYTHON_COMPILING_IN_CPYTHON
if (decref_tuple) {
PyTuple_SET_ITEM(tuple, 0, NULL);
PyTuple_SET_ITEM(tuple, 1, NULL);
} else
#endif
{
Py_INCREF(value1);
Py_INCREF(value2);
}
if (decref_tuple)
Py_DECREF(tuple);
}
*pvalue1 = value1;
*pvalue2 = value2;
return 0;
bad:
Py_XDECREF(iter);
Py_XDECREF(value1);
Py_XDECREF(value2);
if (decref_tuple) Py_DECREF(tuple);
return -1;
}
......@@ -299,6 +299,87 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_Clear(PyObject* d) {
return Py_None;
}
/////////////// dict_iter.proto ///////////////
static CYTHON_INLINE PyObject* __Pyx_dict_iterator(PyObject* dict, int keys, int values, int is_dict,
PyObject* method_name, Py_ssize_t* p_orig_length, int* p_is_dict);
static CYTHON_INLINE int __Pyx_dict_iter_next(PyObject* dict_or_iter, Py_ssize_t orig_length, Py_ssize_t* ppos,
PyObject** pkey, PyObject** pvalue, PyObject** pitem, int is_dict);
/////////////// dict_iter ///////////////
//@requires: ObjectHandling.c::UnpackTuple2
static CYTHON_INLINE PyObject* __Pyx_dict_iterator(PyObject* dict, int keys, int values, int is_dict,
PyObject* method_name, Py_ssize_t* p_orig_length, int* p_is_dict) {
#if !CYTHON_COMPILING_IN_PYPY
if (is_dict || likely(PyDict_CheckExact(dict))) {
*p_is_dict = 1;
*p_orig_length = PyDict_Size(dict);
Py_INCREF(dict);
return dict;
}
#endif
*p_is_dict = 0;
if (!method_name) {
return PyObject_GetIter(dict);
} else {
return PyObject_CallMethodObjArgs(dict, method_name, NULL);
}
}
static CYTHON_INLINE int __Pyx_dict_iter_next(PyObject* dict_or_iter, Py_ssize_t orig_length, Py_ssize_t* ppos,
PyObject** pkey, PyObject** pvalue, PyObject** pitem, int is_dict) {
#if !CYTHON_COMPILING_IN_PYPY
if (is_dict) {
PyObject *key, *value;
if (unlikely(orig_length != PyDict_Size(dict_or_iter))) {
PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration");
return -1;
}
if (unlikely(!PyDict_Next(dict_or_iter, ppos, &key, &value))) {
return 0;
}
if (pitem) {
PyObject* tuple = PyTuple_New(2);
if (unlikely(!tuple)) {
return -1;
}
Py_INCREF(key);
Py_INCREF(value);
PyTuple_SET_ITEM(tuple, 0, key);
PyTuple_SET_ITEM(tuple, 1, value);
*pitem = tuple;
} else {
if (pkey) {
Py_INCREF(key);
*pkey = key;
}
if (pvalue) {
Py_INCREF(value);
*pvalue = value;
}
}
return 1;
}
#endif
{
PyObject* next_item = PyIter_Next(dict_or_iter);
if (unlikely(!next_item)) {
return PyErr_Occurred() ? -1 : 0;
}
if (pitem) {
*pitem = next_item;
} else if (pkey && pvalue) {
if (__Pyx_unpack_tuple2(next_item, pkey, pvalue, is_dict, is_dict, 1))
return -1;
} else if (pkey) {
*pkey = next_item;
} else {
*pvalue = next_item;
}
return 1;
}
}
/////////////// pyobject_as_double.proto ///////////////
......
......@@ -56,6 +56,15 @@ def iteritems_int(dict d):
[(10, 0), (11, 1), (12, 2), (13, 3)]
>>> iteritems_int({})
[]
>>> iteritems_int({'a': 1})
Traceback (most recent call last):
TypeError: an integer is required
>>> iteritems_int({1: 'b'})
Traceback (most recent call last):
TypeError: an integer is required
>>> iteritems_int({'a': 'b'})
Traceback (most recent call last):
TypeError: an integer is required
"""
cdef int k,v
l = []
......@@ -113,6 +122,9 @@ def iterkeys_int(dict d):
[10, 11, 12, 13]
>>> iterkeys_int({})
[]
>>> iterkeys_int({'a': 'b'})
Traceback (most recent call last):
TypeError: an integer is required
"""
cdef int k
l = []
......@@ -146,6 +158,9 @@ def iterdict_int(dict d):
[10, 11, 12, 13]
>>> iterdict_int({})
[]
>>> iterdict_int({'a': 'b'})
Traceback (most recent call last):
TypeError: an integer is required
"""
cdef int k
l = []
......@@ -211,6 +226,9 @@ def itervalues_int(dict d):
[0, 1, 2, 3]
>>> itervalues_int({})
[]
>>> itervalues_int({'a': 'b'})
Traceback (most recent call last):
TypeError: an integer is required
"""
cdef int v
l = []
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment