Commits

Ronald Oussoren  committed 03b596a

* Some code-cleanup
* Some, minor, performance enhancements
* Bugfixes in libffi support
* Initial support for recognizing that init methods might return a
new 'self', which most of the time means the old 'self' is no
longer valid.
* Add <class>.pyobjc_classMethods, <object>.pyobjc_classMethods and
<object>.pyobjc_instanceMethods. The attributes of these attributes
are the class and instance-methods of the <class> or <object>.
* Typo in "Project Templates/00README.txt"

  • Participants
  • Parent commits fa101ad
  • Branches pyobjc-ancient

Comments (0)

Files changed (18)

+2003-01-18  Ronald Oussoren <oussoren@cistron.nl>
+    * Remove the PyOBJC_UNIQUE_PROXY option, it is always on anyway.
+    * Remove mechanism for registering methods that return a 'new'
+      instead of a 'borrowed' reference. This mechanism is too
+      simplistic.
+    * Initial support for special support of init methods: These return
+      self, and if they don't the old value of self probably is no longer
+      valid. 
+    * Bugfixes in libffi support
+    * Some performance enhancements, the effects are fairly minor.
+      I'm not too happy about the new version for objc_methodlist_magic, I'll
+      probably revert this change later on.
+    * Add workaround for a feature of NSData: 
+    	x = NSData.alloc()
+	    y = x.initWithBytes_len_(foo, bar)
+      The initWithBytes_len_ call above acts like realloc(): 'x' might no
+      longer be a pointer to a valid object. 
+    * Add <class>.pyobjc_classMethods, <object>.pyobjc_classMethods and
+      <object>.pyobjc_instanceMethods. The attributes of these attributes
+      are the class and instance-methods of the <class> or <object>.
+    * Typo in "Project Templates/00README.txt"
+
 2003-01-18  Bill Bumgarner  <bbum@codefab.com>
 
     * Modules/objc/OC_PythonDictionary.m ([OC_PythonDictionary
       be in GNUstep?
     (objc_NSRectFillList): Fixed short-circuit where rectCount is 0.
 
-2002-01-11 Ronald Oussoren <oussoren@cistron.nl>
-    * Add workaround for a feature of NSData: 
-    	x = NSData.alloc()
-	    y = x.initWithBytes_len_(foo, bar)
-      The initWithBytes_len_ call above acts like realloc(): 'x' might no
-      longer be a pointer to a valid object. 
-
 2002-01-06 Ronald Oussoren <oussoren@cistron.nl>
     * Merged the second halve of Mirko's patch: Changes to Modules/Cocoa
     * I've done some updates to make ensure that there are no patches to

File Lib/objc/__init__.py

 del gl, nm, _objc, x
 
 
-#
-# Administration of methods that transfer ownership of objects to the
-# caller. This list is used by the runtime to automaticly correct the
-# refcount to the object.
-#
-# These must be set before any proxy classes are created.
-#
-# These 5 are documented in Apple's Objective-C book, in theory these
-# are the only methods that transfer ownership.
-#
-def register_allocator_selector(selector):
-    """
-    register 'selector' as a method that transfers ownership of the 
-    returned object to the caller. 
-    
-    This information is used by the proxy code to correctly maintain 
-    reference counts. It is highly unlikely that this function should
-    be called outside of the 'objc' module.
-    """
-    ALLOCATOR_METHODS[selector] = 1
-
-register_allocator_selector('alloc')
-register_allocator_selector('allocWithZone:')
-register_allocator_selector('copy')
-register_allocator_selector('copyWithZone:')
-register_allocator_selector('mutableCopyWithZone:')
-
-
-
 # Add usefull utility functions below
 
 

File Lib/objc/_convenience.py

     for sel in type_dict.values():
         if not isinstance(sel, selector):
             continue
+
+        
+        if sel.selector.startswith('init') and not sel.class_method:
+            # Instance methods that start with 'init*' are constructors. These
+            # return 'self'. If they don't they reallocated the previous 
+            # value, don't use that afterwards.
+            sel.returns_reallocated_self = 1
+
         sel = sel.selector
 
         if CONVENIENCE_METHODS.has_key(sel):

File Lib/objc/test/test_objc.py

         self.NSObjectInstance = objc.runtime.NSObject.alloc()
 
     def testClassInvocation(self):
-        self.assert_(objc.runtime.NSObject.description(objc.runtime.NSObject), "Failed to invoke the +description method.")
+        self.assert_(objc.runtime.NSObject.pyobjc_classMethods.description(), "Failed to invoke the +description method.")
 
     def testInstanceInvocation(self):
         self.assert_(self.NSObjectInstance.description(), "Failed to invoke the -description method.")
         self.assertEqual(self.NSObjectInstance.self(), self.NSObjectInstance, "-self did not return same self.")
+        self.assertEqual(self.NSObjectInstance.pyobjc_instanceMethods.self(), self.NSObjectInstance.self())
+        self.assertEqual(type(self.NSObjectInstance).pyobjc_instanceMethods.self(self.NSObjectInstance), self.NSObjectInstance.self())
 
     def testVarargsInvocation(self):
         objc.runtime.NSArray.arrayWithObjects_("foo", "bar", None)

File Modules/objc/libffi_support.m

 	int res = 0;
 
 	if (*argtype != _C_STRUCT_B) return -1;
+	while (*argtype != _C_STRUCT_E && *argtype != '=') argtype++;
+	if (*argtype == _C_STRUCT_E) return 0;
 	
 	argtype++;
 	while (*argtype != _C_STRUCT_E) {
 	
 	field_count = 0;
 	curtype = argtype+1;
-	while (*curtype != _C_STRUCT_E) {
-		type->elements[field_count] = signature_to_ffi_type(curtype);
-		if (type->elements[field_count] == NULL) {
-			free(type->elements);
-			return NULL;
+	while (*curtype != _C_STRUCT_E && *curtype != '=') curtype++;
+	if (*curtype == '=') {
+		curtype ++;
+		while (*curtype != _C_STRUCT_E) {
+			type->elements[field_count] = 
+				signature_to_ffi_type(curtype);
+			if (type->elements[field_count] == NULL) {
+				abort();
+				free(type->elements);
+				return NULL;
+			}
+			field_count++;
+			curtype = objc_skip_typespec(curtype);
 		}
-		field_count++;
-		curtype = objc_skip_typespec(curtype);
 	}
 	type->elements[field_count] = NULL;
 
 		return signature_to_ffi_type(argtype+1);
 	case _C_STRUCT_B: 
 		return struct_to_ffi_type(argtype);
+	case 0:
+		abort();
 	default:
 		ObjCErr_Set(PyExc_NotImplementedError,
-			"Type '%s' not supported", argtype);
+			"Type '%x' not supported", *argtype);
 		return NULL;
 	}
 }
 	int               resultSize;
 	int               arglistOffset;
 
-	methinfo = [NSMethodSignature signatureWithObjCTypes:meth->sel_signature];
+	if (meth->sel_oc_signature) {
+		methinfo = meth->sel_oc_signature;
+	} else {
+		methinfo = [NSMethodSignature signatureWithObjCTypes:meth->sel_signature];
+		meth->sel_oc_signature = methinfo;
+	}
 	objc_argcount = [methinfo numberOfArguments];
 	resultSize = objc_sizeof_type([methinfo methodReturnType]);
 
 				/* Allocate space and encode */
 				{
 					void* arg = argbuf + argbuf_cur;
-					argbuf_cur += objc_sizeof_type(argtype+2);
+					argbuf_cur += objc_sizeof_type(argtype+1);
 					byref[i] = argbuf_cur;
 	  				error = depythonify_c_value (
-						argtype+2, 
+						argtype+1, 
 						argument, 
 						arg);
 
 						arg);
 
 					arglist[arglistOffset + i] = signature_to_ffi_type(
-						argtype+2);
+						argtype+1);
 					values[arglistOffset + i] = arg;
 
 				}
 		Py_INCREF(Py_None);
 		objc_result =  Py_None;
 	}
