1/*
2 * A subclass of the builtin super type that works correctly when resolving
3 * class methods.
4 *
5 * The problem is that the default ``super`` object walks the class an peeks
6 * inside the class dict instead of using an API. That causes a problem because
7 * the class methods of an ObjC class aren't stored in the class __dict__ but
8 * in the __dict__ of the metaclass, which isn't advertised to Python code.
9 */
10
11#include "pyobjc.h"
12
13
14/*
15 * NOTE: This is a minor tweak of Python 2.5's super_getattro and is a rather
16 * crude hack.
17 *
18 * NOTE: updated for 3.2, and 2.7
19 *
20 * FIXME: This will require further work when I remove
21 * "PyObjCClass_CheckMethodList".
22 */
23typedef struct {
24        PyObject_HEAD
25	PyTypeObject *type;
26	PyObject *obj;
27	PyTypeObject *obj_type;
28} superobject;
29
30static PyObject *
31super_getattro(PyObject *self, PyObject *name)
32{
33	superobject *su = (superobject *)self;
34	int skip = su->obj_type == NULL;
35
36
37
38	if (!skip) {
39		/* We want __class__ to return the class of the super object
40		   (i.e. super, or a subclass), not the class of su->obj. */
41		if (PyUnicode_Check(name)) {
42			skip = (PyUnicode_GET_SIZE(name) && PyObjC_is_ascii_string(name, "__class__"));
43#if PY_MAJOR_VERSION == 2
44		} else if (PyString_Check(name)) {
45			skip = (
46				PyString_GET_SIZE(name) == 9 &&
47				strcmp(PyString_AS_STRING(name), "__class__") == 0);
48#endif
49		} else {
50			skip = 0;
51		}
52
53	}
54
55	if (!skip) {
56		PyObject *mro, *res, *tmp, *dict;
57		PyTypeObject *starttype;
58		descrgetfunc f;
59		Py_ssize_t i, n;
60
61		starttype = su->obj_type;
62		mro = starttype->tp_mro;
63
64		if (mro == NULL)
65			n = 0;
66		else {
67			assert(PyTuple_Check(mro));
68			n = PyTuple_GET_SIZE(mro);
69		}
70		for (i = 0; i < n; i++) {
71			if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
72				break;
73		}
74		i++;
75		res = NULL;
76		for (; i < n; i++) {
77			tmp = PyTuple_GET_ITEM(mro, i);
78
79			/* PyObjC PATCH: Treat PyObjC class objects specially to maintain
80			 * the proper illusion for users.
81			 * Also make sure that the method tables are up-to-date.
82			 */
83			if (PyObjCClass_Check(tmp)) {
84				PyObjCClass_CheckMethodList(tmp, NO);
85			}
86
87			if (PyObjCClass_Check(tmp) && PyObjCClass_Check(su->obj))  {
88				dict = Py_TYPE(tmp)->tp_dict;
89
90			} else if (PyType_Check(tmp))
91				dict = ((PyTypeObject *)tmp)->tp_dict;
92#if PY_MAJOR_VERSION == 2
93			else if (PyClass_Check(tmp))
94				dict = ((PyClassObject *)tmp)->cl_dict;
95#endif
96			else
97				continue;
98
99			res = PyDict_GetItem(dict, name);
100			if (res != NULL) {
101				Py_INCREF(res);
102				f = Py_TYPE(res)->tp_descr_get;
103				if (f != NULL) {
104					tmp = f(res,
105						/* Only pass 'obj' param if
106						   this is instance-mode super
107						   (See SF ID #743627)
108						*/
109						(su->obj == (PyObject *)
110							    su->obj_type
111							? (PyObject *)NULL
112							: su->obj),
113						(PyObject *)starttype);
114					Py_DECREF(res);
115					res = tmp;
116				}
117				return res;
118			}
119		}
120	}
121	return PyObject_GenericGetAttr(self, name);
122}
123
124PyTypeObject PyObjCSuper_Type = {
125	PyVarObject_HEAD_INIT(&PyType_Type, 0)
126	"objc.super",
127	sizeof(superobject),
128	0,
129	/* methods */
130	0,		 			/* tp_dealloc */
131	0,					/* tp_print */
132	0,					/* tp_getattr */
133	0,					/* tp_setattr */
134	0,					/* tp_compare */
135	0,					/* tp_repr */
136	0,					/* tp_as_number */
137	0,					/* tp_as_sequence */
138	0,		       			/* tp_as_mapping */
139	0,					/* tp_hash */
140	0,					/* tp_call */
141	0,					/* tp_str */
142	super_getattro,				/* tp_getattro */
143	0,					/* tp_setattro */
144	0,					/* tp_as_buffer */
145	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
146		Py_TPFLAGS_BASETYPE,		/* tp_flags */
147 	0,					/* tp_doc */
148 	0,					/* tp_traverse */
149 	0,					/* tp_clear */
150	0,					/* tp_richcompare */
151	0,					/* tp_weaklistoffset */
152	0,					/* tp_iter */
153	0,					/* tp_iternext */
154	0,					/* tp_methods */
155	0,					/* tp_members */
156	0,					/* tp_getset */
157	0,					/* tp_base */
158	0,					/* tp_dict */
159	0,					/* tp_descr_get */
160	0,					/* tp_descr_set */
161	0,					/* tp_dictoffset */
162	0,					/* tp_init */
163	PyType_GenericAlloc,			/* tp_alloc */
164	PyType_GenericNew,			/* tp_new */
165	PyObject_GC_Del,        		/* tp_free */
166	0,					/* tp_is_gc */
167	0,					/* tp_bases */
168	0,					/* tp_mro */
169	0,					/* tp_cache */
170	0,					/* tp_mro */
171	0,					/* tp_weaklist */
172	0					/* tp_del */
173#if PY_VERSION_HEX >= 0x02060000
174	, 0                                     /* tp_version_tag */
175#endif
176
177};
178