1 /***********************************************************
2 Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
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 */
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 */
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 */
54 /* Exception raised for ObjC specific errors */
55 static PyObject
*ObjC_Error
;
57 /* Python wrapper about ObjC id (instance or class) */
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 */
73 newObjCObject(obj
, type
, owned
)
80 self
= PyObject_NEW(ObjCObject
, &ObjC_Type
);
96 self
->obj
= (id
)objc_msgSend(self
->obj
, SELUID("free"));
99 /* Deallocate an ObjCObject */
109 /* Return a string representation of an ObjCObject */
116 if (self
->obj
== nil
)
117 p
= "<Objective-C nil>";
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 */
135 objc_free(self
, args
)
139 if (!PyArg_ParseTuple(args
, ""))
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] */
148 objc_send(self
, args
)
153 char *margBuff
= NULL
;
154 PyObject
*retobject
= NULL
;
160 unsigned int margCount
, margSize
;
163 if (!PyArg_ParseTuple(args
, "sO!", &methodname
, &PyList_Type
, &arglist
))
166 /* Get the method descriptor from the object */
168 receiver
= self
->obj
;
169 sel
= SELUID(methodname
);
173 meth
= class_getClassMethod(receiver
->isa
, sel
);
176 meth
= class_getInstanceMethod(receiver
->isa
, sel
);
179 PyErr_SetString(ObjC_Error
,
180 "receiver's type is neither instance not class!?!?");
185 PyErr_SetString(ObjC_Error
, "receiver has no method by that name");
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");
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
++) {
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)
220 /* common type checks */
223 /* XXX The errors here should point out which argument */
228 if (!PyString_Check(argument
)) {
229 PyErr_SetString(ObjC_Error
, "string argument expected");
241 if (!PyInt_Check(argument
)) {
242 PyErr_SetString(ObjC_Error
, "integer argument expected");
249 if (!PyFloat_Check(argument
)) {
250 PyErr_SetString(ObjC_Error
, "float argument expected");
257 /* convert and store the argument */
261 marg_setValue(margBuff
, offset
, char,
262 PyString_AsString(argument
)[0]);
265 case 'C': /* unsigned char */
266 marg_setValue(margBuff
, offset
, unsigned char,
267 PyString_AsString(argument
)[0]);
270 case '*': /* string */
271 marg_setValue(margBuff
, offset
, char *,
272 PyString_AsString(argument
));
276 marg_setValue(margBuff
, offset
, int,
277 PyInt_AsLong(argument
));
280 case 'I': /* unsigned int */
281 marg_setValue(margBuff
, offset
, unsigned int,
282 PyInt_AsLong(argument
));
285 case 's': /* short */
286 marg_setValue(margBuff
, offset
, short,
287 PyInt_AsLong(argument
));
290 case 'S': /* unsigned short */
291 marg_setValue(margBuff
, offset
, unsigned short,
292 PyInt_AsLong(argument
));
296 marg_setValue(margBuff
, offset
, long,
297 PyInt_AsLong(argument
));
300 case 'L': /* unsigned long */
301 marg_setValue(margBuff
, offset
, unsigned long,
302 PyInt_AsLong(argument
));
305 case 'f': /* float */
306 marg_setValue(margBuff
, offset
, float,
307 (float)PyFloat_AsDouble(argument
));
310 case 'd': /* double */
311 marg_setValue(margBuff
, offset
, double,
312 PyFloat_AsDouble(argument
));
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
);
322 PyErr_SetString(ObjC_Error
, "id or None argument expected");
327 case '^': /* void * (use int) */
328 marg_setValue(margBuff
, offset
, void *,
329 (void *)PyInt_AsLong(argument
));
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
)));
340 PyErr_SetString(ObjC_Error
,
341 "selector string or int argument expected");
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
));
355 PyErr_SetString(ObjC_Error
,
356 "ObjC class object required");
362 PyErr_SetString(ObjC_Error
, "unknown argument type");
368 /* Call the method and set the return value */
370 type
= meth
->method_types
;
372 while (strchr("rnNoOV", *type
))
377 /* Cast objc_msgSendv to a function returning the right thing */
378 #define MS_CAST(type) ((type (*)())objc_msgSendv)
383 retobject
= (PyObject
*)PyString_FromString(
384 MS_CAST(char *)(receiver
, sel
, margSize
, margBuff
));
391 retobject
= (PyObject
*)PyInt_FromLong(
392 MS_CAST(int)(receiver
, sel
, margSize
, margBuff
));
398 retobject
= (PyObject
*)PyInt_FromLong(
399 MS_CAST(long)(receiver
, sel
, margSize
, margBuff
));
403 retobject
= (PyObject
*)PyFloat_FromDouble(
404 MS_CAST(float)(receiver
, sel
, margSize
, margBuff
));
408 retobject
= (PyObject
*)PyFloat_FromDouble(
409 MS_CAST(double)(receiver
, sel
, margSize
, margBuff
));
413 obj
= MS_CAST(id
)(receiver
, sel
, margSize
, margBuff
);
416 Py_INCREF(retobject
);
418 else if (obj
!= receiver
)
419 retobject
= (PyObject
*)newObjCObject(obj
, OBJC_INSTANCE
, 0);
421 retobject
= (PyObject
*)self
;
422 Py_INCREF(retobject
);
427 retobject
= (PyObject
*)PyInt_FromLong(
428 (long)MS_CAST(SEL
)(receiver
, sel
, margSize
, margBuff
));
432 retobject
= (PyObject
*)PyInt_FromLong(
433 (long)MS_CAST(Class
*)(receiver
, sel
, margSize
, margBuff
));
441 PyMem_XDEL(margBuff
);
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 */
454 objc_getattr(self
, name
)
460 /* Try a function method */
461 method
= Py_FindMethod(objc_methods
, (PyObject
*)self
, name
);
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
);
482 /* The type object */
483 static PyTypeObject ObjC_Type
= {
484 PyObject_HEAD_INIT(&PyType_Type
)
487 sizeof(ObjCObject
), /*tp_basicsize*/
490 (destructor
)objc_dealloc
, /*tp_dealloc*/
492 (getattrfunc
)objc_getattr
, /*tp_getattr*/
495 (reprfunc
)objc_repr
, /*tp_repr*/
497 0, /*tp_as_sequence*/
502 0, 0, 0, 0, /*xxx1-4*/
503 "Objective-C id wrapper", /*tp_doc*/
508 /*** Top-level functions ***/
510 /* Max #files passed to loadobjectfile() */
513 /* Load a list of object files */
515 objc_loadobjectfiles(self
, args
)
516 PyObject
*self
; /* Not used */
519 NXStream
*errorStream
;
520 struct mach_header
*new_header
;
521 const char *filenames
[MAXRLD
+1];
524 PyObject
*filelist
, *file
;
525 int listsize
, len
, maxLen
, i
;
527 if (!PyArg_ParseTuple(args
, "O!", &PyList_Type
, &filelist
))
530 listsize
= PyList_Size(filelist
);
532 if (listsize
> MAXRLD
) {
533 PyErr_SetString(ObjC_Error
, "more than 128 files in list");
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");
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 */
559 NXPutc(errorStream
, (char)0);
561 NXGetMemoryBuffer(errorStream
, &streamBuf
, &len
, &maxLen
);
562 PyErr_SetString(ObjC_Error
, streamBuf
);
565 NXCloseMemory(errorStream
, NX_FREEBUFFER
);
575 objc_lookupclass(self
, args
)
576 PyObject
*self
; /* Not used */
582 if (!PyArg_ParseTuple(args
, "s", &classname
))
585 if (!(class = objc_lookUpClass(classname
)))
587 PyErr_SetString(ObjC_Error
, "unknown ObjC class");
591 return (PyObject
*)newObjCObject(class, OBJC_CLASS
, 0);
594 /* List all classes */
596 objc_listclasses(self
, args
)
600 NXHashTable
*class_hash
= objc_getClasses();
601 NXHashState state
= NXInitHashState(class_hash
);
605 if (!PyArg_ParseTuple(args
, ""))
608 list
= PyList_New(0);
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) {
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 */
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");
649 objc_setMultithreaded(1);