Commits

Bob Ippolito  committed 617924a

FIX: [ 1189823 ] objc.protocolNamed doesn't find all protocols

The formal protocol list should be more complete. A new
``objc.protocolsForProcess()`` enumerates over all mach
headers and returns all of the protocols defined in the expected
place. This fixes the scenario where an application defines a
protocol (i.e. for plugins) but does not define any classes that
conform to that protocol. In that case, the protocol would
reside in the mach header but would not be reachable by
iterating over all classes.

  • Participants
  • Parent commits 097973d
  • Branches pyobjc-ancient

Comments (0)

Files changed (4)

File Lib/objc/_protocols.py

         return PROTOCOL_CACHE[name]
     except KeyError:
         pass
+    for p in _objc.protocolsForProcess():
+        pname = p.__name__
+        PROTOCOL_CACHE.setdefault(pname, p)
+        if pname == name:
+            return p
     for cls in _objc.getClassList():
         for p in _objc.protocolsForClass(cls):
             pname = p.__name__

File Modules/objc/module.m

 
 #include "objc_inject.h"
 
+#ifdef MACOSX
+#import <mach-o/dyld.h>
+#import <mach-o/getsect.h>
+#import <mach-o/loader.h>
+#import <objc/Protocol.h>
+
+#define OBJC_PROTOCOL_PTR ProtocolTemplate*
+#define OBJC_PROTOCOL_DEREF .
+typedef struct _ProtocolTemplate { @defs(Protocol) } ProtocolTemplate;	
+#endif
+
 int PyObjC_VerboseLevel = 0;
 PyObject* PyObjCClass_DefaultModule = NULL;
 PyObject* PyObjC_NSNumberWrapper = NULL;
 
 
 PyDoc_STRVAR(classAddMethods_doc,
-     "classAddMethods(targetClass, methodsArray)\n"
-     "\n"
-     "Adds methods in methodsArray to class. The effect is similar to how \n"
-     "categories work. If class already implements a method as defined in \n"
-     "methodsArray, the original implementation will be replaced by the \n"
-     "implementation from methodsArray.");
+	 "classAddMethods(targetClass, methodsArray)\n"
+	 "\n"
+	 "Adds methods in methodsArray to class. The effect is similar to how \n"
+	 "categories work. If class already implements a method as defined in \n"
+	 "methodsArray, the original implementation will be replaced by the \n"
+	 "implementation from methodsArray.");
 
 static PyObject*
 classAddMethods(PyObject* self __attribute__((__unused__)), 
 		bundle = [NSBundle bundleWithIdentifier:bundle_identifier];
 #else  /* !MACOSX */
 		/* GNUstep doesn't seem to support ``bundleWithIdentifier:``
-           but it could be emulated by enumerating allFrameworks and
-           allBundles.. */
+		   but it could be emulated by enumerating allFrameworks and
+		   allBundles.. */
 		PyErr_SetString(PyExc_RuntimeError,
 			"The 'bundle_identifier' argument is only supported "
 			"on MacOS X");
 	return Py_None;
 }
 
