Commits

Bob Ippolito  committed 308ca92

Ignore "returnsSelf" and "isInitializer" when
PYOBJC_NEW_INITIALIZATION_PATTERN is defined. This makes PyObjC use the
canonical designated initializer pattern, namely, anything called after
alloc should be considered the designated initializer.

Pros:

- It makes PyObjC behave more like ObjC.
- Correct code no longer crashes or requires selector wrappers.

Cons:

- Code that does not initialize properly is probably going to crash, where
it was previously throwing a warning and leaking some memory.

  • Participants
  • Parent commits 3a289df
  • Branches pyobjc-ancient

Comments (0)

Files changed (7)

File Lib/objc/test/test_regr.py

         """
         Check that calling methods on unitialized objects raises an error
         """
+        # expected to fail with PYOBJC_NEW_INITIALIZER_PATTERN
         import warnings
         import Foundation
 

File Modules/objc/libffi_support.m

 		Py_INCREF(Py_None);
 		objc_result =  Py_None;
 	}
+#if defined(PYOBJC_NEW_INITIALIZER_PATTERN)
+	if (objc_result != self
+		&& PyObjCObject_Check(self) && PyObjCObject_Check(objc_result)
+		&& !(flags & PyObjCSelector_kRETURNS_UNINITIALIZED)
+		&& (((PyObjCObject*)self)->flags & PyObjCObject_kUNINITIALIZED)) {
+		[PyObjCObject_GetObject(objc_result) release];
+		PyObjCObject_ClearObject(self);
+	}
+#else
 	if ( (flags & PyObjCSelector_kRETURNS_SELF)
 		&& (objc_result != self)) {
 
 		}
 		PyObjCObject_ClearObject(self);
 	}
+#endif /* PYOBJC_NEW_INITIALIZER_PATTERN */
 
 	if (byref_out_count == 0) {
 		result = objc_result;

File Modules/objc/objc-class.m

 		if (!recursive) break;
 		if (info->class->super_class == NULL) break;
 		cls = PyObjCClass_New(info->class->super_class);
-			/* ^^^ REFCNT leak!! */
+			/* XXX REFCNT leak!! */
 		info = get_class_info(cls);
 
 	}

File Modules/objc/objc-object.m

 #include <objc/Object.h>
 
 
+
 /*
  * Basic freelist. 
  * - to delete an object: obj_freelist[obj_freelist_top++] = OBJ
 			"bad argument for register_proxy");
 		return -1;
 	}
+	assert(objc_obj != nil);
 
 	if (proxy_dict == NULL)  {
 		proxy_dict =  NSCreateMapTable(
 
 static PyObject** _get_dictptr(PyObject* obj)
 {
-	int dictoffset = PyObjCClass_DictOffset((PyObject*)obj->ob_type);
-	
+	int dictoffset;
+	id obj_object;
+	dictoffset = PyObjCClass_DictOffset((PyObject*)obj->ob_type);
 	if (dictoffset == 0) return NULL;
-	
-	return (PyObject**)(((char*)PyObjCObject_GetObject(obj)) + dictoffset);
+	obj_object = PyObjCObject_GetObject(obj);
+	assert(obj_object != nil);
+	return (PyObject**)(((char*)obj_object) + dictoffset);
 }
 
 
 	descrgetfunc f;
 	PyObject **dictptr;
 	char*      namestr;
+	Class obj_class;
+	id obj_inst;
 
 	if (!PyString_Check(name)){
 #ifdef Py_USING_UNICODE
 	else
 		Py_INCREF(name);
 
+	namestr = PyString_AS_STRING(name);
+
 	/* Special hack for KVO on MacOS X, when an object is observed it's 
 	 * ISA is changed by the runtime. We change the python type as well.
 	 */
-	tp = (PyTypeObject*)PyObjCClass_New(GETISA(PyObjCObject_GetObject(obj)));
+	obj_inst = PyObjCObject_GetObject(obj);
+	assert(obj_inst != nil);
+	obj_class = GETISA(obj_inst);
+	tp = (PyTypeObject*)PyObjCClass_New(obj_class);
 
 	descr = NULL;
 
 		goto done;
 	}
 
