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