summaryrefslogtreecommitdiffstats
path: root/Modules/objc.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/objc.c')
-rw-r--r--Modules/objc.c651
1 files changed, 651 insertions, 0 deletions
diff --git a/Modules/objc.c b/Modules/objc.c
new file mode 100644
index 0000000..9e70912
--- /dev/null
+++ b/Modules/objc.c
@@ -0,0 +1,651 @@
+/***********************************************************
+Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
+The Netherlands.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/* Objective-C interface for NeXTStep */
+/* Tested with NeXTStep 3.3 on Intel and Sparc architectures */
+
+/* Original author: Jon M. Kutemeier */
+/* Revamped and maintained by: Guido van Rossum */
+
+/* XXX To do:
+ - bug??? x.send('name', []) gives weird error
+ - rename functions from objc_* to ObjC_*
+ - change send(sel, [a, b, c]) to send(self, a, b, c)
+ - call back to Python from Objective-C
+ */
+
+/* Python header file */
+#include "Python.h"
+
+/* NeXT headers */
+#include <sys/param.h>
+#include <mach-o/rld.h>
+#include <objc/objc.h>
+#include <objc/objc-runtime.h>
+#import <remote/NXProxy.h>
+
+/* Distinguish between ObjC classes and instances */
+typedef enum {
+ OBJC_CLASS,
+ OBJC_INSTANCE,
+} ObjC_Typecode;
+
+/* Exception raised for ObjC specific errors */
+static PyObject *ObjC_Error;
+
+/* Python wrapper about ObjC id (instance or class) */
+typedef struct {
+ PyObject_HEAD
+ id obj;
+ ObjC_Typecode type;
+ int owned;
+} ObjCObject;
+
+/* Corresponding Python type object */
+staticforward PyTypeObject ObjC_Type;
+
+/* Corresponding Python type check macro */
+#define ObjC_Check(o) ((o)->ob_type == &ObjC_Type)
+
+/* Create a new ObjCObject */
+static ObjCObject *
+newObjCObject(obj, type, owned)
+ id obj;
+ ObjC_Typecode type;
+ int owned;
+{
+ ObjCObject *self;
+
+ self = PyObject_NEW(ObjCObject, &ObjC_Type);
+ if (self == NULL)
+ return NULL;
+
+ self->obj = obj;
+ self->type = type;
+ self->owned = owned;
+
+ return self;
+}
+
+static void
+objc_sendfree(self)
+ ObjCObject *self;
+{
+ if (self->obj)
+ self->obj = (id)objc_msgSend(self->obj, SELUID("free"));
+}
+
+/* Deallocate an ObjCObject */
+static void
+objc_dealloc(self)
+ ObjCObject *self;
+{
+ if (self->owned)
+ objc_sendfree(self);
+ PyMem_DEL(self);
+}
+
+/* Return a string representation of an ObjCObject */
+static PyObject *
+objc_repr(self)
+ ObjCObject *self;
+{
+ char buffer[512];
+ char *p = buffer;
+ if (self->obj == nil)
+ p = "<Objective-C nil>";
+ else {
+ char *t;
+ switch (self->type) {
+ case OBJC_CLASS: t = "class"; break;
+ case OBJC_INSTANCE: t = "instance"; break;
+ default: t = "???"; break;
+ }
+ sprintf(buffer, "<Objective-C %s %s at %lx>",
+ NAMEOF(self->obj), t, (long)(self->obj));
+ }
+ return PyString_FromString(p);
+}
+
+/*** ObjCObject methods ***/
+
+/* Call an object's free method */
+static PyObject *
+objc_free(self, args)
+ ObjCObject *self;
+ PyObject *args;
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ objc_sendfree(self);
+}
+
+/* Send a message to an ObjCObject.
+ The Python call looks like e.g. obj.send('moveTo::', [arg1, arg2])
+ which translates into Objective-C as [obj moveTo: arg1 : arg2] */
+static PyObject *
+objc_send(self, args)
+ ObjCObject *self;
+ PyObject *args;
+{
+ char *methodname;
+ char *margBuff = NULL;
+ PyObject *retobject = NULL;
+ PyObject *arglist;
+ id receiver, obj;
+ char *type;
+ SEL sel;
+ Method meth;
+ unsigned int margCount, margSize;
+ int offset, i;
+
+ if (!PyArg_ParseTuple(args, "sO!", &methodname, &PyList_Type, &arglist))
+ return NULL;
+
+ /* Get the method descriptor from the object */
+
+ receiver = self->obj;
+ sel = SELUID(methodname);
+
+ switch(self->type) {
+ case OBJC_CLASS:
+ meth = class_getClassMethod(receiver->isa, sel);
+ break;
+ case OBJC_INSTANCE:
+ meth = class_getInstanceMethod(receiver->isa, sel);
+ break;
+ default:
+ PyErr_SetString(ObjC_Error,
+ "receiver's type is neither instance not class!?!?");
+ return NULL;
+ }
+
+ if (!meth) {
+ PyErr_SetString(ObjC_Error, "receiver has no method by that name");
+ return NULL;
+ }
+
+ /* Fill in the argument list, type-checking the arguments */
+
+ margCount = method_getNumberOfArguments(meth);
+
+ if (PyList_Size(arglist) + 2 != margCount) {
+ PyErr_SetString(ObjC_Error,
+ "wrong number of arguments for this method");
+ return NULL;
+ }
+
+ margSize = method_getSizeOfArguments(meth);
+ margBuff = PyMem_NEW(char, margSize+1);
+ if (margBuff == NULL)
+ return PyErr_NoMemory();
+
+ method_getArgumentInfo(meth, 0, &type, &offset);
+ marg_setValue(margBuff, offset, id, receiver);
+
+ method_getArgumentInfo(meth, 1, &type, &offset);
+ marg_setValue(margBuff, offset, SEL, sel);
+
+ for (i = 2; i < margCount; i++) {
+ PyObject *argument;
+ method_getArgumentInfo(meth, i, &type, &offset);
+
+ argument = PyList_GetItem(arglist, i-2);
+
+ /* scan past protocol-type modifiers */
+ while (strchr("rnNoOV", *type) != 0)
+ type++;
+
+ /* common type checks */
+ switch(*type) {
+
+ /* XXX The errors here should point out which argument */
+
+ case 'c':
+ case '*':
+ case 'C':
+ if (!PyString_Check(argument)) {
+ PyErr_SetString(ObjC_Error, "string argument expected");
+ goto error;
+ }
+ break;
+
+ case 'i':
+ case 's':
+ case 'I':
+ case 'S':
+ case 'l':
+ case 'L':
+ case '^':
+ if (!PyInt_Check(argument)) {
+ PyErr_SetString(ObjC_Error, "integer argument expected");
+ goto error;
+ }
+ break;
+
+ case 'f':
+ case 'd':
+ if (!PyFloat_Check(argument)) {
+ PyErr_SetString(ObjC_Error, "float argument expected");
+ goto error;
+ }
+ break;
+
+ }
+
+ /* convert and store the argument */
+ switch (*type) {
+
+ case 'c': /* char */
+ marg_setValue(margBuff, offset, char,
+ PyString_AsString(argument)[0]);
+ break;
+
+ case 'C': /* unsigned char */
+ marg_setValue(margBuff, offset, unsigned char,
+ PyString_AsString(argument)[0]);
+ break;
+
+ case '*': /* string */
+ marg_setValue(margBuff, offset, char *,
+ PyString_AsString(argument));
+ break;
+
+ case 'i': /* int */
+ marg_setValue(margBuff, offset, int,
+ PyInt_AsLong(argument));
+ break;
+
+ case 'I': /* unsigned int */
+ marg_setValue(margBuff, offset, unsigned int,
+ PyInt_AsLong(argument));
+ break;
+
+ case 's': /* short */
+ marg_setValue(margBuff, offset, short,
+ PyInt_AsLong(argument));
+ break;
+
+ case 'S': /* unsigned short */
+ marg_setValue(margBuff, offset, unsigned short,
+ PyInt_AsLong(argument));
+ break;
+
+ case 'l': /* long */
+ marg_setValue(margBuff, offset, long,
+ PyInt_AsLong(argument));
+ break;
+
+ case 'L': /* unsigned long */
+ marg_setValue(margBuff, offset, unsigned long,
+ PyInt_AsLong(argument));
+ break;
+
+ case 'f': /* float */
+ marg_setValue(margBuff, offset, float,
+ (float)PyFloat_AsDouble(argument));
+ break;
+
+ case 'd': /* double */
+ marg_setValue(margBuff, offset, double,
+ PyFloat_AsDouble(argument));
+ break;
+
+ case '@': /* id (or None) */
+ if (ObjC_Check(argument))
+ marg_setValue(margBuff, offset, id,
+ ((ObjCObject *)(argument))->obj);
+ else if (argument == Py_None)
+ marg_setValue(margBuff, offset, id, nil);
+ else {
+ PyErr_SetString(ObjC_Error, "id or None argument expected");
+ goto error;
+ }
+ break;
+
+ case '^': /* void * (use int) */
+ marg_setValue(margBuff, offset, void *,
+ (void *)PyInt_AsLong(argument));
+ break;
+
+ case ':': /* SEL (use string or int) */
+ if (PyInt_Check(argument))
+ marg_setValue(margBuff, offset, SEL,
+ (SEL)PyInt_AsLong(argument));
+ else if (PyString_Check(argument))
+ marg_setValue(margBuff, offset, SEL,
+ SELUID(PyString_AsString(argument)));
+ else {
+ PyErr_SetString(ObjC_Error,
+ "selector string or int argument expected");
+ goto error;
+ }
+ break;
+
+ case '#': /* Class (may also use int) */
+ if (ObjC_Check(argument) &&
+ ((ObjCObject *)argument)->type == OBJC_INSTANCE)
+ marg_setValue(margBuff, offset, Class *,
+ (Class *)((ObjCObject *)argument)->obj);
+ else if (PyInt_Check(argument))
+ marg_setValue(margBuff, offset, Class *,
+ (Class *)PyInt_AsLong(argument));
+ else {
+ PyErr_SetString(ObjC_Error,
+ "ObjC class object required");
+ goto error;
+ }
+ break;
+
+ default:
+ PyErr_SetString(ObjC_Error, "unknown argument type");
+ goto error;
+
+ }
+ }
+
+ /* Call the method and set the return value */
+
+ type = meth->method_types;
+
+ while (strchr("rnNoOV", *type))
+ type++;
+
+ switch(*type) {
+
+/* Cast objc_msgSendv to a function returning the right thing */
+#define MS_CAST(type) ((type (*)())objc_msgSendv)
+
+ case 'c':
+ case '*':
+ case 'C':
+ retobject = (PyObject *)PyString_FromString(
+ MS_CAST(char *)(receiver, sel, margSize, margBuff));
+ break;
+
+ case 'i':
+ case 's':
+ case 'I':
+ case 'S':
+ retobject = (PyObject *)PyInt_FromLong(
+ MS_CAST(int)(receiver, sel, margSize, margBuff));
+ break;
+
+ case 'l':
+ case 'L':
+ case '^':
+ retobject = (PyObject *)PyInt_FromLong(
+ MS_CAST(long)(receiver, sel, margSize, margBuff));
+ break;
+
+ case 'f':
+ retobject = (PyObject *)PyFloat_FromDouble(
+ MS_CAST(float)(receiver, sel, margSize, margBuff));
+ break;
+
+ case 'd':
+ retobject = (PyObject *)PyFloat_FromDouble(
+ MS_CAST(double)(receiver, sel, margSize, margBuff));
+ break;
+
+ case '@':
+ obj = MS_CAST(id)(receiver, sel, margSize, margBuff);
+ if (obj == nil) {
+ retobject = Py_None;
+ Py_INCREF(retobject);
+ }
+ else if (obj != receiver)
+ retobject = (PyObject *)newObjCObject(obj, OBJC_INSTANCE, 0);
+ else {
+ retobject = (PyObject *)self;
+ Py_INCREF(retobject);
+ }
+ break;
+
+ case ':':
+ retobject = (PyObject *)PyInt_FromLong(
+ (long)MS_CAST(SEL)(receiver, sel, margSize, margBuff));
+ break;
+
+ case '#':
+ retobject = (PyObject *)PyInt_FromLong(
+ (long)MS_CAST(Class *)(receiver, sel, margSize, margBuff));
+ break;
+
+#undef MS_CAST
+
+ }
+
+ error:
+ PyMem_XDEL(margBuff);
+ return retobject;
+}
+
+/* List of methods for ObjCObject */
+static PyMethodDef objc_methods[] = {
+ {"send", (PyCFunction)objc_send, 1},
+ {"free", (PyCFunction)objc_free, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+/* Get an attribute of an ObjCObject */
+static PyObject *
+objc_getattr(self, name)
+ ObjCObject *self;
+ char *name;
+{
+ PyObject *method;
+
+ /* Try a function method */
+ method = Py_FindMethod(objc_methods, (PyObject *)self, name);
+ if (method != NULL)
+ return method;
+ PyErr_Clear();
+
+ /* Try an instance variable */
+ if (strcmp(name, "obj") == 0)
+ return PyInt_FromLong((long)self->obj);
+ if (strcmp(name, "type") == 0)
+ return PyInt_FromLong((long)self->type);
+ if (strcmp(name, "owned") == 0)
+ return PyInt_FromLong((long)self->owned);
+ if (strcmp(name, "name") == 0)
+ return PyString_FromString(NAMEOF(self->obj));
+ if (strcmp(name, "__members__") == 0)
+ return Py_BuildValue("[sss]", "name", "obj", "owned", "type");
+
+ PyErr_SetString(PyExc_AttributeError, name);
+ return NULL;
+}
+
+/* The type object */
+static PyTypeObject ObjC_Type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0, /*ob_size*/
+ "objc", /*tp_name*/
+ sizeof(ObjCObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)objc_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ (getattrfunc)objc_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ (reprfunc)objc_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, 0, 0, 0, /*xxx1-4*/
+ "Objective-C id wrapper", /*tp_doc*/
+};
+
+
+
+/*** Top-level functions ***/
+
+/* Max #files passed to loadobjectfile() */
+#define MAXRLD 128
+
+/* Load a list of object files */
+static PyObject *
+objc_loadobjectfiles(self, args)
+PyObject *self; /* Not used */
+PyObject *args;
+{
+ NXStream *errorStream;
+ struct mach_header *new_header;
+ const char *filenames[MAXRLD+1];
+ long ret;
+ char *streamBuf;
+ PyObject *filelist, *file;
+ int listsize, len, maxLen, i;
+
+ if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &filelist))
+ return NULL;
+
+ listsize = PyList_Size(filelist);
+
+ if (listsize > MAXRLD) {
+ PyErr_SetString(ObjC_Error, "more than 128 files in list");
+ return NULL;
+ }
+
+ errorStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
+
+ for (i = 0; i < listsize; i++) {
+ file = PyList_GetItem(filelist, i);
+
+ if (!PyString_Check(file))
+ {
+ PyErr_SetString(ObjC_Error,
+ "all list items must be strings");
+ return NULL;
+ }
+
+ filenames[i] = PyString_AsString(file);
+ }
+
+ filenames[listsize] = NULL;
+
+ ret = objc_loadModules(filenames, errorStream, NULL, &new_header, NULL);
+
+ /* extract the error messages for the exception */
+
+ if(ret) {
+ NXPutc(errorStream, (char)0);
+
+ NXGetMemoryBuffer(errorStream, &streamBuf, &len, &maxLen);
+ PyErr_SetString(ObjC_Error, streamBuf);
+ }
+
+ NXCloseMemory(errorStream, NX_FREEBUFFER);
+
+ if(ret)
+ return NULL;
+
+ Py_XINCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+objc_lookupclass(self, args)
+PyObject *self; /* Not used */
+PyObject *args;
+{
+ char *classname;
+ id class;
+
+ if (!PyArg_ParseTuple(args, "s", &classname))
+ return NULL;
+
+ if (!(class = objc_lookUpClass(classname)))
+ {
+ PyErr_SetString(ObjC_Error, "unknown ObjC class");
+ return NULL;
+ }
+
+ return (PyObject *)newObjCObject(class, OBJC_CLASS, 0);
+}
+
+/* List all classes */
+static PyObject *
+objc_listclasses(self, args)
+ ObjCObject *self;
+ PyObject *args;
+{
+ NXHashTable *class_hash = objc_getClasses();
+ NXHashState state = NXInitHashState(class_hash);
+ Class classid;
+ PyObject *list;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ list = PyList_New(0);
+ if (list == NULL)
+ return NULL;
+
+ while (NXNextHashState(class_hash, &state, (void**)&classid)) {
+ ObjCObject *item = newObjCObject(classid, OBJC_CLASS, 0);
+ if (item == NULL || PyList_Append(list, (PyObject *)item) < 0) {
+ Py_XDECREF(item);
+ Py_DECREF(list);
+ return NULL;
+ }
+ Py_INCREF(item);
+ }
+
+ return list;
+}
+
+/* List of top-level functions */
+static PyMethodDef objc_class_methods[] = {
+ {"loadobjectfiles", objc_loadobjectfiles, 1},
+ {"lookupclass", objc_lookupclass, 1},
+ {"listclasses", objc_listclasses, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+/* Initialize for the module */
+void
+initobjc()
+{
+ PyObject *m, *d;
+
+ m = Py_InitModule("objc", objc_class_methods);
+ d = PyModule_GetDict(m);
+
+ ObjC_Error = PyString_FromString("objc.error");
+ PyDict_SetItemString(d, "error", ObjC_Error);
+
+ if (PyErr_Occurred())
+ Py_FatalError("can't initialize module objc");
+
+#ifdef WITH_THREAD
+ objc_setMultithreaded(1);
+#endif
+}