+	if ( (meth->sel_flags & ObjCSelector_kRETURNS_SELF)
+		&& (objc_result != self)) {
 
+		/* meth is a method that returns a possibly reallocated
+		 * version of self and self != return-value, the current
+		 * value of self is assumed to be no longer valid
+		 */
+		ObjCObject_ClearObject(self);
+	}
 
 	if (byref_out_count == 0) {
 		result = objc_result;

File Modules/objc/method-accessor.m

+/*
+ * This file implements the object/type used to implement
+ *	anObject.pyobjc_classMethods.description()
+ * and
+ *	anObject.pyobjc_instanceMethods.description()
+ *
+ * NOTES:
+ *	Does not support reflection. That can be added when
+ *	needed.
+ */
+#include "pyobjc.h"
+#include "objc_support.h"
+
+static char* 
+flatten_signature(NSMethodSignature* sig, char* buf, int buflen)
+{
+	char* cur = buf;
+	int   curlen = buflen;
+	int   r;
+	int   i, len;
+
+	r = snprintf(cur, curlen, "%s", [sig methodReturnType]);
+	if (r >= curlen) goto error;
+	cur += r;
+	curlen -= r;
+
+	len = [sig numberOfArguments];
+	for (i = 0; i < len; i ++) {
+		r = snprintf(cur, curlen, "%s", [sig getArgumentTypeAtIndex:i]);
+		if (r >= curlen) goto error;
+		cur += r;
+		curlen -= r;
+	}
+	*cur = '\0';
+	return buf;
+
+error:
+	/* FIXME, however 1024 characters should be enough for any reasonable
+	 * signature. E.g. this can wait until we run into problems.
+	 */
+	abort();
+}
+
+static PyObject* 
+find_selector(PyObject* self, char* name, int class_method)
+{
+	SEL   sel = ObjCSelector_DefaultSelector(name);
+	id    objc_object;
+	NSMethodSignature* methsig;
+	char  buf[1024];
+	int   unbound_instance_method = 0;
+
+	if (ObjCClass_Check(self)) {
+		objc_object = (id)ObjCClass_GetClass(self);
+	
+		if (!class_method) {
+			unbound_instance_method = 1;
+		}
+	} else if (ObjCObject_Check(self)) {
+		objc_object = ObjCObject_GetObject(self);
+		if (objc_object == NULL) {
+			PyErr_SetString(PyExc_AttributeError, 
+				"nil has no methods");
+			return NULL;
+		}
+
+		if (class_method) {
+			objc_object = GETISA(objc_object);
+		}
+	} else {
+		ObjCErr_Set(PyExc_TypeError,
+			"Need Objective-C class or instance, got "
+			"a %s", self->ob_type->tp_name);
+		return NULL;
+	}
+
+	if (class_method && strcmp(((Class)objc_object)->name, "NSProxy") == 0){
+		if (sel == @selector(methodSignatureForSelector:)) {
+			ObjCErr_Set(PyExc_AttributeError,
+				"Cannot access NSProxy.%s", name);
+			return NULL;
+		}
+	}
+
+	if (unbound_instance_method) {
+		methsig = [objc_object instanceMethodSignatureForSelector:sel];
+	} else {
+		methsig = [objc_object methodSignatureForSelector:sel];
+	}
+
+	if (methsig == NULL) {
+		ObjCErr_Set(PyExc_AttributeError,
+			"No selector %s", name);
+		return NULL;
+	}
+
+	if (!class_method) {
+		objc_object = GETISA(objc_object);
+	}
+
+	return ObjCSelector_NewNative((Class)objc_object, sel,
+		flatten_signature(methsig, buf, sizeof(buf)), class_method);
+}
+
+static PyObject*
+make_dict(PyObject* self, int class_method)
+{
+	Class     cls;
+	PyObject* res;
+	struct objc_method_list* mlist;
+	void* iterator;
+	char  buf[256];
+	id    objc_class;
+	PyObject* bound_self;
+
+	if (ObjCObject_Check(self)) {
+		id obj = ObjCObject_GetObject(self);
+		if (obj == NULL) {
+			return PyDict_New();
+		}
+
+		if (class_method) {
+			cls = GETISA(GETISA(obj));
+			bound_self = (PyObject*)self->ob_type;
+			objc_class = GETISA(obj);
+		} else {
+			cls = GETISA(obj);
+			objc_class = cls;
+			bound_self = self;
+		}
+
+	} else if (ObjCClass_Check(self)) {
+		cls = ObjCClass_GetClass(self);
+		objc_class = cls;
+		if (class_method) {
+			cls = GETISA(cls);
+			bound_self = self;
+		} else {
+			bound_self = NULL;
+		}
+	} else {
+		PyErr_BadInternalCall();
+		return NULL;
+	}
+
+	res = PyDict_New();
+	if (res == NULL) {
+		return NULL;
+	}
+
+	while (objc_class != NULL) {
+		iterator = NULL;
+		mlist = class_nextMethodList(objc_class, &iterator);
+		while (mlist != NULL) {
+			METHOD meth;
+			PyObject* v;
+			int i;
+
+			for (i = 0; i < mlist->method_count; i++) {
+				char* name;
+
+				meth = mlist->method_list + i;
+				name = pythonify_selector(meth->method_name, 
+					buf, sizeof(buf));
+				
+				v = PyObject_GetAttrString(self, name);
+				if (v == NULL) {
+					PyErr_Clear();
+				} else if (!ObjCSelector_Check(v)) {
+					Py_DECREF(v);
+					v = NULL;
+				} else {
+					int cm;
+					cm = ((ObjCSelector*)v)->sel_flags & ObjCSelector_kCLASS_METHOD;
+					if (!cm  != !class_method) {
+						Py_DECREF(v);
+						v = NULL;
+					}
+				}
+
+				if (v == NULL) {
+					v = ObjCSelector_NewNative(
+						objc_class, meth->method_name,
+						meth->method_types, class_method);
+					if (v == NULL) {
+						Py_DECREF(res);
+						return NULL;
+					}
+				}
+
+				if (PyDict_SetItemString(res, name, v) == -1) {
+					Py_DECREF(res);
+					return NULL;
+				}
+				Py_DECREF(v);
+
+			}
+			mlist = class_nextMethodList(objc_class, &iterator);
+		}
+
+		objc_class = ((Class)objc_class)->super_class;
+	}
+
+	return res;
+}
+	
+
+typedef struct {
+	PyObject_HEAD
+	PyObject*	base;
+	int		class_method;
+} ObjCMethodAccessor;
+
+static void
+obj_dealloc(ObjCMethodAccessor* self)
+{
+	Py_XDECREF(self->base);
+	self->base = NULL;
+
+	if (self->ob_type->tp_free) {
+		self->ob_type->tp_free(self);
+	} else {
+		PyObject_Del(self);
+	}
+}
+
+static PyObject*
+obj_getattro(ObjCMethodAccessor* self, PyObject* name)
+{
+	PyObject* result;
+	int	  class_method;
+
+	if (!PyString_Check(name)) {
+		ObjCErr_Set(PyExc_TypeError, 
+			"Expecting string, got %s",
+			name->ob_type->tp_name);
+		return NULL;
+	}
+
+	if (strcmp(PyString_AS_STRING(name), "__dict__") == 0) {
+		return make_dict(self->base, self->class_method);
+	}
+
+	/* First try to access through base, this way the method replacements
+	 * in objc._convenience are seen here.
+	 */
+	result = PyObject_GetAttr(self->base, name);
+	if (result == NULL) {
+		PyErr_Clear();
+	} else if (!ObjCSelector_Check(result)) {
+		Py_DECREF(result);
+		result = NULL;
+	} else {
+		class_method = ((ObjCSelector*)result)->sel_flags & ObjCSelector_kCLASS_METHOD;
+		if (!self->class_method  == !class_method) {
+			/* NOTE: ! is used to normalize the values */
+			return result;
+		} 
+		Py_XDECREF(result);
+		result = NULL;
+	}
+
+	result = find_selector(self->base, 
+		PyString_AS_STRING(name), self->class_method);
+	if (result == NULL) return result;
+
+	if (self->class_method && ObjCObject_Check(self->base)) {
+		/* Class method */
+		((ObjCSelector*)result)->sel_self = (PyObject*)(self->base->ob_type);
+	} else if (!self->class_method && ObjCClass_Check(self->base)) {
+		/* Unbound instance method */
+		((ObjCSelector*)result)->sel_self = NULL;
+	} else {
+		/* Bound instance method */
+		((ObjCSelector*)result)->sel_self = self->base;
+	}
+	Py_XINCREF(((ObjCSelector*)result)->sel_self);
+	return result;
+}
+
+static PyObject*
+obj_repr(ObjCMethodAccessor* self)
+{
+	char buf[1024];
+	PyObject* repr;
+
+	repr = PyObject_Repr(self->base);
+	if (repr == NULL) return NULL;
+	if (!PyString_Check(repr)) {
+		PyErr_SetString(PyExc_TypeError, "base repr was not a string");
+		return NULL;
+	}
+
+	snprintf(buf, sizeof(buf), 
+		"<%s method-accessor for %s>",
+		self->class_method?"class":"instance",
+		PyString_AS_STRING(repr));
+	Py_DECREF(repr);
+	return PyString_FromString(buf);
+}
+
+static PyTypeObject ObjCMethodAccessor_Type = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,					/* ob_size */
+	"objc.method_acces",			/* tp_name */
+	sizeof(ObjCMethodAccessor),		/* tp_basicsize */
+	0,					/* tp_itemsize */
+	/* methods */
+	(destructor)obj_dealloc,	 	/* tp_dealloc */
+	0,					/* tp_print */
+	0,					/* tp_getattr */
+	0,					/* tp_setattr */
+	0,					/* tp_compare */
+	(reprfunc)obj_repr,			/* tp_repr */
+	0,					/* tp_as_number */
+	0,					/* tp_as_sequence */
+	0,		       			/* tp_as_mapping */
+	0,					/* tp_hash */
+	0,					/* tp_call */
+	0,					/* tp_str */
+	(getattrofunc)obj_getattro,		/* tp_getattro */
+	0,					/* tp_setattro */
+	0,					/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,			/* tp_flags */
+ 	0,					/* tp_doc */
+ 	0,					/* tp_traverse */
+ 	0,					/* tp_clear */
+	0,					/* tp_richcompare */
+	0,					/* tp_weaklistoffset */
+	0,					/* tp_iter */
+	0,					/* tp_iternext */
+	0,					/* tp_methods */
+	0,					/* tp_members */
+	0,					/* tp_getset */
+	0,					/* tp_base */
+	0,					/* tp_dict */
+	0,					/* tp_descr_get */
+	0,					/* tp_descr_set */
+	0,					/* tp_dictoffset */
+	0,					/* tp_init */
+	0,					/* tp_alloc */
+	0,					/* tp_new */
+	0,		        		/* tp_free */
+};
+
+PyObject* ObjCMethodAccessor_New(PyObject* base, int class_method)
+{
+	ObjCMethodAccessor* result;
+
+	result = PyObject_New(ObjCMethodAccessor, &ObjCMethodAccessor_Type);
+	if (result == NULL) return NULL;
+
+	result->base = base;
+	Py_XINCREF(base);
+	result->class_method = class_method;
+
+	return (PyObject*)result;
+}

