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