summaryrefslogtreecommitdiff
path: root/Objects/object.c
diff options
context:
space:
mode:
authorYury Selivanov <yury@magic.io>2016-12-13 19:03:51 -0500
committerYury Selivanov <yury@magic.io>2016-12-13 19:03:51 -0500
commitf2392133eba777f05947a8996c507690b95379c3 (patch)
tree3d3e352b04691dabeab1eb8502c2417d2af04826 /Objects/object.c
parente6bb53bf61ac24feca775bdaa651433b8466d2fa (diff)
downloadcpython-git-f2392133eba777f05947a8996c507690b95379c3.tar.gz
Issue #26110: Add LOAD_METHOD/CALL_METHOD opcodes.
Special thanks to INADA Naoki for pushing the patch through the last mile, Serhiy Storchaka for reviewing the code, and to Victor Stinner for suggesting the idea (originally implemented in the PyPy project).
Diffstat (limited to 'Objects/object.c')
-rw-r--r--Objects/object.c90
1 files changed, 89 insertions, 1 deletions
diff --git a/Objects/object.c b/Objects/object.c
index 4844bd7669..9a7c7f7896 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1025,11 +1025,99 @@ _PyObject_NextNotImplemented(PyObject *self)
return NULL;
}
-/* Generic GetAttr functions - put these in your tp_[gs]etattro slot */
+
+/* Specialized version of _PyObject_GenericGetAttrWithDict
+ specifically for the LOAD_METHOD opcode.
+
+ Return 1 if a method is found, 0 if it's a regular attribute
+ from __dict__ or something returned by using a descriptor
+ protocol.
+
+ `method` will point to the resolved attribute or NULL. In the
+ latter case, an error will be set.
+*/
+int
+_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
+{
+ PyTypeObject *tp = Py_TYPE(obj);
+ PyObject *descr;
+ descrgetfunc f = NULL;
+ PyObject **dictptr, *dict;
+ PyObject *attr;
+ int meth_found = 0;
+
+ assert(*method == NULL);
+
+ if (Py_TYPE(obj)->tp_getattro != PyObject_GenericGetAttr
+ || !PyUnicode_Check(name)) {
+ *method = PyObject_GetAttr(obj, name);
+ return 0;
+ }
+
+ if (tp->tp_dict == NULL && PyType_Ready(tp) < 0)
+ return 0;
+
+ descr = _PyType_Lookup(tp, name);
+ if (descr != NULL) {
+ Py_INCREF(descr);
+ if (PyFunction_Check(descr)) {
+ /* A python method. */
+ meth_found = 1;
+ } else {
+ f = descr->ob_type->tp_descr_get;
+ if (f != NULL && PyDescr_IsData(descr)) {
+ *method = f(descr, obj, (PyObject *)obj->ob_type);
+ Py_DECREF(descr);
+ return 0;
+ }
+ }
+ }
+
+ dictptr = _PyObject_GetDictPtr(obj);
+ if (dictptr != NULL && (dict = *dictptr) != NULL) {
+ Py_INCREF(dict);
+ attr = PyDict_GetItem(dict, name);
+ if (attr != NULL) {
+ Py_INCREF(attr);
+ *method = attr;
+ Py_DECREF(dict);
+ Py_XDECREF(descr);
+ return 0;
+ }
+ Py_DECREF(dict);
+ }
+
+ if (meth_found) {
+ *method = descr;
+ return 1;
+ }
+
+ if (f != NULL) {
+ *method = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ Py_DECREF(descr);
+ return 0;
+ }
+
+ if (descr != NULL) {
+ *method = descr;
+ return 0;
+ }
+
+ PyErr_Format(PyExc_AttributeError,
+ "'%.50s' object has no attribute '%U'",
+ tp->tp_name, name);
+ return 0;
+}
+
+/* Generic GetAttr functions - put these in your tp_[gs]etattro slot. */
PyObject *
_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
{
+ /* Make sure the logic of _PyObject_GetMethod is in sync with
+ this method.
+ */
+
PyTypeObject *tp = Py_TYPE(obj);
PyObject *descr = NULL;
PyObject *res = NULL;