[PyObjC-svn] r2261 - in trunk/pyobjc/pyobjc-core: Modules/objc Modules/objc/test PyObjCTest

[email protected] Wed, 08 Jul 2009 06:10:17 -0500
Newsgroups gmane.comp.python.pyobjc.cvs
Message-ID <[email protected]>
Author: ronaldoussoren
Date: Wed Jul  8 06:10:17 2009
New Revision: 2261

Log:
This fixes a bug reported by Dirk Stoop: when
a python object is allocated from ObjC and the
Python class implements the designated initializer
the object will get leaked.


Modified:
   trunk/pyobjc/pyobjc-core/Modules/objc/libffi_support.m
   trunk/pyobjc/pyobjc-core/Modules/objc/test/copying.m
   trunk/pyobjc/pyobjc-core/PyObjCTest/test_regr.py

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/libffi_support.m
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/libffi_support.m	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/libffi_support.m	Wed Jul  8 06:10:17 2009
@@ -1221,11 +1221,8 @@
 	}
 
 	res = PyObject_Call(callable, arglist, NULL);
-
 	Py_DECREF(arglist);
-	if (pyself) {
-		PyObjCObject_ReleaseTransient(pyself, cookie);
-	}
+
 	if (res == NULL) {
 		goto error;
 	}
@@ -1756,9 +1753,16 @@
 			}
 		}
 
+	}
+	Py_DECREF(res);
 
-		Py_DECREF(res);
-
+	/* Do this at the end to ensure we work correctly when
+	 * 'res' is 'pyself' and 'pyself' those are the only
+	 * references from Python (that is, 'pyself' is a 
+	 * "transient" reference.
+	 */
+	if (pyself) {
+		PyObjCObject_ReleaseTransient(pyself, cookie);
 	}
 
 	PyGILState_Release(state);
@@ -1766,6 +1770,9 @@
 	return;
 
 error:
+	if (pyself) {
+		PyObjCObject_ReleaseTransient(pyself, cookie);
+	}
 	PyObjCErr_ToObjCWithGILState(&state);
 }
 
@@ -2018,6 +2025,7 @@
 		PyObjCPythonSelector *pythonSelector = (PyObjCPythonSelector *) aSelector;
 		PyObjCMethodSignature* methinfo = PyObjCMethodSignature_ForSelector(
 				pythonSelector->sel_class, 
+//				(pythonSelector->sel_flags & PyObjCSelector_kCLASS_METHOD) != 0,
 				pythonSelector->sel_selector,
 				pythonSelector->sel_python_signature);
 

Modified: trunk/pyobjc/pyobjc-core/Modules/objc/test/copying.m
==============================================================================
--- trunk/pyobjc/pyobjc-core/Modules/objc/test/copying.m	(original)
+++ trunk/pyobjc/pyobjc-core/Modules/objc/test/copying.m	Wed Jul  8 06:10:17 2009
@@ -13,6 +13,7 @@
 @interface OC_CopyHelper : NSObject
 { }
 +(NSObject*)doCopySetup:(Class)aClass;
++(NSObject*)newObjectOfClass:(Class)aClass;
 @end
 
 @implementation OC_CopyHelper
@@ -28,6 +29,10 @@
 	[tmp release];
 	return retval;
 }
++(NSObject*)newObjectOfClass:(Class)aClass
+{
+	return [[aClass alloc] init];
+}
 @end
 
 @interface OC_CopyBase : NSObject <NSCopying>

Modified: trunk/pyobjc/pyobjc-core/PyObjCTest/test_regr.py
==============================================================================
--- trunk/pyobjc/pyobjc-core/PyObjCTest/test_regr.py	(original)
+++ trunk/pyobjc/pyobjc-core/PyObjCTest/test_regr.py	Wed Jul  8 06:10:17 2009
@@ -1,9 +1,10 @@
 from PyObjCTools.TestSupport import *
 from PyObjCTest import structargs
 from PyObjCTest import testbndl
+from PyObjCTest import copying
 
 import objc, sys
-from PyObjCTest.fnd import NSObject
+from PyObjCTest.fnd import NSObject, NSAutoreleasePool
 
 rct = structargs.StructArgClass.someRect.__metadata__()['retval']['type']
 
@@ -150,5 +151,68 @@
         p = self.AlignmentTestClass.alloc().init()
         self.assertEquals(p.testWithObject_(o) % 16, o.stackPtr() % 16)
 
+
+
+#
+# Regression in retainCount management when a Python object
+# is created from Objective-C. This only happened when the
+# Python class has an implementation of the designated initializer.
+#
+# Mentioned by Dirk Stoop on the Pyobjc-dev mailing-list.
+#
+
+gDeallocCounter = 0
+class OC_LeakTest_20090704_init (NSObject):
+    def init(self):
+        #self = super(OC_LeakTest_20090704_init, self).init()
+        return self
+
+    def dealloc(self):
+        global gDeallocCounter
+        gDeallocCounter += 1
+
+class OC_LeakTest_20090704_noinit (NSObject):
+    def dealloc(self):
+        global gDeallocCounter 
+        gDeallocCounter += 1
+
+
+class TestInitMemoryLeak (TestCase):
+    def testNoPythonInit(self):
+        # This test is basicly a self-test of the test-case, the
+        # test even passed before the regression was fixed.
+
+        global gDeallocCounter
+
+        pool = NSAutoreleasePool.alloc().init()
+        try:
+            v = copying.OC_CopyHelper.newObjectOfClass_(OC_LeakTest_20090704_noinit)
+            self.failUnlessIsInstance(v, OC_LeakTest_20090704_noinit)
+
+            gDeallocCounter = 0
+            del v
+
+        finally:
+            del pool
+
+        self.failIfEqual(gDeallocCounter, 0)
+
+    def testWithPythonInit(self):
+        global gDeallocCounter
+
+        pool = NSAutoreleasePool.alloc().init()
+        try:
+            v = copying.OC_CopyHelper.newObjectOfClass_(OC_LeakTest_20090704_init)
+            self.failUnlessIsInstance(v, OC_LeakTest_20090704_init)
+
+            gDeallocCounter = 0
+            del v
+
+        finally:
+            del pool
+
+        self.failIfEqual(gDeallocCounter, 0)
+
+
 if __name__ == '__main__':
     main()

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge  
This is your chance to win up to $100,000 in prizes! For a limited time, 
vendors submitting new applications to BlackBerry App World(TM) will have
the opportunity to enter the BlackBerry Developer Challenge. See full prize  
details at: http://p.sf.net/sfu/Challenge