diff --git a/sipgen/gencode.c b/sipgen/gencode.c --- a/sipgen/gencode.c +++ b/sipgen/gencode.c @@ -4574,7 +4574,7 @@ { argType atype = vd->type.atype; const char *first_arg, *second_arg, *last_arg; - int needsNew, key; + int needsNew, var_key, self_key; if (generating_c || !isStaticVar(vd)) first_arg = "sipSelf"; @@ -4584,9 +4584,33 @@ last_arg = (generating_c || usedInCode(vd->getcode, "sipPyType")) ? "sipPyType" : ""; needsNew = ((atype == class_type || atype == mapped_type) && vd->type.nrderefs == 0 && isConstArg(&vd->type)); - key = ((atype == class_type || atype == mapped_type) && vd->type.nrderefs == 0) ? vd->module->next_key-- : 0; - - second_arg = (generating_c || (key < 0 && !isStaticVar(vd))) ? "sipPySelf" : ""; + + /* + * If the variable is itself a non-const instance of a wrapped class then + * two things must happen. Firstly, the getter must return the same Python + * object each time - it must not re-wrap the the instance. This is + * because the Python object can contain important state information that + * must not be lost (specifically references to other Python objects that + * it owns). Therefore the Python object wrapping the containing class + * must cache a reference to the Python object wrapping the variable. + * Secondly, the Python object wrapping the containing class must not be + * garbage collected before the Python object wrapping the variable is + * (because the latter references memory, ie. the variable itself, that is + * managed by the former). Therefore the Python object wrapping the + * variable must keep a reference to the Python object wrapping the + * containing class (but only if the latter is non-static). + */ + var_key = self_key = 0; + + if (atype == class_type && vd->type.nrderefs == 0 && !isConstArg(&vd->type)) + { + var_key = vd->type.u.cd->iff->module->next_key--; + + if (!isStaticVar(vd)) + self_key = vd->module->next_key--; + } + + second_arg = (generating_c || var_key < 0 ) ? "sipPySelf" : ""; prcode(fp, "\n" @@ -4609,7 +4633,7 @@ " PyObject *sipPy;\n" ); } - else if (key < 0) + else if (var_key < 0) { if (isStaticVar(vd)) prcode(fp, @@ -4662,10 +4686,10 @@ return; } - if (key < 0) + /* Get any previously wrapped cached object. */ + if (var_key < 0) { if (isStaticVar(vd)) - { prcode(fp, " if (sipPy)\n" " {\n" @@ -4674,16 +4698,14 @@ " }\n" "\n" ); - } - else - { + else prcode(fp, " sipPy = sipGetReference(sipPySelf, %d);\n" +"\n" " if (sipPy)\n" " return sipPy;\n" "\n" - , key); - } + , self_key); } if (needsNew) @@ -4723,7 +4745,7 @@ iff = vd->type.u.cd->iff; prcode(fp, -" %s sipConvertFrom%sType(", (key < 0 ? "sipPy =" : "return"), (needsNew ? "New" : "")); +" %s sipConvertFrom%sType(", (var_key < 0 ? "sipPy =" : "return"), (needsNew ? "New" : "")); if (isConstArg(&vd->type)) prcode(fp, "const_cast<%b *>(sipVal)", &vd->type); @@ -4733,18 +4755,26 @@ prcode(fp, ", sipType_%C, NULL);\n" , iff->fqcname); - if (key < 0) - { + if (var_key < 0) + { + prcode(fp, +"\n" +" if (sipPy)\n" +" {\n" +" sipKeepReference(sipPy, %d, sipPySelf);\n" + , var_key); + if (isStaticVar(vd)) prcode(fp, -" Py_XINCREF(sipPy);\n" +" Py_INCREF(sipPy);\n" ); else prcode(fp, -" sipKeepReference(sipPySelf, %d, sipPy);\n" - , key); - - prcode(fp, +" sipKeepReference(sipPySelf, %d, sipPy);\n" + , self_key); + + prcode(fp, +" }\n" "\n" " return sipPy;\n" ); @@ -4983,6 +5013,10 @@ char *deref; int might_be_temp, keep, need_py, need_cpp; + /* + * We need to keep a reference to the original Python object if it + * providing the memory that the C/C++ variable is pointing to. + */ keep = keepPyReference(&vd->type); if (generating_c || !isStaticVar(vd)) @@ -5149,12 +5183,10 @@ } else { - vd->type.key = scope->module->next_key--; - prcode(fp, "\n" " sipKeepReference(sipPySelf, %d, sipPy);\n" - , vd->type.key); + , scope->module->next_key--); } } diff --git a/siplib/sip.h.in.in b/siplib/sip.h.in.in --- a/siplib/sip.h.in.in +++ b/siplib/sip.h.in.in @@ -1510,6 +1510,8 @@ sipSimpleWrapper *, sip_gilstate_t); int (*api_init_mixin)(PyObject *self, PyObject *args, PyObject *kwds, const sipClassTypeDef *ctd); + PyObject *(*api_get_reference)(PyObject *self, int key); + /* * The following are part of the public API. */ @@ -1530,10 +1532,6 @@ */ PyObject *(*api_invoke_slot_ex)(const sipSlot *slot, PyObject *sigargs, int check_receiver); - /* - * The following are not part of the public API. - */ - PyObject *(*api_get_reference)(PyObject *self, int key); } sipAPIDef; diff --git a/siplib/siplib.c.in b/siplib/siplib.c.in --- a/siplib/siplib.c.in +++ b/siplib/siplib.c.in @@ -407,6 +407,7 @@ sip_api_parse_result_ex, sip_api_call_error_handler, sip_api_init_mixin, + sip_api_get_reference, /* * The following are part of the public API. */ @@ -420,10 +421,6 @@ * code. */ sip_api_invoke_slot_ex, - /* - * The following are not part of the public API. - */ - sip_api_get_reference, }; @@ -8534,6 +8531,7 @@ return NULL; obj = PyDict_GetItem(dict, key_obj); + Py_DECREF(key_obj); Py_XINCREF(obj); return obj;