Index: Python/peephole.c =================================================================== --- Python/peephole.c (revision 72993) +++ Python/peephole.c (working copy) @@ -30,7 +30,7 @@ The consts table must still be in list form so that the new constant (c1, c2, ... cn) can be appended. Called with codestr pointing to the first LOAD_CONST. - Bails out with no change if one or more of the LOAD_CONSTs is missing. + Bails out with no change if one or more of the LOAD_CONSTs is missing. Also works for BUILD_LIST when followed by an "in" or "not in" test. */ static int @@ -78,8 +78,8 @@ with LOAD_CONST binop(c1,c2) The consts table must still be in list form so that the new constant can be appended. - Called with codestr pointing to the first LOAD_CONST. - Abandons the transformation if the folding fails (i.e. 1+'a'). + Called with codestr pointing to the first LOAD_CONST. + Abandons the transformation if the folding fails (i.e. 1+'a'). If the new constant is a sequence, only folds when the size is below a threshold value. That keeps pyc files from becoming large in the presence of code like: (None,)*1000. @@ -180,11 +180,12 @@ } static int -fold_unaryops_on_constants(unsigned char *codestr, PyObject *consts) +fold_unaryops_on_constants(unsigned char *codestr, PyObject *consts, + PyObject *names) { PyObject *newconst=NULL, *v; Py_ssize_t len_consts; - int opcode; + int opcode, i = 1; /* Pre-conditions */ assert(PyList_CheckExact(consts)); @@ -205,6 +206,11 @@ case UNARY_INVERT: newconst = PyNumber_Invert(v); break; + case LOAD_ATTR: + i = 3; + newconst = PyObject_GetAttr(v, + PyTuple_GET_ITEM(names, GETARG(codestr, 3))); + break; default: /* Called with an unknown opcode */ PyErr_Format(PyExc_SystemError, @@ -226,9 +232,9 @@ Py_DECREF(newconst); /* Write NOP LOAD_CONST newconst */ - codestr[0] = NOP; - codestr[1] = LOAD_CONST; - SETARG(codestr, 1, len_consts); + memset(codestr, NOP, i); + codestr[i] = LOAD_CONST; + SETARG(codestr, i, len_consts); return 1; } @@ -274,18 +280,18 @@ } /* Perform basic peephole optimizations to components of a code object. - The consts object should still be in list form to allow new constants + The consts object should still be in list form to allow new constants to be appended. To keep the optimizer simple, it bails out (does nothing) for code - containing extended arguments or that has a length over 32,700. That + containing extended arguments or that has a length over 32,700. That allows us to avoid overflow and sign issues. Likewise, it bails when the lineno table has complex encoding for gaps >= 255. Optimizations are restricted to simple transformations occuring within a - single basic block. All transformations keep the code size the same or - smaller. For those that reduce size, the gaps are initially filled with - NOPs. Later those NOPs are removed and the jump addresses retargeted in + single basic block. All transformations keep the code size the same or + smaller. For those that reduce size, the gaps are initially filled with + NOPs. Later those NOPs are removed and the jump addresses retargeted in a single pass. Line numbering is adjusted accordingly. */ PyObject * @@ -324,7 +330,7 @@ codestr = (unsigned char *)PyMem_Malloc(codelen); if (codestr == NULL) goto exitUnchanged; - codestr = (unsigned char *)memcpy(codestr, + codestr = (unsigned char *)memcpy(codestr, PyString_AS_STRING(code), codelen); /* Verify that RETURN_VALUE terminates the codestring. This allows @@ -353,7 +359,7 @@ cumlc = 0; switch (opcode) { - /* Replace UNARY_NOT POP_JUMP_IF_FALSE + /* Replace UNARY_NOT POP_JUMP_IF_FALSE with POP_JUMP_IF_TRUE */ case UNARY_NOT: if (codestr[i+1] != POP_JUMP_IF_FALSE @@ -373,7 +379,7 @@ case COMPARE_OP: j = GETARG(codestr, i); if (j < 6 || j > 9 || - codestr[i+3] != UNARY_NOT || + codestr[i+3] != UNARY_NOT || !ISBASICBLOCK(blocks,i,4)) continue; SETARG(codestr, i, (j^1)); @@ -394,7 +400,7 @@ } if (j == PyList_GET_SIZE(consts)) { if (PyList_Append(consts, Py_None) == -1) - goto exitUnchanged; + goto exitUnchanged; } assert(PyList_GET_ITEM(consts, j) == Py_None); codestr[i] = LOAD_CONST; @@ -427,10 +433,10 @@ h = i - 3 * j; if (h >= 0 && j <= lastlc && - ((opcode == BUILD_TUPLE && + ((opcode == BUILD_TUPLE && ISBASICBLOCK(blocks, h, 3*(j+1))) || - (opcode == BUILD_LIST && - codestr[i+3]==COMPARE_OP && + (opcode == BUILD_LIST && + codestr[i+3]==COMPARE_OP && ISBASICBLOCK(blocks, h, 3*(j+2)) && (GETARG(codestr,i+3)==6 || GETARG(codestr,i+3)==7))) && @@ -484,10 +490,13 @@ case UNARY_NEGATIVE: case UNARY_CONVERT: case UNARY_INVERT: + case LOAD_ATTR: if (lastlc >= 1 && ISBASICBLOCK(blocks, i-3, 4) && - fold_unaryops_on_constants(&codestr[i-3], consts)) { - i -= 2; + fold_unaryops_on_constants(&codestr[i-3], consts, names)) { + if (opcode != LOAD_ATTR) { + i -= 2; + } assert(codestr[i] == LOAD_CONST); cumlc = 1; } Index: Python/marshal.c =================================================================== --- Python/marshal.c (revision 72993) +++ Python/marshal.c (working copy) @@ -43,6 +43,7 @@ #define TYPE_UNKNOWN '?' #define TYPE_SET '<' #define TYPE_FROZENSET '>' +#define TYPE_FUNCTION 'm' typedef struct { FILE *fp; @@ -221,7 +222,7 @@ else if (PyFloat_CheckExact(v)) { if (p->version > 1) { unsigned char buf[8]; - if (_PyFloat_Pack8(PyFloat_AsDouble(v), + if (_PyFloat_Pack8(PyFloat_AsDouble(v), buf, 1) < 0) { p->error = 1; return; @@ -249,7 +250,7 @@ } w_byte(TYPE_BINARY_COMPLEX, p); w_string((char*)buf, 8, p); - if (_PyFloat_Pack8(PyComplex_ImagAsDouble(v), + if (_PyFloat_Pack8(PyComplex_ImagAsDouble(v), buf, 1) < 0) { p->error = 1; return; @@ -433,6 +434,12 @@ w_long((long)n, p); w_string(s, (int)n, p); } + else if (PyCFunction_Check(v)) { + /* Write C function out */ + w_byte(TYPE_FUNCTION, p); + w_object(PyCFunction_GET_SELF(v), p); + w_object(PyString_FromString(((PyCFunctionObject*)v)->m_ml->ml_name), p); + } else { w_byte(TYPE_UNKNOWN, p); p->error = 1; @@ -978,7 +985,7 @@ PyObject *name = NULL; int firstlineno; PyObject *lnotab = NULL; - + v = NULL; /* XXX ignore long->int overflows for now */ @@ -1036,6 +1043,11 @@ retval = v; break; + case TYPE_FUNCTION: + v = r_object(p); + retval = PyObject_GetAttr(v, r_object(p)); + break; + default: /* Bogus data got written, which isn't ideal. This will let you keep working and recover. */ Index: Lib/test/test_marshal.py =================================================================== --- Lib/test/test_marshal.py (revision 72993) +++ Lib/test/test_marshal.py (working copy) @@ -196,6 +196,14 @@ self.assertEqual(t, new) os.unlink(test_support.TESTFN) +class MethodTestCase(unittest.TestCase): + def test_method(self): + m = ', '.join + new = marshal.loads(marshal.dumps(m)) + self.assertEqual(m, new) + self.assertEqual(m(['1', '2', '3']), new(['1', '2', '3'])) + self.assertNotEqual(id(m), id(new)) + class BugsTestCase(unittest.TestCase): def test_bug_5888452(self): # Simple-minded check for SF 588452: Debug build crashes @@ -270,6 +278,7 @@ CodeTestCase, ContainerTestCase, ExceptionTestCase, + MethodTestCase, BugsTestCase) if __name__ == "__main__":