+PyDoc_STRVAR(protocolsForProcess_doc,
+	"protocolsForProcess() -> [Protocols]\n"
+	"\n"
+	"Returns a list of Protocol objects that the class claims\n"
+	"to implement directly."
+);
+static PyObject*
+protocolsForProcess(PyObject* self __attribute__((__unused__)))
+{
+	PyObject *protocols = PyList_New(0);
+	if (protocols == NULL) {
+		return NULL;
+	}
+#ifdef MACOSX
+	uint32_t image_count, image_index;
+	image_count = _dyld_image_count();
+	for (image_index = 0; image_index < image_count; image_index++) {
+		uint32_t size;
+		const struct mach_header *mh = _dyld_get_image_header(image_index);
+		ProtocolTemplate *protos = (ProtocolTemplate*)getsectdatafromheader(mh, SEG_OBJC, "__protocol", &size);
+		uint32_t nprotos = size / sizeof(ProtocolTemplate);
+		uint32_t i;
+		for (i = 0; i < nprotos; i++) {
+			PyObject *protocol = PyObjCFormalProtocol_ForProtocol((Protocol *)&protos[i]);
+			if (protocol == NULL) {
+				Py_DECREF(protocols);
+				return NULL;
+			}
+			PyList_Append(protocols, protocol);
+			Py_DECREF(protocol);
+		}
+	}
+#else
+#endif
+	return protocols;
+}
+
+
 PyDoc_STRVAR(protocolsForClass_doc,
 	"protocolsForClass(cls) -> [Protocols]\n"
 	"\n"
 	{ "allocateBuffer", (PyCFunction)allocateBuffer, METH_VARARGS|METH_KEYWORDS, allocateBuffer_doc },
 	{ "enableThreading", (PyCFunction)enableThreading, METH_NOARGS, enableThreading_doc },
 	{ "protocolsForClass", (PyCFunction)protocolsForClass, METH_VARARGS|METH_KEYWORDS, protocolsForClass_doc },
+	{ "protocolsForProcess", (PyCFunction)protocolsForProcess, METH_NOARGS, protocolsForProcess_doc },
 #ifdef MACOSX
 	{ "CFToObject", (PyCFunction)objc_CFToObject, METH_VARARGS|METH_KEYWORDS, objc_CFToObject_doc },
 	{ "ObjectToCF", (PyCFunction)objc_ObjectToCF, METH_VARARGS|METH_KEYWORDS, objc_ObjectToCF_doc },
 	 * add that seperately to avoid distributing pyobjc-api.h for now 
 	 */
 	{
-		PyObject* v = PyCObject_FromVoidPtr((void*)(PyObjCClass_GetClass), NULL);
+		v = PyCObject_FromVoidPtr((void*)(PyObjCClass_GetClass), NULL);
 		if (v == NULL) return;
 
 		PyModule_AddObject(m, "__C_GETCLASS__", v);
 <p>An overview of the relevant changes in new, and older, releases.</p>
 <h2><a name="version-1-3-1-2005-04">Version 1.3.1 (2005-04-??)</a></h2>
 <ul>
+<li>The formal protocol list should be more complete.  A new 
+<code><span>objc.protocolsForProcess()</span></code> enumerates over all mach
+headers and returns all of the protocols defined in the expected
+place.  This fixes the scenario where an application defines a
+protocol (i.e. for plugins) but does not define any classes that
+conform to that protocol.  In that case, the protocol would
+reside in the mach header but would not be reachable by
+iterating over all classes.</li>
+<li>You can now pass the special value <code><span>objc.NULL</span></code> as the value
+for 'in' and 'inout' arguments. This tells the bridge to pass
+a NULL pointer to the Objective-C method, instead of a pointer
+to the value.</li>
 <li>First stab at wrapping some Tiger frameworks:<ul>
 <li><code><span>AppleScriptKit</span></code></li>
-<li><code><span>AppKitScripting</span></code></li>
 <li><code><span>Automator</span></code></li>
 <li><code><span>CoreData</span></code></li>
 <li><code><span>DiscRecording</span></code></li>
 <li><code><span>DiscRecordingUI</span></code></li>
+<li><code><span>Quartz</span></code></li>
+<li><code><span>QTKit</span></code></li>
 <li><code><span>SyncServices</span></code></li>
 <li><code><span>XgridFoundation</span></code></li>
 </ul>
 <p>Documentation and tests not yet written.</p>
 </li>
+<li>Add a CoreData example: a python version of the OutlineEditor example
+from Apple.<p>TODO: compile the .xcdatamodel file.</p>
+</li>
+<li>If the selector name ends with ':error:' and the last argument is a pointer
+to an object, the argument is now assumed to be an output argument.</li>
 <li>More conveniences for <code><span>list</span></code>-like and <code><span>dict</span></code>-like
 objects: <code><span>__reversed__</span></code>, <code><span>reverse</span></code>, <code><span>pop</span></code>,
 <code><span>remove</span></code>, <code><span>fromkeys</span></code>.</li>
 <li><code><span>objc.inject</span></code> now injects on main thread by default,
 takes third useMainThread argument to change this behavior.
 Effectively a complete rewrite which should be more correct
-and stable, also synchronized with mach_* 1.1.
-XXX: Still doesn't work with Darwin 8!</li>
+and stable, also synchronized with mach_* 1.1.</li>
 <li>Removed NSAutoreleasePool category hack that has been deprecated for
 quite some time.</li>
 <li>New <code><span>objc.removeAutoreleasePool</span></code> function that will remove
 Version 1.3.1 (2005-04-??)
 --------------------------
 
+- The formal protocol list should be more complete.  A new 
+  ``objc.protocolsForProcess()`` enumerates over all mach
+  headers and returns all of the protocols defined in the expected
+  place.  This fixes the scenario where an application defines a
+  protocol (i.e. for plugins) but does not define any classes that
+  conform to that protocol.  In that case, the protocol would
+  reside in the mach header but would not be reachable by
+  iterating over all classes.
+
 - You can now pass the special value ``objc.NULL`` as the value
   for 'in' and 'inout' arguments. This tells the bridge to pass
   a NULL pointer to the Objective-C method, instead of a pointer
   takes third useMainThread argument to change this behavior.
   Effectively a complete rewrite which should be more correct
   and stable, also synchronized with mach_* 1.1.
-  XXX: Still doesn't work with Darwin 8!
 
 - Removed NSAutoreleasePool category hack that has been deprecated for
   quite some time.