File Modules/objc/module.m

 	PyDict_SetItemString(d, "ivar", (PyObject*)&ObjCIvar_Type);
 	PyDict_SetItemString(d, "informal_protocol", (PyObject*)&ObjCInformalProtocol_Type);
 
-	allocator_dict = PyDict_New();
-	if (allocator_dict == NULL) return;
-
-	Py_INCREF(allocator_dict);
-	PyDict_SetItemString(d, "ALLOCATOR_METHODS", allocator_dict);
-
 	if (ObjCUtil_Init(m) < 0) return;
 	if (ObjCAPI_Register(d) < 0) return;
 	if (ObjC_RegisterStdStubs(&objc_api) < 0) return;

File Modules/objc/objc-class.m

  * The struct class_info contains the additional information for a class object,
  * and class_to_objc stores a mapping from a class object to its additional
  * information.
+ *
+ * TODO: Rewrite these using NSHashTable and check if this improves performance
  */
 struct class_info {
 	Class	  class;
  * Fetch the additional information for a class. If the information is
  * not yet available add it to the dictionary.
  */
-static struct class_info*
+static inline struct class_info*
 get_class_info(PyObject* class)
 {	
 	PyObject*          item;
 
 
 /*
- * convert a python class-name to an objective-C name.
- *
- * XXX: This function may not be necessary, I've never seen it called with
- *      names that contain dots.
- */
-static char*
-normalize_classname(char* classname, char* buf, size_t buflen)
-{
-	char* cur;
-
-	snprintf(buf, buflen, "%s", classname);
-	cur = strchr(buf, '.');
-	while (cur != NULL) {
-		*cur = '_';
-		cur = strchr(cur, '.');
-	}
-	return buf;
-}
-	
-
-/*
  * Create a new objective-C class, as a subclass of 'type'. This is
  * ObjCClass_Type.tp_new.
  *
 		return NULL;
 	}
 
-	name = normalize_classname(name, 
-		normalized_name, sizeof(normalized_name));
-
 	if (objc_lookUpClass(name) != NULL) {
 		PyErr_SetString(ObjCExc_error, 
 			"Class already exists in Objective-C runtime");
 
 	if (info->class == NULL) return;
 
-
 	if (/*info->method_magic == 0 && */ info->class->super_class != 0) {
 		ObjCClass_CheckMethodList(ObjCClass_New(info->class->super_class));
 	}
 		}
 		r =  ObjC_UpdateConvenienceMethods(cls);
 		if (r < 0) {
+			PyErr_Print();
 			PyErr_SetString(PyExc_RuntimeError,
 				"Cannot rescan method table");
 			return;
 	return (long)self;
 }
 
