1/*
2 * Implementation of support type for formal protocols.
3 *
4 * See the module DOCSTR for more information.
5 *
6 * XXX:
7 * - deal with optional methods (new in ObjC 2.0)
8 * - creating new protocols isn't actually supported in ObjC 2.0
9 */
10#include "pyobjc.h"
11
12PyDoc_STRVAR(proto_cls_doc,
13"objc.formal_protocol(name, supers, selector_list)\n"
14"\n"
15"This class is used to proxy Objective-C formal protocols, and can also be \n"
16"used to define new formal protocols.\n"
17"");
18
19typedef struct {
20	PyObject_HEAD
21
22	Protocol* objc;
23} PyObjCFormalProtocol;
24
25
26static void
27proto_dealloc(PyObject* object)
28{
29	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
30	PyObjC_UnregisterPythonProxy(self->objc, object);
31	Py_TYPE(object)->tp_free(object);
32}
33
34
35static PyObject*
36proto_repr(PyObject* object)
37{
38	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
39	const char* name;
40
41	name = protocol_getName(self->objc);
42	if (name == NULL) {
43		name = "<nil>";
44	}
45
46	return PyText_FromFormat("<%s %s at %p>", Py_TYPE(self)->tp_name, name, (void*)self);
47}
48
49static PyObject*
50proto_get__class__(PyObject* object __attribute__((__unused__)), void* closure __attribute__((__unused__)))
51{
52	return PyObjCClass_New([Protocol class]);
53}
54
55static PyObject*
56proto_get__name__(PyObject* object, void* closure __attribute__((__unused__)))
57{
58	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
59	const char* name = protocol_getName(self->objc);
60
61	if (name == NULL) {
62		Py_INCREF(Py_None);
63		return Py_None;
64	}
65
66	return PyText_FromString(name);
67}
68
69
70static PyObject*
71proto_new(PyTypeObject* type __attribute__((__unused__)),
72	PyObject* args, PyObject* kwds)
73{
74static	char*	keywords[] = { "name", "supers", "selectors", NULL };
75	char* name;
76	PyObject* supers;
77	PyObject* selectors;
78	Py_ssize_t i, len;
79
80	PyObjCFormalProtocol* result = NULL;
81	Protocol* theProtocol;
82
83	if (!PyArg_ParseTupleAndKeywords(args, kwds, "sOO:formal_protocol",
84			keywords, &name, &supers, &selectors)) {
85		return NULL;
86	}
87
88	if (supers != Py_None) {
89		supers = PySequence_Fast(supers, "supers need to be a sequence of objc.formal_protocols");
90		if (supers == NULL) return NULL;
91		len = PySequence_Fast_GET_SIZE(supers);
92		for (i = 0; i < len; i++) {
93			PyObject* v = PySequence_Fast_GET_ITEM(supers, i);
94			if (!PyObjCFormalProtocol_Check(v)) {
95				PyErr_SetString(PyExc_TypeError, "supers need to be a sequence of objc.formal_protocols");
96				Py_DECREF(supers);
97				return NULL;
98			}
99		}
100
101	} else {
102		Py_INCREF(supers);
103	}
104
105	selectors = PySequence_Fast(selectors, "selectors need to be a sequence of selectors");
106	if (selectors == NULL) {
107		Py_DECREF(supers);
108		return NULL;
109	}
110
111	len = PySequence_Fast_GET_SIZE(selectors);
112	for (i = 0; i < len; i++) {
113		PyObject* sel = PySequence_Fast_GET_ITEM(selectors, i);
114		if (!PyObjCSelector_Check(sel)) {
115			PyErr_SetString(PyExc_TypeError, "Selectors is not a list of selectors");
116			Py_DECREF(supers);
117			return NULL;
118		}
119	}
120
121
122	if (objc_allocateProtocol == NULL) {
123		/* Protocol creation API is new in OSX 10.7, can will be weak-linked when
124		 * building on OSX 10.7 with a 10.6 deployment target.
125		 */
126		Py_DECREF(selectors);
127		PyErr_SetString(PyObjCExc_Error, "Cannot create formal protocols on this platform");
128		return NULL;
129	}
130
131	theProtocol = objc_allocateProtocol(name);
132	if (theProtocol == NULL) {
133		PyErr_NoMemory();
134		goto error;
135	}
136
137	if (supers != Py_None) {
138		len = PySequence_Fast_GET_SIZE(supers);
139		for (i = 0; i < len; i++) {
140			PyObject* v = PySequence_Fast_GET_ITEM(supers, i);
141			protocol_addProtocol(theProtocol, PyObjCFormalProtocol_GetProtocol(v));
142		}
143	}
144
145
146	len = PySequence_Fast_GET_SIZE(selectors);
147	for (i = 0; i < len; i++) {
148		PyObject* sel = PySequence_Fast_GET_ITEM(selectors, i);
149		SEL theSel = PyObjCSelector_GetSelector(sel);
150		const char* theSignature = PyObjCSelector_Signature(sel);
151		if (theSignature == NULL) {
152			goto error;
153		}
154		protocol_addMethodDescription(
155			theProtocol,
156			theSel,
157			theSignature,
158			(BOOL)PyObjCSelector_Required(sel),
159			PyObjCSelector_IsClassMethod(sel)?NO:YES);
160	}
161
162	objc_registerProtocol(theProtocol);
163
164	result = (PyObjCFormalProtocol*)PyObject_New(
165			PyObjCFormalProtocol, &PyObjCFormalProtocol_Type);
166	if (result == NULL) goto error;
167
168	Py_DECREF(selectors);
169	Py_DECREF(supers);
170
171	result->objc = theProtocol;
172	PyObjC_RegisterPythonProxy(result->objc, (PyObject*)result);
173	return (PyObject*)result;
174
175error:
176	Py_DECREF(selectors);
177	Py_DECREF(supers);
178
179	return NULL;
180}
181
182static PyObject*
183proto_name(PyObject* object)
184{
185	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
186	const char* name = protocol_getName(self->objc);
187
188	if (name == NULL) {
189		Py_INCREF(Py_None);
190		return Py_None;
191	}
192
193	return PyText_FromString(name);
194}
195
196static PyObject*
197proto_conformsTo_(PyObject* object, PyObject* args)
198{
199	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
200	PyObject* protocol;
201	Protocol* objc_protocol;
202
203	if (!PyArg_ParseTuple(args, "O", &protocol)) {
204		return NULL;
205	}
206
207	if (!PyObjCFormalProtocol_Check(protocol)) {
208		PyErr_SetString(PyExc_TypeError, "Expecting a formal protocol");
209		return NULL;
210	}
211	objc_protocol = PyObjCFormalProtocol_GetProtocol(protocol);
212
213	if (protocol_conformsToProtocol(self->objc, objc_protocol)) {
214		return PyBool_FromLong(1);
215	} else {
216		return PyBool_FromLong(0);
217	}
218}
219
220static int
221append_method_list(PyObject* lst, Protocol* protocol, BOOL isRequired, BOOL isInstance)
222{
223	struct objc_method_description * methods;
224	unsigned int method_count, i;
225
226
227	methods = protocol_copyMethodDescriptionList(protocol, isRequired, isInstance, &method_count);
228	if (!methods) {
229		return 0;
230	}
231
232	for (i = 0; i < method_count; i++) {
233		char buf[512];
234		PyObjCRT_SimplifySignature(methods[i].types, buf, sizeof(buf));
235		PyObject* item = Py_BuildValue(
236#if PY_MAJOR_VERSION == 2
237			"{sssssO}",
238#else
239			"{sysysO}",
240#endif
241			"selector", sel_getName(methods[i].name),
242			"typestr",  buf,
243			"required", isRequired?Py_True:Py_False);
244		if (item == NULL) {
245			free(methods);
246			return -1;
247		}
248		if (PyList_Append(lst, item) < 0) {
249			Py_DECREF(item);
250			free(methods);
251			return -1;
252		}
253		Py_DECREF(item);
254	}
255
256	free(methods);
257	return 0;
258}
259
260
261static PyObject*
262instanceMethods(PyObject* object)
263{
264	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
265	int r;
266
267	PyObject* result = PyList_New(0);
268	if (result == NULL) {
269		return NULL;
270	}
271
272	r = append_method_list(result, self->objc, YES, YES);
273	if (r == -1) {
274		Py_DECREF(result);
275		return NULL;
276	}
277
278	r = append_method_list(result, self->objc, NO, YES);
279	if (r == -1) {
280		Py_DECREF(result);
281		return NULL;
282	}
283
284	return result;
285
286}
287
288static PyObject*
289classMethods(PyObject* object)
290{
291	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
292	int r;
293
294	PyObject* result = PyList_New(0);
295	if (result == NULL) {
296		return NULL;
297	}
298
299	r = append_method_list(result, self->objc, YES, NO);
300	if (r == -1) {
301		Py_DECREF(result);
302		return NULL;
303	}
304
305	r = append_method_list(result, self->objc, NO, NO);
306	if (r == -1) {
307		Py_DECREF(result);
308		return NULL;
309	}
310
311	return result;
312
313}
314
315static PyObject*
316descriptionForInstanceMethod_(PyObject* object, PyObject* sel)
317{
318	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
319	SEL aSelector = NULL;
320	struct objc_method_description descr;
321
322	if (PyObjCSelector_Check(sel)) {
323		aSelector = PyObjCSelector_GetSelector(sel);
324#if PY_MAJOR_VERSION == 2
325	} else if (PyUnicode_Check(sel)) {
326		PyObject* bytes = PyUnicode_AsEncodedString(sel, NULL, NULL);
327		if (bytes == NULL) {
328			return NULL;
329		}
330		char* s = PyBytes_AsString(bytes);
331		if (s == NULL || *s == '\0') {
332			PyErr_SetString(PyExc_ValueError,
333					"empty selector name");
334			return NULL;
335		}
336
337		aSelector = sel_getUid(s);
338		Py_DECREF(bytes);
339
340#endif
341	} else if (PyBytes_Check(sel)) {
342		char* s = PyBytes_AsString(sel);
343		if (*s == '\0') {
344			PyErr_SetString(PyExc_ValueError,
345					"empty selector name");
346			return NULL;
347		}
348
349		aSelector = sel_getUid(s);
350	} else {
351		PyErr_Format(PyExc_TypeError, "expecting a SEL, got instance of '%s'",
352				Py_TYPE(sel)->tp_name);
353		return NULL;
354	}
355
356	descr = protocol_getMethodDescription(self->objc, aSelector, YES, YES);
357	if (descr.name == NULL) {
358		descr = protocol_getMethodDescription(self->objc, aSelector, NO, YES);
359	}
360
361	if (descr.name == NULL) {
362		Py_INCREF(Py_None);
363		return Py_None;
364
365	} else {
366		char buf[512];
367		PyObjCRT_SimplifySignature(descr.types, buf, sizeof(buf));
368		return Py_BuildValue(
369#if PY_MAJOR_VERSION == 2
370			"(ss)",
371#else
372			"(yy)",
373#endif
374			sel_getName(descr.name),
375			buf);
376	}
377}
378
379static PyObject*
380descriptionForClassMethod_(PyObject* object, PyObject* sel)
381{
382	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
383	SEL aSelector = NULL;
384	struct objc_method_description descr;
385
386	if (PyObjCSelector_Check(sel)) {
387		aSelector = PyObjCSelector_GetSelector(sel);
388#if PY_MAJOR_VERSION == 2
389	} else if (PyUnicode_Check(sel)) {
390		PyObject* bytes = PyUnicode_AsEncodedString(sel, NULL, NULL);
391		if (bytes == NULL) {
392			return NULL;
393		}
394		char* s = PyBytes_AsString(bytes);
395		if (s == NULL || *s == '\0') {
396			PyErr_SetString(PyExc_ValueError,
397					"empty selector name");
398			return NULL;
399		}
400
401		aSelector = sel_getUid(s);
402		Py_DECREF(bytes);
403
404#endif
405	} else if (PyBytes_Check(sel)) {
406		char* s = PyBytes_AsString(sel);
407		if (*s == '\0') {
408			PyErr_SetString(PyExc_ValueError,
409					"empty selector name");
410			return NULL;
411		}
412
413		aSelector = sel_getUid(s);
414	} else {
415		PyErr_Format(PyExc_TypeError, "expecting a SEL, got instance of '%s'",
416				Py_TYPE(sel)->tp_name);
417		return NULL;
418	}
419
420	descr = protocol_getMethodDescription(self->objc, aSelector, YES, NO);
421	if (descr.name == NULL) {
422		descr = protocol_getMethodDescription(self->objc, aSelector, NO, NO);
423	}
424	if (descr.name == NULL) {
425		Py_INCREF(Py_None);
426		return Py_None;
427	} else {
428		char buf[256];
429		PyObjCRT_SimplifySignature(descr.types, buf, sizeof(buf));
430		return Py_BuildValue(
431#if PY_MAJOR_VERSION == 2
432			"(ss)",
433#else
434			"(yy)",
435#endif
436			sel_getName(descr.name),
437			buf);
438	}
439}
440
441static PyMethodDef proto_methods[] = {
442	{
443		"name",
444		(PyCFunction)proto_name,
445		METH_NOARGS,
446		"Return the  protocol name",
447	},
448	{
449		"conformsTo_",
450		(PyCFunction)proto_conformsTo_,
451		METH_VARARGS,
452		"Does this protocol conform to another protocol"
453	},
454	{
455		"descriptionForInstanceMethod_",
456		(PyCFunction)descriptionForInstanceMethod_,
457		METH_O,
458		"Description for an instance method in the protocol"
459	},
460	{
461		"descriptionForClassMethod_",
462		(PyCFunction)descriptionForClassMethod_,
463		METH_O,
464		"Description for a class method in the protocol"
465	},
466	{
467		"instanceMethods",
468		(PyCFunction)instanceMethods,
469		METH_NOARGS,
470		"List of instance methods in this protocol"
471	},
472	{
473		"classMethods",
474		(PyCFunction)classMethods,
475		METH_NOARGS,
476		"List of class methods in this protocol"
477	},
478	{ 0, 0, 0, 0 }
479};
480
481static PyGetSetDef proto_getset[] = {
482	{
483		"__class__",
484		(getter)proto_get__class__,
485		NULL,
486		NULL,
487		0
488	},
489	{
490		"__name__",
491		(getter)proto_get__name__,
492		NULL,
493		NULL,
494		0
495	},
496	{ NULL, NULL, NULL, NULL, 0 }
497};
498
499PyTypeObject PyObjCFormalProtocol_Type = {
500	PyVarObject_HEAD_INIT(&PyType_Type, 0)
501	"objc.formal_protocol",			/* tp_name */
502	sizeof(PyObjCFormalProtocol),		/* tp_basicsize */
503	0,					/* tp_itemsize */
504	/* methods */
505	proto_dealloc,	 			/* tp_dealloc */
506	0,					/* tp_print */
507	0,					/* tp_getattr */
508	0,					/* tp_setattr */
509	0,					/* tp_compare */
510	proto_repr,				/* tp_repr */
511	0,					/* tp_as_number */
512	0,					/* tp_as_sequence */
513	0,		       			/* tp_as_mapping */
514	0,					/* tp_hash */
515	0,					/* tp_call */
516	0,					/* tp_str */
517	PyObject_GenericGetAttr,		/* tp_getattro */
518	0,					/* tp_setattro */
519	0,					/* tp_as_buffer */
520	Py_TPFLAGS_DEFAULT,			/* tp_flags */
521 	proto_cls_doc,				/* tp_doc */
522 	0,					/* tp_traverse */
523 	0,					/* tp_clear */
524	0,					/* tp_richcompare */
525	0,					/* tp_weaklistoffset */
526	0,					/* tp_iter */
527	0,					/* tp_iternext */
528	proto_methods,				/* tp_methods */
529	0,					/* tp_members */
530	proto_getset,				/* tp_getset */
531	0,					/* tp_base */
532	0,					/* tp_dict */
533	0,					/* tp_descr_get */
534	0,					/* tp_descr_set */
535	0,					/* tp_dictoffset */
536	0,					/* tp_init */
537	0,					/* tp_alloc */
538	proto_new,				/* tp_new */
539	0,		        		/* tp_free */
540	0,					/* tp_is_gc */
541        0,                                      /* tp_bases */
542        0,                                      /* tp_mro */
543        0,                                      /* tp_cache */
544        0,                                      /* tp_subclasses */
545        0,                                      /* tp_weaklist */
546        0                                       /* tp_del */
547#if PY_VERSION_HEX >= 0x02060000
548	, 0                                     /* tp_version_tag */
549#endif
550
551};
552
553
554/*
555 * Find information about a selector in the protocol.
556 *
557 * Return NULL if no information can be found, but does not set an
558 * exception.
559 */
560const char*
561PyObjCFormalProtocol_FindSelectorSignature(PyObject* object, SEL selector, int isClassMethod)
562{
563	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
564	struct objc_method_description descr;
565
566	descr = protocol_getMethodDescription(self->objc, selector, YES, !isClassMethod);
567	if (descr.name != NULL) {
568		return descr.types;
569	}
570	descr = protocol_getMethodDescription(self->objc, selector, NO, !isClassMethod);
571	if (descr.name != NULL) {
572		return descr.types;
573	}
574	return NULL;
575}
576
577static int
578do_verify(
579	const char* protocol_name,
580	struct objc_method_description* descr,
581	BOOL is_class,
582	BOOL is_required,
583	char* name,
584	PyObject* super_class,
585	PyObject* clsdict, PyObject* metadict)
586{
587	PyObject* meth;
588
589	if (is_class) {
590		meth = findSelInDict(metadict, descr->name);
591	} else {
592		meth = findSelInDict(clsdict, descr->name);
593	}
594	if (meth == NULL || !PyObjCSelector_Check(meth)) {
595
596		meth = PyObjCClass_FindSelector(super_class, descr->name, is_class);
597		if (meth == NULL || !PyObjCSelector_Check(meth)) {
598			if (is_required) {
599				PyErr_Format(PyExc_TypeError,
600					"class %s does not full implement protocol "
601					"%s: no implementation for %s",
602					name,
603					protocol_name,
604					sel_getName(descr->name));
605				return 0;
606			} else {
607				/* Method is not required, ignore */
608				return 1;
609			}
610		}
611	}
612
613	if (is_class) {
614		if (!PyObjCSelector_IsClassMethod(meth)) {
615			PyErr_Format(PyExc_TypeError,
616				"class %s does not correctly implement "
617				"protocol %s: method %s is not a "
618				"class method",
619				name,
620				protocol_name,
621				sel_getName(descr->name)
622			);
623			return 0;
624		}
625	} else {
626		if (PyObjCSelector_IsClassMethod(meth)) {
627			PyErr_Format(PyExc_TypeError,
628				"class %s does not correctly implement "
629				"protocol %s: method %s is not an "
630				"instance method",
631				name,
632				protocol_name,
633				sel_getName(descr->name)
634			);
635			return 0;
636		}
637	}
638
639	if (signaturesEqual(descr->types,
640				PyObjCSelector_Signature(meth))) {
641		return 1;
642	}
643
644	PyErr_Format(PyExc_TypeError,
645		"class %s does not correctly implement "
646		"protocol %s: the signature for method %s "
647		"is %s instead of %s",
648		name,
649		protocol_name,
650		sel_getName(descr->name),
651		PyObjCSelector_Signature(meth),
652		descr->types);
653	return 0;
654}
655
656static int
657do_check(
658    const char* protocol_name,
659    Protocol* protocol,
660    char* name,
661    PyObject* super_class,
662    PyObject* clsdict,
663    PyObject* metadict)
664{
665	int r;
666	unsigned idx;
667
668	unsigned parentCount;
669	Protocol** parents = protocol_copyProtocolList(protocol, &parentCount);
670	if (parents) {
671		for (idx = 0; idx < parentCount; idx++) {
672			r = do_check(protocol_name, parents[idx], name, super_class, clsdict, metadict);
673			if (r == 0) {
674				free(parents);
675				return r;
676			}
677		}
678		free(parents);
679	}
680
681	unsigned int methCount;
682	struct objc_method_description* methinfo;
683
684	methCount = 0;
685	methinfo = protocol_copyMethodDescriptionList(protocol, YES, YES, &methCount);
686	if (methinfo) {
687		for (idx = 0; idx < methCount; idx++) {
688			if (!do_verify(protocol_name, methinfo + idx, NO, YES, name, super_class, clsdict, metadict)) {
689				free(methinfo);
690				return 0;
691			}
692		}
693		free(methinfo);
694	}
695
696	methinfo = protocol_copyMethodDescriptionList(protocol, NO, YES, &methCount);
697	if (methinfo) {
698		for (idx = 0; idx < methCount; idx++) {
699			if (!do_verify(protocol_name, methinfo + idx, NO, NO, name, super_class, clsdict, metadict)) {
700				free(methinfo);
701				return 0;
702			}
703		}
704		free(methinfo);
705	}
706
707	methinfo = protocol_copyMethodDescriptionList(protocol, YES, NO, &methCount);
708	if (methinfo) {
709		for (idx = 0; idx < methCount; idx++) {
710			if (!do_verify(protocol_name, methinfo + idx, YES, YES, name, super_class, clsdict, metadict)) {
711				free(methinfo);
712				return 0;
713			}
714		}
715		free(methinfo);
716	}
717
718	methinfo = protocol_copyMethodDescriptionList(protocol, NO, NO, &methCount);
719	if (methinfo) {
720		for (idx = 0; idx < methCount; idx++) {
721			if (!do_verify(protocol_name, methinfo + idx, YES, NO, name, super_class, clsdict, metadict)) {
722				free(methinfo);
723				return 0;
724			}
725		}
726		free(methinfo);
727	}
728
729	return 1;
730}
731
732int
733PyObjCFormalProtocol_CheckClass(
734	PyObject* obj, char* name, PyObject* super_class, PyObject* clsdict, PyObject* metadict)
735{
736	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)obj;
737
738	if (!PyObjCFormalProtocol_Check(obj)) {
739		PyErr_Format(PyExc_TypeError,
740			"First argument is not an 'objc.formal_protocol' but "
741			"'%s'", Py_TYPE(obj)->tp_name);
742		return 0;
743	}
744	if (!PyObjCClass_Check(super_class)) {
745		PyErr_Format(PyExc_TypeError,
746			"Third argument is not an 'objc.objc_class' but "
747			"'%s'", Py_TYPE(super_class)->tp_name);
748		return 0;
749	}
750	if (!PyDict_Check(clsdict)) {
751		PyErr_Format(PyExc_TypeError,
752			"Fourth argument is not a 'dict' but '%s'",
753			Py_TYPE(clsdict)->tp_name);
754		return 0;
755	}
756
757	return do_check(protocol_getName(self->objc), self->objc, name, super_class, clsdict, metadict);
758}
759
760PyObject* PyObjCFormalProtocol_ForProtocol(Protocol* protocol)
761{
762	PyObjCFormalProtocol* result;
763
764	result = (PyObjCFormalProtocol*)PyObject_New(
765			PyObjCFormalProtocol, &PyObjCFormalProtocol_Type);
766	if (result == NULL) {
767		return NULL;
768	}
769
770	result->objc = protocol;
771	PyObjC_RegisterPythonProxy(result->objc, (PyObject*)result);
772	return (PyObject*)result;
773}
774
775Protocol* PyObjCFormalProtocol_GetProtocol(PyObject* object)
776{
777	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;
778
779	if (!PyObjCFormalProtocol_Check(self)) {
780		PyErr_Format(PyExc_TypeError,
781			"Expecting objc.formal_protocol, got instance of '%s'",
782			Py_TYPE(self)->tp_name);
783		return NULL;
784	}
785
786	return self->objc;
787}
788