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 PyCObject_FromVoidPtr(((OpaquePointerObject*)self)->pointer_value, 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", NULL }; 47 PyObject* arg = NULL; 48 49 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", keywords, &arg)) { 50 return NULL; 51 } 52 53 if (arg == NULL || !PyCObject_Check(arg)) { 54 PyErr_Format(PyExc_TypeError, "Cannot create %s objects", 55 type->tp_name); 56 return NULL; 57 } else { 58 OpaquePointerObject* result; 59 60 result = PyObject_New(OpaquePointerObject, type); 61 if (result == NULL) { 62 return NULL; 63 } 64 result->pointer_value = PyCObject_AsVoidPtr(arg); 65 return (PyObject*)result; 66 } 67} 68 69static void 70opaque_dealloc(PyObject* self) 71{ 72 PyObject_Del(self); 73} 74 75static void 76opaque_from_c( 77 ffi_cif* cif __attribute__((__unused__)), 78 void* retval, 79 void** args, 80 void* userdata) 81{ 82 void* pointer_value = *(void**)args[0]; 83 PyTypeObject* opaque_type = (PyTypeObject*)userdata; 84 OpaquePointerObject* result; 85 86 result = PyObject_New(OpaquePointerObject, opaque_type); 87 if (result == NULL) { 88 *(PyObject**)retval = NULL; 89 return; 90 } 91 result->pointer_value = pointer_value; 92 *(PyObject**)retval = (PyObject*)result; 93} 94 95static void 96opaque_to_c( 97 ffi_cif* cif __attribute__((__unused__)), 98 void* retval, 99 void** args, 100 void* userdata) 101{ 102 PyObject* obj = *(PyObject**)args[0]; 103 void* pObj = *(void**)args[1]; 104 PyTypeObject* opaque_type = (PyTypeObject*)userdata; 105 106 if (!PyObject_TypeCheck((obj), opaque_type)) { 107 *(void**)pObj = (void*)0xDEADBEEF; /* force errors */ 108 PyErr_Format(PyExc_TypeError, 109 "Need instance of %s, got instance of %s", 110 opaque_type->tp_name, obj->ob_type->tp_name); 111 *(int*)retval = -1; 112 return; 113 } 114 115 *(void**)pObj = ((OpaquePointerObject*)obj)->pointer_value; 116 *(int*)retval = 0; 117} 118 119 120/* 121 * Usage: 122 * PyDict_SetItemString(moduleDict, "NSZonePointer", 123 * PyObjCCreateOpaquePointerType( 124 * "Foundation.NSZonePointer", 125 * @encode(NSZone*), 126 * NSZonePointer_doc)); 127 */ 128PyObject* 129PyObjCCreateOpaquePointerType( 130 const char* name, 131 const char* typestr, 132 const char* docstr) 133{ 134static const char convert_cif_signature[] = { _C_INT, _C_PTR, _C_VOID, _C_PTR, _C_VOID, 0 }; 135static const char new_cif_signature[] = { _C_PTR, _C_VOID, _C_PTR, _C_VOID, 0 }; 136static ffi_cif* convert_cif = NULL; 137static ffi_cif* new_cif = NULL; 138 139 PyHeapTypeObject* newType = NULL; 140 PyObjCPointerWrapper_ToPythonFunc from_c = NULL; 141 PyObjCPointerWrapper_FromPythonFunc to_c = NULL; 142 ffi_closure* cl = NULL; 143 ffi_status rv; 144 int r; 145 PyObject* v = NULL; 146 PyObject* w = NULL; 147 148 if (new_cif == NULL) { 149 PyObjCMethodSignature* signature; 150 signature = PyObjCMethodSignature_FromSignature(new_cif_signature); 151 new_cif = PyObjCFFI_CIFForSignature(signature); 152 Py_DECREF(signature); 153 if (new_cif == NULL) { 154 return NULL; 155 } 156 } 157 158 if (convert_cif == NULL) { 159 PyObjCMethodSignature* signature; 160 signature = PyObjCMethodSignature_FromSignature(convert_cif_signature); 161 convert_cif = PyObjCFFI_CIFForSignature(signature); 162 Py_DECREF(signature); 163 if (convert_cif == NULL) { 164 return NULL; 165 } 166 } 167 168 169 newType = (PyHeapTypeObject*)PyType_Type.tp_alloc(&PyType_Type, 0); 170 if (newType == NULL) { 171 return NULL; 172 } 173#if PY_VERSION_HEX < 0x02050000 174# define ht_type type 175# define ht_name name 176#endif 177 //memcpy(newType, &opaque_template, sizeof(*newType)); 178 newType->ht_type.tp_basicsize = sizeof(OpaquePointerObject); 179 newType->ht_type.tp_dealloc = opaque_dealloc; 180 newType->ht_type.tp_getattro = PyObject_GenericGetAttr; 181 newType->ht_type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HEAPTYPE; 182 newType->ht_type.tp_methods = opaque_methods; 183 newType->ht_type.tp_members = opaque_members; 184 newType->ht_type.tp_new = opaque_new; 185 186 newType->ht_type.tp_as_number = &newType->as_number; 187 newType->ht_type.tp_as_mapping = &newType->as_mapping; 188 newType->ht_type.tp_as_sequence = &newType->as_sequence; 189 newType->ht_type.tp_as_buffer = &newType->as_buffer; 190 191 /* Force type to be a heap type. Not only is that technically correct, 192 * it also makes the type mutable (annoyingly enough all heap types 193 * and only heap types are mutable). 194 */ 195 newType->ht_type.tp_flags |= Py_TPFLAGS_HEAPTYPE; 196 newType->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC; 197 198 newType->ht_name = PyString_FromString(name); 199 if (newType->ht_name == NULL) { 200 PyMem_Free(newType); 201 PyErr_NoMemory(); 202 return NULL; 203 } 204 newType->ht_type.tp_name = PyString_AsString(newType->ht_name); 205 206 v = PyDict_New(); 207 if (v == NULL) { 208 goto error_cleanup; 209 } 210 211 w = PyString_FromString(typestr); 212 if (w == NULL) { 213 goto error_cleanup; 214 } 215 216 if (PyDict_SetItemString(v, "__typestr__", w) != 0) { 217 goto error_cleanup; 218 } 219 Py_DECREF(w); w = NULL; 220 221 newType->ht_type.tp_dict = v; v = NULL; 222 223 if (docstr != NULL) { 224 newType->ht_type.tp_doc = PyObjCUtil_Strdup(docstr); 225 if (newType->ht_type.tp_doc == NULL) { 226 PyErr_NoMemory(); 227 goto error_cleanup; 228 } 229 } 230 231 cl = PyObjC_malloc_closure(); 232 if (cl == NULL) { 233 goto error_cleanup; 234 } 235 236 newType->ht_type.tp_alloc = PyType_GenericAlloc; 237 Py_INCREF(newType->ht_type.ob_type); 238 PyType_Ready((PyTypeObject*)newType); 239 Py_INCREF((PyObject*)newType); 240 Py_INCREF((PyObject*)newType); 241 Py_INCREF((PyObject*)newType); 242 243 244 rv = ffi_prep_closure(cl, convert_cif, opaque_to_c, newType); 245 if (rv != FFI_OK) { 246 PyErr_Format(PyExc_RuntimeError, 247 "Cannot create FFI closure: %d", rv); 248 goto error_cleanup; 249 } 250 to_c = (PyObjCPointerWrapper_FromPythonFunc)cl; 251 cl = NULL; 252 253 cl = PyObjC_malloc_closure(); 254 if (cl == NULL) { 255 goto error_cleanup; 256 } 257 258 rv = ffi_prep_closure(cl, new_cif, opaque_from_c, newType); 259 if (rv != FFI_OK) { 260 PyErr_Format(PyExc_RuntimeError, 261 "Cannot create FFI closure: %d", rv); 262 goto error_cleanup; 263 } 264 from_c = (PyObjCPointerWrapper_ToPythonFunc)cl; 265 cl = NULL; 266 267 r = PyObjCPointerWrapper_Register(typestr, from_c, to_c); 268 if (r == -1) { 269 goto error_cleanup; 270 } 271 272 return (PyObject*)newType; 273 274error_cleanup: 275 if (newType) { 276 if (newType->ht_type.tp_name) PyMem_Free((char*)newType->ht_type.tp_name); 277 if (newType->ht_type.tp_doc) PyMem_Free((char*)newType->ht_type.tp_doc); 278 Py_XDECREF(newType->ht_type.tp_dict); 279 PyMem_Free(newType); 280 } 281 if (cl) { 282 PyObjC_free_closure(cl); 283 } 284 if (to_c) { 285 PyObjC_free_closure((ffi_closure*)to_c); 286 } 287 if (from_c) { 288 PyObjC_free_closure((ffi_closure*)from_c); 289 } 290 Py_XDECREF(v); 291 Py_XDECREF(w); 292 return NULL; 293} 294