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 PyText_FromFormat(
24			"<%s CoreFoundation magic instance %p>",
25			Py_TYPE(self)->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			Py_TYPE(self)->tp_name,
39			PyObjCObject_GetObject(self));
40
41		return PyText_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 as a
112		 * a plain CFTypeRef
113		 */
114		Py_INCREF(PyObjC_NSCFTypeClass);
115		return PyObjC_NSCFTypeClass;
116	}
117
118	PyObject* cf = PyLong_FromUnsignedLongLong(typeID);
119	if (cf == NULL) {
120		return NULL;
121	}
122
123
124	result = PyDict_GetItem(gTypeid2class, cf);
125	if (result != NULL) {
126		/* This type is the same as an already registered type,
127		 * return that type
128		 */
129		Py_DECREF(cf);
130		Py_INCREF(result);
131		return result;
132	}
133
134	/*
135	 * If that doesn't exist create a new one.
136	 */
137
138	protectedMethods = PyDict_New();
139	if (protectedMethods == NULL) {
140		Py_DECREF(cf);
141		return NULL;
142	}
143
144	dict = PyDict_New();
145	if (dict == NULL) {
146		Py_DECREF(cf);
147		return NULL;
148	}
149
150	PyDict_SetItemString(dict, "__slots__", PyTuple_New(0));
151
152	bases = PyTuple_New(1);
153
154	PyTuple_SET_ITEM(bases, 0, PyObjC_NSCFTypeClass);
155	Py_INCREF(PyObjC_NSCFTypeClass);
156
157	args = PyTuple_New(3);
158	PyTuple_SetItem(args, 0, PyText_FromString(name));
159	PyTuple_SetItem(args, 1, bases);
160	PyTuple_SetItem(args, 2, dict);
161
162	result = PyType_Type.tp_new(&PyObjCClass_Type, args, NULL);
163	Py_DECREF(args);
164	if (result == NULL) {
165		Py_DECREF(cf);
166		return NULL;
167	}
168
169	((PyTypeObject*)result)->tp_repr = cf_repr;
170	((PyTypeObject*)result)->tp_str = cf_repr;
171
172	info = (PyObjCClassObject*)result;
173	info->class = PyObjCClass_GetClass(PyObjC_NSCFTypeClass);
174	info->sel_to_py = NULL;
175	info->method_magic = 0;
176	info->dictoffset = 0;
177	info->useKVO = 0;
178	info->delmethod = NULL;
179	info->hasPythonImpl = 0;
180	info->isCFWrapper = 1;
181	info->protectedMethods = protectedMethods;
182
183	if (PyObject_SetAttrString(result,
184			"__module__", PyObjCClass_DefaultModule) < 0) {
185		PyErr_Clear();
186	}
187
188
189	if (PyDict_SetItem(gTypeid2class, cf, result) == -1) {
190		Py_DECREF(result);
191		Py_DECREF(cf);
192		return NULL;
193	}
194
195	Py_DECREF(cf); cf = NULL;
196
197	/* Force an artificially high refcount to avoid deallocation of the
198	 * class.
199	 * XXX: This code is wrong, it hides the real problem, but I (ronald)
200	 * have no idea where that problem hides itself.
201	 */
202	Py_INCREF(Py_TYPE(result));
203
204	return result;
205}
206
207
208int
209PyObjCCFType_Setup(void)
210{
211	static char encodingBuf[128];
212	Class cls;
213
214	gTypeid2class = PyDict_New();
215	if (gTypeid2class == NULL) {
216		return -1;
217	}
218
219	cls = objc_lookUpClass("__NSCFType");
220	if (cls == nil) {
221		cls = objc_lookUpClass("NSCFType");
222	}
223	if (cls == nil) {
224		PyErr_SetString(PyExc_RuntimeError,
225			"Cannot locate NSCFType");
226		return -1;
227	}
228	PyObjC_NSCFTypeClass = PyObjCClass_New(cls);
229	if (PyObjC_NSCFTypeClass == NULL) {
230		return -1;
231	}
232
233	/* Add a __pyobjc_PythonObject__ method to NSCFType. Can't use a
234	 * category because the type isn't public.
235	 */
236	snprintf(encodingBuf, sizeof(encodingBuf), "%s%c%c", @encode(PyObject*), _C_ID, _C_SEL);
237	if (!class_addMethod(cls, @selector(__pyobjc_PythonObject__),
238		(IMP)pyobjc_PythonObject, encodingBuf)) {
239
240		return -1;
241	}
242
243	return 0;
244}
245
246/*
247 * Create proxy object for a special value of a CFType, that
248 * is a value that just a magic cookie and not a valid
249 * object. Such objects are sometimes used in CoreFoundation
250 * (sadly enough).
251 */
252PyObject* PyObjCCF_NewSpecial(char* typestr, void* datum)
253{
254	PyObject* rval = NULL;
255	PyObject* v = PyDict_GetItemString(PyObjC_TypeStr2CFTypeID, typestr);
256	if (v == NULL) {
257		PyErr_Format(PyExc_ValueError, "Don't know CF type for typestr '%s', cannot create special wrapper", typestr);
258		return NULL;
259	}
260	CFTypeID typeid;
261
262	if (depythonify_c_value(@encode(CFTypeID), v, &typeid) < 0) {
263		return NULL;
264	}
265
266	if (gTypeid2class != NULL) {
267		PyObject* cfid;
268		PyTypeObject* tp;
269
270		cfid = PyInt_FromLong(typeid);
271		tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid);
272		Py_DECREF(cfid);
273
274		if (tp != NULL) {
275			rval = tp->tp_alloc(tp, 0);
276			if (rval == NULL) {
277				return NULL;
278			}
279
280			((PyObjCObject*)rval)->objc_object = (id)datum;
281			((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT|PyObjCObject_kSHOULD_NOT_RELEASE|PyObjCObject_kMAGIC_COOKIE;
282		}
283	} else {
284		rval = NULL;
285		PyErr_Format(PyExc_ValueError,
286			"Sorry, cannot wrap special value of typeid %d\n",
287			(int)typeid);
288	}
289
290	return rval;
291}
292
293/*
294 * Create proxy object for a special value of a CFType, that
295 * is a value that just a magic cookie and not a valid
296 * object. Such objects are sometimes used in CoreFoundation
297 * (sadly enough).
298 */
299PyObject* PyObjCCF_NewSpecial2(CFTypeID typeid, void* datum)
300{
301	PyObject *rval = NULL;
302
303	if (gTypeid2class != NULL) {
304		PyObject* cfid;
305		PyTypeObject* tp;
306
307		cfid = PyInt_FromLong(typeid);
308		tp = (PyTypeObject*)PyDict_GetItem(gTypeid2class, cfid);
309		Py_DECREF(cfid);
310
311		if (tp != NULL) {
312			rval = tp->tp_alloc(tp, 0);
313			if (rval == NULL) {
314				return NULL;
315			}
316
317			((PyObjCObject*)rval)->objc_object = (id)datum;
318			((PyObjCObject*)rval)->flags = PyObjCObject_kDEFAULT|PyObjCObject_kSHOULD_NOT_RELEASE|PyObjCObject_kMAGIC_COOKIE;
319		}
320	} else {
321		rval = NULL;
322		PyErr_Format(PyExc_ValueError,
323			"Sorry, cannot wrap special value of typeid %d\n",
324			(int)typeid);
325	}
326
327	return rval;
328}
329