1#include "pyobjc.h"
2
3typedef struct {
4	PyObject_HEAD
5	IMP imp;
6	PyObjC_CallFunc callfunc;
7	PyObjCMethodSignature* signature;
8	SEL selector;
9	int flags;
10} PyObjCIMPObject;
11
12PyObject* PyObjCIMP_New(
13		IMP imp, 
14		SEL selector,
15		PyObjC_CallFunc callfunc,
16		PyObjCMethodSignature* signature,
17		int flags)
18{
19	PyObjCIMPObject* result;
20
21	result = PyObject_New(PyObjCIMPObject, &PyObjCIMP_Type);
22	if (result == NULL) return NULL;
23
24	result->imp = imp;
25	result->selector = selector;
26	result->callfunc = callfunc;
27	result->signature = signature;
28	if (signature) {
29		Py_INCREF(signature);
30	}
31	result->flags = flags;
32	return (PyObject*)result;
33}
34
35SEL PyObjCIMP_GetSelector(PyObject* self)
36{
37	if (!PyObjCIMP_Check(self)) {
38		PyErr_BadInternalCall();
39		return NULL;
40	}
41
42	return ((PyObjCIMPObject*)self)->selector;
43}
44
45IMP PyObjCIMP_GetIMP(PyObject* self)
46{
47	if (!PyObjCIMP_Check(self)) {
48		PyErr_BadInternalCall();
49		return NULL;
50	}
51
52	return ((PyObjCIMPObject*)self)->imp;
53}
54
55int PyObjCIMP_GetFlags(PyObject* self)
56{
57	if (!PyObjCIMP_Check(self)) {
58		PyErr_BadInternalCall();
59		return -1;
60	}
61
62	return ((PyObjCIMPObject*)self)->flags;
63}
64
65PyObjC_CallFunc PyObjCIMP_GetCallFunc(PyObject* self)
66{
67	if (!PyObjCIMP_Check(self)) {
68		PyErr_BadInternalCall();
69		return NULL;
70	}
71
72	return ((PyObjCIMPObject*)self)->callfunc;
73}
74
75PyObjCMethodSignature*   PyObjCIMP_GetSignature(PyObject* self)
76{
77	if (!PyObjCIMP_Check(self)) {
78		PyErr_BadInternalCall();
79		return NULL;
80	}
81
82	return ((PyObjCIMPObject*)self)->signature;
83}
84
85/* ========================================================================= */
86
87
88static PyObject*
89imp_call(PyObject* _self, PyObject* args, PyObject* kwds)
90{
91	PyObjCIMPObject* self = (PyObjCIMPObject*)_self;
92	PyObject* pyself;
93	PyObjC_CallFunc execute = NULL;
94	PyObject* res;
95	PyObject* pyres;
96	Py_ssize_t argslen;
97	PyObject* arglist;
98	Py_ssize_t i;
99
100	if (kwds != NULL && PyObject_Size(kwds) != 0) {
101		PyErr_SetString(PyExc_TypeError,
102		    "Objective-C selectors don't support keyword arguments");
103		return NULL;
104	}
105
106	argslen = PyTuple_Size(args);
107	if (argslen < 1) {
108		PyErr_SetString(PyExc_TypeError, "Missing argument: self");
109		return NULL;
110	}
111
112	pyself = PyTuple_GET_ITEM(args, 0);
113	if (pyself == NULL) {
114		return NULL;
115	}
116
117	execute = self->callfunc;
118
119	arglist = PyTuple_New(argslen - 1);
120	for (i = 1; i < argslen; i++) {
121		PyObject* v = PyTuple_GET_ITEM(args, i);
122		if (v == NULL) {
123			Py_DECREF(arglist);
124			return NULL;
125		}
126
127		PyTuple_SET_ITEM(arglist, i-1, v);
128		Py_INCREF(v);
129	}
130
131	pyres = res = execute((PyObject*)self, pyself, arglist);
132	Py_DECREF(arglist);
133
134	if (pyres != NULL
135		&& PyTuple_Check(pyres)
136		&& PyTuple_GET_SIZE(pyres) > 1
137		&& PyTuple_GET_ITEM(pyres, 0) == pyself) {
138		pyres = pyself;
139	}
140
141	if (PyObjCObject_Check(pyself) && (((PyObjCObject*)pyself)->flags & PyObjCObject_kUNINITIALIZED)) {
142		if (pyself != pyres && !PyErr_Occurred()) {
143			PyObjCObject_ClearObject(pyself);
144		}
145	}
146
147	if (pyres && PyObjCObject_Check(res)) {
148		if (self->flags & PyObjCSelector_kRETURNS_UNINITIALIZED) {
149			((PyObjCObject*)pyres)->flags |= PyObjCObject_kUNINITIALIZED;
150		} else if (((PyObjCObject*)pyres)->flags & PyObjCObject_kUNINITIALIZED) {
151			((PyObjCObject*)pyres)->flags &=
152				~PyObjCObject_kUNINITIALIZED;
153			if (pyself && pyself != pyres && PyObjCObject_Check(pyself) && !PyErr_Occurred()) {
154				PyObjCObject_ClearObject(pyself);
155			}
156		}
157	}
158
159	return res;
160}
161
162static PyObject* 
163imp_repr(PyObject* _self)
164{
165	PyObjCIMPObject* self = (PyObjCIMPObject*)_self;
166	return PyText_FromFormat("<IMP %s at %p for %p>",
167		sel_getName(self->selector),
168		self, self->imp);
169}
170
171static void
172imp_dealloc(PyObject* _self)
173{
174	PyObjCIMPObject* self = (PyObjCIMPObject*)_self;
175	Py_XDECREF(self->signature);
176	PyObject_Free(self);
177}
178
179PyDoc_STRVAR(imp_signature_doc, "Objective-C signature for the IMP");
180static PyObject*
181imp_signature(PyObject* _self, void* closure __attribute__((__unused__)))
182{
183	PyObjCIMPObject* self = (PyObjCIMPObject*)_self;
184	if (self->signature) {
185		return PyBytes_FromString(self->signature->signature);
186	} else {
187		Py_INCREF(Py_None);
188		return Py_None;
189	}
190}
191
192PyDoc_STRVAR(imp_selector_doc, "Objective-C name for the IMP");
193static PyObject*
194imp_selector(PyObject* _self, void* closure __attribute__((__unused__)))
195{
196	PyObjCIMPObject* self = (PyObjCIMPObject*)_self;
197	return PyBytes_FromString(sel_getName(self->selector));
198}
199
200PyDoc_STRVAR(imp_class_method_doc, 
201	"True if this is a class method, False otherwise");
202static PyObject*
203imp_class_method(PyObject* _self, void* closure __attribute__((__unused__)))
204{
205	PyObjCIMPObject* self = (PyObjCIMPObject*)_self;
206	return PyBool_FromLong(0 != (self->flags & PyObjCSelector_kCLASS_METHOD));
207}
208
209PyDoc_STRVAR(imp_is_alloc_doc, 
210"True if this is method returns a a freshly allocated object (uninitialized)\n"
211"\n"
212"NOTE: This field is used by the implementation."
213);
214static PyObject*
215imp_is_alloc(PyObject* _self, void* closure __attribute__((__unused__)))
216{
217	PyObjCIMPObject* self = (PyObjCIMPObject*)_self;
218	return PyBool_FromLong(0 != (self->flags & PyObjCSelector_kRETURNS_UNINITIALIZED));
219}
220
221
222static PyGetSetDef imp_getset[] = {
223	{
224		"isAlloc",
225		imp_is_alloc,
226		0,
227		imp_is_alloc_doc,
228		0
229	},
230	{
231		"isClassMethod",
232		imp_class_method,
233		0,
234		imp_class_method_doc,
235		0
236	},
237	{ 
238		"signature", 
239		imp_signature, 
240		0,
241		imp_signature_doc, 
242		0
243	},
244	{ 
245		"selector",  
246		imp_selector, 
247		0, 
248		imp_selector_doc,
249		0
250	},
251	{ 
252		"__name__",  
253		imp_selector, 
254		0, 
255		imp_selector_doc,
256		0
257	},
258	{ 0, 0, 0, 0, 0 }
259};
260
261static PyObject* 
262imp_metadata(PyObject* self)
263{
264	PyObject* result = PyObjCMethodSignature_AsDict(
265			((PyObjCIMPObject*)self)->signature);
266	int r;
267	if (((PyObjCIMPObject*)self)->flags & PyObjCSelector_kCLASS_METHOD) {
268		r = PyDict_SetItemString(result, "classmethod", Py_True);
269	} else {
270		r = PyDict_SetItemString(result, "classmethod", Py_False);
271	}
272	if (r == -1) {
273		Py_DECREF(result);
274		return NULL;
275	}
276
277
278	if (((PyObjCIMPObject*)self)->flags & PyObjCSelector_kRETURNS_UNINITIALIZED) {
279		r = PyDict_SetItemString(result, "return_unitialized_object", Py_True);
280		if (r == -1) {
281			Py_DECREF(result);
282			return NULL;
283		}
284	}
285
286	return result;
287}
288
289static PyMethodDef imp_methods[] = {
290	{
291		"__metadata__",
292		(PyCFunction)imp_metadata,
293		METH_NOARGS,
294		"Return metadata for the method",
295	},
296	{ 0, 0, 0, 0}
297};
298
299
300PyTypeObject PyObjCIMP_Type = {
301	PyVarObject_HEAD_INIT(NULL, 0)
302	"objc.IMP",				/* tp_name */
303	sizeof(PyObjCIMPObject),		/* tp_basicsize */
304	0,					/* tp_itemsize */
305	/* methods */
306	imp_dealloc,				/* tp_dealloc */
307	0,					/* tp_print */
308	0,					/* tp_getattr */
309	0,					/* tp_setattr */
310	0,					/* tp_compare */
311	imp_repr,				/* tp_repr */
312	0,					/* tp_as_number */
313	0,					/* tp_as_sequence */
314	0,		       			/* tp_as_mapping */
315	0,					/* tp_hash */
316	imp_call,				/* tp_call */
317	0,					/* tp_str */
318	PyObject_GenericGetAttr,		/* tp_getattro */
319	0,					/* tp_setattro */
320	0,					/* tp_as_buffer */
321	Py_TPFLAGS_DEFAULT,			/* tp_flags */
322 	0,					/* tp_doc */
323 	0,					/* tp_traverse */
324 	0,					/* tp_clear */
325	0,					/* tp_richcompare */
326	0,					/* tp_weaklistoffset */
327	0,					/* tp_iter */
328	0,					/* tp_iternext */
329	imp_methods,				/* tp_methods */
330	0,					/* tp_members */
331	imp_getset,				/* tp_getset */
332	0,					/* tp_base */
333	0,					/* tp_dict */
334	0,					/* tp_descr_get */
335	0,					/* tp_descr_set */
336	0,					/* tp_dictoffset */
337	0,					/* tp_init */
338	0,					/* tp_alloc */
339	0,					/* tp_new */
340	0,		        		/* tp_free */
341	0,					/* tp_is_gc */
342	0,                                      /* tp_bases */
343	0,                                      /* tp_mro */
344	0,                                      /* tp_cache */
345	0,                                      /* tp_subclasses */
346	0,                                      /* tp_weaklist */
347	0                                       /* tp_del */
348#if PY_VERSION_HEX >= 0x02060000
349	, 0                                     /* tp_version_tag */
350#endif
351
352};
353
354
355/* ========================================================================= */
356
357static PyObject*
358call_instanceMethodForSelector_(PyObject* method, PyObject* self, PyObject* args)
359{
360	PyObject* sel;
361	SEL selector;
362	IMP retval;
363	PyObject* attr;
364	PyObject* res;
365
366	if (!PyArg_ParseTuple(args, "O", &sel)) {
367		return NULL;
368	}
369
370	if (depythonify_c_value(@encode(SEL), sel, &selector) == -1) {
371		return NULL;
372	}
373
374	if (!PyObjCClass_Check(self)) {
375		PyErr_Format(PyExc_TypeError, 
376			"Expecting instance of 'objc.objc_class' as 'self', "
377			"got '%s'", Py_TYPE(self)->tp_name);
378		return NULL;
379	}
380
381	PyObjC_DURING
382		retval = (IMP)objc_msgSend(PyObjCClass_GetClass(self),
383			PyObjCSelector_GetSelector(method),
384			selector);
385	PyObjC_HANDLER
386		PyObjCErr_FromObjC(localException);
387		retval = NULL;
388
389	PyObjC_ENDHANDLER
390
391	if (retval == NULL) {
392		if (PyErr_Occurred()) {
393			return NULL;
394		}
395		Py_INCREF(Py_None);
396		return Py_None;
397	}
398
399	attr = PyObjCClass_FindSelector(self, selector, NO); 
400	if (attr == NULL) {
401		return NULL;
402	}
403
404	/* 
405	 * XXX: what if the method is implemented in Python. We should be able to deal with that!!!
406	 */
407
408	if (!PyObjCNativeSelector_Check(attr)) {
409		PyErr_Format(PyExc_TypeError, "Cannot locate Python representation of %s",
410				sel_getName(selector));
411		return NULL;
412	}
413
414	if (((PyObjCNativeSelector*)attr)->sel_call_func == NULL) {
415		((PyObjCNativeSelector*)attr)->sel_call_func = PyObjC_FindCallFunc(((PyObjCNativeSelector*)attr)->sel_class, ((PyObjCNativeSelector*)attr)->sel_selector);
416		if (((PyObjCNativeSelector*)attr)->sel_call_func == NULL) {
417			return NULL;
418		}
419	}
420
421	res = PyObjCIMP_New(
422			retval,
423			selector,
424			((PyObjCNativeSelector*)attr)->sel_call_func,
425			PyObjCSelector_GetMetadata(attr),
426			PyObjCSelector_GetFlags(attr)
427		);
428	Py_DECREF(attr);
429	return res;
430}
431
432static PyObject*
433call_methodForSelector_(PyObject* method, PyObject* self, PyObject* args)
434{
435	PyObject* sel;
436	SEL selector;
437	struct objc_super super;
438	IMP retval;
439	PyObject* attr;
440	PyObject* res;
441
442	if (!PyArg_ParseTuple(args, "O", &sel)) {
443		return NULL;
444	}
445
446	if (depythonify_c_value(@encode(SEL), sel, &selector) == -1) {
447		return NULL;
448	}
449
450	if (PyObjCClass_Check(self)) {
451		objc_superSetReceiver(super, PyObjCClass_GetClass(self));
452	} else {
453		objc_superSetReceiver(super, PyObjCObject_GetObject(self));
454	}
455	objc_superSetClass(super, object_getClass(objc_superGetReceiver(super)));
456
457	PyObjC_DURING
458		retval = (IMP)objc_msgSendSuper(&super,
459			PyObjCSelector_GetSelector(method),
460			selector);
461	PyObjC_HANDLER
462		PyObjCErr_FromObjC(localException);
463		retval = NULL;
464	PyObjC_ENDHANDLER
465
466	if (retval == NULL) {
467		if (PyErr_Occurred()) {
468			return NULL;
469		}
470		Py_INCREF(Py_None);
471		return Py_None;
472	}
473
474	if (PyObjCClass_Check(self)) {
475		attr = PyObjCClass_FindSelector(self, selector, YES);
476	} else {
477		attr = PyObjCObject_FindSelector(self, selector);
478	}
479	if (attr == NULL) {
480		return NULL;
481	}
482
483	/* FIXME:  We should be able to deal with Python methods as well */
484
485	if (!PyObjCNativeSelector_Check(attr)) {
486		PyErr_Format(PyExc_TypeError, "Cannot locate Python representation of %s",
487				sel_getName(selector));
488		return NULL;
489	}
490
491
492	/* FIXME: there should be a function for retrieving the call function */
493	if (((PyObjCNativeSelector*)attr)->sel_call_func == NULL) {
494		((PyObjCNativeSelector*)attr)->sel_call_func = PyObjC_FindCallFunc(((PyObjCNativeSelector*)attr)->sel_class, ((PyObjCNativeSelector*)attr)->sel_selector);
495		if (((PyObjCNativeSelector*)attr)->sel_call_func == NULL) {
496			return NULL;
497		}
498	}
499
500
501	res = PyObjCIMP_New(
502			retval,
503			selector,
504			((PyObjCNativeSelector*)attr)->sel_call_func,
505			PyObjCSelector_GetMetadata(attr),
506			PyObjCSelector_GetFlags(attr)
507		);
508	Py_DECREF(attr);
509	return res;
510}
511
512int PyObjCIMP_SetUpMethodWrappers(void)
513{
514	int r;
515
516	r = PyObjC_RegisterMethodMapping(
517			nil, 
518			@selector(instanceMethodForSelector:),
519			call_instanceMethodForSelector_,
520			PyObjCUnsupportedMethod_IMP);
521	if (r == -1) return -1;
522
523	r = PyObjC_RegisterMethodMapping(
524			nil, 
525			@selector(methodForSelector:),
526			call_methodForSelector_,
527			PyObjCUnsupportedMethod_IMP);
528	if (r == -1) return -1;
529
530	return 0;
531}
532