diff options
Diffstat (limited to 'Modules/objc.c')
-rw-r--r-- | Modules/objc.c | 651 |
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 +} |