summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael W. Hudson <mwh@python.net>2004-08-12 17:56:29 (GMT)
committerMichael W. Hudson <mwh@python.net>2004-08-12 17:56:29 (GMT)
commit609346273903cd848d055b046ec46d9cc831b750 (patch)
tree1cae68cd2e72748e02ae5d5179ccfe1f8dc1bb98
parentfd39ad4937f5f48142c7dafdac9d727931137c96 (diff)
downloadcpython-609346273903cd848d055b046ec46d9cc831b750.zip
cpython-609346273903cd848d055b046ec46d9cc831b750.tar.gz
cpython-609346273903cd848d055b046ec46d9cc831b750.tar.bz2
Fix bug
[ 1005248 ] new.code() not cleanly checking its arguments using the result of new.code() can still destroy the sun, but merely calling the function shouldn't any more. I also rewrote the existing tests of new.code() to use vastly less un-bogus arguments, and added tests for the previous insane behaviours.
-rw-r--r--Lib/test/test_new.py68
-rw-r--r--Python/compile.c115
2 files changed, 150 insertions, 33 deletions
diff --git a/Lib/test/test_new.py b/Lib/test/test_new.py
index 48f184e..33eba14 100644
--- a/Lib/test/test_new.py
+++ b/Lib/test/test_new.py
@@ -1,4 +1,4 @@
-from test.test_support import verbose, verify
+from test.test_support import verbose, verify, TestFailed
import sys
import new
@@ -99,11 +99,67 @@ print 'new.code()'
# bogus test of new.code()
# Note: Jython will never have new.code()
if hasattr(new, 'code'):
- # XXX should use less criminally bogus arguments!
- d = new.code(3, 3, 3, 3, codestr, (), (), (),
- "<string>", "<name>", 1, "", (), ())
+ def f(a): pass
+
+ c = f.func_code
+ argcount = c.co_argcount
+ nlocals = c.co_nlocals
+ stacksize = c.co_stacksize
+ flags = c.co_flags
+ codestring = c.co_code
+ constants = c.co_consts
+ names = c.co_names
+ varnames = c.co_varnames
+ filename = c.co_filename
+ name = c.co_name
+ firstlineno = c.co_firstlineno
+ lnotab = c.co_lnotab
+ freevars = c.co_freevars
+ cellvars = c.co_cellvars
+
+ d = new.code(argcount, nlocals, stacksize, flags, codestring,
+ constants, names, varnames, filename, name,
+ firstlineno, lnotab, freevars, cellvars)
+
# test backwards-compatibility version with no freevars or cellvars
- d = new.code(3, 3, 3, 3, codestr, (), (), (),
- "<string>", "<name>", 1, "")
+ d = new.code(argcount, nlocals, stacksize, flags, codestring,
+ constants, names, varnames, filename, name,
+ firstlineno, lnotab)
+
+ try: # this used to trigger a SystemError
+ d = new.code(-argcount, nlocals, stacksize, flags, codestring,
+ constants, names, varnames, filename, name,
+ firstlineno, lnotab)
+ except ValueError:
+ pass
+ else:
+ raise TestFailed, "negative co_argcount didn't trigger an exception"
+
+ try: # this used to trigger a SystemError
+ d = new.code(argcount, -nlocals, stacksize, flags, codestring,
+ constants, names, varnames, filename, name,
+ firstlineno, lnotab)
+ except ValueError:
+ pass
+ else:
+ raise TestFailed, "negative co_nlocals didn't trigger an exception"
+
+ try: # this used to trigger a Py_FatalError!
+ d = new.code(argcount, nlocals, stacksize, flags, codestring,
+ constants, (5,), varnames, filename, name,
+ firstlineno, lnotab)
+ except TypeError:
+ pass
+ else:
+ raise TestFailed, "non-string co_name didn't trigger an exception"
+
+ # new.code used to be a way to mutate a tuple...
+ class S(str): pass
+ t = (S("ab"),)
+ d = new.code(argcount, nlocals, stacksize, flags, codestring,
+ constants, t, varnames, filename, name,
+ firstlineno, lnotab)
+ verify(type(t[0]) is S, "eek, tuple changed under us!")
+
if verbose:
print d
diff --git a/Python/compile.c b/Python/compile.c
index 5c67987..0c405cc 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -88,6 +88,50 @@ static PyMemberDef code_memberlist[] = {
{NULL} /* Sentinel */
};
+/* Helper for code_new: return a shallow copy of a tuple that is
+ guaranteed to contain exact strings, by converting string subclasses
+ to exact strings and complaining if a non-string is found. */
+static PyObject*
+validate_and_copy_tuple(PyObject *tup)
+{
+ PyObject *newtuple;
+ PyObject *item;
+ int i, len;
+
+ len = PyTuple_GET_SIZE(tup);
+ newtuple = PyTuple_New(len);
+ if (newtuple == NULL)
+ return NULL;
+
+ for (i = 0; i < len; i++) {
+ item = PyTuple_GET_ITEM(tup, i);
+ if (PyString_CheckExact(item)) {
+ Py_INCREF(item);
+ }
+ else if (!PyString_Check(item)) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "name tuples must contain only "
+ "strings, not '%.500s'",
+ item->ob_type->tp_name);
+ Py_DECREF(newtuple);
+ return NULL;
+ }
+ else {
+ item = PyString_FromStringAndSize(
+ PyString_AS_STRING(item),
+ PyString_GET_SIZE(item));
+ if (item == NULL) {
+ Py_DECREF(newtuple);
+ return NULL;
+ }
+ }
+ PyTuple_SET_ITEM(newtuple, i, item);
+ }
+
+ return newtuple;
+}
+
PyDoc_STRVAR(code_doc,
"code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n\
varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\
@@ -101,14 +145,13 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw)
int nlocals;
int stacksize;
int flags;
- PyObject *co;
- PyObject *empty = NULL;
+ PyObject *co = NULL;;
PyObject *code;
PyObject *consts;
- PyObject *names;
- PyObject *varnames;
- PyObject *freevars = NULL;
- PyObject *cellvars = NULL;
+ PyObject *names, *ournames = NULL;
+ PyObject *varnames, *ourvarnames = NULL;
+ PyObject *freevars = NULL, *ourfreevars = NULL;
+ PyObject *cellvars = NULL, *ourcellvars = NULL;
PyObject *filename;
PyObject *name;
int firstlineno;
@@ -126,27 +169,48 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw)
&PyTuple_Type, &cellvars))
return NULL;
- if (!PyObject_CheckReadBuffer(code)) {
- PyErr_SetString(PyExc_TypeError,
- "bytecode object must be a single-segment read-only buffer");
- return NULL;
+ if (argcount < 0) {
+ PyErr_SetString(
+ PyExc_ValueError,
+ "code: argcount must not be negative");
+ goto cleanup;
}
- if (freevars == NULL || cellvars == NULL) {
- empty = PyTuple_New(0);
- if (empty == NULL)
- return NULL;
- if (freevars == NULL)
- freevars = empty;
- if (cellvars == NULL)
- cellvars = empty;
+ if (nlocals < 0) {
+ PyErr_SetString(
+ PyExc_ValueError,
+ "code: nlocals must not be negative");
+ goto cleanup;
}
+ ournames = validate_and_copy_tuple(names);
+ if (ournames == NULL)
+ goto cleanup;
+ ourvarnames = validate_and_copy_tuple(varnames);
+ if (ourvarnames == NULL)
+ goto cleanup;
+ if (freevars)
+ ourfreevars = validate_and_copy_tuple(freevars);
+ else
+ ourfreevars = PyTuple_New(0);
+ if (ourfreevars == NULL)
+ goto cleanup;
+ if (cellvars)
+ ourcellvars = validate_and_copy_tuple(cellvars);
+ else
+ ourcellvars = PyTuple_New(0);
+ if (ourcellvars == NULL)
+ goto cleanup;
+
co = (PyObject *) PyCode_New(argcount, nlocals, stacksize, flags,
- code, consts, names, varnames,
- freevars, cellvars, filename, name,
- firstlineno, lnotab);
- Py_XDECREF(empty);
+ code, consts, ournames, ourvarnames,
+ ourfreevars, ourcellvars, filename,
+ name, firstlineno, lnotab);
+ cleanup:
+ Py_XDECREF(ournames);
+ Py_XDECREF(ourvarnames);
+ Py_XDECREF(ourfreevars);
+ Py_XDECREF(ourcellvars);
return co;
}
@@ -302,21 +366,18 @@ all_name_chars(unsigned char *s)
return 1;
}
-static int
+static void
intern_strings(PyObject *tuple)
{
int i;
for (i = PyTuple_GET_SIZE(tuple); --i >= 0; ) {
PyObject *v = PyTuple_GET_ITEM(tuple, i);
- if (v == NULL || !PyString_Check(v)) {
+ if (v == NULL || !PyString_CheckExact(v)) {
Py_FatalError("non-string found in code slot");
- PyErr_BadInternalCall();
- return -1;
}
PyString_InternInPlace(&PyTuple_GET_ITEM(tuple, i));
}
- return 0;
}
#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1]))