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", NULL }; 47 PyObject* arg = NULL; 48 49 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", keywords, &arg)) { 50 return NULL; 51 } 52 53 if (arg == NULL || !PyCapsule_CheckExact(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 = PyCapsule_GetPointer(arg, "objc.__opaque__"); 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, Py_TYPE(obj)->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, NO); 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, YES); 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 = PyText_FromString(name); 199 if (newType->ht_name == NULL) { 200 PyMem_Free(newType); 201 PyErr_NoMemory(); 202 return NULL; 203 } 204 205 newType->ht_type.tp_name = PyText_AsString(newType->ht_name); 206 207 v = PyDict_New(); 208 if (v == NULL) { 209 goto error_cleanup; 210 } 211 212 w = PyText_FromString(typestr); 213 if (w == NULL) { 214 goto error_cleanup; 215 } 216 217 if (PyDict_SetItemString(v, "__typestr__", w) != 0) { 218 goto error_cleanup; 219 } 220 Py_DECREF(w); w = NULL; 221 222 newType->ht_type.tp_dict = v; v = NULL; 223 224 if (docstr != NULL) { 225 newType->ht_type.tp_doc = PyObjCUtil_Strdup(docstr); 226 if (newType->ht_type.tp_doc == NULL) { 227 PyErr_NoMemory(); 228 goto error_cleanup; 229 } 230 } 231 232 cl = PyObjC_malloc_closure(); 233 if (cl == NULL) { 234 goto error_cleanup; 235 } 236 237 newType->ht_type.tp_alloc = PyType_GenericAlloc; 238 Py_INCREF(Py_TYPE(&(newType->ht_type))); 239 PyType_Ready((PyTypeObject*)newType); 240 Py_INCREF((PyObject*)newType); 241 Py_INCREF((PyObject*)newType); 242 Py_INCREF((PyObject*)newType); 243 244 245 rv = ffi_prep_closure(cl, convert_cif, opaque_to_c, newType); 246 if (rv != FFI_OK) { 247 PyErr_Format(PyExc_RuntimeError, 248 "Cannot create FFI closure: %d", rv); 249 goto error_cleanup; 250 } 251 to_c = (PyObjCPointerWrapper_FromPythonFunc)cl; 252 cl = NULL; 253 254 cl = PyObjC_malloc_closure(); 255 if (cl == NULL) { 256 goto error_cleanup; 257 } 258 259 rv = ffi_prep_closure(cl, new_cif, opaque_from_c, newType); 260 if (rv != FFI_OK) { 261 PyErr_Format(PyExc_RuntimeError, 262 "Cannot create FFI closure: %d", rv); 263 goto error_cleanup; 264 } 265 from_c = (PyObjCPointerWrapper_ToPythonFunc)cl; 266 cl = NULL; 267 268 r = PyObjCPointerWrapper_Register(typestr, from_c, to_c); 269 if (r == -1) { 270 goto error_cleanup; 271 } 272 273 return (PyObject*)newType; 274 275error_cleanup: 276 if (newType) { 277 if (newType->ht_type.tp_name) PyMem_Free((char*)newType->ht_type.tp_name); 278 if (newType->ht_type.tp_doc) PyMem_Free((char*)newType->ht_type.tp_doc); 279 Py_XDECREF(newType->ht_type.tp_dict); 280 PyMem_Free(newType); 281 } 282 if (cl) { 283 PyObjC_free_closure(cl); 284 } 285 if (to_c) { 286 PyObjC_free_closure((ffi_closure*)to_c); 287 } 288 if (from_c) { 289 PyObjC_free_closure((ffi_closure*)from_c); 290 } 291 Py_XDECREF(v); 292 Py_XDECREF(w); 293 return NULL; 294} 295