1/* 2 * Generic support for opaque pointer types, such as NSZone* 3 */ 4#include "pyobjc.h" 5 6typedef struct { 7 PyObject_HEAD 8 void* pointer_value; 9} OpaquePointerObject; 10 11static PyMemberDef opaque_members[] = { 12 { 13 "__pointer__", 14 T_LONG, 15 offsetof(OpaquePointerObject, pointer_value), 16 READONLY, 17 "raw value of the pointer" 18 }, 19 { 0, 0, 0, 0, 0 } 20}; 21 22static PyObject* 23as_cobject(PyObject* self) 24{ 25 if (((OpaquePointerObject*)self)->pointer_value == NULL) { 26 Py_INCREF(Py_None); 27 return Py_None; 28 } 29 return PyCapsule_New(((OpaquePointerObject*)self)->pointer_value, "objc.__opaque__", NULL); 30} 31 32static PyMethodDef opaque_methods[] = { 33 { 34 "__cobject__", 35 (PyCFunction)as_cobject, 36 METH_NOARGS, 37 "get a CObject representing this object" 38 }, 39 { 0, 0, 0, 0 } 40}; 41 42 43static PyObject* 44opaque_new(PyTypeObject* type, PyObject* args, PyObject* kwds) 45{ 46static char* keywords[] = { "cobject", "c_void_p", NULL }; 47 PyObject* cobject = NULL; 48 PyObject* c_void_p = NULL; 49 50 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", keywords, &cobject, &c_void_p)) { 51 return NULL; 52 } 53 54 if (cobject != NULL && c_void_p != NULL) { 55 PyErr_SetString(PyExc_ValueError, 56 "pass 'cobject' or 'c_void_p', not both"); 57 return NULL; 58 } 59 60 if (cobject != NULL) { 61 OpaquePointerObject* result; 62 void* p; 63 if (!PyCapsule_CheckExact(cobject)) { 64 PyErr_SetString(PyExc_TypeError, 65 "'cobject' argument is not a PyCapsule"); 66 return NULL; 67 } 68 p = PyCapsule_GetPointer(cobject, "objc.__opaque__"); 69 if (PyErr_Occurred()) { 70 return NULL; 71 } 72 73 74 result = PyObject_New(OpaquePointerObject, type); 75 if (result == NULL) { 76 return NULL; 77 } 78 result->pointer_value = p; 79 return (PyObject*)result; 80 81 } else if (c_void_p != NULL) { 82 OpaquePointerObject* result; 83 void* p; 84 PyObject* attrval; 85 86 if (PyLong_Check(c_void_p) 87#if PY_MAJOR_VERSION == 2 88 || PyInt_Check(c_void_p) 89#endif 90 ) { 91 attrval = c_void_p; 92 Py_INCREF(attrval); 93 } else { 94 attrval = PyObject_GetAttrString(c_void_p, "value"); 95 if (attrval == NULL) { 96 return NULL; 97 } 98 } 99 100 if ( 101#if PY_MAJOR_VERSION == 2 102 PyInt_Check(attrval) || 103 /* NOTE: PyLong_AsVoidPtr works on Int objects as well */ 104#endif /* PY_MAJOR_VERSION == 2 */ 105 PyLong_Check(attrval) 106 ) { 107 p = PyLong_AsVoidPtr(attrval); 108 if (p == NULL && PyErr_Occurred()) { 109 Py_DECREF(attrval); 110 return NULL; 111 } 112 113 } else { 114 PyErr_SetString(PyExc_ValueError, 115 "c_void_p.value is not an integer"); 116 return NULL; 117 } 118 Py_DECREF(attrval); 119 result = PyObject_New(OpaquePointerObject, type); 120 if (result == NULL) { 121 return NULL; 122 } 123 result->pointer_value = p; 124 return (PyObject*)result; 125 126 127 } else { 128 PyErr_Format(PyExc_TypeError, "Cannot create %s objects", 129 type->tp_name); 130 return NULL; 131 } 132} 133 134static void 135opaque_dealloc(PyObject* self) 136{ 137 PyObject_Del(self); 138} 139 140static void 141opaque_from_c( 142 ffi_cif* cif __attribute__((__unused__)), 143 void* retval, 144 void** args, 145 void* userdata) 146{ 147 void* pointer_value = *(void**)args[0]; 148 PyTypeObject* opaque_type = (PyTypeObject*)userdata; 149 OpaquePointerObject* result; 150 151 result = PyObject_New(OpaquePointerObject, opaque_type); 152 if (result == NULL) { 153 *(PyObject**)retval = NULL; 154 return; 155 } 156 result->pointer_value = pointer_value; 157 *(PyObject**)retval = (PyObject*)result; 158} 159 160static void 161opaque_to_c( 162 ffi_cif* cif __attribute__((__unused__)), 163 void* retval, 164 void** args, 165 void* userdata) 166{ 167 PyObject* obj = *(PyObject**)args[0]; 168 void* pObj = *(void**)args[1]; 169 PyTypeObject* opaque_type = (PyTypeObject*)userdata; 170 171 if (!PyObject_TypeCheck((obj), opaque_type)) { 172 *(void**)pObj = (void*)0xDEADBEEF; /* force errors */ 173 PyErr_Format(PyExc_TypeError, 174 "Need instance of %s, got instance of %s", 175 opaque_type->tp_name, Py_TYPE(obj)->tp_name); 176 *(int*)retval = -1; 177 return; 178 } 179 180 *(void**)pObj = ((OpaquePointerObject*)obj)->pointer_value; 181 *(int*)retval = 0; 182} 183 184 185/* 186 * Usage: 187 * PyDict_SetItemString(moduleDict, "NSZonePointer", 188 * PyObjCCreateOpaquePointerType( 189 * "Foundation.NSZonePointer", 190 * @encode(NSZone*), 191 * NSZonePointer_doc)); 192 */ 193PyObject* 194PyObjCCreateOpaquePointerType( 195 const char* name, 196 const char* typestr, 197 const char* docstr) 198{ 199static const char convert_cif_signature[] = { _C_INT, _C_PTR, _C_VOID, _C_PTR, _C_VOID, 0 }; 200static const char new_cif_signature[] = { _C_PTR, _C_VOID, _C_PTR, _C_VOID, 0 }; 201static ffi_cif* convert_cif = NULL; 202static ffi_cif* new_cif = NULL; 203 204 /* 205 * XXX: This code is questionable, should use same approach as in struct-wrapper.m 206 */ 207 208 PyHeapTypeObject* newType = NULL; 209 PyObjCPointerWrapper_ToPythonFunc from_c = NULL; 210 PyObjCPointerWrapper_FromPythonFunc to_c = NULL; 211 ffi_closure* cl = NULL; 212 ffi_status rv; 213 int r; 214 PyObject* v = NULL; 215 PyObject* w = NULL; 216 217 if (new_cif == NULL) { 218 PyObjCMethodSignature* signature; 219 signature = PyObjCMethodSignature_FromSignature(new_cif_signature, NO); 220 new_cif = PyObjCFFI_CIFForSignature(signature); 221 Py_DECREF(signature); 222 if (new_cif == NULL) { 223 return NULL; 224 } 225 } 226 227 if (convert_cif == NULL) { 228 PyObjCMethodSignature* signature; 229 signature = PyObjCMethodSignature_FromSignature(convert_cif_signature, YES); 230 convert_cif = PyObjCFFI_CIFForSignature(signature); 231 Py_DECREF(signature); 232 if (convert_cif == NULL) { 233 return NULL; 234 } 235 } 236 237 238 newType = (PyHeapTypeObject*)PyType_Type.tp_alloc(&PyType_Type, 0); 239 if (newType == NULL) { 240 return NULL; 241 } 242#if PY_VERSION_HEX < 0x02050000 243# define ht_type type 244# define ht_name name 245#endif 246 //memcpy(newType, &opaque_template, sizeof(*newType)); 247 newType->ht_type.tp_basicsize = sizeof(OpaquePointerObject); 248 newType->ht_type.tp_dealloc = opaque_dealloc; 249 newType->ht_type.tp_getattro = PyObject_GenericGetAttr; 250 newType->ht_type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HEAPTYPE; 251 newType->ht_type.tp_methods = opaque_methods; 252 newType->ht_type.tp_members = opaque_members; 253 newType->ht_type.tp_new = opaque_new; 254 255 newType->ht_type.tp_as_number = &newType->as_number; 256 newType->ht_type.tp_as_mapping = &newType->as_mapping; 257 newType->ht_type.tp_as_sequence = &newType->as_sequence; 258 newType->ht_type.tp_as_buffer = &newType->as_buffer; 259 260 /* Force type to be a heap type. Not only is that technically correct, 261 * it also makes the type mutable (annoyingly enough all heap types 262 * and only heap types are mutable). 263 */ 264 newType->ht_type.tp_flags |= Py_TPFLAGS_HEAPTYPE; 265 newType->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC; 266 267 newType->ht_name = PyText_FromString(name); 268 if (newType->ht_name == NULL) { 269 PyMem_Free(newType); 270 PyErr_NoMemory(); 271 return NULL; 272 } 273 274 newType->ht_type.tp_name = PyText_AsString(newType->ht_name); 275 276 277#if PY_VERSION_HEX >= 0x03030000 278 newType->ht_qualname = PyText_FromString(name); 279 if (newType->ht_qualname == NULL) { 280 PyMem_Free(newType); 281 PyErr_NoMemory(); 282 return NULL; 283 } 284#endif 285 286 v = PyDict_New(); 287 if (v == NULL) { 288 goto error_cleanup; 289 } 290 291 w = PyText_FromString(typestr); 292 if (w == NULL) { 293 goto error_cleanup; 294 } 295 296 if (PyDict_SetItemString(v, "__typestr__", w) != 0) { 297 goto error_cleanup; 298 } 299 Py_DECREF(w); w = NULL; 300 301 newType->ht_type.tp_dict = v; v = NULL; 302 303 if (docstr != NULL) { 304 newType->ht_type.tp_doc = PyObjCUtil_Strdup(docstr); 305 if (newType->ht_type.tp_doc == NULL) { 306 PyErr_NoMemory(); 307 goto error_cleanup; 308 } 309 } 310 311 cl = PyObjC_malloc_closure(); 312 if (cl == NULL) { 313 goto error_cleanup; 314 } 315 316 newType->ht_type.tp_alloc = PyType_GenericAlloc; 317 Py_INCREF(Py_TYPE(&(newType->ht_type))); 318 PyType_Ready((PyTypeObject*)newType); 319 Py_INCREF((PyObject*)newType); 320 Py_INCREF((PyObject*)newType); 321 Py_INCREF((PyObject*)newType); 322 323 324 rv = ffi_prep_closure(cl, convert_cif, opaque_to_c, newType); 325 if (rv != FFI_OK) { 326 PyErr_Format(PyExc_RuntimeError, 327 "Cannot create FFI closure: %d", rv); 328 goto error_cleanup; 329 } 330 to_c = (PyObjCPointerWrapper_FromPythonFunc)cl; 331 cl = NULL; 332 333 cl = PyObjC_malloc_closure(); 334 if (cl == NULL) { 335 goto error_cleanup; 336 } 337 338 rv = ffi_prep_closure(cl, new_cif, opaque_from_c, newType); 339 if (rv != FFI_OK) { 340 PyErr_Format(PyExc_RuntimeError, 341 "Cannot create FFI closure: %d", rv); 342 goto error_cleanup; 343 } 344 from_c = (PyObjCPointerWrapper_ToPythonFunc)cl; 345 cl = NULL; 346 347 r = PyObjCPointerWrapper_Register(typestr, from_c, to_c); 348 if (r == -1) { 349 goto error_cleanup; 350 } 351 352 return (PyObject*)newType; 353 354error_cleanup: 355 if (newType) { 356 if (newType->ht_type.tp_name) PyMem_Free((char*)newType->ht_type.tp_name); 357 if (newType->ht_type.tp_doc) PyMem_Free((char*)newType->ht_type.tp_doc); 358 Py_XDECREF(newType->ht_type.tp_dict); 359 PyMem_Free(newType); 360 } 361 if (cl) { 362 PyObjC_free_closure(cl); 363 } 364 if (to_c) { 365 PyObjC_free_closure((ffi_closure*)to_c); 366 } 367 if (from_c) { 368 PyObjC_free_closure((ffi_closure*)from_c); 369 } 370 Py_XDECREF(v); 371 Py_XDECREF(w); 372 return NULL; 373} 374