1/* 2 * Special wrappers for pointer values 3 * 4 * Pointer arguments to methods can be split into 3 classes: 5 * - Pass-by-reference arguments (in, out, inout). 6 * This is supported by using the modifiers _C_IN, _C_OUT and _C_INOUT 7 * - Pointers to buffers 8 * This requires special support, you can't detect the size of the buffer 9 * from the signature 10 * - Opaque values 11 * Types like FSRef are for all intents and purposes opaque values, 12 * that happen to be represented using pointers (to structs). The functions 13 * in this file allow extension modules to register functions to convert these 14 * values to and from their Python representation. 15 * 16 * NOTE: 17 * - The pythonify and depythonify functions have the same interface as 18 * the *_New and *_Convert functions in MacPython (pymactoolbox.h), this 19 * makes it easier to interface with that packages. 20 */ 21#include "pyobjc.h" 22 23 24#import <CoreFoundation/CoreFoundation.h> 25 26struct wrapper { 27 const char* signature; 28 int offset; 29 PyObject* (*pythonify)(void*); 30 int (*depythonify)(PyObject*, void*); 31}; 32 33/* Using an array is pretty lame, this needs to be replaced by a more 34 * efficient datastructure. However: As long as their is only a limited 35 * number of custom wrappers this should not be a problem. 36 */ 37static struct wrapper* items = 0; 38static Py_ssize_t item_count = 0; 39 40/* 41 * If signature is a pointer to a structure return the index of the character 42 * just beyond the end of the struct name. This information is needed because 43 * @encode(struct foo*) can return two different strings: 44 * 1) ^{foo} if the compiler has not yet seen a full definition of struct foo 45 * 2) ^{foo=...} if the compiler has not yet seen a full definition of 46 * the struct 47 * We want to treat those two pointer as the same type, therefore we need to 48 * ignore everything beyond the end of the struct name. 49 */ 50static int find_end_of_structname(const char* signature) { 51 if (signature[1] == _C_CONST && signature[2] == _C_STRUCT_B) { 52 char* end1; 53 char* end2; 54 55 end1 = strchr(signature, _C_STRUCT_E); 56 end2 = strchr(signature, '='); 57 58 if (end2 == NULL) { 59 return end1 - signature; 60 } else { 61 return end2 - signature; 62 } 63 64 } else if (signature[1] == _C_STRUCT_B) { 65 char* end1; 66 char* end2; 67 68 end1 = strchr(signature, _C_STRUCT_E); 69 end2 = strchr(signature, '='); 70 71 if (end2 == NULL) { 72 return end1 - signature; 73 } else { 74 return end2 - signature; 75 } 76 } 77 return strlen(signature); 78} 79 80static struct wrapper* 81FindWrapper(const char* signature) 82{ 83 Py_ssize_t i; 84 85 for (i = 0; i < item_count; i++) { 86 if (strncmp(signature, items[i].signature, items[i].offset) == 0) { 87 /* See comment just above find_end_of_structname */ 88 if (signature[1] == _C_CONST && signature[2] == _C_STRUCT_B) { 89 char ch = signature[items[i].offset]; 90 if (ch == '=' || ch == _C_STRUCT_E) { 91 return items + i; 92 } 93 94 } else if (signature[1] == _C_STRUCT_B) { 95 char ch = signature[items[i].offset]; 96 if (ch == '=' || ch == _C_STRUCT_E) { 97 return items + i; 98 } 99 100 } else { 101 if (signature[items[i].offset] == '\0') { 102 return items + i; 103 } 104 } 105 } 106 } 107 return NULL; 108} 109 110static PyObject* 111ID_to_py(void* idValue) 112{ 113 return pythonify_c_value(@encode(id), &idValue); 114} 115 116static int 117py_to_ID(PyObject* obj, void* output) 118{ 119 return depythonify_c_value(@encode(id), obj, output); 120} 121 122int PyObjCPointerWrapper_RegisterID(const char *signature) { 123 return PyObjCPointerWrapper_Register(signature, 124 (PyObjCPointerWrapper_ToPythonFunc)&ID_to_py, 125 (PyObjCPointerWrapper_FromPythonFunc)&py_to_ID); 126} 127 128int 129PyObjCPointerWrapper_Register( 130 const char* signature, 131 PyObjCPointerWrapper_ToPythonFunc pythonify, 132 PyObjCPointerWrapper_FromPythonFunc depythonify 133 134 ) 135{ 136 struct wrapper* value; 137 138 /* 139 * Check if we already have a wrapper, if so replace that. 140 * This makes it possible to replace a default wrapper by something 141 * better. 142 */ 143 if (signature == NULL) { 144 return -1; 145 } 146 value = FindWrapper(signature); 147 if (value != NULL) { 148 value->pythonify = pythonify; 149 value->depythonify = depythonify; 150 return 0; 151 } 152 153 if (items == NULL) { 154 items = PyMem_Malloc(sizeof(struct wrapper)); 155 if (items == NULL) { 156 PyErr_NoMemory(); 157 return -1; 158 } 159 item_count = 1; 160 } else { 161 struct wrapper* tmp; 162 163 tmp = PyMem_Realloc( 164 items, sizeof(struct wrapper) * (item_count+1)); 165 if (tmp == NULL) { 166 PyErr_NoMemory(); 167 return -1; 168 } 169 items = tmp; 170 item_count ++; 171 } 172 173 value = items + (item_count-1); 174 175 value->signature = PyObjCUtil_Strdup(signature); 176 if (value->signature == NULL) { 177 PyErr_NoMemory(); 178 item_count --; 179 return -1; 180 } 181 182 value->offset = find_end_of_structname(value->signature); 183 184 value->pythonify = pythonify; 185 value->depythonify = depythonify; 186 187 return 0; 188} 189 190 191 192PyObject* 193PyObjCPointerWrapper_ToPython(const char* type, void* datum) 194{ 195 struct wrapper* item; 196 PyObject* result; 197 198 item = FindWrapper(type); 199 if (item == NULL) { 200 return NULL; 201 } 202 203 if (item->pythonify == ID_to_py) { 204 result = PyObjC_FindPythonProxy(*(id*)datum); 205 if (result != NULL) { 206 return result; 207 208 } else if (*(void**)datum == kCFAllocatorUseContext) { 209 /* kCFAllocatorUseContext is a bit too magic for its 210 * own good. 211 * 212 * Note that this is a crude hack, but as long as this 213 * is the only such object I don't think its worthwhile 214 * to add generic support for this. 215 */ 216 result = PyObjCCF_NewSpecial2( 217 CFAllocatorGetTypeID(), *(void**)datum); 218 219 PyObjC_RegisterPythonProxy(*(id*)datum, result); 220 return result; 221 } 222 } 223 224 result = item->pythonify(*(void**)datum); 225 return result; 226} 227 228 229int 230PyObjCPointerWrapper_FromPython( 231 const char* type, PyObject* value, void* datum) 232{ 233 struct wrapper* item; 234 int r; 235 236 if (value == PyObjC_NULL) { 237 *(void**)datum = NULL; 238 return 0; 239 } 240 241 item = FindWrapper(type); 242 if (item == NULL) { 243 return -1; 244 } 245 246 r = item->depythonify(value, datum); 247 if (r == 0) { 248 return 0; 249 } else { 250 return -1; 251 } 252} 253 254int PyObjCPointerWrapper_HaveWrapper(const char* type) 255{ 256 return (FindWrapper(type) != NULL); 257} 258 259 260static PyObject* 261PyObjectPtr_New(void *obj) 262{ 263 return (PyObject*)obj; 264} 265 266static int 267PyObjectPtr_Convert(PyObject* obj, void* pObj) 268{ 269 *(void**)pObj = (void *)obj; 270 return 0; 271} 272 273static PyObject* 274class_new(void *obj) 275{ 276 return pythonify_c_value("#", obj); 277} 278 279static int 280class_convert(PyObject* obj, void* pObj) 281{ 282 return depythonify_c_value("#", obj, pObj); 283} 284 285 286#if PY_MAJOR_VERSION == 2 287 288static int dontClose(FILE* fp __attribute__((__unused__))) 289{ 290 return 0; 291} 292static PyObject* 293FILE_New(void *obj) 294{ 295 FILE* fp = (FILE*)obj; 296 char* mode = "r"; 297 298#if defined(__SRW) 299 /* This is a hack, but allows us to pass the right file mode into 300 * Python. 301 */ 302 if (fp->_flags & __SWR) { 303 mode = "w"; 304 } else if (fp->_flags & __SRW) { 305 mode = "w+"; 306 } 307 308#endif 309 return PyFile_FromFile(fp, "<objc-file>", mode, dontClose); 310} 311 312static int 313FILE_Convert(PyObject* obj, void* pObj) 314{ 315 *(FILE**)pObj = PyFile_AsFile(obj); 316 if (*(FILE**)pObj == NULL) { 317 return 1; 318 } 319 320 return 0; 321} 322 323#endif 324 325/* 326 * Generic CF type support 327 */ 328static PyObject* 329CF_to_py(void* cfValue) 330{ 331 return PyObjC_IDToCFType((id)cfValue); 332} 333 334static int 335py_to_CF(PyObject* obj, void* output) 336{ 337 id tmp = PyObjC_CFTypeToID(obj); 338 if (tmp == NULL && PyErr_Occurred()) { 339 return -1; 340 } 341 *(void**)output = tmp; 342 return 0; 343} 344 345int PyObjCPointerWrapper_RegisterCF(const char *signature) { 346 return PyObjCPointerWrapper_Register(signature, 347 (PyObjCPointerWrapper_ToPythonFunc)&CF_to_py, 348 (PyObjCPointerWrapper_FromPythonFunc)&py_to_CF); 349} 350 351 352int 353PyObjCPointerWrapper_Init(void) 354{ 355 int r = 0; 356 357 r = PyObjCPointerWrapper_RegisterCF(@encode(CFURLRef)); 358 if (r == -1) return -1; 359 360 r = PyObjCPointerWrapper_RegisterCF(@encode(CFSetRef)); 361 if (r == -1) return -1; 362 363#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 364 r = PyObjCPointerWrapper_RegisterCF(@encode(CFNetServiceRef)); 365 if (r == -1) return -1; 366#endif 367 368 r = PyObjCPointerWrapper_RegisterCF(@encode(CFReadStreamRef)); 369 if (r == -1) return -1; 370 371 r = PyObjCPointerWrapper_RegisterCF(@encode(CFRunLoopRef)); 372 if (r == -1) return -1; 373 374 r = PyObjCPointerWrapper_Register(@encode(PyObject*), 375 PyObjectPtr_New, PyObjectPtr_Convert); 376 if (r == -1) return -1; 377 378 r = PyObjCPointerWrapper_Register("^{objc_class=}", 379 class_new, class_convert); 380 if (r == -1) return -1; 381 382 383#if PY_MAJOR_VERSION == 2 384 r = PyObjCPointerWrapper_Register(@encode(FILE*), 385 FILE_New, FILE_Convert); 386 if (r == -1) return -1; 387#endif 388 389 return 0; 390} 391