Index: Python/peephole.c =================================================================== --- Python/peephole.c (revision 72994) +++ Python/peephole.c (working copy) @@ -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; } @@ -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 72994) +++ Python/marshal.c (working copy) @@ -43,6 +43,8 @@ #define TYPE_UNKNOWN '?' #define TYPE_SET '<' #define TYPE_FROZENSET '>' +#define TYPE_FUNCTION 'm' +#define TYPE_METHOD_WRAPPER 'M' typedef struct { FILE *fp; @@ -433,6 +435,17 @@ 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 if (Py_TYPE(v) == &wrappertype) { + w_byte(TYPE_METHOD_WRAPPER, p); + w_object(((wrapperobject*)v)->self, p); + w_object(wrapper_name((wrapperobject*)v), p); + } else { w_byte(TYPE_UNKNOWN, p); p->error = 1; @@ -1036,6 +1049,32 @@ retval = v; break; + case TYPE_FUNCTION: + v = r_object(p); + if (v == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "NULL object in marshal data for function"); + } + retval = NULL; + break; + } + retval = PyObject_GetAttr(v, r_object(p)); + break; + + case TYPE_METHOD_WRAPPER: + v = r_object(p); + if (v == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "NULL object in marshal data for method-wrapper"); + } + retval = NULL; + break; + } + 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: Include/descrobject.h =================================================================== --- Include/descrobject.h (revision 72994) +++ Include/descrobject.h (working copy) @@ -87,8 +87,16 @@ PyAPI_DATA(PyTypeObject) PyProperty_Type; + +typedef struct { + PyObject_HEAD + PyWrapperDescrObject *descr; + PyObject *self; +} wrapperobject; + +PyAPI_DATA(PyTypeObject) wrappertype; +PyAPI_FUNC(PyObject *) wrapper_name(wrapperobject *wp); #ifdef __cplusplus } #endif #endif /* !Py_DESCROBJECT_H */ - Index: Objects/descrobject.c =================================================================== --- Objects/descrobject.c (revision 72994) +++ Objects/descrobject.c (working copy) @@ -877,12 +877,6 @@ /* This has no reason to be in this file except that adding new files is a bit of a pain */ -typedef struct { - PyObject_HEAD - PyWrapperDescrObject *descr; - PyObject *self; -} wrapperobject; - static void wrapper_dealloc(wrapperobject *wp) { @@ -942,7 +936,7 @@ return c; } -static PyObject * +PyObject * wrapper_name(wrapperobject *wp) { char *s = wp->descr->d_base->name; @@ -1000,7 +994,7 @@ return 0; } -static PyTypeObject wrappertype = { +PyTypeObject wrappertype = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "method-wrapper", /* tp_name */ sizeof(wrapperobject), /* tp_basicsize */ @@ -1254,7 +1248,7 @@ PyObject *get = NULL, *set = NULL, *del = NULL, *doc = NULL; static char *kwlist[] = {"fget", "fset", "fdel", "doc", 0}; propertyobject *prop = (propertyobject *)self; - + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property", kwlist, &get, &set, &del, &doc)) return -1; Index: Lib/test/test_marshal.py =================================================================== --- Lib/test/test_marshal.py (revision 72994) +++ Lib/test/test_marshal.py (working copy) @@ -196,6 +196,18 @@ 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(['1', '2', '3']), new(['1', '2', '3'])) + self.assertNotEqual(id(m), id(new)) + + m = '%s'.__mod__ + new = marshal.loads(marshal.dumps(m)) + self.assertEqual(m(3), new(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 +282,7 @@ CodeTestCase, ContainerTestCase, ExceptionTestCase, + MethodTestCase, BugsTestCase) if __name__ == "__main__":