blob: 9e70912712859d4581978d8673334dbd450adfd5 [file] [log] [blame]
Guido van Rossum667d7041995-08-04 04:20:48 +00001/***********************************************************
2Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
3The Netherlands.
4
5 All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the names of Stichting Mathematisch
12Centrum or CWI not be used in advertising or publicity pertaining to
13distribution of the software without specific, written prior permission.
14
15STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23******************************************************************/
24
25/* Objective-C interface for NeXTStep */
26/* Tested with NeXTStep 3.3 on Intel and Sparc architectures */
27
28/* Original author: Jon M. Kutemeier */
29/* Revamped and maintained by: Guido van Rossum */
30
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
36 */
37
38/* Python header file */
39#include "Python.h"
40
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>
47
48/* Distinguish between ObjC classes and instances */
49typedef enum {
50 OBJC_CLASS,
51 OBJC_INSTANCE,
52} ObjC_Typecode;
53
54/* Exception raised for ObjC specific errors */
55static PyObject *ObjC_Error;
56
57/* Python wrapper about ObjC id (instance or class) */
58typedef struct {
59 PyObject_HEAD
60 id obj;
61 ObjC_Typecode type;
62 int owned;
63} ObjCObject;
64
65/* Corresponding Python type object */
66staticforward PyTypeObject ObjC_Type;
67
68/* Corresponding Python type check macro */
69#define ObjC_Check(o) ((o)->ob_type == &ObjC_Type)
70
71/* Create a new ObjCObject */
72static ObjCObject *
73newObjCObject(obj, type, owned)
74 id obj;
75 ObjC_Typecode type;
76 int owned;
77{
78 ObjCObject *self;
79
80 self = PyObject_NEW(ObjCObject, &ObjC_Type);
81 if (self == NULL)
82 return NULL;
83
84 self->obj = obj;
85 self->type = type;
86 self->owned = owned;
87
88 return self;
89}
90
91static void
92objc_sendfree(self)
93 ObjCObject *self;
94{
95 if (self->obj)
96 self->obj = (id)objc_msgSend(self->obj, SELUID("free"));
97}
98
99/* Deallocate an ObjCObject */
100static void
101objc_dealloc(self)
102 ObjCObject *self;
103{
104 if (self->owned)
105 objc_sendfree(self);
106 PyMem_DEL(self);
107}
108
109/* Return a string representation of an ObjCObject */
110static PyObject *
111objc_repr(self)
112 ObjCObject *self;
113{
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;
124 }
125 sprintf(buffer, "<Objective-C %s %s at %lx>",
126 NAMEOF(self->obj), t, (long)(self->obj));
127 }
128 return PyString_FromString(p);
129}
130
131/*** ObjCObject methods ***/
132
133/* Call an object's free method */
134static PyObject *
135objc_free(self, args)
136 ObjCObject *self;
137 PyObject *args;
138{
139 if (!PyArg_ParseTuple(args, ""))
140 return NULL;
141 objc_sendfree(self);
142}
143
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] */
147static PyObject *
148objc_send(self, args)
149 ObjCObject *self;
150 PyObject *args;
151{
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;
162
163 if (!PyArg_ParseTuple(args, "sO!", &methodname, &PyList_Type, &arglist))
164 return NULL;
165
166 /* Get the method descriptor from the object */
167
168 receiver = self->obj;
169 sel = SELUID(methodname);
170
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;
182 }
183
184 if (!meth) {
185 PyErr_SetString(ObjC_Error, "receiver has no method by that name");
186 return NULL;
187 }
188
189 /* Fill in the argument list, type-checking the arguments */
190
191 margCount = method_getNumberOfArguments(meth);
192
193 if (PyList_Size(arglist) + 2 != margCount) {
194 PyErr_SetString(ObjC_Error,
195 "wrong number of arguments for this method");
196 return NULL;
197 }
198
199 margSize = method_getSizeOfArguments(meth);
200 margBuff = PyMem_NEW(char, margSize+1);
201 if (margBuff == NULL)
202 return PyErr_NoMemory();
203
204 method_getArgumentInfo(meth, 0, &type, &offset);
205 marg_setValue(margBuff, offset, id, receiver);
206
207 method_getArgumentInfo(meth, 1, &type, &offset);
208 marg_setValue(margBuff, offset, SEL, sel);
209
210 for (i = 2; i < margCount; i++) {
211 PyObject *argument;
212 method_getArgumentInfo(meth, i, &type, &offset);
213
214 argument = PyList_GetItem(arglist, i-2);
215
216 /* scan past protocol-type modifiers */
217 while (strchr("rnNoOV", *type) != 0)
218 type++;
219
220 /* common type checks */
221 switch(*type) {
222
223 /* XXX The errors here should point out which argument */
224
225 case 'c':
226 case '*':
227 case 'C':
228 if (!PyString_Check(argument)) {
229 PyErr_SetString(ObjC_Error, "string argument expected");
230 goto error;
231 }
232 break;
233
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;
244 }
245 break;
246
247 case 'f':
248 case 'd':
249 if (!PyFloat_Check(argument)) {
250 PyErr_SetString(ObjC_Error, "float argument expected");
251 goto error;
252 }
253 break;
254
255 }
256
257 /* convert and store the argument */
258 switch (*type) {
259
260 case 'c': /* char */
261 marg_setValue(margBuff, offset, char,
262 PyString_AsString(argument)[0]);
263 break;
264
265 case 'C': /* unsigned char */
266 marg_setValue(margBuff, offset, unsigned char,
267 PyString_AsString(argument)[0]);
268 break;
269
270 case '*': /* string */
271 marg_setValue(margBuff, offset, char *,
272 PyString_AsString(argument));
273 break;
274
275 case 'i': /* int */
276 marg_setValue(margBuff, offset, int,
277 PyInt_AsLong(argument));
278 break;
279
280 case 'I': /* unsigned int */
281 marg_setValue(margBuff, offset, unsigned int,
282 PyInt_AsLong(argument));
283 break;
284
285 case 's': /* short */
286 marg_setValue(margBuff, offset, short,
287 PyInt_AsLong(argument));
288 break;
289
290 case 'S': /* unsigned short */
291 marg_setValue(margBuff, offset, unsigned short,
292 PyInt_AsLong(argument));
293 break;
294
295 case 'l': /* long */
296 marg_setValue(margBuff, offset, long,
297 PyInt_AsLong(argument));
298 break;
299
300 case 'L': /* unsigned long */
301 marg_setValue(margBuff, offset, unsigned long,
302 PyInt_AsLong(argument));
303 break;
304
305 case 'f': /* float */
306 marg_setValue(margBuff, offset, float,
307 (float)PyFloat_AsDouble(argument));
308 break;
309
310 case 'd': /* double */
311 marg_setValue(margBuff, offset, double,
312 PyFloat_AsDouble(argument));
313 break;
314
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;
324 }
325 break;
326
327 case '^': /* void * (use int) */
328 marg_setValue(margBuff, offset, void *,
329 (void *)PyInt_AsLong(argument));
330 break;
331
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;
343 }
344 break;
345
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;
358 }
359 break;
360
361 default:
362 PyErr_SetString(ObjC_Error, "unknown argument type");
363 goto error;
364
365 }
366 }
367
368 /* Call the method and set the return value */
369
370 type = meth->method_types;
371
372 while (strchr("rnNoOV", *type))
373 type++;
374
375 switch(*type) {
376
377/* Cast objc_msgSendv to a function returning the right thing */
378#define MS_CAST(type) ((type (*)())objc_msgSendv)
379
380 case 'c':
381 case '*':
382 case 'C':
383 retobject = (PyObject *)PyString_FromString(
384 MS_CAST(char *)(receiver, sel, margSize, margBuff));
385 break;
386
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;
394
395 case 'l':
396 case 'L':
397 case '^':
398 retobject = (PyObject *)PyInt_FromLong(
399 MS_CAST(long)(receiver, sel, margSize, margBuff));
400 break;
401
402 case 'f':
403 retobject = (PyObject *)PyFloat_FromDouble(
404 MS_CAST(float)(receiver, sel, margSize, margBuff));
405 break;
406
407 case 'd':
408 retobject = (PyObject *)PyFloat_FromDouble(
409 MS_CAST(double)(receiver, sel, margSize, margBuff));
410 break;
411
412 case '@':
413 obj = MS_CAST(id)(receiver, sel, margSize, margBuff);
414 if (obj == nil) {
415 retobject = Py_None;
416 Py_INCREF(retobject);
417 }
418 else if (obj != receiver)
419 retobject = (PyObject *)newObjCObject(obj, OBJC_INSTANCE, 0);
420 else {
421 retobject = (PyObject *)self;
422 Py_INCREF(retobject);
423 }
424 break;
425
426 case ':':
427 retobject = (PyObject *)PyInt_FromLong(
428 (long)MS_CAST(SEL)(receiver, sel, margSize, margBuff));
429 break;
430
431 case '#':
432 retobject = (PyObject *)PyInt_FromLong(
433 (long)MS_CAST(Class *)(receiver, sel, margSize, margBuff));
434 break;
435
436#undef MS_CAST
437
438 }
439
440 error:
441 PyMem_XDEL(margBuff);
442 return retobject;
443}
444
445/* List of methods for ObjCObject */
446static PyMethodDef objc_methods[] = {
447 {"send", (PyCFunction)objc_send, 1},
448 {"free", (PyCFunction)objc_free, 1},
449 {NULL, NULL} /* sentinel */
450};
451
452/* Get an attribute of an ObjCObject */
453static PyObject *
454objc_getattr(self, name)
455 ObjCObject *self;
456 char *name;
457{
458 PyObject *method;
459
460 /* Try a function method */
461 method = Py_FindMethod(objc_methods, (PyObject *)self, name);
462 if (method != NULL)
463 return method;
464 PyErr_Clear();
465
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");
477
478 PyErr_SetString(PyExc_AttributeError, name);
479 return NULL;
480}
481
482/* The type object */
483static 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*/
504};
505
506
507
508/*** Top-level functions ***/
509
510/* Max #files passed to loadobjectfile() */
511#define MAXRLD 128
512
513/* Load a list of object files */
514static PyObject *
515objc_loadobjectfiles(self, args)
516PyObject *self; /* Not used */
517PyObject *args;
518{
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;
526
527 if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &filelist))
528 return NULL;
529
530 listsize = PyList_Size(filelist);
531
532 if (listsize > MAXRLD) {
533 PyErr_SetString(ObjC_Error, "more than 128 files in list");
534 return NULL;
535 }
536
537 errorStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
538
539 for (i = 0; i < listsize; i++) {
540 file = PyList_GetItem(filelist, i);
541
542 if (!PyString_Check(file))
543 {
544 PyErr_SetString(ObjC_Error,
545 "all list items must be strings");
546 return NULL;
547 }
548
549 filenames[i] = PyString_AsString(file);
550 }
551
552 filenames[listsize] = NULL;
553
554 ret = objc_loadModules(filenames, errorStream, NULL, &new_header, NULL);
555
556 /* extract the error messages for the exception */
557
558 if(ret) {
559 NXPutc(errorStream, (char)0);
560
561 NXGetMemoryBuffer(errorStream, &streamBuf, &len, &maxLen);
562 PyErr_SetString(ObjC_Error, streamBuf);
563 }
564
565 NXCloseMemory(errorStream, NX_FREEBUFFER);
566
567 if(ret)
568 return NULL;
569
570 Py_XINCREF(Py_None);
571 return Py_None;
572}
573
574static PyObject *
575objc_lookupclass(self, args)
576PyObject *self; /* Not used */
577PyObject *args;
578{
579 char *classname;
580 id class;
581
582 if (!PyArg_ParseTuple(args, "s", &classname))
583 return NULL;
584
585 if (!(class = objc_lookUpClass(classname)))
586 {
587 PyErr_SetString(ObjC_Error, "unknown ObjC class");
588 return NULL;
589 }
590
591 return (PyObject *)newObjCObject(class, OBJC_CLASS, 0);
592}
593
594/* List all classes */
595static PyObject *
596objc_listclasses(self, args)
597 ObjCObject *self;
598 PyObject *args;
599{
600 NXHashTable *class_hash = objc_getClasses();
601 NXHashState state = NXInitHashState(class_hash);
602 Class classid;
603 PyObject *list;
604
605 if (!PyArg_ParseTuple(args, ""))
606 return NULL;
607
608 list = PyList_New(0);
609 if (list == NULL)
610 return NULL;
611
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;
618 }
619 Py_INCREF(item);
620 }
621
622 return list;
623}
624
625/* List of top-level functions */
626static PyMethodDef objc_class_methods[] = {
627 {"loadobjectfiles", objc_loadobjectfiles, 1},
628 {"lookupclass", objc_lookupclass, 1},
629 {"listclasses", objc_listclasses, 1},
630 {NULL, NULL} /* sentinel */
631};
632
633/* Initialize for the module */
634void
635initobjc()
636{
637 PyObject *m, *d;
638
639 m = Py_InitModule("objc", objc_class_methods);
640 d = PyModule_GetDict(m);
641
642 ObjC_Error = PyString_FromString("objc.error");
643 PyDict_SetItemString(d, "error", ObjC_Error);
644
645 if (PyErr_Occurred())
646 Py_FatalError("can't initialize module objc");
647
648#ifdef WITH_THREAD
649 objc_setMultithreaded(1);
650#endif
651}