Commits

Ronald Oussoren  committed 8beb6c8

Remove reference to the python object when destroying the objective-C half
of a python/objective-C hybrid object.

  • Participants
  • Parent commits a49b706

Comments (0)

Files changed (3)

File pyobjc/ChangeLog

+2002-12-15 Ronald Oussoren <oussoren@cistron.nl>
+    * Erase reference to the Python half before doing the [super release] 
+      call that will release the Objective-C half of an instance of a
+      Python subclass of an Objective-C class. This way we can't accidently
+      call into the, already dead, python object. 
+
 2002-12-08  Bill Bumgarner  <bbum@codefab.com>
     * Examples/WebServicesTool: Added a lot of inline documentation to the
           python code.

File pyobjc/Modules/objc/class-builder.m

 			"normal object of class %s", obj->isa->name);
 		return NULL;
 	}
-
 	if (pyobj == NULL) {
 		return Py_None;
 	}
+
 	return pyobj;
 }
 
+static int
+ObjC_SetPythonImplementation(id obj, PyObject* newval)
+{
+	Ivar      var   = NULL;
+
+	if (obj == nil) {
+		ObjCErr_Set(objc_internal_error,
+			"ObjC_GetPythonImplementation called for <nil>");
+		return -1;
+	}
+
+	var = class_getInstanceVariable(obj->isa, pyobj_ivar);
+	if (var == NULL) {
+		ObjCErr_Set(objc_internal_error,
+			"ObjC_SetPythonImplementation called for "
+			"normal object of class %s", obj->isa->name);
+		return -1;
+	}
+	*(PyObject**)(((char*)obj)+var->ivar_offset) = newval;
+	return 0;
+}
+
 /*
  * Last step of the construction a python subclass of an objective-C class.
  *
    pyobject->is_paired = 1;
 
    /* obj->__pyobjc_obj__ = pyobjct */ 
-   if (object_setInstanceVariable(obj, pyobj_ivar, pyobject) == NULL) {
-       ObjCErr_Set(objc_internal_error, "Cannot set python reference");
+   if (ObjC_SetPythonImplementation(obj, (PyObject*)pyobject) == -1) {
        Py_DECREF(pyobject);
        ObjCErr_ToObjC();
        return nil;
    }
-
    return obj;
 }
 
 
 	pyself = ObjC_GetPythonImplementation(self);
 
-	if (pyself) {
+	if (pyself == Py_None) {
+		struct objc_super super;
+		
+   		super.class = find_real_superclass(self->isa, 
+			@selector(retain), class_getInstanceMethod, 
+			(IMP)object_method_retain);
+		super.receiver = self;
+
+		self = objc_msgSendSuper(&super, @selector(retain)); 
+	} else if (pyself) {
+		OC_CheckRevive(pyself);
 		Py_INCREF(pyself);
 	} else {
 		PyErr_Clear();
 	if (obj == NULL) {
 		PyErr_Clear();
 		return;
+	} else if (obj == Py_None) {
+   		super.class = find_real_superclass(self->isa, 
+			@selector(release), class_getInstanceMethod, 
+			(IMP)object_method_release);
+		super.receiver = self;
+
+		self = objc_msgSendSuper(&super, @selector(release)); 
+		return;
 	}
 
-	if (obj->ob_refcnt == 0) {
+	if (obj->ob_refcnt <= 0) {
+		
+		/* Remove reference to the Python object. We don't need it
+		 * any more (because the ObjCObject code will remove it 
+		 * when this function returns) and [super release] may 
+		 * call back to us some time later on ([NSWindow release] in
+		 * a seperator thread).
+		 */
+		if (ObjC_SetPythonImplementation(self, 0) == -1) {
+		       ObjCErr_ToObjC();
+		       return;
+		}
+
 		/* [super release] */
    		super.class = find_real_superclass(self->isa, 
 			@selector(release), class_getInstanceMethod, 
 /* -retainCount */
 static unsigned object_method_retainCount(id self, SEL sel)
 {
-	return ObjC_GetPythonImplementation(self)->ob_refcnt;
+	PyObject* obj = ObjC_GetPythonImplementation(self);
+
+	if (obj == Py_None) {
+		struct objc_super super;
+		
+   		super.class = find_real_superclass(self->isa, 
+			@selector(retainCount), class_getInstanceMethod, 
+			(IMP)object_method_retainCount);
+		super.receiver = self;
+
+		return (int)objc_msgSendSuper(&super, @selector(retainCount)); 
+	}
+	return obj->ob_refcnt;
 }
 
 /* -respondsToSelector: */

File pyobjc/Modules/objc/pyobjc.h

 
 int     ObjCIPVerify(PyObject* obj, PyObject* cls);
 PyObject* ObjCIPFindInfo(PyObject* obj, SEL selector);
+#ifdef OBJC_PARANOIA_MODE
 
+#define OC_CheckRevive(obj)     \
+        do {  \
+                if ((obj)->ob_refcnt == 0) {                            \
+                        PySys_WriteStderr("%s:%d %s revives %p\n",      \
+                                      __FILE__, __LINE__, __FUNCTION__, \
+                                      (obj)); \
+                        abort(); \
+                } \
+	} while (0)
 
+#else /* !OBJC_PARANOIA_MODE */
+
+#define OC_CheckRevive(obj)
+
+#endif /* !OBJC_PARANOIA_MODE */
 
 #endif /* META_H */