1/* 2 * Implementation of support type for informal protocols. 3 * 4 * See the module DOCSTR for more information. 5 */ 6#include "pyobjc.h" 7 8PyDoc_STRVAR(proto_cls_doc, 9"objc.informal_protocol(name, selector_list)\n" 10"\n" 11"This class can be used to specify which methods are supported by an informal\n" 12"protocol. Instances of this type can by used while creating subclasses of \n" 13"objective-C classes to automaticly specify method signatures et.al." 14""); 15 16typedef struct { 17 PyObject_HEAD 18 19 PyObject* name; 20 PyObject* selectors; 21} PyObjCInformalProtocol; 22 23 24static PyObject* selToProtocolMapping = NULL; 25 26 27static void 28proto_dealloc(PyObject* object) 29{ 30 PyObjCInformalProtocol* self = (PyObjCInformalProtocol*)object; 31#if 0 32 /* 33 * For some reason this code causes a crash, while it should 34 * be the reverse of the code in proto_new. 35 */ 36 Py_ssize_t len = PyTuple_Size(self->selectors); 37 Py_ssize_t i; 38 39 for (i = 0; i < len; i++) { 40 PyObjCSelector* tmp = 41 (PyObjCSelector*)PyTuple_GET_ITEM( 42 self->selectors, i); 43 44 PyDict_DelItemString(selToProtocolMapping, 45 sel_getName(tmp->sel_selector)); 46 } 47#endif 48 49 Py_XDECREF(self->name); 50 Py_XDECREF(self->selectors); 51 Py_TYPE(object)->tp_free(object); 52} 53 54static PyObject* 55proto_repr(PyObject* object) 56{ 57 PyObjCInformalProtocol* self = (PyObjCInformalProtocol*)object; 58 PyObject* b = NULL; 59 60 if (PyUnicode_Check(self->name)) { 61 b = PyUnicode_AsEncodedString(self->name, NULL, NULL); 62#if PY_MAJOR_VERSION == 2 63 } else if (PyString_Check(self->name)) { 64 b = self->name; Py_INCREF(b); 65#endif 66 } else { 67 b = PyBytes_FromString("<null>"); 68 } 69 if (b == NULL) { 70 return NULL; 71 } 72 73 PyObject* r = PyText_FromFormat("<%s %s at %p>", Py_TYPE(self)->tp_name, PyBytes_AsString(b), (void*)self); 74 Py_XDECREF(b); 75 return r; 76} 77 78static PyObject* 79proto_new(PyTypeObject* type __attribute__((__unused__)), 80 PyObject* args, PyObject* kwds) 81{ 82static char* keywords[] = { "name", "selectors", NULL }; 83 PyObjCInformalProtocol* result; 84 PyObject* name; 85 PyObject* selectors; 86 Py_ssize_t i, len; 87 88 if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:informal_protocol", 89 keywords, &name, &selectors)) { 90 return NULL; 91 } 92 93 if (PyUnicode_Check(name)) { 94 /* pass */ 95#if PY_MAJOR_VERSION == 2 96 } else if (PyString_Check(name)) { 97 /* pass */ 98#endif 99 } else { 100 PyErr_SetString(PyExc_TypeError, 101 "Name must be a string"); 102 return NULL; 103 } 104 105 selectors = PySequence_Tuple(selectors); 106 if (selectors == NULL) { 107 return NULL; 108 } 109 110 result = (PyObjCInformalProtocol*)PyObject_New( 111 PyObjCInformalProtocol, &PyObjCInformalProtocol_Type); 112 113 result->name = name; 114 Py_XINCREF(name); 115 result->selectors = selectors; 116 117 len = PyTuple_GET_SIZE(selectors); 118 for (i = 0; i < len; i++) { 119 if (!PyObjCSelector_Check( 120 PyTuple_GET_ITEM(selectors, i))) { 121 PyErr_Format(PyExc_TypeError, 122 "Item %"PY_FORMAT_SIZE_T"d is not a selector", i); 123 Py_DECREF(result); 124 return NULL; 125 } 126 } 127 128 if (selToProtocolMapping == NULL) { 129 selToProtocolMapping = PyDict_New(); 130 if (selToProtocolMapping == NULL) { 131 Py_DECREF(result); 132 return NULL; 133 } 134 } 135 136 for (i = 0; i < len; i++) { 137 PyObjCSelector* tmp = 138 (PyObjCSelector*)PyTuple_GET_ITEM(selectors, i); 139 140 PyDict_SetItemString(selToProtocolMapping, 141 (char*)sel_getName(tmp->sel_selector), 142 (PyObject*)result); 143 } 144 145 146 return (PyObject*)result; 147} 148 149static int 150proto_traverse(PyObject* self, visitproc visit, void* handle) 151{ 152 PyObjCInformalProtocol* me = (PyObjCInformalProtocol*)self; 153 int err; 154 155 err = visit(me->name, handle); 156 if (err) return err; 157 err = visit(me->selectors, handle); 158 if (err) return err; 159 return 0; 160} 161 162static PyMemberDef proto_members[] = { 163 { 164 "__name__", 165 T_OBJECT, 166 offsetof(PyObjCInformalProtocol, name), 167 READONLY, 168 NULL 169 }, 170 { 171 "selectors", 172 T_OBJECT, 173 offsetof(PyObjCInformalProtocol, selectors), 174 READONLY, 175 NULL 176 }, 177 178 { 0, 0, 0, 0, 0 } 179}; 180 181PyTypeObject PyObjCInformalProtocol_Type = { 182 PyVarObject_HEAD_INIT(&PyType_Type, 0) 183 "objc.informal_protocol", /* tp_name */ 184 sizeof(PyObjCInformalProtocol), /* tp_basicsize */ 185 0, /* tp_itemsize */ 186 /* methods */ 187 proto_dealloc, /* tp_dealloc */ 188 0, /* tp_print */ 189 0, /* tp_getattr */ 190 0, /* tp_setattr */ 191 0, /* tp_compare */ 192 proto_repr, /* tp_repr */ 193 0, /* tp_as_number */ 194 0, /* tp_as_sequence */ 195 0, /* tp_as_mapping */ 196 0, /* tp_hash */ 197 0, /* tp_call */ 198 0, /* tp_str */ 199 PyObject_GenericGetAttr, /* tp_getattro */ 200 0, /* tp_setattro */ 201 0, /* tp_as_buffer */ 202 Py_TPFLAGS_DEFAULT, /* tp_flags */ 203 proto_cls_doc, /* tp_doc */ 204 proto_traverse, /* tp_traverse */ 205 0, /* tp_clear */ 206 0, /* tp_richcompare */ 207 0, /* tp_weaklistoffset */ 208 0, /* tp_iter */ 209 0, /* tp_iternext */ 210 0, /* tp_methods */ 211 proto_members, /* tp_members */ 212 0, /* proto_getset , */ /* tp_getset */ 213 0, /* tp_base */ 214 0, /* tp_dict */ 215 0, /* tp_descr_get */ 216 0, /* tp_descr_set */ 217 0, /* tp_dictoffset */ 218 0, /* tp_init */ 219 0, /* tp_alloc */ 220 proto_new, /* tp_new */ 221 0, /* tp_free */ 222 0, /* tp_is_gc */ 223 0, /* tp_bases */ 224 0, /* tp_mro */ 225 0, /* tp_cache */ 226 0, /* tp_subclasses */ 227 0, /* tp_weaklist */ 228 0 /* tp_del */ 229#if PY_VERSION_HEX >= 0x02060000 230 , 0 /* tp_version_tag */ 231#endif 232 233}; 234 235 236/* 237 * Find information about a selector in the protocol. 238 * 239 * Return NULL if no information can be found, but does not set an 240 * exception. 241 */ 242PyObject* 243PyObjCInformalProtocol_FindSelector(PyObject* obj, SEL selector, int isClassMethod) 244{ 245 PyObjCInformalProtocol* self = (PyObjCInformalProtocol*)obj; 246 Py_ssize_t i, len; 247 PyObject* cur; 248 PyObject* seq; 249 250 if (!PyObjCInformalProtocol_Check(obj)) { 251 PyErr_Format(PyExc_TypeError, 252 "First argument is not an 'objc.informal_protocol' " 253 "but '%s'", Py_TYPE(obj)->tp_name); 254 return 0; 255 } 256 257 seq = PySequence_Fast(self->selectors,"selector list not a sequence?"); 258 if (seq == NULL) { 259 return 0; 260 } 261 262 len = PySequence_Fast_GET_SIZE(seq); 263 for (i = 0; i < len; i++) { 264 cur = PySequence_Fast_GET_ITEM(self->selectors, i); 265 if (cur == NULL) { 266 continue; 267 } 268 269 if (PyObjCSelector_Check(cur)) { 270 int class_sel = ( 271 PyObjCSelector_GetFlags(cur) 272 & PyObjCSelector_kCLASS_METHOD) != 0; 273 if ((isClassMethod && !class_sel) 274 || (!isClassMethod && class_sel)) { 275 continue; 276 } 277 278 if (sel_isEqual(PyObjCSelector_GetSelector(cur), selector)) { 279 Py_DECREF(seq); 280 return cur; 281 } 282 } 283 } 284 Py_DECREF(seq); 285 return NULL; 286} 287 288PyObject* 289findSelInDict(PyObject* clsdict, SEL selector) 290{ 291 PyObject* values; 292 PyObject* seq; 293 Py_ssize_t i, len; 294 295 values = PyDict_Values(clsdict); 296 if (values == NULL) { 297 return NULL; 298 } 299 300 seq = PySequence_Fast(values, "PyDict_Values result not a sequence"); 301 if (seq == NULL) { 302 return NULL; 303 } 304 305 len = PySequence_Fast_GET_SIZE(seq); 306 for (i = 0; i < len; i++) { 307 PyObject* v = PySequence_Fast_GET_ITEM(seq, i); 308 if (!PyObjCSelector_Check(v)) continue; 309 if (PyObjCSelector_GetSelector(v) == selector) { 310 Py_DECREF(seq); 311 Py_DECREF(values); 312 Py_INCREF(v); 313 return v; 314 } 315 } 316 Py_DECREF(seq); 317 Py_DECREF(values); 318 return NULL; 319} 320 321int 322signaturesEqual(const char* sig1, const char* sig2) 323{ 324 char buf1[1024]; 325 char buf2[1024]; 326 int r; 327 328 /* Return 0 if the two signatures are not equal */ 329 if (strcmp(sig1, sig2) == 0) return 1; 330 331 /* For some reason compiler-generated signatures contain numbers that 332 * are not used by the runtime. These are irrelevant for our comparison 333 */ 334 r = PyObjCRT_SimplifySignature(sig1, buf1, sizeof(buf1)); 335 if (r == -1) { 336 return 0; 337 } 338 339 r = PyObjCRT_SimplifySignature(sig2, buf2, sizeof(buf2)); 340 if (r == -1) { 341 return 0; 342 } 343 return strcmp(buf1, buf2) == 0; 344} 345 346/* 347 * Verify that 'cls' conforms to the informal protocol 348 */ 349int 350PyObjCInformalProtocol_CheckClass( 351 PyObject* obj, char* name, PyObject* super_class, PyObject* clsdict) 352{ 353 PyObjCInformalProtocol* self = (PyObjCInformalProtocol*)obj; 354 Py_ssize_t i, len; 355 PyObject* cur; 356 PyObject* seq; 357 358 if (!PyObjCInformalProtocol_Check(obj)) { 359 PyErr_Format(PyExc_TypeError, 360 "First argument is not an 'objc.informal_protocol' " 361 "but '%s'", Py_TYPE(obj)->tp_name); 362 return 0; 363 } 364 if (!PyObjCClass_Check(super_class)) { 365 PyErr_Format(PyExc_TypeError, 366 "Third argument is not an 'objc.objc_class' but " 367 "'%s'", Py_TYPE(super_class)->tp_name); 368 return 0; 369 } 370 if (!PyDict_Check(clsdict)) { 371 PyErr_Format(PyExc_TypeError, 372 "Fourth argument is not a 'dict' but '%s'", 373 Py_TYPE(clsdict)->tp_name); 374 return 0; 375 } 376 377 seq = PySequence_Fast(self->selectors, "selector list not a sequence"); 378 if (seq == NULL) { 379 return 0; 380 } 381 382 len = PySequence_Fast_GET_SIZE(seq); 383 for (i = 0; i < len; i++) { 384 SEL sel; 385 PyObject* m; 386 387 cur = PySequence_Fast_GET_ITEM(seq, i); 388 if (cur == NULL) { 389 continue; 390 } 391 392 if (!PyObjCSelector_Check(cur)) { 393 continue; 394 } 395 396 sel = PyObjCSelector_GetSelector(cur); 397 398 m = findSelInDict(clsdict, sel); 399 if (m == NULL) { 400 m = PyObjCClass_FindSelector(super_class, sel, PyObjCSelector_IsClassMethod(cur)); 401 } 402 403 if (m == NULL || !PyObjCSelector_Check(m)) { 404 Py_XDECREF(m); 405 if (PyObjCSelector_Required(cur)) { 406 PyErr_Format(PyExc_TypeError, 407 "class %s does not fully implement " 408 "protocol %S: no implementation for %s", 409 name, 410 self->name, 411 sel_getName(sel)); 412 Py_DECREF(seq); 413 return 0; 414 } else { 415 PyErr_Clear(); 416 } 417 } else { 418 if (!signaturesEqual(PyObjCSelector_Signature(m), 419 PyObjCSelector_Signature(cur)) != 0) { 420 421 PyErr_Format(PyExc_TypeError, 422 "class %s does not correctly implement " 423 "protocol %S: " 424 "the signature for method %s is " 425 "%s instead of %s", 426 name, 427 self->name, 428 sel_getName(sel), 429 PyObjCSelector_Signature(m), 430 PyObjCSelector_Signature(cur) 431 ); 432 Py_DECREF(seq); 433 Py_DECREF(m); 434 return 0; 435 } 436 Py_DECREF(m); 437 } 438 } 439 Py_DECREF(seq); 440 return 1; 441} 442 443PyObject* 444PyObjCInformalProtocol_FindProtocol(SEL selector) 445{ 446 PyObject* item; 447 448 if (selToProtocolMapping == NULL) return NULL; 449 450 item = PyDict_GetItemString(selToProtocolMapping, (char*)sel_getName(selector)); 451 if (item != NULL) { 452 return item; 453 } 454 455 PyErr_Clear(); 456 return NULL; 457} 458