1/*
2 */
3#include "pyobjc.h"
4
5static Ivar
6find_ivar(NSObject* base, char* name)
7{
8	Class cur = object_getClass((id)base);
9	Ivar ivar;
10
11	while (cur != nil) {
12		ivar = class_getInstanceVariable(cur, name);
13		if (ivar != nil) {
14			return ivar;
15		}
16		cur = class_getSuperclass(cur);
17	}
18	return nil;
19}
20
21PyObject*
22PyObjCIvar_Info(PyObject* self __attribute__((__unused__)), PyObject* object)
23{
24	Class cur;
25	PyObject* v;
26	PyObject* result;
27	int r;
28
29	if (PyObjCObject_Check(object)) {
30		cur = object_getClass((id)PyObjCObject_GetObject(object));
31	} else if (PyObjCClass_Check(object)) {
32		cur = PyObjCClass_GetClass(object);
33	} else {
34		PyErr_Format(PyExc_TypeError, "not a class or object");
35		return NULL;
36	}
37
38
39	result = PyList_New(0);
40	if (result == NULL) {
41		return result;
42	}
43
44	/* Handle 'isa' specially, due to Objective-C 2.0 weirdness */
45	v = Py_BuildValue(
46			"(s"Py_ARG_BYTES")",
47			"isa", @encode(Class));
48	if (v == NULL) {
49		Py_DECREF(result);
50		return result;
51	}
52
53	r = PyList_Append(result, v);
54	Py_DECREF(v);
55	if  (r == -1) {
56		Py_DECREF(result);
57		return result;
58	}
59
60
61
62	while (cur != nil) {
63		Ivar ivar;
64		Ivar* ivarList;
65		unsigned i, ivarCount;
66
67		ivarList = class_copyIvarList(cur, &ivarCount);
68		if (ivarList == NULL) {
69			PyErr_SetString(PyExc_SystemError, "copyIvarList failed");
70			Py_DECREF(result);
71			return NULL;
72		}
73
74		for (i = 0; i < ivarCount; i++) {
75			ivar = ivarList[i];
76			const char* ivar_name = ivar_getName(ivar);
77
78			if (ivar == NULL) continue;
79
80			if (strcmp(ivar_name, "isa") == 0) {
81				/* See comment above */
82				continue;
83			}
84
85			v = Py_BuildValue(
86				"(s"Py_ARG_BYTES")",
87				ivar_name,
88				ivar_getTypeEncoding(ivar));
89			if (v == NULL) {
90				free(ivarList);
91				Py_DECREF(result);
92				return NULL;
93			}
94			r = PyList_Append(result, v);
95			Py_DECREF(v);
96			if (r == -1) {
97				free(ivarList);
98				Py_DECREF(result);
99				return NULL;
100			}
101		}
102
103		free(ivarList);
104
105		cur = class_getSuperclass(cur);
106	}
107	return result;
108}
109
110PyObject*
111PyObjCIvar_Get(PyObject* self __attribute__((__unused__)),
112		PyObject* args, PyObject* kwds)
113{
114static char* keywords[] = {"obj", "name", NULL };
115	PyObject* anObject;
116	char*     name;
117	Ivar	  ivar;
118	NSObject* objcValue;
119	PyObject* result;
120	const char* ivar_type;
121	ptrdiff_t ivar_offset;
122
123	if (!PyArg_ParseTupleAndKeywords(args, kwds, "Os", keywords, &anObject, &name)) {
124		return NULL;
125	}
126
127	if (!PyObjCObject_Check(anObject)) {
128		PyErr_Format(PyExc_TypeError,
129			"Expecting an Objective-C object, got instance of %s",
130			Py_TYPE(anObject)->tp_name);
131		return NULL;
132	}
133
134
135	objcValue = PyObjCObject_GetObject(anObject);
136
137	/* Shortcut for isa, mostly due to Objective-C 2.0 weirdness */
138	if (strcmp(name, "isa") == 0) {
139		Class cls = object_getClass(objcValue);
140		return pythonify_c_value(@encode(Class), &cls);
141	}
142
143	ivar = find_ivar(objcValue, name);
144	if (ivar == NULL) {
145		PyErr_Format(PyExc_AttributeError, "%s", name);
146		return NULL;
147	}
148
149	ivar_type = ivar_getTypeEncoding(ivar);
150	ivar_offset = ivar_getOffset(ivar);
151
152	if (strcmp(ivar_type, @encode(PyObject*)) == 0) {
153		result = *(PyObject**)(((char*)(objcValue)) + ivar_offset);
154		Py_XINCREF(result);
155	} else {
156		result = pythonify_c_value(ivar_type,
157			((char*)(objcValue)) + ivar_offset);
158	}
159
160	return result;
161}
162
163PyObject*
164PyObjCIvar_Set(PyObject* self __attribute__((__unused__)),
165		PyObject* args, PyObject* kwds)
166{
167static char* keywords[] = {"obj", "name", "value", "updateRefCounts", NULL };
168	PyObject* anObject;
169	char*     name;
170	Ivar	  ivar;
171	PyObject* value;
172	PyObject* updateRefCounts = NULL;
173	NSObject* objcValue;
174	int       result;
175	const char* ivar_type;
176	ptrdiff_t ivar_offset;
177
178	if (!PyArg_ParseTupleAndKeywords(args, kwds, "OsO|O",
179			keywords, &anObject, &name, &value, &updateRefCounts)) {
180		return NULL;
181	}
182
183	if (!PyObjCObject_Check(anObject)) {
184		PyErr_Format(PyExc_TypeError,
185			"Expecting an Objective-C object, got instance of %s",
186			Py_TYPE(anObject)->tp_name);
187		return NULL;
188	}
189
190	objcValue = PyObjCObject_GetObject(anObject);
191
192	if (strcmp(name, "isa") == 0) {
193		/*
194		 * Change the class of the object, this means we'll have to
195		 * update the python proxy object as well.
196		 */
197		Class cls;
198		PyObject* pycls;
199
200		result = depythonify_c_value(@encode(Class), value, &cls);
201		if (result == -1) {
202			return NULL;
203		}
204
205		(void)object_setClass(objcValue, cls);
206
207		pycls = PyObjCClass_New(cls);
208		if (pycls == NULL) {
209			return NULL;
210		}
211
212		Py_DECREF((PyObject*)(Py_TYPE(anObject)));
213		Py_TYPE(anObject) = (PyTypeObject*)pycls;
214		Py_INCREF(Py_None);
215		return Py_None;
216	}
217
218
219	ivar = find_ivar(objcValue, name);
220	if (ivar == NULL) {
221		PyErr_Format(PyExc_AttributeError, "%s", name);
222		return NULL;
223	}
224
225	ivar_type = ivar_getTypeEncoding(ivar);
226	ivar_offset = ivar_getOffset(ivar);
227
228
229	if (strcmp(ivar_type, @encode(PyObject*)) == 0) {
230		/*
231		 * Python object, need to handle refcounts
232		 */
233		Py_XINCREF(value);
234		Py_XDECREF(*(PyObject**)(((char*)(objcValue)) + ivar_offset));
235		*(PyObject**)(((char*)(objcValue)) + ivar_offset) = value;
236
237	} else if (ivar_type[0] == _C_ID) {
238		/*
239		 * Objective-C object, need to handle refcounts.
240		 */
241
242		NSObject* tmpValue;
243
244		if (updateRefCounts == NULL) {
245			PyErr_SetString(PyExc_TypeError,
246				"Instance variable is an object, "
247				"updateRefCounts argument is required");
248			return NULL;
249		}
250
251		result = depythonify_c_value(ivar_type, value, &tmpValue);
252		if (result != 0) {
253			return NULL;
254		}
255
256		if (PyObject_IsTrue(updateRefCounts)) {
257			[tmpValue retain];
258
259			id v = object_getIvar(objcValue, ivar);
260			[v release];
261		}
262		object_setIvar(objcValue, ivar, tmpValue);
263
264	} else {
265		/* A simple value */
266
267		result = depythonify_c_value(ivar_type, value,
268			((char*)(objcValue)) + ivar_offset);
269		if (result != 0) {
270			return NULL;
271		}
272	}
273	Py_INCREF(Py_None);
274	return Py_None;
275}
276