+PyDoc_STRVAR(cls_get_classMethods_doc,
+"The attributes of this field are the class methods of this object. This can\n"
+"be used to force access to a class method."
+);
+static PyObject*
+cls_get_classMethods(ObjCObject* self, void* closure)
+{
+	return ObjCMethodAccessor_New((PyObject*)self, 1);
+}
+
+PyDoc_STRVAR(cls_get_instanceMethods_doc,
+"The attributes of this field are the instance methods of this object. This \n"
+"can be used to force access to an instance method."
+);
+static PyObject*
+cls_get_instanceMethods(ObjCObject* self, void* closure)
+{
+	return ObjCMethodAccessor_New((PyObject*)self, 0);
+}
+
+static PyGetSetDef cls_getset[] = {
+        {
+                "pyobjc_classMethods",
+                (getter)cls_get_classMethods,
+                NULL,
+                cls_get_classMethods_doc,
+                0
+        },
+        {
+                "pyobjc_instanceMethods",
+                (getter)cls_get_instanceMethods,
+                NULL,
+                cls_get_instanceMethods_doc,
+                0
+        },
+        { 0, 0, 0, 0, 0 }
+};
+
+
 PyTypeObject ObjCClass_Type = {
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,					/* ob_size */
 	0,					/* tp_iternext */
 	0,					/* tp_methods */
 	0,					/* tp_members */
-	0,					/* tp_getset */
+	cls_getset,				/* tp_getset */
 	&PyType_Type,				/* tp_base */
 	0,					/* tp_dict */
 	0,					/* tp_descr_get */
 
 
 /* FIXME: objc_support.[hm] also has version of this function! */
-static char*
+char*
 pythonify_selector(SEL sel, char* buf, size_t buflen)
 {
 	size_t res = snprintf(buf, buflen, SELNAME(sel));

File Modules/objc/objc-object.m

  * at most one python proxy. This allows users to use the 'is' operator
  * to check if two proxy instances refer to the same objective-C object.
  *
- */
-#ifndef PyOBJC_UNIQUE_PROXY
-
-#define find_existing_proxy(objc_obj) NULL
-#define register_proxy(proxy_obj)  0
-#define unregister_proxy(proxy_obj)  ((void)0)
-
-#else /* PyOBJC_UNIQUE_PROXY */
-
-/*
  * There are three functions:
  * - register_proxy
  *   Add the proxy for an objective-C object to the weakref dictionary
  * there are no more references to that proxy. Note that we use the 'self' 
  * object to pass the key that should be remove, that seems to be the easiest
  * (but ugly) method of creating a closure.
+ *
+ * TODO: Implement using NSHashTable and check if performance improves.
  */
 
 static PyObject* proxy_dict = NULL;
 	int r;
 	PyObject* key;
 
-	if (proxy_dict == NULL) return NULL;
+	if (proxy_dict == NULL) return;
 
 	key = PyInt_FromLong((long)objc_obj);
 	r = PyDict_DelItem(proxy_dict, key);
 	return 0;
 }
 
-#endif /* PyOBJC_UNIQUE_PROXY */
-
 int ObjC_RegisterClassProxy(Class cls, PyObject* classProxy)
 {
 	return register_proxy(classProxy);
 	return result;
 }
 
+PyDoc_STRVAR(obj_get_classMethods_doc,
+"The attributes of this field are the class methods of this object. This can\n"
+"be used to force access to a class method."
+);
+static PyObject*
+obj_get_classMethods(ObjCObject* self, void* closure)
+{
+	return ObjCMethodAccessor_New((PyObject*)self, 1);
+}
+
+PyDoc_STRVAR(obj_get_instanceMethods_doc,
+"The attributes of this field are the instance methods of this object. This\n"
+"can be used to force access to a class method."
+);
+static PyObject*
+obj_get_instanceMethods(ObjCObject* self, void* closure)
+{
+	return ObjCMethodAccessor_New((PyObject*)self, 0);
+}
+
+static PyGetSetDef obj_getset[] = {
+	{
+		"pyobjc_classMethods",
+		(getter)obj_get_classMethods,
+		NULL,
+		obj_get_classMethods_doc,
+		0
+	},
+	{
+		"pyobjc_instanceMethods",
+		(getter)obj_get_instanceMethods,
+		NULL,
+		obj_get_instanceMethods_doc,
+		0
+	},
+	{ 0, 0, 0, 0, 0 }
+};
+
 
 PyTypeObject ObjCObject_Type = {
 	PyObject_HEAD_INIT(&ObjCClass_Type)
 	0,					/* tp_iternext */
 	0,					/* tp_methods */
 	0,					/* tp_members */
-	0,					/* tp_getset */
+	obj_getset,				/* tp_getset */
 	0,					/* tp_base */
 	0,					/* tp_dict */
 	0,					/* tp_descr_get */

File Modules/objc/objc_support.h

 static inline int
 objc_methodlist_magic(Class cls)
 {
+	int res = 0; 
+	int cnt = 0;
+
+
+	/* The documented way of walking over the method-list is by using
+	 * class_nextMethodList. Handcoding it is noticeable faster (probably
+	 * because this exposes more information to the optimizer).
+	 */
+#if 0
 	struct objc_method_list* mlist;
-	int res, cnt;
 	void* iterator = 0;
-	res = cnt = 0;
 
 	if (cls == NULL) return -1;
-
+	
 	while ((mlist = class_nextMethodList(cls, &iterator))) {
 		res += mlist->method_count;
 		cnt ++;
-#if 0
-	/* add information about actuall methods, this is expensive and
-	 * shouldn't be necessary
-	 */ 
-	     {
-		int i;
-		for (i = 0; i < mlist->method_count; i++) {
-			int x = (int)(mlist->method_list[i].method_imp);
+	}
 
-			res ^= (x >> 16) ^ (x && 0xffff);
-		}
-	     }
+#else
+	if (cls == NULL) return -1;
+
+	struct objc_method_list** p;
+
+	for (p = cls->methodLists; (*p != -1) && (*p != 0); p++) {
+		res += (*p)->method_count;
+		cnt++;
+	}
 #endif
+	
 
-	}
 
 	return (cnt << 16) | (res & 0xFFFF);
 }

File Modules/objc/objc_support.m

     case _C_FLT:
     case _C_DBL:
     case _C_VOID:
-      return ++type;
+      ++type;
       break;
 
     case _C_ARY_B:
       while (isdigit (*++type));
       type = objc_skip_typespec (type);
       //assert (*type == _C_ARY_E);
-      return ++type;
+      ++type;
       break;
       
     case _C_STRUCT_B:
       while (*type != _C_STRUCT_E && *type++ != '=');
       while (*type != _C_STRUCT_E)
         type = objc_skip_typespec (type);
-      return ++type;
+      ++type;
+      break;
 
     case _C_UNION_B:
       /* skip name, and elements until closing ')'  */
       type++;
       while (*type != _C_UNION_E) { type = objc_skip_typespec (type); }
-      return ++type;
+      ++type;
+      break;
       
     case _C_PTR:
       /* Just skip the following typespec */
-      return objc_skip_typespec (++type);
+      type = objc_skip_typespec (type+1);
     
     default:
       PySys_WriteStderr("PyObjC: objc_skip_typespec: Unhandled type '%c' (%d)\n", 
 			*type, *type);
       abort();
     }
