summaryrefslogtreecommitdiffstats
path: root/Objects/object.c
diff options
context:
space:
mode:
authorYury Selivanov <yury@magic.io>2016-12-14 00:03:51 (GMT)
committerYury Selivanov <yury@magic.io>2016-12-14 00:03:51 (GMT)
commitf2392133eba777f05947a8996c507690b95379c3 (patch)
tree3d3e352b04691dabeab1eb8502c2417d2af04826 /Objects/object.c
parente6bb53bf61ac24feca775bdaa651433b8466d2fa (diff)
downloadcpython-f2392133eba777f05947a8996c507690b95379c3.zip
cpython-f2392133eba777f05947a8996c507690b95379c3.tar.gz
cpython-f2392133eba777f05947a8996c507690b95379c3.tar.bz2
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 4844bd7..9a7c7f7 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;