@@ -353,6 +353,69 @@ _Pickle_FastCall(PyObject *func, PyObject *obj)
353353
354354/*************************************************************************/
355355
356+ /* Retrieve and deconstruct a method for avoiding a reference cycle
357+ (pickler -> bound method of pickler -> pickler) */
358+ static int
359+ init_method_ref (PyObject * self , _Py_Identifier * name ,
360+ PyObject * * method_func , PyObject * * method_self )
361+ {
362+ PyObject * func , * func2 ;
363+
364+ /* *method_func and *method_self should be consistent. All refcount decrements
365+ should be occurred after setting *method_self and *method_func. */
366+ func = _PyObject_GetAttrId (self , name );
367+ if (func == NULL ) {
368+ * method_self = NULL ;
369+ Py_CLEAR (* method_func );
370+ if (!PyErr_ExceptionMatches (PyExc_AttributeError )) {
371+ return -1 ;
372+ }
373+ PyErr_Clear ();
374+ return 0 ;
375+ }
376+
377+ if (PyMethod_Check (func ) && PyMethod_GET_SELF (func ) == self ) {
378+ /* Deconstruct a bound Python method */
379+ func2 = PyMethod_GET_FUNCTION (func );
380+ Py_INCREF (func2 );
381+ * method_self = self ; /* borrowed */
382+ Py_XSETREF (* method_func , func2 );
383+ Py_DECREF (func );
384+ return 0 ;
385+ }
386+ else {
387+ * method_self = NULL ;
388+ Py_XSETREF (* method_func , func );
389+ return 0 ;
390+ }
391+ }
392+
393+ /* Bind a method if it was deconstructed */
394+ static PyObject *
395+ reconstruct_method (PyObject * func , PyObject * self )
396+ {
397+ if (self ) {
398+ return PyMethod_New (func , self );
399+ }
400+ else {
401+ Py_INCREF (func );
402+ return func ;
403+ }
404+ }
405+
406+ static PyObject *
407+ call_method (PyObject * func , PyObject * self , PyObject * obj )
408+ {
409+ if (self ) {
410+ return PyObject_CallFunctionObjArgs (func , self , obj , NULL );
411+ }
412+ else {
413+ return PyObject_CallFunctionObjArgs (func , obj , NULL );
414+ }
415+ }
416+
417+ /*************************************************************************/
418+
356419/* Internal data type used as the unpickling stack. */
357420typedef struct {
358421 PyObject_VAR_HEAD
@@ -545,6 +608,8 @@ typedef struct PicklerObject {
545608 objects to support self-referential objects
546609 pickling. */
547610 PyObject * pers_func ; /* persistent_id() method, can be NULL */
611+ PyObject * pers_func_self ; /* borrowed reference to self if pers_func
612+ is an unbound method, NULL otherwise */
548613 PyObject * dispatch_table ; /* private dispatch_table, can be NULL */
549614
550615 PyObject * write ; /* write() method of the output stream. */
@@ -583,6 +648,8 @@ typedef struct UnpicklerObject {
583648 Py_ssize_t memo_len ; /* Number of objects in the memo */
584649
585650 PyObject * pers_func ; /* persistent_load() method, can be NULL. */
651+ PyObject * pers_func_self ; /* borrowed reference to self if pers_func
652+ is an unbound method, NULL otherwise */
586653
587654 Py_buffer buffer ;
588655 char * input_buffer ;
@@ -3401,16 +3468,15 @@ save_type(PicklerObject *self, PyObject *obj)
34013468}
34023469
34033470static int
3404- save_pers (PicklerObject * self , PyObject * obj , PyObject * func )
3471+ save_pers (PicklerObject * self , PyObject * obj )
34053472{
34063473 PyObject * pid = NULL ;
34073474 int status = 0 ;
34083475
34093476 const char persid_op = PERSID ;
34103477 const char binpersid_op = BINPERSID ;
34113478
3412- Py_INCREF (obj );
3413- pid = _Pickle_FastCall (func , obj );
3479+ pid = call_method (self -> pers_func , self -> pers_func_self , obj );
34143480 if (pid == NULL )
34153481 return -1 ;
34163482
@@ -3788,7 +3854,7 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
37883854 0 if it did nothing successfully;
37893855 1 if a persistent id was saved.
37903856 */
3791- if ((status = save_pers (self , obj , self -> pers_func )) != 0 )
3857+ if ((status = save_pers (self , obj )) != 0 )
37923858 goto done ;
37933859 }
37943860
@@ -4203,13 +4269,10 @@ _pickle_Pickler___init___impl(PicklerObject *self, PyObject *file,
42034269 self -> fast_nesting = 0 ;
42044270 self -> fast_memo = NULL ;
42054271
4206- self -> pers_func = _PyObject_GetAttrId ((PyObject * )self ,
4207- & PyId_persistent_id );
4208- if (self -> pers_func == NULL ) {
4209- if (!PyErr_ExceptionMatches (PyExc_AttributeError )) {
4210- return -1 ;
4211- }
4212- PyErr_Clear ();
4272+ if (init_method_ref ((PyObject * )self , & PyId_persistent_id ,
4273+ & self -> pers_func , & self -> pers_func_self ) < 0 )
4274+ {
4275+ return -1 ;
42134276 }
42144277
42154278 self -> dispatch_table = _PyObject_GetAttrId ((PyObject * )self ,
@@ -4476,11 +4539,11 @@ Pickler_set_memo(PicklerObject *self, PyObject *obj)
44764539static PyObject *
44774540Pickler_get_persid (PicklerObject * self )
44784541{
4479- if (self -> pers_func == NULL )
4542+ if (self -> pers_func == NULL ) {
44804543 PyErr_SetString (PyExc_AttributeError , "persistent_id" );
4481- else
4482- Py_INCREF ( self -> pers_func );
4483- return self -> pers_func ;
4544+ return NULL ;
4545+ }
4546+ return reconstruct_method ( self -> pers_func , self -> pers_func_self ) ;
44844547}
44854548
44864549static int
@@ -4497,6 +4560,7 @@ Pickler_set_persid(PicklerObject *self, PyObject *value)
44974560 return -1 ;
44984561 }
44994562
4563+ self -> pers_func_self = NULL ;
45004564 Py_INCREF (value );
45014565 Py_XSETREF (self -> pers_func , value );
45024566
@@ -5446,7 +5510,7 @@ load_stack_global(UnpicklerObject *self)
54465510static int
54475511load_persid (UnpicklerObject * self )
54485512{
5449- PyObject * pid ;
5513+ PyObject * pid , * obj ;
54505514 Py_ssize_t len ;
54515515 char * s ;
54525516
@@ -5466,13 +5530,12 @@ load_persid(UnpicklerObject *self)
54665530 return -1 ;
54675531 }
54685532
5469- /* This does not leak since _Pickle_FastCall() steals the reference
5470- to pid first. */
5471- pid = _Pickle_FastCall (self -> pers_func , pid );
5472- if (pid == NULL )
5533+ obj = call_method (self -> pers_func , self -> pers_func_self , pid );
5534+ Py_DECREF (pid );
5535+ if (obj == NULL )
54735536 return -1 ;
54745537
5475- PDATA_PUSH (self -> stack , pid , -1 );
5538+ PDATA_PUSH (self -> stack , obj , -1 );
54765539 return 0 ;
54775540 }
54785541 else {
@@ -5487,20 +5550,19 @@ load_persid(UnpicklerObject *self)
54875550static int
54885551load_binpersid (UnpicklerObject * self )
54895552{
5490- PyObject * pid ;
5553+ PyObject * pid , * obj ;
54915554
54925555 if (self -> pers_func ) {
54935556 PDATA_POP (self -> stack , pid );
54945557 if (pid == NULL )
54955558 return -1 ;
54965559
5497- /* This does not leak since _Pickle_FastCall() steals the
5498- reference to pid first. */
5499- pid = _Pickle_FastCall (self -> pers_func , pid );
5500- if (pid == NULL )
5560+ obj = call_method (self -> pers_func , self -> pers_func_self , pid );
5561+ Py_DECREF (pid );
5562+ if (obj == NULL )
55015563 return -1 ;
55025564
5503- PDATA_PUSH (self -> stack , pid , -1 );
5565+ PDATA_PUSH (self -> stack , obj , -1 );
55045566 return 0 ;
55055567 }
55065568 else {
@@ -6637,13 +6699,10 @@ _pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file,
66376699
66386700 self -> fix_imports = fix_imports ;
66396701
6640- self -> pers_func = _PyObject_GetAttrId ((PyObject * )self ,
6641- & PyId_persistent_load );
6642- if (self -> pers_func == NULL ) {
6643- if (!PyErr_ExceptionMatches (PyExc_AttributeError )) {
6644- return -1 ;
6645- }
6646- PyErr_Clear ();
6702+ if (init_method_ref ((PyObject * )self , & PyId_persistent_load ,
6703+ & self -> pers_func , & self -> pers_func_self ) < 0 )
6704+ {
6705+ return -1 ;
66476706 }
66486707
66496708 self -> stack = (Pdata * )Pdata_New ();
@@ -6930,11 +6989,11 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
69306989static PyObject *
69316990Unpickler_get_persload (UnpicklerObject * self )
69326991{
6933- if (self -> pers_func == NULL )
6992+ if (self -> pers_func == NULL ) {
69346993 PyErr_SetString (PyExc_AttributeError , "persistent_load" );
6935- else
6936- Py_INCREF ( self -> pers_func );
6937- return self -> pers_func ;
6994+ return NULL ;
6995+ }
6996+ return reconstruct_method ( self -> pers_func , self -> pers_func_self ) ;
69386997}
69396998
69406999static int
@@ -6952,6 +7011,7 @@ Unpickler_set_persload(UnpicklerObject *self, PyObject *value)
69527011 return -1 ;
69537012 }
69547013
7014+ self -> pers_func_self = NULL ;
69557015 Py_INCREF (value );
69567016 Py_XSETREF (self -> pers_func , value );
69577017
0 commit comments