+
+    while (isdigit(*type)) type++;
+    return type;
 }
 
 /*
 	*(id *) datum = [NSNumber numberWithDouble:PyFloat_AS_DOUBLE (argument)];
       else if (PyLong_Check(argument)) 
 	*(id *) datum = [NSNumber numberWithLongLong:PyLong_AsLongLong(argument) ];
-      else if (PySequence_Check(argument))  
+      else if (PyList_Check(argument) || PyTuple_Check(argument))  
 	*(id *) datum = [OC_PythonArray newWithPythonObject:argument];
       else if (PyDict_Check(argument))  
 	*(id *) datum = [OC_PythonDictionary newWithPythonObject:argument];
 	PyObject*	  result = NULL;
 	id		  self_obj = nil;
 	id                nil_obj = nil;
+	char*		  curspec;
 
-	methinfo = [NSMethodSignature signatureWithObjCTypes:meth->sel_signature];
-	objc_argcount = [methinfo numberOfArguments];
+	if (meth->sel_oc_signature) {
+		methinfo = meth->sel_oc_signature;
+	} else {
+	 	methinfo = [NSMethodSignature signatureWithObjCTypes:meth->sel_signature];
+		meth->sel_oc_signature = methinfo;
+	}
 
 	/* First count the number of by reference parameters, and the number
 	 * of bytes of storage needed for them. Note that arguments 0 and 1
 	 * are self and the selector, no need to count counted or checked those.
 	 */
