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 PyString_FromFormat( 24 "<%s CoreFoundation magic instance %p>", 25 self->ob_type->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 self->ob_type->tp_name, 39 PyObjCObject_GetObject(self)); 40 41 return PyString_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 is a 112 * a plain CFTypeRef 113 */ 114 return NULL; 115 } 116 117 PyObject* cf = PyLong_FromUnsignedLongLong(typeID); 118 if (cf == NULL) { 119 return NULL; 120 } 121 122 123 result = PyDict_GetItem(gTypeid2class, cf); 124 if (result != NULL) { 125 /* This type is the same as an already registered type, 126 * return that type 127 */ 128 Py_DECREF(cf); 129 Py_INCREF(result); 130 return result; 131 } 132 133 /* 134 * If that doesn't exist create a new one. 135 */ 136 137 protectedMethods = PyDict_New(); 138 if (protectedMethods == NULL) { 139 Py_DECREF(cf); 140 return NULL; 141 } 142 143 dict = PyDict_New(); 144 if (dict == NULL) { 145 Py_DECREF(cf); 146 return NULL; 147 } 148 149 PyDict_SetItemString(dict, "__slots__", PyTuple_New(0)); 150 151 bases = PyTuple_New(1); 152 153 PyTuple_SET_ITEM(bases, 0, PyObjC_NSCFTypeClass); 154 Py_INCREF(PyObjC_NSCFTypeClass); 155 156 args = PyTuple_New(3); 157 PyTuple_SetItem(args, 0, PyString_FromString(name)); 158 PyTuple_SetItem(args, 1, bases); 159 PyTuple_SetItem(args, 2, dict); 160 161 result = PyType_Type.tp_new(&PyObjCClass_Type, args, NULL); 162 Py_DECREF(args); 163 if (result == NULL) { 164 Py_DECREF(cf); 165 return NULL; 166 } 167 168 ((PyTypeObject*)result)->tp_repr = cf_repr; 169 ((PyTypeObject*)result)->tp_str = cf_repr; 170 171 info = (PyObjCClassObject*)result; 172 info->class = PyObjCClass_GetClass(PyObjC_NSCFTypeClass); 173 info->sel_to_py = NULL; 174 info->method_magic = 0; 175 info->dictoffset = 0; 176 info->useKVO = 0; 177 info->delmethod = NULL; 178 info->hasPythonImpl = 0; 179 info->isCFWrapper = 1; 180 info->protectedMethods = protectedMethods; 181 182 if (PyObject_SetAttrString(result, 183 "__module__", PyObjCClass_DefaultModule) < 0) { 184 PyErr_Clear(); 185 } 186 187 188 if (PyDict_SetItem(gTypeid2class, cf, result) == -1) { 189 Py_DECREF(result); 190 Py_DECREF(cf); 191 return NULL; 192 } 193 194 Py_DECREF(cf); cf = NULL; 195 196 /* Force an artificially high refcount to avoid deallocation of the 197 * class. 198 * XXX: This code is wrong, it hides the real problem, but I (ronald) 199 * have no idea where that problem hides itself. 200 */ 201 Py_INCREF(result->ob_type); 202 203 return result; 204} 205 206 207int 208PyObjCCFType_Setup(void) 209{ 210 static char encodingBuf[128]; 211 Class cls; 212 213 gTypeid2class = PyDict_New(); 214 if (gTypeid2class == NULL) { 215 return -1; 216 } 217 218 cls = objc_lookUpClass("NSCFType"); 219 if (cls == nil) { 220 PyErr_SetString(PyExc_RuntimeError, 221 "Cannot locate NSCFType"); 222 return -1; 223 } 224 PyObjC_NSCFTypeClass = PyObjCClass_New(cls); 225 if (PyObjC_NSCFTypeClass == NULL) { 226 return -1; 227 } 228 229 /* Add a __pyobjc_PythonObject__ method to NSCFType. Can't use a 230 * category because the type isn't public. 231 */ 232 snprintf(encodingBuf, sizeof(encodingBuf), "%s%c%c", @encode(PyObject*), _C_ID, _C_SEL); 233 if (!class_addMethod(cls, @selector(__pyobjc_PythonObject__), 234 (IMP)pyobjc_PythonObject, encodingBuf)) { 235 236 return -1; 237 } 238 239 return 0; 240} 241 242/* 243 * Create proxy object for a special value of a CFType, that 244 * is a value that just a magic cookie and not a valid 245 * object. Such objects are sometimes used in CoreFoundation 246 * (sadly enough). 247 */ 248PyObject* PyObjCCF_NewSpecial(char* typestr, void* datum) 249{ 250 PyObject* rval = NULL; 251 PyObject* v = PyDict_GetItemString(PyObjC_TypeStr2CFTypeID, typestr); 252 if (v == NULL) { 253 PyErr_Format(PyExc_ValueError, "Unknown typestr: %s", typestr); 254 return NULL; 255 } 256 CFTypeID typeid; 257 258 if (depythonify_c_value(@encode(CFTypeID), v, &typeid) < 0) { 259 Py_DECREF(v); 260 return NULL; 261 } 262 Py_DECREF(v); 263 264 if (gTypeid2class != NULL) { 265 PyObject* cfid; 266 PyTypeObject* tp; 267 268 cfid = PyInt_FromLong(typeid); 269 tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid); 270 Py_DECREF(cfid); 271 272 if (tp != NULL) { 273 rval = tp->tp_alloc(tp, 0); 274 if (rval == NULL) { 275 return NULL; 276 } 277 278 ((PyObjCObject*)rval)->objc_object = (id)datum; 279 ((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT|PyObjCObject_kSHOULD_NOT_RELEASE|PyObjCObject_kMAGIC_COOKIE; 280 } 281 } else { 282 rval = NULL; 283 PyErr_Format(PyExc_ValueError, 284 "Sorry, cannot wrap special value of typeid %d\n", 285 (int)typeid); 286 } 287 288 return rval; 289} 290 291/* 292 * Create proxy object for a special value of a CFType, that 293 * is a value that just a magic cookie and not a valid 294 * object. Such objects are sometimes used in CoreFoundation 295 * (sadly enough). 296 */ 297PyObject* PyObjCCF_NewSpecial2(CFTypeID typeid, void* datum) 298{ 299 PyObject *rval = NULL; 300 301 if (gTypeid2class != NULL) { 302 PyObject* cfid; 303 PyTypeObject* tp; 304 305 cfid = PyInt_FromLong(typeid); 306 tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid); 307 Py_DECREF(cfid); 308 309 if (tp != NULL) { 310 rval = tp->tp_alloc(tp, 0); 311 if (rval == NULL) { 312 return NULL; 313 } 314 315 ((PyObjCObject*)rval)->objc_object = (id)datum; 316 ((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT|PyObjCObject_kSHOULD_NOT_RELEASE|PyObjCObject_kMAGIC_COOKIE; 317 } 318 } else { 319 rval = NULL; 320 PyErr_Format(PyExc_ValueError, 321 "Sorry, cannot wrap special value of typeid %d\n", 322 (int)typeid); 323 } 324 325 return rval; 326} 327