Use py_resource module
[python/dscho.git] / Modules / objc.c
blob9e70912712859d4581978d8673334dbd450adfd5
1 /***********************************************************
2 Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
3 The Netherlands.
5 All Rights Reserved
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose and without fee is hereby granted,
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in
11 supporting documentation, and that the names of Stichting Mathematisch
12 Centrum or CWI not be used in advertising or publicity pertaining to
13 distribution of the software without specific, written prior permission.
15 STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18 FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 ******************************************************************/
25 /* Objective-C interface for NeXTStep */
26 /* Tested with NeXTStep 3.3 on Intel and Sparc architectures */
28 /* Original author: Jon M. Kutemeier */
29 /* Revamped and maintained by: Guido van Rossum */
31 /* XXX To do:
32 - bug??? x.send('name', []) gives weird error
33 - rename functions from objc_* to ObjC_*
34 - change send(sel, [a, b, c]) to send(self, a, b, c)
35 - call back to Python from Objective-C
38 /* Python header file */
39 #include "Python.h"
41 /* NeXT headers */
42 #include <sys/param.h>
43 #include <mach-o/rld.h>
44 #include <objc/objc.h>
45 #include <objc/objc-runtime.h>
46 #import <remote/NXProxy.h>
48 /* Distinguish between ObjC classes and instances */
49 typedef enum {
50 OBJC_CLASS,
51 OBJC_INSTANCE,
52 } ObjC_Typecode;
54 /* Exception raised for ObjC specific errors */
55 static PyObject *ObjC_Error;
57 /* Python wrapper about ObjC id (instance or class) */
58 typedef struct {
59 PyObject_HEAD
60 id obj;
61 ObjC_Typecode type;
62 int owned;
63 } ObjCObject;
65 /* Corresponding Python type object */
66 staticforward PyTypeObject ObjC_Type;
68 /* Corresponding Python type check macro */
69 #define ObjC_Check(o) ((o)->ob_type == &ObjC_Type)
71 /* Create a new ObjCObject */
72 static ObjCObject *
73 newObjCObject(obj, type, owned)
74 id obj;
75 ObjC_Typecode type;
76 int owned;
78 ObjCObject *self;
80 self = PyObject_NEW(ObjCObject, &ObjC_Type);
81 if (self == NULL)
82 return NULL;
84 self->obj = obj;
85 self->type = type;
86 self->owned = owned;
88 return self;
91 static void
92 objc_sendfree(self)
93 ObjCObject *self;
95 if (self->obj)
96 self->obj = (id)objc_msgSend(self->obj, SELUID("free"));
99 /* Deallocate an ObjCObject */
100 static void
101 objc_dealloc(self)
102 ObjCObject *self;
104 if (self->owned)
105 objc_sendfree(self);
106 PyMem_DEL(self);
109 /* Return a string representation of an ObjCObject */
110 static PyObject *
111 objc_repr(self)
112 ObjCObject *self;
114 char buffer[512];
115 char *p = buffer;
116 if (self->obj == nil)
117 p = "<Objective-C nil>";
118 else {
119 char *t;
120 switch (self->type) {
121 case OBJC_CLASS: t = "class"; break;
122 case OBJC_INSTANCE: t = "instance"; break;
123 default: t = "???"; break;
125 sprintf(buffer, "<Objective-C %s %s at %lx>",
126 NAMEOF(self->obj), t, (long)(self->obj));
128 return PyString_FromString(p);
131 /*** ObjCObject methods ***/
133 /* Call an object's free method */
134 static PyObject *
135 objc_free(self, args)
136 ObjCObject *self;
137 PyObject *args;
139 if (!PyArg_ParseTuple(args, ""))
140 return NULL;
141 objc_sendfree(self);
144 /* Send a message to an ObjCObject.
145 The Python call looks like e.g. obj.send('moveTo::', [arg1, arg2])
146 which translates into Objective-C as [obj moveTo: arg1 : arg2] */
147 static PyObject *
148 objc_send(self, args)
149 ObjCObject *self;
150 PyObject *args;
152 char *methodname;
153 char *margBuff = NULL;
154 PyObject *retobject = NULL;
155 PyObject *arglist;
156 id receiver, obj;
157 char *type;
158 SEL sel;
159 Method meth;
160 unsigned int margCount, margSize;
161 int offset, i;
163 if (!PyArg_ParseTuple(args, "sO!", &methodname, &PyList_Type, &arglist))
164 return NULL;
166 /* Get the method descriptor from the object */
168 receiver = self->obj;
169 sel = SELUID(methodname);
171 switch(self->type) {
172 case OBJC_CLASS:
173 meth = class_getClassMethod(receiver->isa, sel);
174 break;
175 case OBJC_INSTANCE:
176 meth = class_getInstanceMethod(receiver->isa, sel);
177 break;
178 default:
179 PyErr_SetString(ObjC_Error,
180 "receiver's type is neither instance not class!?!?");
181 return NULL;
184 if (!meth) {
185 PyErr_SetString(ObjC_Error, "receiver has no method by that name");
186 return NULL;
189 /* Fill in the argument list, type-checking the arguments */
191 margCount = method_getNumberOfArguments(meth);
193 if (PyList_Size(arglist) + 2 != margCount) {
194 PyErr_SetString(ObjC_Error,
195 "wrong number of arguments for this method");
196 return NULL;
199 margSize = method_getSizeOfArguments(meth);
200 margBuff = PyMem_NEW(char, margSize+1);
201 if (margBuff == NULL)
202 return PyErr_NoMemory();
204 method_getArgumentInfo(meth, 0, &type, &offset);
205 marg_setValue(margBuff, offset, id, receiver);
207 method_getArgumentInfo(meth, 1, &type, &offset);
208 marg_setValue(margBuff, offset, SEL, sel);
210 for (i = 2; i < margCount; i++) {
211 PyObject *argument;
212 method_getArgumentInfo(meth, i, &type, &offset);
214 argument = PyList_GetItem(arglist, i-2);
216 /* scan past protocol-type modifiers */
217 while (strchr("rnNoOV", *type) != 0)
218 type++;
220 /* common type checks */
221 switch(*type) {
223 /* XXX The errors here should point out which argument */
225 case 'c':
226 case '*':
227 case 'C':
228 if (!PyString_Check(argument)) {
229 PyErr_SetString(ObjC_Error, "string argument expected");
230 goto error;
232 break;
234 case 'i':
235 case 's':
236 case 'I':
237 case 'S':
238 case 'l':
239 case 'L':
240 case '^':
241 if (!PyInt_Check(argument)) {
242 PyErr_SetString(ObjC_Error, "integer argument expected");
243 goto error;
245 break;
247 case 'f':
248 case 'd':
249 if (!PyFloat_Check(argument)) {
250 PyErr_SetString(ObjC_Error, "float argument expected");
251 goto error;
253 break;
257 /* convert and store the argument */
258 switch (*type) {
260 case 'c': /* char */
261 marg_setValue(margBuff, offset, char,
262 PyString_AsString(argument)[0]);
263 break;
265 case 'C': /* unsigned char */
266 marg_setValue(margBuff, offset, unsigned char,
267 PyString_AsString(argument)[0]);
268 break;
270 case '*': /* string */
271 marg_setValue(margBuff, offset, char *,
272 PyString_AsString(argument));
273 break;
275 case 'i': /* int */
276 marg_setValue(margBuff, offset, int,
277 PyInt_AsLong(argument));
278 break;
280 case 'I': /* unsigned int */
281 marg_setValue(margBuff, offset, unsigned int,
282 PyInt_AsLong(argument));
283 break;
285 case 's': /* short */
286 marg_setValue(margBuff, offset, short,
287 PyInt_AsLong(argument));
288 break;
290 case 'S': /* unsigned short */
291 marg_setValue(margBuff, offset, unsigned short,
292 PyInt_AsLong(argument));
293 break;
295 case 'l': /* long */
296 marg_setValue(margBuff, offset, long,
297 PyInt_AsLong(argument));
298 break;
300 case 'L': /* unsigned long */
301 marg_setValue(margBuff, offset, unsigned long,
302 PyInt_AsLong(argument));
303 break;
305 case 'f': /* float */
306 marg_setValue(margBuff, offset, float,
307 (float)PyFloat_AsDouble(argument));
308 break;
310 case 'd': /* double */
311 marg_setValue(margBuff, offset, double,
312 PyFloat_AsDouble(argument));
313 break;
315 case '@': /* id (or None) */
316 if (ObjC_Check(argument))
317 marg_setValue(margBuff, offset, id,
318 ((ObjCObject *)(argument))->obj);
319 else if (argument == Py_None)
320 marg_setValue(margBuff, offset, id, nil);
321 else {
322 PyErr_SetString(ObjC_Error, "id or None argument expected");
323 goto error;
325 break;
327 case '^': /* void * (use int) */
328 marg_setValue(margBuff, offset, void *,
329 (void *)PyInt_AsLong(argument));
330 break;
332 case ':': /* SEL (use string or int) */
333 if (PyInt_Check(argument))
334 marg_setValue(margBuff, offset, SEL,
335 (SEL)PyInt_AsLong(argument));
336 else if (PyString_Check(argument))
337 marg_setValue(margBuff, offset, SEL,
338 SELUID(PyString_AsString(argument)));
339 else {
340 PyErr_SetString(ObjC_Error,
341 "selector string or int argument expected");
342 goto error;
344 break;
346 case '#': /* Class (may also use int) */
347 if (ObjC_Check(argument) &&
348 ((ObjCObject *)argument)->type == OBJC_INSTANCE)
349 marg_setValue(margBuff, offset, Class *,
350 (Class *)((ObjCObject *)argument)->obj);
351 else if (PyInt_Check(argument))
352 marg_setValue(margBuff, offset, Class *,
353 (Class *)PyInt_AsLong(argument));
354 else {
355 PyErr_SetString(ObjC_Error,
356 "ObjC class object required");
357 goto error;
359 break;
361 default:
362 PyErr_SetString(ObjC_Error, "unknown argument type");
363 goto error;
368 /* Call the method and set the return value */
370 type = meth->method_types;
372 while (strchr("rnNoOV", *type))
373 type++;
375 switch(*type) {
377 /* Cast objc_msgSendv to a function returning the right thing */
378 #define MS_CAST(type) ((type (*)())objc_msgSendv)
380 case 'c':
381 case '*':
382 case 'C':
383 retobject = (PyObject *)PyString_FromString(
384 MS_CAST(char *)(receiver, sel, margSize, margBuff));
385 break;
387 case 'i':
388 case 's':
389 case 'I':
390 case 'S':
391 retobject = (PyObject *)PyInt_FromLong(
392 MS_CAST(int)(receiver, sel, margSize, margBuff));
393 break;
395 case 'l':
396 case 'L':
397 case '^':
398 retobject = (PyObject *)PyInt_FromLong(
399 MS_CAST(long)(receiver, sel, margSize, margBuff));
400 break;
402 case 'f':
403 retobject = (PyObject *)PyFloat_FromDouble(
404 MS_CAST(float)(receiver, sel, margSize, margBuff));
405 break;
407 case 'd':
408 retobject = (PyObject *)PyFloat_FromDouble(
409 MS_CAST(double)(receiver, sel, margSize, margBuff));
410 break;
412 case '@':
413 obj = MS_CAST(id)(receiver, sel, margSize, margBuff);
414 if (obj == nil) {
415 retobject = Py_None;
416 Py_INCREF(retobject);
418 else if (obj != receiver)
419 retobject = (PyObject *)newObjCObject(obj, OBJC_INSTANCE, 0);
420 else {
421 retobject = (PyObject *)self;
422 Py_INCREF(retobject);
424 break;
426 case ':':
427 retobject = (PyObject *)PyInt_FromLong(
428 (long)MS_CAST(SEL)(receiver, sel, margSize, margBuff));
429 break;
431 case '#':
432 retobject = (PyObject *)PyInt_FromLong(
433 (long)MS_CAST(Class *)(receiver, sel, margSize, margBuff));
434 break;
436 #undef MS_CAST
440 error:
441 PyMem_XDEL(margBuff);
442 return retobject;
445 /* List of methods for ObjCObject */
446 static PyMethodDef objc_methods[] = {
447 {"send", (PyCFunction)objc_send, 1},
448 {"free", (PyCFunction)objc_free, 1},
449 {NULL, NULL} /* sentinel */
452 /* Get an attribute of an ObjCObject */
453 static PyObject *
454 objc_getattr(self, name)
455 ObjCObject *self;
456 char *name;
458 PyObject *method;
460 /* Try a function method */
461 method = Py_FindMethod(objc_methods, (PyObject *)self, name);
462 if (method != NULL)
463 return method;
464 PyErr_Clear();
466 /* Try an instance variable */
467 if (strcmp(name, "obj") == 0)
468 return PyInt_FromLong((long)self->obj);
469 if (strcmp(name, "type") == 0)
470 return PyInt_FromLong((long)self->type);
471 if (strcmp(name, "owned") == 0)
472 return PyInt_FromLong((long)self->owned);
473 if (strcmp(name, "name") == 0)
474 return PyString_FromString(NAMEOF(self->obj));
475 if (strcmp(name, "__members__") == 0)
476 return Py_BuildValue("[sss]", "name", "obj", "owned", "type");
478 PyErr_SetString(PyExc_AttributeError, name);
479 return NULL;
482 /* The type object */
483 static PyTypeObject ObjC_Type = {
484 PyObject_HEAD_INIT(&PyType_Type)
485 0, /*ob_size*/
486 "objc", /*tp_name*/
487 sizeof(ObjCObject), /*tp_basicsize*/
488 0, /*tp_itemsize*/
489 /* methods */
490 (destructor)objc_dealloc, /*tp_dealloc*/
491 0, /*tp_print*/
492 (getattrfunc)objc_getattr, /*tp_getattr*/
493 0, /*tp_setattr*/
494 0, /*tp_compare*/
495 (reprfunc)objc_repr, /*tp_repr*/
496 0, /*tp_as_number*/
497 0, /*tp_as_sequence*/
498 0, /*tp_as_mapping*/
499 0, /*tp_hash*/
500 0, /*tp_call*/
501 0, /*tp_str*/
502 0, 0, 0, 0, /*xxx1-4*/
503 "Objective-C id wrapper", /*tp_doc*/
508 /*** Top-level functions ***/
510 /* Max #files passed to loadobjectfile() */
511 #define MAXRLD 128
513 /* Load a list of object files */
514 static PyObject *
515 objc_loadobjectfiles(self, args)
516 PyObject *self; /* Not used */
517 PyObject *args;
519 NXStream *errorStream;
520 struct mach_header *new_header;
521 const char *filenames[MAXRLD+1];
522 long ret;
523 char *streamBuf;
524 PyObject *filelist, *file;
525 int listsize, len, maxLen, i;
527 if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &filelist))
528 return NULL;
530 listsize = PyList_Size(filelist);
532 if (listsize > MAXRLD) {
533 PyErr_SetString(ObjC_Error, "more than 128 files in list");
534 return NULL;
537 errorStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
539 for (i = 0; i < listsize; i++) {
540 file = PyList_GetItem(filelist, i);
542 if (!PyString_Check(file))
544 PyErr_SetString(ObjC_Error,
545 "all list items must be strings");
546 return NULL;
549 filenames[i] = PyString_AsString(file);
552 filenames[listsize] = NULL;
554 ret = objc_loadModules(filenames, errorStream, NULL, &new_header, NULL);
556 /* extract the error messages for the exception */
558 if(ret) {
559 NXPutc(errorStream, (char)0);
561 NXGetMemoryBuffer(errorStream, &streamBuf, &len, &maxLen);
562 PyErr_SetString(ObjC_Error, streamBuf);
565 NXCloseMemory(errorStream, NX_FREEBUFFER);
567 if(ret)
568 return NULL;
570 Py_XINCREF(Py_None);
571 return Py_None;
574 static PyObject *
575 objc_lookupclass(self, args)
576 PyObject *self; /* Not used */
577 PyObject *args;
579 char *classname;
580 id class;
582 if (!PyArg_ParseTuple(args, "s", &classname))
583 return NULL;
585 if (!(class = objc_lookUpClass(classname)))
587 PyErr_SetString(ObjC_Error, "unknown ObjC class");
588 return NULL;
591 return (PyObject *)newObjCObject(class, OBJC_CLASS, 0);
594 /* List all classes */
595 static PyObject *
596 objc_listclasses(self, args)
597 ObjCObject *self;
598 PyObject *args;
600 NXHashTable *class_hash = objc_getClasses();
601 NXHashState state = NXInitHashState(class_hash);
602 Class classid;
603 PyObject *list;
605 if (!PyArg_ParseTuple(args, ""))
606 return NULL;
608 list = PyList_New(0);
609 if (list == NULL)
610 return NULL;
612 while (NXNextHashState(class_hash, &state, (void**)&classid)) {
613 ObjCObject *item = newObjCObject(classid, OBJC_CLASS, 0);
614 if (item == NULL || PyList_Append(list, (PyObject *)item) < 0) {
615 Py_XDECREF(item);
616 Py_DECREF(list);
617 return NULL;
619 Py_INCREF(item);
622 return list;
625 /* List of top-level functions */
626 static PyMethodDef objc_class_methods[] = {
627 {"loadobjectfiles", objc_loadobjectfiles, 1},
628 {"lookupclass", objc_lookupclass, 1},
629 {"listclasses", objc_listclasses, 1},
630 {NULL, NULL} /* sentinel */
633 /* Initialize for the module */
634 void
635 initobjc()
637 PyObject *m, *d;
639 m = Py_InitModule("objc", objc_class_methods);
640 d = PyModule_GetDict(m);
642 ObjC_Error = PyString_FromString("objc.error");
643 PyDict_SetItemString(d, "error", ObjC_Error);
645 if (PyErr_Occurred())
646 Py_FatalError("can't initialize module objc");
648 #ifdef WITH_THREAD
649 objc_setMultithreaded(1);
650 #endif