1/*
2 * This file implements generic support for CoreFoundation types.
3 *
4 * CF-based proxy types are implemented as subclasses of the proxy for NSCFType,
5 * that way CF-based types fit in nicely with the PyObjC machinery (specifically
6 * subclass tests keep working).
7 *
8 * Major assumption:
9 * - NSCFType is the ObjC type that all non-toll-free bridged types inherit
10 *   from, toll-free bridged types are not subclasses of NSCFType.
11 */
12#include "pyobjc.h"
13
14#include <CoreFoundation/CoreFoundation.h>
15
16static PyObject* gTypeid2class = NULL;
17PyObject* PyObjC_NSCFTypeClass = NULL;
18
19static PyObject*
20cf_repr(PyObject* self)
21{
22	if (PyObjCObject_GetFlags(self) & PyObjCObject_kMAGIC_COOKIE) {
23		return PyString_FromFormat(
24			"<%s CoreFoundation magic instance %p>",
25			self->ob_type->tp_name, PyObjCObject_GetObject(self));
26	}
27
28
29
30	CFStringRef repr = CFCopyDescription(PyObjCObject_GetObject(self));
31	if (repr) {
32		PyObject* result = pythonify_c_value(@encode(id), &repr);
33		CFRelease(repr);
34		return result;
35	} else {
36		char buf[128];
37		snprintf(buf, sizeof(buf), "<%s object at %p>",
38			self->ob_type->tp_name,
39			PyObjCObject_GetObject(self));
40
41		return PyString_FromString(buf);
42	}
43}
44
45
46/* Implementation for: -(PyObject*)__pyobjc_PythonObject__ on NSCFType. We cannot
47 * define a category on that type because the class definition isn't public.
48 */
49static PyObject* pyobjc_PythonObject(NSObject* self, SEL _sel __attribute__((__unused__)))
50{
51	PyObject *rval = NULL;
52
53	rval = PyObjC_FindPythonProxy(self);
54	if (rval == NULL) {
55		if (gTypeid2class != NULL) {
56			PyObject* cfid;
57			PyTypeObject* tp;
58
59			cfid = PyInt_FromLong(CFGetTypeID((CFTypeRef)self));
60			tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid);
61			Py_DECREF(cfid);
62
63			if (tp != NULL) {
64				rval = tp->tp_alloc(tp, 0);
65				if (rval == NULL) {
66					return NULL;
67				}
68
69				((PyObjCObject*)rval)->objc_object = self;
70				((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT | PyObjCObject_kCFOBJECT;
71				CFRetain(self);
72			}
73		}
74
75		if (rval == NULL) {
76			/* There is no wrapper for this type, fall back to
77			 * the generic behaviour.
78			 */
79			rval = (PyObject *)PyObjCObject_New(self,
80				PyObjCObject_kDEFAULT, YES);
81		}
82
83		if (rval) {
84			PyObjC_RegisterPythonProxy(self, rval);
85		}
86	}
87
88	return rval;
89}
90
91
92PyObject*
93PyObjCCFType_New(char* name, char* encoding, CFTypeID typeID)
94{
95	PyObject* args;
96	PyObject* dict;
97	PyObject* result;
98	PyObject* bases;
99	PyObjCClassObject* info;
100	PyObject* protectedMethods;
101
102	/*
103	 * First look for an already registerd type
104	 */
105	if (encoding[0] != _C_ID) {
106		if (PyObjCPointerWrapper_RegisterID(encoding) == -1) {
107			return NULL;
108		}
109	}
110	if (typeID == 0) {
111		/* Partially registered type, just wrap is a
112		 * a plain CFTypeRef
113		 */
114		return NULL;
115	}
116
117	PyObject* cf = PyLong_FromUnsignedLongLong(typeID);
118	if (cf == NULL) {
119		return NULL;
120	}
121
122
123	result = PyDict_GetItem(gTypeid2class, cf);
124	if (result != NULL) {
125		/* This type is the same as an already registered type,
126		 * return that type
127		 */
128		Py_DECREF(cf);
129		Py_INCREF(result);
130		return result;
131	}
132
133	/*
134	 * If that doesn't exist create a new one.
135	 */
136
137	protectedMethods = PyDict_New();
138	if (protectedMethods == NULL) {
139		Py_DECREF(cf);
140		return NULL;
141	}
142
143	dict = PyDict_New();
144	if (dict == NULL) {
145		Py_DECREF(cf);
146		return NULL;
147	}
148
149	PyDict_SetItemString(dict, "__slots__", PyTuple_New(0));
150
151	bases = PyTuple_New(1);
152
153	PyTuple_SET_ITEM(bases, 0, PyObjC_NSCFTypeClass);
154	Py_INCREF(PyObjC_NSCFTypeClass);
155
156	args = PyTuple_New(3);
157	PyTuple_SetItem(args, 0, PyString_FromString(name));
158	PyTuple_SetItem(args, 1, bases);
159	PyTuple_SetItem(args, 2, dict);
160
161	result = PyType_Type.tp_new(&PyObjCClass_Type, args, NULL);
162	Py_DECREF(args);
163	if (result == NULL) {
164		Py_DECREF(cf);
165		return NULL;
166	}
167
168	((PyTypeObject*)result)->tp_repr = cf_repr;
169	((PyTypeObject*)result)->tp_str = cf_repr;
170
171	info = (PyObjCClassObject*)result;
172	info->class = PyObjCClass_GetClass(PyObjC_NSCFTypeClass);
173	info->sel_to_py = NULL;
174	info->method_magic = 0;
175	info->dictoffset = 0;
176	info->useKVO = 0;
177	info->delmethod = NULL;
178	info->hasPythonImpl = 0;
179	info->isCFWrapper = 1;
180	info->protectedMethods = protectedMethods;
181
182	if (PyObject_SetAttrString(result,
183			"__module__", PyObjCClass_DefaultModule) < 0) {
184		PyErr_Clear();
185	}
186
187
188	if (PyDict_SetItem(gTypeid2class, cf, result) == -1) {
189		Py_DECREF(result);
190		Py_DECREF(cf);
191		return NULL;
192	}
193
194	Py_DECREF(cf); cf = NULL;
195
196	/* Force an artificially high refcount to avoid deallocation of the
197	 * class.
198	 * XXX: This code is wrong, it hides the real problem, but I (ronald)
199	 * have no idea where that problem hides itself.
200	 */
201	Py_INCREF(result->ob_type);
202
203	return result;
204}
205
206
207int
208PyObjCCFType_Setup(void)
209{
210	static char encodingBuf[128];
211	Class cls;
212
213	gTypeid2class = PyDict_New();
214	if (gTypeid2class == NULL) {
215		return -1;
216	}
217
218	cls = objc_lookUpClass("NSCFType");
219	if (cls == nil) {
220		PyErr_SetString(PyExc_RuntimeError,
221			"Cannot locate NSCFType");
222		return -1;
223	}
224	PyObjC_NSCFTypeClass = PyObjCClass_New(cls);
225	if (PyObjC_NSCFTypeClass == NULL) {
226		return -1;
227	}
228
229	/* Add a __pyobjc_PythonObject__ method to NSCFType. Can't use a
230	 * category because the type isn't public.
231	 */
232	snprintf(encodingBuf, sizeof(encodingBuf), "%s%c%c", @encode(PyObject*), _C_ID, _C_SEL);
233	if (!class_addMethod(cls, @selector(__pyobjc_PythonObject__),
234		(IMP)pyobjc_PythonObject, encodingBuf)) {
235
236		return -1;
237	}
238
239	return 0;
240}
241
242/*
243 * Create proxy object for a special value of a CFType, that
244 * is a value that just a magic cookie and not a valid
245 * object. Such objects are sometimes used in CoreFoundation
246 * (sadly enough).
247 */
248PyObject* PyObjCCF_NewSpecial(char* typestr, void* datum)
249{
250	PyObject* rval = NULL;
251	PyObject* v = PyDict_GetItemString(PyObjC_TypeStr2CFTypeID, typestr);
252	if (v == NULL) {
253		PyErr_Format(PyExc_ValueError, "Unknown typestr: %s", typestr);
254		return NULL;
255	}
256	CFTypeID typeid;
257
258	if (depythonify_c_value(@encode(CFTypeID), v, &typeid) < 0) {
259		Py_DECREF(v);
260		return NULL;
261	}
262	Py_DECREF(v);
263
264	if (gTypeid2class != NULL) {
265		PyObject* cfid;
266		PyTypeObject* tp;
267
268		cfid = PyInt_FromLong(typeid);
269		tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid);
270		Py_DECREF(cfid);
271
272		if (tp != NULL) {
273			rval = tp->tp_alloc(tp, 0);
274			if (rval == NULL) {
275				return NULL;
276			}
277
278			((PyObjCObject*)rval)->objc_object = (id)datum;
279			((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT|PyObjCObject_kSHOULD_NOT_RELEASE|PyObjCObject_kMAGIC_COOKIE;
280		}
281	} else {
282		rval = NULL;
283		PyErr_Format(PyExc_ValueError,
284			"Sorry, cannot wrap special value of typeid %d\n",
285			(int)typeid);
286	}
287
288	return rval;
289}
290
291/*
292 * Create proxy object for a special value of a CFType, that
293 * is a value that just a magic cookie and not a valid
294 * object. Such objects are sometimes used in CoreFoundation
295 * (sadly enough).
296 */
297PyObject* PyObjCCF_NewSpecial2(CFTypeID typeid, void* datum)
298{
299	PyObject *rval = NULL;
300
301	if (gTypeid2class != NULL) {
302		PyObject* cfid;
303		PyTypeObject* tp;
304
305		cfid = PyInt_FromLong(typeid);
306		tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid);
307		Py_DECREF(cfid);
308
309		if (tp != NULL) {
310			rval = tp->tp_alloc(tp, 0);
311			if (rval == NULL) {
312				return NULL;
313			}
314
315			((PyObjCObject*)rval)->objc_object = (id)datum;
316			((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT|PyObjCObject_kSHOULD_NOT_RELEASE|PyObjCObject_kMAGIC_COOKIE;
317		}
318	} else {
319		rval = NULL;
320		PyErr_Format(PyExc_ValueError,
321			"Sorry, cannot wrap special value of typeid %d\n",
322			(int)typeid);
323	}
324
325	return rval;
326}
327