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 */
18typedef struct {
19        PyObject_HEAD
20	PyTypeObject *type;
21	PyObject *obj;
22	PyTypeObject *obj_type;
23} superobject;
24
25static PyObject *
26super_getattro(PyObject *self, PyObject *name)
27{
28	superobject *su = (superobject *)self;
29	int skip = su->obj_type == NULL;
30
31
32
33	if (!skip) {
34		/* We want __class__ to return the class of the super object
35		   (i.e. super, or a subclass), not the class of su->obj. */
36		skip = (PyString_Check(name) &&
37			PyString_GET_SIZE(name) == 9 &&
38			strcmp(PyString_AS_STRING(name), "__class__") == 0);
39	}
40
41	if (!skip) {
42		PyObject *mro, *res, *tmp, *dict;
43		PyTypeObject *starttype;
44		descrgetfunc f;
45		Py_ssize_t i, n;
46
47		starttype = su->obj_type;
48		mro = starttype->tp_mro;
49
50		if (mro == NULL)
51			n = 0;
52		else {
53			assert(PyTuple_Check(mro));
54			n = PyTuple_GET_SIZE(mro);
55		}
56		for (i = 0; i < n; i++) {
57			if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
58				break;
59		}
60		i++;
61		res = NULL;
62		for (; i < n; i++) {
63			tmp = PyTuple_GET_ITEM(mro, i);
64
65			/* PyObjC PATCH: Treat PyObjC class objects specially to maintain
66			 * the proper illusion for users.
67			 * Also make sure that the method tables are up-to-date.
68			 */
69			if (PyObjCClass_Check(tmp)) {
70				PyObjCClass_CheckMethodList(tmp, NO);
71			}
72
73			if (PyObjCClass_Check(tmp) && PyObjCClass_Check(su->obj))  {
74				dict = tmp->ob_type->tp_dict;
75
76			} else if (PyType_Check(tmp))
77				dict = ((PyTypeObject *)tmp)->tp_dict;
78			else if (PyClass_Check(tmp))
79				dict = ((PyClassObject *)tmp)->cl_dict;
80			else
81				continue;
82
83			res = PyDict_GetItem(dict, name);
84			if (res != NULL) {
85				Py_INCREF(res);
86				f = res->ob_type->tp_descr_get;
87				if (f != NULL) {
88					tmp = f(res,
89						/* Only pass 'obj' param if
90						   this is instance-mode super
91						   (See SF ID #743627)
92						*/
93						(su->obj == (PyObject *)
94							    su->obj_type
95							? (PyObject *)NULL
96							: su->obj),
97						(PyObject *)starttype);
98					Py_DECREF(res);
99					res = tmp;
100				}
101				return res;
102			}
103		}
104	}
105	return PyObject_GenericGetAttr(self, name);
106}
107
108PyTypeObject PyObjCSuper_Type = {
109	PyObject_HEAD_INIT(&PyType_Type)
110	0,
111	"objc.super",
112	sizeof(superobject),
113	0,
114	/* methods */
115	0,		 			/* tp_dealloc */
116	0,					/* tp_print */
117	0,					/* tp_getattr */
118	0,					/* tp_setattr */
119	0,					/* tp_compare */
120	0,					/* tp_repr */
121	0,					/* tp_as_number */
122	0,					/* tp_as_sequence */
123	0,		       			/* tp_as_mapping */
124	0,					/* tp_hash */
125	0,					/* tp_call */
126	0,					/* tp_str */
127	super_getattro,				/* tp_getattro */
128	0,					/* tp_setattro */
129	0,					/* tp_as_buffer */
130	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
131		Py_TPFLAGS_BASETYPE,		/* tp_flags */
132 	0,					/* tp_doc */
133 	0,					/* tp_traverse */
134 	0,					/* tp_clear */
135	0,					/* tp_richcompare */
136	0,					/* tp_weaklistoffset */
137	0,					/* tp_iter */
138	0,					/* tp_iternext */
139	0,					/* tp_methods */
140	0,					/* tp_members */
141	0,					/* tp_getset */
142	0,					/* tp_base */
143	0,					/* tp_dict */
144	0,					/* tp_descr_get */
145	0,					/* tp_descr_set */
146	0,					/* tp_dictoffset */
147	0,					/* tp_init */
148	PyType_GenericAlloc,			/* tp_alloc */
149	PyType_GenericNew,			/* tp_new */
150	PyObject_GC_Del,        		/* tp_free */
151	0,					/* tp_is_gc */
152	0,					/* tp_bases */
153	0,					/* tp_mro */
154	0,					/* tp_cache */
155	0,					/* tp_mro */
156	0,					/* tp_weaklist */
157	0					/* tp_del */
158#if PY_VERSION_HEX >= 0x02060000
159	, 0                                     /* tp_version_tag */
160#endif
161
162};
163