1/* 2 * This file implements generic support for CoreFoundation types. 3 * 4 * CF-based proxy types are implemented as subclasses of the proxy for NSCFType, 5 * that way CF-based types fit in nicely with the PyObjC machinery (specifically 6 * subclass tests keep working). 7 * 8 * Major assumption: 9 * - NSCFType is the ObjC type that all non-toll-free bridged types inherit 10 * from, toll-free bridged types are not subclasses of NSCFType. 11 */ 12#include "pyobjc.h" 13 14#include <CoreFoundation/CoreFoundation.h> 15 16static PyObject* gTypeid2class = NULL; 17PyObject* PyObjC_NSCFTypeClass = NULL; 18 19static PyObject* 20cf_repr(PyObject* self) 21{ 22 if (PyObjCObject_GetFlags(self) & PyObjCObject_kMAGIC_COOKIE) { 23 return PyText_FromFormat( 24 "<%s CoreFoundation magic instance %p>", 25 Py_TYPE(self)->tp_name, PyObjCObject_GetObject(self)); 26 } 27 28 29 30 CFStringRef repr = CFCopyDescription(PyObjCObject_GetObject(self)); 31 if (repr) { 32 PyObject* result = pythonify_c_value(@encode(id), &repr); 33 CFRelease(repr); 34 return result; 35 } else { 36 char buf[128]; 37 snprintf(buf, sizeof(buf), "<%s object at %p>", 38 Py_TYPE(self)->tp_name, 39 PyObjCObject_GetObject(self)); 40 41 return PyText_FromString(buf); 42 } 43} 44 45 46/* Implementation for: -(PyObject*)__pyobjc_PythonObject__ on NSCFType. We cannot 47 * define a category on that type because the class definition isn't public. 48 */ 49static PyObject* pyobjc_PythonObject(NSObject* self, SEL _sel __attribute__((__unused__))) 50{ 51 PyObject *rval = NULL; 52 53 rval = PyObjC_FindPythonProxy(self); 54 if (rval == NULL) { 55 if (gTypeid2class != NULL) { 56 PyObject* cfid; 57 PyTypeObject* tp; 58 59 cfid = PyInt_FromLong(CFGetTypeID((CFTypeRef)self)); 60 tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid); 61 Py_DECREF(cfid); 62 63 if (tp != NULL) { 64 rval = tp->tp_alloc(tp, 0); 65 if (rval == NULL) { 66 return NULL; 67 } 68 69 ((PyObjCObject*)rval)->objc_object = self; 70 ((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT | PyObjCObject_kCFOBJECT; 71 CFRetain(self); 72 } 73 } 74 75 if (rval == NULL) { 76 /* There is no wrapper for this type, fall back to 77 * the generic behaviour. 78 */ 79 rval = (PyObject *)PyObjCObject_New(self, 80 PyObjCObject_kDEFAULT, YES); 81 } 82 83 if (rval) { 84 PyObjC_RegisterPythonProxy(self, rval); 85 } 86 } 87 88 return rval; 89} 90 91 92PyObject* 93PyObjCCFType_New(char* name, char* encoding, CFTypeID typeID) 94{ 95 PyObject* args; 96 PyObject* dict; 97 PyObject* result; 98 PyObject* bases; 99 PyObjCClassObject* info; 100 PyObject* protectedMethods; 101 102 /* 103 * First look for an already registerd type 104 */ 105 if (encoding[0] != _C_ID) { 106 if (PyObjCPointerWrapper_RegisterID(encoding) == -1) { 107 return NULL; 108 } 109 } 110 if (typeID == 0) { 111 /* Partially registered type, just wrap as a 112 * a plain CFTypeRef 113 */ 114 Py_INCREF(PyObjC_NSCFTypeClass); 115 return PyObjC_NSCFTypeClass; 116 } 117 118 PyObject* cf = PyLong_FromUnsignedLongLong(typeID); 119 if (cf == NULL) { 120 return NULL; 121 } 122 123 124 result = PyDict_GetItem(gTypeid2class, cf); 125 if (result != NULL) { 126 /* This type is the same as an already registered type, 127 * return that type 128 */ 129 Py_DECREF(cf); 130 Py_INCREF(result); 131 return result; 132 } 133 134 /* 135 * If that doesn't exist create a new one. 136 */ 137 138 protectedMethods = PyDict_New(); 139 if (protectedMethods == NULL) { 140 Py_DECREF(cf); 141 return NULL; 142 } 143 144 dict = PyDict_New(); 145 if (dict == NULL) { 146 Py_DECREF(cf); 147 return NULL; 148 } 149 150 PyDict_SetItemString(dict, "__slots__", PyTuple_New(0)); 151 152 bases = PyTuple_New(1); 153 154 PyTuple_SET_ITEM(bases, 0, PyObjC_NSCFTypeClass); 155 Py_INCREF(PyObjC_NSCFTypeClass); 156 157 args = PyTuple_New(3); 158 PyTuple_SetItem(args, 0, PyText_FromString(name)); 159 PyTuple_SetItem(args, 1, bases); 160 PyTuple_SetItem(args, 2, dict); 161 162 result = PyType_Type.tp_new(&PyObjCClass_Type, args, NULL); 163 Py_DECREF(args); 164 if (result == NULL) { 165 Py_DECREF(cf); 166 return NULL; 167 } 168 169 ((PyTypeObject*)result)->tp_repr = cf_repr; 170 ((PyTypeObject*)result)->tp_str = cf_repr; 171 172 info = (PyObjCClassObject*)result; 173 info->class = PyObjCClass_GetClass(PyObjC_NSCFTypeClass); 174 info->sel_to_py = NULL; 175 info->method_magic = 0; 176 info->dictoffset = 0; 177 info->useKVO = 0; 178 info->delmethod = NULL; 179 info->hasPythonImpl = 0; 180 info->isCFWrapper = 1; 181 info->protectedMethods = protectedMethods; 182 183 if (PyObject_SetAttrString(result, 184 "__module__", PyObjCClass_DefaultModule) < 0) { 185 PyErr_Clear(); 186 } 187 188 189 if (PyDict_SetItem(gTypeid2class, cf, result) == -1) { 190 Py_DECREF(result); 191 Py_DECREF(cf); 192 return NULL; 193 } 194 195 Py_DECREF(cf); cf = NULL; 196 197 /* Force an artificially high refcount to avoid deallocation of the 198 * class. 199 * XXX: This code is wrong, it hides the real problem, but I (ronald) 200 * have no idea where that problem hides itself. 201 */ 202 Py_INCREF(Py_TYPE(result)); 203 204 return result; 205} 206 207 208int 209PyObjCCFType_Setup(void) 210{ 211 static char encodingBuf[128]; 212 Class cls; 213 214 gTypeid2class = PyDict_New(); 215 if (gTypeid2class == NULL) { 216 return -1; 217 } 218 219 cls = objc_lookUpClass("__NSCFType"); 220 if (cls == nil) { 221 cls = objc_lookUpClass("NSCFType"); 222 } 223 if (cls == nil) { 224 PyErr_SetString(PyExc_RuntimeError, 225 "Cannot locate NSCFType"); 226 return -1; 227 } 228 PyObjC_NSCFTypeClass = PyObjCClass_New(cls); 229 if (PyObjC_NSCFTypeClass == NULL) { 230 return -1; 231 } 232 233 /* Add a __pyobjc_PythonObject__ method to NSCFType. Can't use a 234 * category because the type isn't public. 235 */ 236 snprintf(encodingBuf, sizeof(encodingBuf), "%s%c%c", @encode(PyObject*), _C_ID, _C_SEL); 237 if (!class_addMethod(cls, @selector(__pyobjc_PythonObject__), 238 (IMP)pyobjc_PythonObject, encodingBuf)) { 239 240 return -1; 241 } 242 243 return 0; 244} 245 246/* 247 * Create proxy object for a special value of a CFType, that 248 * is a value that just a magic cookie and not a valid 249 * object. Such objects are sometimes used in CoreFoundation 250 * (sadly enough). 251 */ 252PyObject* PyObjCCF_NewSpecial(char* typestr, void* datum) 253{ 254 PyObject* rval = NULL; 255 PyObject* v = PyDict_GetItemString(PyObjC_TypeStr2CFTypeID, typestr); 256 if (v == NULL) { 257 PyErr_Format(PyExc_ValueError, "Don't know CF type for typestr '%s', cannot create special wrapper", typestr); 258 return NULL; 259 } 260 CFTypeID typeid; 261 262 if (depythonify_c_value(@encode(CFTypeID), v, &typeid) < 0) { 263 return NULL; 264 } 265 266 if (gTypeid2class != NULL) { 267 PyObject* cfid; 268 PyTypeObject* tp; 269 270 cfid = PyInt_FromLong(typeid); 271 tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid); 272 Py_DECREF(cfid); 273 274 if (tp != NULL) { 275 rval = tp->tp_alloc(tp, 0); 276 if (rval == NULL) { 277 return NULL; 278 } 279 280 ((PyObjCObject*)rval)->objc_object = (id)datum; 281 ((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT|PyObjCObject_kSHOULD_NOT_RELEASE|PyObjCObject_kMAGIC_COOKIE; 282 } 283 } else { 284 rval = NULL; 285 PyErr_Format(PyExc_ValueError, 286 "Sorry, cannot wrap special value of typeid %d\n", 287 (int)typeid); 288 } 289 290 return rval; 291} 292 293/* 294 * Create proxy object for a special value of a CFType, that 295 * is a value that just a magic cookie and not a valid 296 * object. Such objects are sometimes used in CoreFoundation 297 * (sadly enough). 298 */ 299PyObject* PyObjCCF_NewSpecial2(CFTypeID typeid, void* datum) 300{ 301 PyObject *rval = NULL; 302 303 if (gTypeid2class != NULL) { 304 PyObject* cfid; 305 PyTypeObject* tp; 306 307 cfid = PyInt_FromLong(typeid); 308 tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid); 309 Py_DECREF(cfid); 310 311 if (tp != NULL) { 312 rval = tp->tp_alloc(tp, 0); 313 if (rval == NULL) { 314 return NULL; 315 } 316 317 ((PyObjCObject*)rval)->objc_object = (id)datum; 318 ((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT|PyObjCObject_kSHOULD_NOT_RELEASE|PyObjCObject_kMAGIC_COOKIE; 319 } 320 } else { 321 rval = NULL; 322 PyErr_Format(PyExc_ValueError, 323 "Sorry, cannot wrap special value of typeid %d\n", 324 (int)typeid); 325 } 326 327 return rval; 328} 329