-	for (i = 2; i < objc_argcount; i++) {
-		const char *argtype = [methinfo getArgumentTypeAtIndex:i];
+	objc_argcount = 0;
+	for (curspec = objc_skip_typespec(meth->sel_signature), i=0; *curspec; curspec = objc_skip_typespec(curspec), i++) {
+		const char *argtype = curspec;
+		objc_argcount += 1;
+
+		if (i < 2) continue;
 
 		switch (*argtype) {
 		case _C_PTR: 
 	}
 
 	if (argbuf_len) {
-		argbuf = PyMem_Malloc(argbuf_len);
+		argbuf = alloca(argbuf_len);
 		if (argbuf == 0) {
 			PyErr_NoMemory();
 			goto error_cleanup;
 		}
-		byref = PyMem_Malloc(sizeof(int) * objc_argcount);
+		byref = alloca(sizeof(int) * objc_argcount);
 		if (byref == NULL) {
 			PyErr_NoMemory();
 			goto error_cleanup;
 		}
-		memset(byref, 0, sizeof(int) * objc_argcount);
+		
 	} else {
 		argbuf = NULL;
 		byref = NULL;
 	[inv setSelector:meth->sel_selector];
 
 	py_arg = 0;
-	for (i = 2; i < objc_argcount; i++) {
+	for (curspec = objc_skip_typespec(meth->sel_signature), i = 0; *curspec; curspec = objc_skip_typespec(curspec), i ++) {
 		const char* error;
 		PyObject *argument;
-		const char *argtype = [methinfo getArgumentTypeAtIndex:i];
+		const char *argtype = curspec;
+
+		if (i < 2) continue; /* Skip self, _sel */
 
 		if (argtype[0] == _C_OUT && argtype[1] == _C_PTR) {
 			/* Just allocate room in argbuf and set that*/
 	NS_ENDHANDLER
 	if (PyErr_Occurred()) goto error_cleanup;
 
-	rettype = [methinfo methodReturnType];
+	rettype = meth->sel_signature; 
 	if ( (*rettype != _C_VOID) && ([methinfo isOneway] == NO) )
 	{
-		char *retbuffer = alloca ([methinfo methodReturnLength]);
+		char *retbuffer = alloca (objc_sizeof_type(rettype));
 
 		[inv getReturnValue:retbuffer];
 
-		objc_result = pythonify_c_value ([methinfo methodReturnType],
-					retbuffer);
+		objc_result = pythonify_c_value (rettype, retbuffer);
 	} else {
 		Py_INCREF(Py_None);
 		objc_result =  Py_None;
 	}
 
+	if ( (meth->sel_flags & ObjCSelector_kRETURNS_SELF)
+		&& (objc_result != self)) {
+
+		/* meth is a method that returns a possibly reallocated
+		 * version of self and self != return-value, the current
+		 * value of self is assumed to be no longer valid
+		 */
+		ObjCObject_ClearObject(self);
+	}
+
 
 	if (byref_out_count == 0) {
 		result = objc_result;
 		objc_result = NULL;
 
 		py_arg = 1;
-		for (i = 2; i < objc_argcount; i++) {
-			const char *argtype = [methinfo 
-						getArgumentTypeAtIndex:i];
+		for (curspec = objc_skip_typespec(meth->sel_signature), i = 0; *curspec; curspec = objc_skip_typespec(curspec), i ++) {
+			const char *argtype = curspec;
 			void*       arg;
 			PyObject*   v;
 
+			if (i < 2) continue;
+
 			switch (*argtype) {
 			case _C_PTR: 
 				arg = argbuf + byref[i];
 		}
 	}
 
-#if 1
 	self_obj = nil;
-	if (*[methinfo methodReturnType] == _C_ID) {
+	if (*rettype == _C_ID) {
 		[inv setReturnValue:&nil_obj];
 	}
 	[inv setTarget:nil];
-#endif
 	[inv release];
 	inv = nil;
 
-	PyMem_Free(argbuf);
 	argbuf = NULL;
-	PyMem_Free(byref);
 	byref = NULL;
-	[methinfo release];
 	methinfo = nil;
 	
 	return result;
 
 	if (inv) {
 		self_obj = nil;
-#if 1
+		// FIXME: Shouldn't use this...
 		if (*[methinfo methodReturnType] == _C_ID) {
 			[inv setReturnValue:&self_obj];
 		}
-#endif
 		[inv setTarget:nil];
 		[inv release];
 		inv = nil;
 		Py_DECREF(result);
 		result = NULL;
 	}
-	if (argbuf) {
-		PyMem_Free(argbuf);
-		argbuf = NULL;
-	}
-	if (byref) {
-		PyMem_Free(byref);
-		byref = NULL;
-	}
-	if (methinfo) {
-		[methinfo release];
-		methinfo = nil;
-	}
 	return NULL;
 }
 

File Modules/objc/objc_util.m

 	PyObject_SetAttrString(exc_value, "_pyobjc_info_", dict);
 	PyObject_SetAttrString(exc_value, "name", PyString_FromString(
 		[[localException name] cString]));
-#if 0	
-	Py_DECREF(dict);
-#endif
 	PyErr_Restore(exc_type, exc_value, exc_traceback);
 }
 

File Modules/objc/pyobjc.h

  * implemented in Objective-C)
  */
 
-extern PyObject* allocator_dict;
-
 #define ObjCSelector_kCLASS_METHOD    0x1
 #define ObjCSelector_kDONATE_REF      0x2
 #define ObjCSelector_kREQUIRED        0x4
+#define ObjCSelector_kRETURNS_SELF    0x8
 
 #define ObjCSelector_HEAD \
 	PyObject_HEAD 			\
 
 typedef struct {
 	ObjCSelector_HEAD
+	NSMethodSignature* sel_oc_signature;
 	ObjC_CallFunc_t sel_call_self; 
 	ObjC_CallFunc_t sel_call_super;
 } ObjCNativeSelector;
 
 int     ObjCIPVerify(PyObject* obj, PyObject* cls);
 PyObject* ObjCIPFindInfo(PyObject* obj, SEL selector);
-#ifdef OBJC_PARANOIA_MODE
+#ifdef OBJC_PARANOIA_MODExxx
 
 #define OC_CheckRevive(obj)     \
         do {  \
 
 #endif /* OC_WITH_LIBFFI */
 
+extern PyObject* ObjCMethodAccessor_New(PyObject* base, int class_method);
+
+/* Needed by method-accessor, name will be changed soon */
+char* pythonify_selector(SEL, char*, size_t);
+
 #endif /* META_H */

File Modules/objc/selector.m

 #include "objc_support.h"
 
 /*
- * 'Inside Cocoa: OOP and the Objective-C Language'  says the following about
- * object ownership:
- *
- * - If you create an object (using alloc or allocWithZone:) or copy an
- *   object (using copy, copyWithZone:, or mutableCopyWithZone:), you alone
- *   are responsible for releasing it.
- *
- * The end effect of this is that the normal 'trick' of retain-ing an object
- * when creating the Python proxy object, and release-ing it when that proxy
- * dies, gives us one reference too many. The datastructure and code below
- * help to maintain an administration of methods that transfer object ownership
- * to us.
- */
-
-PyObject* allocator_dict = NULL;
-
-static int is_allocator_method(SEL sel)
-{
-	PyObject* v;
-
-	if (allocator_dict == NULL) return 0;
-
-	v = PyDict_GetItemString(allocator_dict, (char*)SELNAME(sel));
-	if (v == NULL) {
-		return 0;
-	}
-
-	return PyObject_IsTrue(v);
-}
-
-	
-
-
-/*
  * First section deals with registering replacement signatures for methods.
  * This is meant to be used to add _C_IN, _C_OUT and _C_INOUT specifiers for
  * pass-by-reference parameters.
 	} else {
 		self->sel_flags &= ~ObjCSelector_kDONATE_REF;
 	}
-	return 1;
+	return 0;
 }
 
+PyDoc_STRVAR(base_returns_self_doc, 
+"True if this is method returns a reallocated 'self', False otherwise\n"
+"\n"
+"NOTE: This field is used by the implementation."
+);
+static PyObject*
+base_returns_self(ObjCNativeSelector* self, void* closure)
+{
+	return PyBool_FromLong(0 != (self->sel_flags & ObjCSelector_kRETURNS_SELF));
+}
+static int
+base_returns_self_setter(ObjCNativeSelector* self, PyObject* newVal, void* closure)
+{
+	if (PyObject_IsTrue(newVal)) {
+		self->sel_flags |= ObjCSelector_kRETURNS_SELF;
+	} else {
+		self->sel_flags &= ~ObjCSelector_kRETURNS_SELF;
+	}
+	return 0;
+}
 static PyGetSetDef base_getset[] = {
 	{
 		"donates_ref",
 		base_selector_doc,
 		0
 	},
+	{
+		"returns_reallocated_self",
+		(getter)base_returns_self,
+		(setter)base_returns_self_setter,
+		base_returns_self_doc,
+		0
+	},
 	{ 
 		"__name__",  
 		(getter)base_selector, 
 {
 	ObjCSelector* self = (ObjCSelector*)object;	
 
+	/* HACK */
+	if (ObjCNativeSelector_Check(self)) {
+		[((ObjCNativeSelector*)self)->sel_oc_signature release];
+	}
+
 	PyMem_Free(self->sel_signature);
 	self->sel_signature = NULL;
 	if (self->sel_self) { 
 	}
 
 	/* First stab at detecting super-calls... */
-	if (!self->sel_flags & ObjCSelector_kCLASS_METHOD) {
+	if (!(self->sel_flags & ObjCSelector_kCLASS_METHOD)) {
 		if (ObjCObject_Check(pyself)) {
-			pyself_class = ObjCClass_GetClass(
-				(PyObject*)pyself->ob_type);
+			pyself_class = GETISA(ObjCObject_GetObject(pyself));
 			if (pyself_class == NULL) {
 				return NULL;
 			}
 		}
 	}
 
-
 	if (pyself_class != NULL && pyself_class != self->sel_class) {
 		METHOD self_m;
 		METHOD pyself_m;
 	}
 
 	if (is_super_call) {
-		execute = ObjC_FindSupercaller(self->sel_class, self->sel_selector);
-		if (execute == NULL) return NULL;
-		self->sel_call_super = execute;
+		if (self->sel_call_super) {
+			execute = self->sel_call_super;
+		} else {
+			execute = ObjC_FindSupercaller(self->sel_class, self->sel_selector);
+			if (execute == NULL) return NULL;
+			self->sel_call_super = execute;
+		}
 	} else {
-		execute = ObjC_FindSelfCaller(pyself_class, self->sel_selector);
-		if (execute == NULL) return NULL;
-		self->sel_call_self = execute;
+		if (self->sel_call_self) {
+			execute = self->sel_call_self;
+		} else {
+			execute = ObjC_FindSelfCaller(pyself_class, self->sel_selector);
+			if (execute == NULL) return NULL;
+			self->sel_call_self = execute;
+		}
 	}
 
 	if (self->sel_self != NULL) {
 	}
 	result->sel_flags = meth->sel_flags;
 	result->sel_class = meth->sel_class;
+
+	if (meth->sel_call_self == NULL || meth->sel_call_super == NULL) {
+		ObjC_FindCaller(meth->sel_class, meth->sel_selector,
+			&meth->sel_call_self, &meth->sel_call_super);
+	}
 	result->sel_call_self = meth->sel_call_self;
 	result->sel_call_super = meth->sel_call_super;
 
+	if (meth->sel_oc_signature == NULL) {
+		meth->sel_oc_signature = [NSMethodSignature signatureWithObjCTypes:meth->sel_signature];
+	}
+	result->sel_oc_signature = meth->sel_oc_signature;
+	[result->sel_oc_signature retain];
+
 	result->sel_self       = obj;
 	if (result->sel_self) {
 		OC_CheckRevive(result->sel_self);
 	result->sel_class = class;
 	result->sel_call_self = NULL;
 	result->sel_call_super = NULL;
+	result->sel_oc_signature = NULL;
 	result->sel_flags = 0;
 	if (class_method) {
 		result->sel_flags |= ObjCSelector_kCLASS_METHOD;
 	}
-#if 0
-	/* This isn't really necessary: Use objc._convenience.py instead */
-	if (is_allocator_method(result->sel_selector)) {
-		result->sel_flags |= ObjCSelector_kDONATE_REF;
-	}
-#endif
 	return (PyObject*)result;
 }
 
 	if (class_method) {
 		result->sel_flags |= ObjCSelector_kCLASS_METHOD;
 	}
-	if (is_allocator_method(result->sel_selector)) {
-		result->sel_flags |= ObjCSelector_kDONATE_REF;
-	}
 	Py_INCREF(result->callable);
 
 	return (PyObject*)result;
 		PyTuple_SetItem(actual_args, 0, self->sel_self);
 		for (i = 0; i < argc; i++) {
 			PyObject* v = PyTuple_GET_ITEM(args, i);
-			if (v == NULL) return NULL;
+			/*if (v == NULL) return NULL;*/
 			OC_CheckRevive(v);
-			Py_INCREF(v);
-			if (PyTuple_SetItem(actual_args, i+1, v) < 0) 
-				return NULL;
+			Py_XINCREF(v);
+			PyTuple_SET_ITEM(actual_args, i+1, v);
 		}
 		result = PyObject_Call(self->callable, 
 			actual_args, NULL);	

File Modules/objc/super-call.h

 extern IMP             ObjC_FindIMPForSignature(char* signature);
 extern ObjC_CallFunc_t ObjC_FindSupercaller(Class class, SEL sel);
 extern ObjC_CallFunc_t ObjC_FindSelfCaller(Class class, SEL sel);
+extern void            ObjC_FindCaller(Class class, SEL sel, ObjC_CallFunc_t* call_self, ObjC_CallFunc_t* call_super);
 
 #endif /* OBJC_SUPER_CALL_H */

File Modules/objc/super-call.m

 	ObjC_CallFunc_t result;
 	struct registry* rec;
 
+#if 0 /* def OC_WITH_LIBFFI */
+	result = ObjC_FFICaller;
+#else
 	result = execute_and_pythonify_objc_method;
+#endif
 	if (special_registry == NULL) return result;
 
 	/* Check the list of exceptions */
 	struct registry* special;
 	METHOD           m;
 
+#ifndef OC_WITH_LIBFFI
 	m = class_getInstanceMethod(class, sel);
 	if (!m) {
 		ObjCErr_Set(ObjCExc_error,
 			class->name, SELNAME(sel));
 		return NULL;
 	}
+#endif
 
 	special = search_special(class, sel);
 	if (special) {
 		PyErr_Clear();
 	}
 
+#ifndef OC_WITH_LIBFFI 
 	generic = find_signature(m->method_types);
 	if (generic) {
 		return generic->call_to_super;
 	}
+#endif
 
-#ifdef OC_WITH_LIBFFI
+#ifdef OC_WITH_LIBFFI 
+	return ObjC_FFICaller;
 
+#if 0
 	generic = create_ffi(m->method_types);
 	if (generic) {
 		return generic->call_to_super;
 	}
+#endif
 
 #endif /* OC_WITH_LIBFFI */
 
 	return NULL;
 }
+
+void ObjC_FindCaller(Class class, SEL sel, ObjC_CallFunc_t* call_self, ObjC_CallFunc_t* call_super)
+{
+	/* TODO: Inline these */
+	*call_self = ObjC_FindSelfCaller(class, sel);
+	*call_super = ObjC_FindSupercaller(class, sel);
+}

File Project Templates/00README.txt

 --------------------------------------------------------------
 
 This template works like the `Cocoa-Python Application (Embedded
-Interpreter)`_ template in that it is compatible with the Apple build of
+Interpreter)`_ template in that it is incompatible with the Apple build of
 Python.   It creates an application that uses Cocoa's Multiple Document
 Architecture in the same fashion as the default Cocoa Document-based
 Application supplied with Project Builder.
 
 # TODO: Autodetect libFFI, including LIBFFI_BASE
 # ... But first implement FFI support!
-if 1:
+if 0:
     LIBFFI_CFLAGS=[]
     LIBFFI_LDFLAGS=[]
     LIBFFI_SOURCEFILES=[]
         "Modules/objc/objc-object.m",
         "Modules/objc/super-call.m",
         "Modules/objc/selector.m",
+        "Modules/objc/method-accessor.m",
         "Modules/objc/instance-var.m",
         "Modules/objc/OC_PythonInt.m",
         "Modules/objc/OC_PythonObject.m",
     # MacOS X 
     #
     CFLAGS=[
-        "-DMACOSX"
+        "-DMACOSX",
         ]
 
     OBJC_LDFLAGS=[
               sourceFiles + LIBFFI_SOURCEFILES,
               extra_compile_args=[
                     "-DOBJC_PARANOIA_MODE",
-                    "-DPyOBJC_UNIQUE_PROXY",
               ] + LIBFFI_CFLAGS + CFLAGS,
               extra_link_args=LIBFFI_LDFLAGS + OBJC_LDFLAGS)
     ]