From 366f4ee4da4420842eac26b3fa00c2b01d4515a6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 1 Dec 2016 14:43:22 +0100 Subject: Replace PyObject_CallFunctionObjArgs() with fastcall * PyObject_CallFunctionObjArgs(func, NULL) => _PyObject_CallNoArg(func) * PyObject_CallFunctionObjArgs(func, arg, NULL) => _PyObject_CallArg1(func, arg) PyObject_CallFunctionObjArgs() allocates 40 bytes on the C stack and requires extra work to "parse" C arguments to build a C array of PyObject*. _PyObject_CallNoArg() and _PyObject_CallArg1() are simpler and don't allocate memory on the C stack. This change is part of the fastcall project. The change on listsort() is related to the issue #23507. --- Objects/object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Objects/object.c') diff --git a/Objects/object.c b/Objects/object.c index d88ae3b94f..4844bd7669 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -596,7 +596,7 @@ PyObject_Bytes(PyObject *v) func = _PyObject_LookupSpecial(v, &PyId___bytes__); if (func != NULL) { - result = PyObject_CallFunctionObjArgs(func, NULL); + result = _PyObject_CallNoArg(func); Py_DECREF(func); if (result == NULL) return NULL; @@ -1314,7 +1314,7 @@ _dir_object(PyObject *obj) return NULL; } /* use __dir__ */ - result = PyObject_CallFunctionObjArgs(dirfunc, NULL); + result = _PyObject_CallNoArg(dirfunc); Py_DECREF(dirfunc); if (result == NULL) return NULL; -- cgit v1.2.1 From 39ae5bedd90f9caf9a78efa82f4d11e838933b3a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 4 Dec 2016 22:59:09 +0100 Subject: Backed out changeset b9c9691c72c5 Issue #28858: The change b9c9691c72c5 introduced a regression. It seems like _PyObject_CallArg1() uses more stack memory than PyObject_CallFunctionObjArgs(). --- Objects/object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Objects/object.c') diff --git a/Objects/object.c b/Objects/object.c index 4844bd7669..d88ae3b94f 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -596,7 +596,7 @@ PyObject_Bytes(PyObject *v) func = _PyObject_LookupSpecial(v, &PyId___bytes__); if (func != NULL) { - result = _PyObject_CallNoArg(func); + result = PyObject_CallFunctionObjArgs(func, NULL); Py_DECREF(func); if (result == NULL) return NULL; @@ -1314,7 +1314,7 @@ _dir_object(PyObject *obj) return NULL; } /* use __dir__ */ - result = _PyObject_CallNoArg(dirfunc); + result = PyObject_CallFunctionObjArgs(dirfunc, NULL); Py_DECREF(dirfunc); if (result == NULL) return NULL; -- cgit v1.2.1 From a95a307d2cbb7fc1c778691f142e0c21c05fd5bc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Dec 2016 18:46:19 +0100 Subject: Use _PyObject_CallNoArg() Replace: PyObject_CallFunctionObjArgs(callable, NULL) with: _PyObject_CallNoArg(callable) --- Objects/object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Objects/object.c') diff --git a/Objects/object.c b/Objects/object.c index d88ae3b94f..4844bd7669 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -596,7 +596,7 @@ PyObject_Bytes(PyObject *v) func = _PyObject_LookupSpecial(v, &PyId___bytes__); if (func != NULL) { - result = PyObject_CallFunctionObjArgs(func, NULL); + result = _PyObject_CallNoArg(func); Py_DECREF(func); if (result == NULL) return NULL; @@ -1314,7 +1314,7 @@ _dir_object(PyObject *obj) return NULL; } /* use __dir__ */ - result = PyObject_CallFunctionObjArgs(dirfunc, NULL); + result = _PyObject_CallNoArg(dirfunc); Py_DECREF(dirfunc); if (result == NULL) return NULL; -- cgit v1.2.1 From 4f5d39b98cc760dcb7641bb71bdaeb2933b1ee40 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 13 Dec 2016 19:03:51 -0500 Subject: 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). --- Objects/object.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) (limited to 'Objects/object.c') 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; -- cgit v1.2.1 From f22b56b777f7134b5c4e6c9caa9249e92c4f4fd9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 16 Dec 2016 16:18:57 +0200 Subject: Issue #28959: Added private macro PyDict_GET_SIZE for retrieving the size of dict. --- Objects/object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Objects/object.c') diff --git a/Objects/object.c b/Objects/object.c index 9a7c7f7896..dc50131e3f 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1454,7 +1454,7 @@ none_dealloc(PyObject* ignore) static PyObject * none_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { - if (PyTuple_GET_SIZE(args) || (kwargs && PyDict_Size(kwargs))) { + if (PyTuple_GET_SIZE(args) || (kwargs && PyDict_GET_SIZE(kwargs))) { PyErr_SetString(PyExc_TypeError, "NoneType takes no arguments"); return NULL; } @@ -1573,7 +1573,7 @@ static PyMethodDef notimplemented_methods[] = { static PyObject * notimplemented_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { - if (PyTuple_GET_SIZE(args) || (kwargs && PyDict_Size(kwargs))) { + if (PyTuple_GET_SIZE(args) || (kwargs && PyDict_GET_SIZE(kwargs))) { PyErr_SetString(PyExc_TypeError, "NotImplementedType takes no arguments"); return NULL; } -- cgit v1.2.1 From 4a6bc8201521488a56ced3d3825e21915f681fae Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 18 Jan 2017 14:12:51 +0100 Subject: Rephrase !PyErr_Occurred() comment: may=>can Issue #29259. --- Objects/object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Objects/object.c') diff --git a/Objects/object.c b/Objects/object.c index dc50131e3f..7b80bcb91d 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -477,7 +477,7 @@ PyObject_Repr(PyObject *v) #ifdef Py_DEBUG /* PyObject_Repr() must not be called with an exception set, - because it may clear it (directly or indirectly) and so the + because it can clear it (directly or indirectly) and so the caller loses its exception */ assert(!PyErr_Occurred()); #endif @@ -526,7 +526,7 @@ PyObject_Str(PyObject *v) #ifdef Py_DEBUG /* PyObject_Str() must not be called with an exception set, - because it may clear it (directly or indirectly) and so the + because it can clear it (directly or indirectly) and so the caller loses its exception */ assert(!PyErr_Occurred()); #endif -- cgit v1.2.1 From 72ded8136b92c1b107f629021cf231fd3163f041 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 22 Jan 2017 23:07:07 +0200 Subject: Issue #28769: The result of PyUnicode_AsUTF8AndSize() and PyUnicode_AsUTF8() is now of type "const char *" rather of "char *". --- Objects/object.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Objects/object.c') diff --git a/Objects/object.c b/Objects/object.c index 7b80bcb91d..93cdc1014f 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -890,10 +890,10 @@ PyObject_GetAttr(PyObject *v, PyObject *name) if (tp->tp_getattro != NULL) return (*tp->tp_getattro)(v, name); if (tp->tp_getattr != NULL) { - char *name_str = PyUnicode_AsUTF8(name); + const char *name_str = PyUnicode_AsUTF8(name); if (name_str == NULL) return NULL; - return (*tp->tp_getattr)(v, name_str); + return (*tp->tp_getattr)(v, (char *)name_str); } PyErr_Format(PyExc_AttributeError, "'%.50s' object has no attribute '%U'", @@ -934,10 +934,10 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value) return err; } if (tp->tp_setattr != NULL) { - char *name_str = PyUnicode_AsUTF8(name); + const char *name_str = PyUnicode_AsUTF8(name); if (name_str == NULL) return -1; - err = (*tp->tp_setattr)(v, name_str, value); + err = (*tp->tp_setattr)(v, (char *)name_str, value); Py_DECREF(name); return err; } -- cgit v1.2.1 From 99c092a531abf57a8f5c37356dc6971cdfed497a Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 3 Feb 2017 07:43:03 +0900 Subject: Issue #29263: LOAD_METHOD support for C methods Calling builtin method is at most 10% faster. --- Objects/object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Objects/object.c') diff --git a/Objects/object.c b/Objects/object.c index 93cdc1014f..5da6cffdb9 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1060,8 +1060,8 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) descr = _PyType_Lookup(tp, name); if (descr != NULL) { Py_INCREF(descr); - if (PyFunction_Check(descr)) { - /* A python method. */ + if (PyFunction_Check(descr) || + (Py_TYPE(descr) == &PyMethodDescr_Type)) { meth_found = 1; } else { f = descr->ob_type->tp_descr_get; -- cgit v1.2.1