-	namestr = PyString_AS_STRING(name);
-
 	if (!PyObjCObject_IsClassic(obj)) {
 		res = PyObjCSelector_FindNative(obj, namestr);
 		if (res) goto done;
 PyDoc_STRVAR(objc_get_real_class_doc, "Return the current ISA of the object");
 static PyObject* objc_get_real_class(PyObject* self, void* closure __attribute__((__unused__)))
 {
-	PyObject* ret = PyObjCClass_New(GETISA(PyObjCObject_GetObject(self)));
+	id obj_object;
+	PyObject* ret;
+	obj_object = PyObjCObject_GetObject(self);
+	assert(obj_object != nil);
+	ret = PyObjCClass_New(GETISA(obj_object));
 	if (ret != (PyObject*)self->ob_type) {
+		/* XXX doesn't this leak a reference to the original ob_type? */
 		self->ob_type = (PyTypeObject*)ret;
 		Py_INCREF(ret);
 	}
 static PyObject*
 meth_reduce(PyObject* self __attribute__((__unused__)))
 {
-        PyErr_SetString(PyExc_TypeError,
+	PyErr_SetString(PyExc_TypeError,
 		"Cannot pickle Objective-C objects");
 	return NULL;
 }
 	PyObject* res;
 	PyTypeObject* cls_type;
 
+	assert(objc_object != nil);
 	cls = GETISA(objc_object);
 	cls_type = (PyTypeObject*)PyObjCClass_New(cls);
 	if (cls_type == NULL) {
 
 	PyObjCClass_CheckMethodList((PyObject*)res->ob_type, 1);
 	
+	assert(objc_object != nil);
 	((PyObjCObject*)res)->objc_object = objc_object;
 	((PyObjCObject*)res)->flags = PyObjCObject_kDEALLOC_HELPER;
 	return res;
 	 */
 	PyObjCClass_CheckMethodList((PyObject*)res->ob_type, 1);
 	
+	assert(objc_object != nil);
 	((PyObjCObject*)res)->objc_object = objc_object;
 	((PyObjCObject*)res)->flags = 0;
 
 	 */
 	PyObjCClass_CheckMethodList((PyObject*)res->ob_type, 1);
 	
+	assert(objc_object != nil);
 	((PyObjCObject*)res)->objc_object = objc_object;
 	((PyObjCObject*)res)->flags = PyObjCObject_kCLASSIC;
 
 	 */
 	PyObjCClass_CheckMethodList((PyObject*)res->ob_type, 1);
 	
+	assert(objc_object != nil);
 	((PyObjCObject*)res)->objc_object = objc_object;
 	((PyObjCObject*)res)->flags = 0;
 
 	}	
 }
 
-id        
+id
 (PyObjCObject_GetObject)(PyObject* object)
 {
 	if (!PyObjCObject_Check(object)) {
 void        
 PyObjCObject_ClearObject(PyObject* object)
 {
+    if (object == NULL) abort();
 	if (!PyObjCObject_Check(object)) {
 		PyErr_Format(PyExc_TypeError,
 			"'objc.objc_object' expected, got '%s'",

File Modules/objc/objc_util.m

 PyGILState_STATE PyObjCGILState_Ensure(void)
 {
 	/* Now that PyObjCUnicode_New no longer uses autorelead objects it 
-	 * should no longer be necessaryt to create a transient release-pool
+	 * should no longer be necessary to create a transient release-pool
 	 * for calls from ObjC to python. 
 	 * The pool might also be unsafe because at least some python methods
 	 * return objects whose only reference is in the autorelease pool (

File Modules/objc/selector.m

 		self->sel_call_func = execute;
 	}
 
+#if !defined(PYOBJC_NEW_INITIALIZER_PATTERN)
 	if (self->sel_self && PyObjCObject_Check(self->sel_self) 
 	    && (((PyObjCObject*)self->sel_self)->flags & PyObjCObject_kUNINITIALIZED)
 	    && !(self->sel_flags & PyObjCSelector_kINITIALIZER)) {
 			return NULL;
 		}
 	}
+#endif /* ! PYOBJC_NEW_INITIALIZER_PATTERN */
 
 
 
 	if (self->sel_self != NULL) {
 		res = execute((PyObject*)self, self->sel_self, args);
 
+#if defined(PYOBJC_NEW_INITIALIZER_PATTERN)
+		if (((PyObjCObject*)self->sel_self)->flags & PyObjCObject_kUNINITIALIZED) {
+			if (self->sel_self != res && !PyErr_Occurred()) {
+				PyObjCObject_ClearObject(pyself);
+			}
+		}
+#else
 		if (self->sel_flags & PyObjCSelector_kINITIALIZER) {
 			if (self->sel_self != res && !PyErr_Occurred()) {
 				PyObjCObject_ClearObject(self->sel_self);
 			}
 		}
+#endif /* PYOBJC_NEW_INITIALIZER_PATTERN */
 	} else {
 		PyObject* arglist;
 		PyObject* myClass;
 		
 
 		res = execute((PyObject*)self, pyself, arglist);
+#if !defined(PYOBJC_NEW_INITIALIZER_PATTERN)
 		if (self->sel_flags & PyObjCSelector_kINITIALIZER) {
 			if (pyself != res && !PyErr_Occurred()) {
 				PyObjCObject_ClearObject(pyself);
 			}
 		}
+#endif /* ! PYOBJC_NEW_INITIALIZER_PATTERN */
 		Py_DECREF(arglist);
 	}
 
 		if (self->sel_flags & PyObjCSelector_kRETURNS_UNINITIALIZED) {
 			((PyObjCObject*)res)->flags |= PyObjCObject_kUNINITIALIZED;
 		}
+#if defined(PYOBJC_NEW_INITIALIZER_PATTERN)
+		else if (((PyObjCObject*)res)->flags & PyObjCObject_kUNINITIALIZED) {
+			((PyObjCObject*)res)->flags &= 
+				~PyObjCObject_kUNINITIALIZED;
+			if (self->sel_self && self->sel_self != res && !PyErr_Occurred()) {
+				PyObjCObject_ClearObject(self->sel_self);
+			}
+		}
+#else
 		if (self->sel_flags & PyObjCSelector_kINITIALIZER) {
 			if (((PyObjCObject*)res)->flags & PyObjCObject_kUNINITIALIZED)
 			{
 					~PyObjCObject_kUNINITIALIZED;
 			}
 		}
+#endif /* PYOBJC_NEW_INITIALIZER_PATTERN */
 				
 		if (self->sel_flags & PyObjCSelector_kDONATE_REF) {
 			/* Ownership transfered to us, but 'execute' method has
 	}
 
 	/* TODO: Do same if self->sel_self is NULL */
+#if !defined(PYOBJC_NEW_INITIALIZER_PATTERN)
 	if ( !(self->sel_flags & PyObjCSelector_kINITIALIZER)
 	     && (self->sel_self) && (PyObjCObject_Check(self->sel_self)) &&
 	     ((PyObjCObject*)self->sel_self)->flags & PyObjCObject_kUNINITIALIZED) {
 			return NULL;
 		}
 	}
+#endif /* ! PYOBJC_NEW_INITIALIZER_PATTERN */
 
 	/*
 	 * Assume callable will check arguments
 		Py_DECREF(actual_args);
 	}
 
+#if defined(PYOBJC_NEW_INITIALIZER_PATTERN)
+	if ( result && (self->sel_self) && (PyObjCObject_Check(self->sel_self)) &&
+	     ((PyObjCObject*)self->sel_self)->flags & PyObjCObject_kUNINITIALIZED) {
+
+	     ((PyObjCObject*)self->sel_self)->flags &= ~PyObjCObject_kUNINITIALIZED;
+	}
+#else
 	/* TODO: Do same if self->sel_self is NULL */
 	if ( result && (self->sel_flags & PyObjCSelector_kINITIALIZER)
 	     && (self->sel_self) && (PyObjCObject_Check(self->sel_self)) &&
 	     ((PyObjCObject*)self->sel_self)->flags &= ~PyObjCObject_kUNINITIALIZED;
 	    
 	}
+#endif /* PYOBJC_NEW_INITIALIZER_PATTERN */
 
 	return result;
 }
     LIBFFI_CFLAGS=[
         "-isystem", "%s/include"%LIBFFI_BASE,
     ]
+    if os.path.exists('%s/lib/gcc/include/libffi'%LIBFFI_BASE):
+        LIBFFI_CFLAGS.extend([
+            "-isystem", "%s/lib/gcc/include/libffi"%LIBFFI_BASE,
+        ])
     LIBFFI_LDFLAGS=[
         '-L%s/lib'%LIBFFI_BASE, '-lffi',
     ]
         sys.exit(1)
 
     CFLAGS=[
+        "-DPYOBJC_NEW_INITIALIZER_PATTERN",
         "-DMACOSX",
         "-DAPPLE_RUNTIME",
         "-no-cpp-precomp",
         #"-pedantic",
 
         "-Wno-import",
-        #"-O0", "-g",
         #"-Werror",
+
+        # no optimization, for debugging
+        #"-O0"
+
+        # g4 optimized
         #"-O3", "-mcpu=7450", "-maltivec",
+
+        # g5 optimized
+        #"-fast", "-fPIC"
         ]
 
     OBJC_LDFLAGS=[