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