mirror of https://github.com/python/cpython.git
Always try nb_* slots before trying sq_concat, sq_inplace_concat, sq_repeat,
andsq_inplace_repeat. This fixes a number of corner case bugs (see #624807). Consolidate the int and long sequence repeat code. Before the change, integers checked for integer overflow but longs did not.
This commit is contained in:
parent
6005a344ce
commit
d4b0fea43a
|
@ -405,19 +405,24 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name)
|
binop_type_error(PyObject *v, PyObject *w, const char *op_name)
|
||||||
{
|
{
|
||||||
PyObject *result = binary_op1(v, w, op_slot);
|
PyErr_Format(PyExc_TypeError,
|
||||||
if (result == Py_NotImplemented) {
|
|
||||||
Py_DECREF(Py_NotImplemented);
|
|
||||||
PyErr_Format(
|
|
||||||
PyExc_TypeError,
|
|
||||||
"unsupported operand type(s) for %s: '%s' and '%s'",
|
"unsupported operand type(s) for %s: '%s' and '%s'",
|
||||||
op_name,
|
op_name,
|
||||||
v->ob_type->tp_name,
|
v->ob_type->tp_name,
|
||||||
w->ob_type->tp_name);
|
w->ob_type->tp_name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name)
|
||||||
|
{
|
||||||
|
PyObject *result = binary_op1(v, w, op_slot);
|
||||||
|
if (result == Py_NotImplemented) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
return binop_type_error(v, w, op_name);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +600,6 @@ BINARY_FUNC(PyNumber_And, nb_and, "&")
|
||||||
BINARY_FUNC(PyNumber_Lshift, nb_lshift, "<<")
|
BINARY_FUNC(PyNumber_Lshift, nb_lshift, "<<")
|
||||||
BINARY_FUNC(PyNumber_Rshift, nb_rshift, ">>")
|
BINARY_FUNC(PyNumber_Rshift, nb_rshift, ">>")
|
||||||
BINARY_FUNC(PyNumber_Subtract, nb_subtract, "-")
|
BINARY_FUNC(PyNumber_Subtract, nb_subtract, "-")
|
||||||
BINARY_FUNC(PyNumber_Multiply, nb_multiply, "*")
|
|
||||||
BINARY_FUNC(PyNumber_Divide, nb_divide, "/")
|
BINARY_FUNC(PyNumber_Divide, nb_divide, "/")
|
||||||
BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()")
|
BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()")
|
||||||
|
|
||||||
|
@ -611,17 +615,74 @@ PyNumber_Add(PyObject *v, PyObject *w)
|
||||||
}
|
}
|
||||||
if (result == Py_NotImplemented) {
|
if (result == Py_NotImplemented) {
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
PyErr_Format(
|
return binop_type_error(v, w, "+");
|
||||||
PyExc_TypeError,
|
|
||||||
"unsupported operand types for +: '%s' and '%s'",
|
|
||||||
v->ob_type->tp_name,
|
|
||||||
w->ob_type->tp_name);
|
|
||||||
result = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
sequence_repeat(intargfunc repeatfunc, PyObject *seq, PyObject *n)
|
||||||
|
{
|
||||||
|
long count;
|
||||||
|
if (PyInt_Check(n)) {
|
||||||
|
count = PyInt_AsLong(n);
|
||||||
|
}
|
||||||
|
else if (PyLong_Check(n)) {
|
||||||
|
count = PyLong_AsLong(n);
|
||||||
|
if (count == -1 && PyErr_Occurred())
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return type_error(
|
||||||
|
"can't multiply sequence to non-int");
|
||||||
|
}
|
||||||
|
#if LONG_MAX != INT_MAX
|
||||||
|
if (count > INT_MAX) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"sequence repeat count too large");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if (count < INT_MIN)
|
||||||
|
count = INT_MIN;
|
||||||
|
/* XXX Why don't I either
|
||||||
|
|
||||||
|
- set count to -1 whenever it's negative (after all,
|
||||||
|
sequence repeat usually treats negative numbers
|
||||||
|
as zero(); or
|
||||||
|
|
||||||
|
- raise an exception when it's less than INT_MIN?
|
||||||
|
|
||||||
|
I'm thinking about a hypothetical use case where some
|
||||||
|
sequence type might use a negative value as a flag of
|
||||||
|
some kind. In those cases I don't want to break the
|
||||||
|
code by mapping all negative values to -1. But I also
|
||||||
|
don't want to break e.g. []*(-sys.maxint), which is
|
||||||
|
perfectly safe, returning []. As a compromise, I do
|
||||||
|
map out-of-range negative values.
|
||||||
|
*/
|
||||||
|
#endif
|
||||||
|
return (*repeatfunc)(seq, (int)count);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyNumber_Multiply(PyObject *v, PyObject *w)
|
||||||
|
{
|
||||||
|
PyObject *result = binary_op1(v, w, NB_SLOT(nb_multiply));
|
||||||
|
if (result == Py_NotImplemented) {
|
||||||
|
PySequenceMethods *mv = v->ob_type->tp_as_sequence;
|
||||||
|
PySequenceMethods *mw = w->ob_type->tp_as_sequence;
|
||||||
|
if (mv && mv->sq_repeat) {
|
||||||
|
return sequence_repeat(mv->sq_repeat, v, w);
|
||||||
|
}
|
||||||
|
else if (mw && mw->sq_repeat) {
|
||||||
|
return sequence_repeat(mw->sq_repeat, w, v);
|
||||||
|
}
|
||||||
|
result = binop_type_error(v, w, "*");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
PyNumber_FloorDivide(PyObject *v, PyObject *w)
|
PyNumber_FloorDivide(PyObject *v, PyObject *w)
|
||||||
{
|
{
|
||||||
|
@ -668,8 +729,7 @@ PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
|
||||||
PyType_HasFeature((t)->ob_type, Py_TPFLAGS_HAVE_INPLACEOPS)
|
PyType_HasFeature((t)->ob_type, Py_TPFLAGS_HAVE_INPLACEOPS)
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
binary_iop(PyObject *v, PyObject *w, const int iop_slot, const int op_slot,
|
binary_iop1(PyObject *v, PyObject *w, const int iop_slot, const int op_slot)
|
||||||
const char *op_name)
|
|
||||||
{
|
{
|
||||||
PyNumberMethods *mv = v->ob_type->tp_as_number;
|
PyNumberMethods *mv = v->ob_type->tp_as_number;
|
||||||
if (mv != NULL && HASINPLACE(v)) {
|
if (mv != NULL && HASINPLACE(v)) {
|
||||||
|
@ -682,7 +742,19 @@ binary_iop(PyObject *v, PyObject *w, const int iop_slot, const int op_slot,
|
||||||
Py_DECREF(x);
|
Py_DECREF(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return binary_op(v, w, op_slot, op_name);
|
return binary_op1(v, w, op_slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
binary_iop(PyObject *v, PyObject *w, const int iop_slot, const int op_slot,
|
||||||
|
const char *op_name)
|
||||||
|
{
|
||||||
|
PyObject *result = binary_iop1(v, w, iop_slot, op_slot);
|
||||||
|
if (result == Py_NotImplemented) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
return binop_type_error(v, w, op_name);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define INPLACE_BINOP(func, iop, op, op_name) \
|
#define INPLACE_BINOP(func, iop, op, op_name) \
|
||||||
|
@ -718,47 +790,53 @@ PyNumber_InPlaceTrueDivide(PyObject *v, PyObject *w)
|
||||||
PyObject *
|
PyObject *
|
||||||
PyNumber_InPlaceAdd(PyObject *v, PyObject *w)
|
PyNumber_InPlaceAdd(PyObject *v, PyObject *w)
|
||||||
{
|
{
|
||||||
|
PyObject *result = binary_iop1(v, w, NB_SLOT(nb_inplace_add),
|
||||||
|
NB_SLOT(nb_add));
|
||||||
|
if (result == Py_NotImplemented) {
|
||||||
|
PySequenceMethods *m = v->ob_type->tp_as_sequence;
|
||||||
|
Py_DECREF(result);
|
||||||
|
if (m != NULL) {
|
||||||
binaryfunc f = NULL;
|
binaryfunc f = NULL;
|
||||||
|
|
||||||
if (v->ob_type->tp_as_sequence != NULL) {
|
|
||||||
if (HASINPLACE(v))
|
if (HASINPLACE(v))
|
||||||
f = v->ob_type->tp_as_sequence->sq_inplace_concat;
|
f = m->sq_inplace_concat;
|
||||||
if (f == NULL)
|
if (f == NULL)
|
||||||
f = v->ob_type->tp_as_sequence->sq_concat;
|
f = m->sq_concat;
|
||||||
if (f != NULL)
|
if (f != NULL)
|
||||||
return (*f)(v, w);
|
return (*f)(v, w);
|
||||||
}
|
}
|
||||||
return binary_iop(v, w, NB_SLOT(nb_inplace_add),
|
result = binop_type_error(v, w, "+=");
|
||||||
NB_SLOT(nb_add), "+=");
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
PyNumber_InPlaceMultiply(PyObject *v, PyObject *w)
|
PyNumber_InPlaceMultiply(PyObject *v, PyObject *w)
|
||||||
{
|
{
|
||||||
PyObject * (*g)(PyObject *, int) = NULL;
|
PyObject *result = binary_iop1(v, w, NB_SLOT(nb_inplace_multiply),
|
||||||
if (HASINPLACE(v) &&
|
NB_SLOT(nb_multiply));
|
||||||
v->ob_type->tp_as_sequence &&
|
if (result == Py_NotImplemented) {
|
||||||
(g = v->ob_type->tp_as_sequence->sq_inplace_repeat) &&
|
intargfunc f = NULL;
|
||||||
!(v->ob_type->tp_as_number &&
|
PySequenceMethods *mv = v->ob_type->tp_as_sequence;
|
||||||
v->ob_type->tp_as_number->nb_inplace_multiply))
|
PySequenceMethods *mw = w->ob_type->tp_as_sequence;
|
||||||
{
|
Py_DECREF(result);
|
||||||
long n;
|
if (mv != NULL) {
|
||||||
if (PyInt_Check(w)) {
|
if (HASINPLACE(v))
|
||||||
n = PyInt_AsLong(w);
|
f = mv->sq_inplace_repeat;
|
||||||
|
if (f == NULL)
|
||||||
|
f = mv->sq_repeat;
|
||||||
|
if (f != NULL)
|
||||||
|
return sequence_repeat(f, v, w);
|
||||||
}
|
}
|
||||||
else if (PyLong_Check(w)) {
|
else if (mw != NULL) {
|
||||||
n = PyLong_AsLong(w);
|
/* Note that the right hand operand should not be
|
||||||
if (n == -1 && PyErr_Occurred())
|
* mutated in this case so sq_inplace_repeat is not
|
||||||
return NULL;
|
* used. */
|
||||||
|
if (mw->sq_repeat)
|
||||||
|
return sequence_repeat(mw->sq_repeat, w, v);
|
||||||
}
|
}
|
||||||
else {
|
result = binop_type_error(v, w, "*=");
|
||||||
return type_error(
|
|
||||||
"can't multiply sequence to non-int");
|
|
||||||
}
|
}
|
||||||
return (*g)(v, (int)n);
|
return result;
|
||||||
}
|
|
||||||
return binary_iop(v, w, NB_SLOT(nb_inplace_multiply),
|
|
||||||
NB_SLOT(nb_multiply), "*=");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
Loading…
Reference in New Issue