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