summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorNeil Schemenauer <nascheme@enme.ucalgary.ca>2002-12-30 20:18:15 (GMT)
committerNeil Schemenauer <nascheme@enme.ucalgary.ca>2002-12-30 20:18:15 (GMT)
commitd4b0fea43a8655d15562fa42d56def93fe2348ef (patch)
tree91bf92c7a3eaaef0e55302f8e558eb7d2189ba3a /Objects
parent6005a344ce9938256e0e8bb986ebad29c449f3c6 (diff)
downloadcpython-d4b0fea43a8655d15562fa42d56def93fe2348ef.zip
cpython-d4b0fea43a8655d15562fa42d56def93fe2348ef.tar.gz
cpython-d4b0fea43a8655d15562fa42d56def93fe2348ef.tar.bz2
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.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/abstract.c178
1 files changed, 128 insertions, 50 deletions
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 8389774..4ac7342 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -405,18 +405,23 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot)
}
static PyObject *
+binop_type_error(PyObject *v, PyObject *w, const char *op_name)
+{
+ PyErr_Format(PyExc_TypeError,
+ "unsupported operand type(s) for %s: '%s' and '%s'",
+ op_name,
+ v->ob_type->tp_name,
+ w->ob_type->tp_name);
+ 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(Py_NotImplemented);
- PyErr_Format(
- PyExc_TypeError,
- "unsupported operand type(s) for %s: '%s' and '%s'",
- op_name,
- v->ob_type->tp_name,
- w->ob_type->tp_name);
- return NULL;
+ Py_DECREF(result);
+ return binop_type_error(v, w, op_name);
}
return result;
}
@@ -595,7 +600,6 @@ BINARY_FUNC(PyNumber_And, nb_and, "&")
BINARY_FUNC(PyNumber_Lshift, nb_lshift, "<<")
BINARY_FUNC(PyNumber_Rshift, nb_rshift, ">>")
BINARY_FUNC(PyNumber_Subtract, nb_subtract, "-")
-BINARY_FUNC(PyNumber_Multiply, nb_multiply, "*")
BINARY_FUNC(PyNumber_Divide, nb_divide, "/")
BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()")
@@ -611,17 +615,74 @@ PyNumber_Add(PyObject *v, PyObject *w)
}
if (result == Py_NotImplemented) {
Py_DECREF(result);
- PyErr_Format(
- PyExc_TypeError,
- "unsupported operand types for +: '%s' and '%s'",
- v->ob_type->tp_name,
- w->ob_type->tp_name);
- result = NULL;
+ return binop_type_error(v, w, "+");
}
}
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 *
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)
static PyObject *
-binary_iop(PyObject *v, PyObject *w, const int iop_slot, const int op_slot,
- const char *op_name)
+binary_iop1(PyObject *v, PyObject *w, const int iop_slot, const int op_slot)
{
PyNumberMethods *mv = v->ob_type->tp_as_number;
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);
}
}
- 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) \
@@ -718,47 +790,53 @@ PyNumber_InPlaceTrueDivide(PyObject *v, PyObject *w)
PyObject *
PyNumber_InPlaceAdd(PyObject *v, PyObject *w)
{
- binaryfunc f = NULL;
-
- if (v->ob_type->tp_as_sequence != NULL) {
- if (HASINPLACE(v))
- f = v->ob_type->tp_as_sequence->sq_inplace_concat;
- if (f == NULL)
- f = v->ob_type->tp_as_sequence->sq_concat;
- if (f != NULL)
- return (*f)(v, 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;
+ if (HASINPLACE(v))
+ f = m->sq_inplace_concat;
+ if (f == NULL)
+ f = m->sq_concat;
+ if (f != NULL)
+ return (*f)(v, w);
+ }
+ result = binop_type_error(v, w, "+=");
}
- return binary_iop(v, w, NB_SLOT(nb_inplace_add),
- NB_SLOT(nb_add), "+=");
+ return result;
}
PyObject *
PyNumber_InPlaceMultiply(PyObject *v, PyObject *w)
{
- PyObject * (*g)(PyObject *, int) = NULL;
- if (HASINPLACE(v) &&
- v->ob_type->tp_as_sequence &&
- (g = v->ob_type->tp_as_sequence->sq_inplace_repeat) &&
- !(v->ob_type->tp_as_number &&
- v->ob_type->tp_as_number->nb_inplace_multiply))
- {
- long n;
- if (PyInt_Check(w)) {
- n = PyInt_AsLong(w);
- }
- else if (PyLong_Check(w)) {
- n = PyLong_AsLong(w);
- if (n == -1 && PyErr_Occurred())
- return NULL;
+ PyObject *result = binary_iop1(v, w, NB_SLOT(nb_inplace_multiply),
+ NB_SLOT(nb_multiply));
+ if (result == Py_NotImplemented) {
+ intargfunc f = NULL;
+ PySequenceMethods *mv = v->ob_type->tp_as_sequence;
+ PySequenceMethods *mw = w->ob_type->tp_as_sequence;
+ Py_DECREF(result);
+ if (mv != NULL) {
+ if (HASINPLACE(v))
+ f = mv->sq_inplace_repeat;
+ if (f == NULL)
+ f = mv->sq_repeat;
+ if (f != NULL)
+ return sequence_repeat(f, v, w);
}
- else {
- return type_error(
- "can't multiply sequence to non-int");
+ else if (mw != NULL) {
+ /* Note that the right hand operand should not be
+ * mutated in this case so sq_inplace_repeat is not
+ * used. */
+ if (mw->sq_repeat)
+ return sequence_repeat(mw->sq_repeat, w, v);
}
- return (*g)(v, (int)n);
+ result = binop_type_error(v, w, "*=");
}
- return binary_iop(v, w, NB_SLOT(nb_inplace_multiply),
- NB_SLOT(nb_multiply), "*=");
+ return result;
}
PyObject *