summaryrefslogtreecommitdiffstats
path: root/Python/ceval.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 /Python/ceval.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 'Python/ceval.c')
-rw-r--r--Python/ceval.c97
1 files changed, 97 insertions, 0 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index c6c6e05..0f1f36e 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -30,6 +30,9 @@
#define CHECKEXC 1 /* Double-check exception checking */
#endif
+/* Private API for the LOAD_METHOD opcode. */
+extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **);
+
typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *);
/* Forward declarations */
@@ -3225,6 +3228,100 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
DISPATCH();
}
+ TARGET(LOAD_METHOD) {
+ /* Designed to work in tamdem with CALL_METHOD. */
+ PyObject *name = GETITEM(names, oparg);
+ PyObject *obj = TOP();
+ PyObject *meth = NULL;
+
+ int meth_found = _PyObject_GetMethod(obj, name, &meth);
+
+ SET_TOP(meth); /* Replace `obj` on top; OK if NULL. */
+ if (meth == NULL) {
+ /* Most likely attribute wasn't found. */
+ Py_DECREF(obj);
+ goto error;
+ }
+
+ if (meth_found) {
+ /* The method object is now on top of the stack.
+ Push `obj` back to the stack, so that the stack
+ layout would be:
+
+ method | obj | arg1 | ... | argN
+ */
+ PUSH(obj);
+ }
+ else {
+ /* Not a method (but a regular attr, or something
+ was returned by a descriptor protocol). Push
+ NULL to the top of the stack, to signal
+ CALL_METHOD that it's not a method call.
+ */
+ Py_DECREF(obj);
+ PUSH(NULL);
+ }
+ DISPATCH();
+ }
+
+ TARGET(CALL_METHOD) {
+ /* Designed to work in tamdem with LOAD_METHOD. */
+ PyObject **sp, *res, *obj;
+
+ sp = stack_pointer;
+
+ obj = PEEK(oparg + 1);
+ if (obj == NULL) {
+ /* `obj` is NULL when LOAD_METHOD thinks that it's not
+ a method call. Swap the NULL and callable.
+
+ Stack layout:
+
+ ... | callable | NULL | arg1 | ... | argN
+ ^- TOP()
+ ^- (-oparg)
+ ^- (-oparg-1)
+ ^- (-oparg-2)
+
+ after the next line it will be:
+
+ ... | callable | callable | arg1 | ... | argN
+ ^- TOP()
+ ^- (-oparg)
+ ^- (-oparg-1)
+ ^- (-oparg-2)
+
+ Right side `callable` will be POPed by call_funtion.
+ Left side `callable` will be POPed manually later
+ (one of "callbale" refs on the stack is borrowed.)
+ */
+ SET_VALUE(oparg + 1, PEEK(oparg + 2));
+ res = call_function(&sp, oparg, NULL);
+ stack_pointer = sp;
+ (void)POP(); /* POP the left side callable. */
+ }
+ else {
+ /* This is a method call. Stack layout:
+
+ ... | method | obj | arg1 | ... | argN
+ ^- TOP()
+ ^- (-oparg)
+ ^- (-oparg-1)
+
+ `obj` and `method` will be POPed by call_function.
+ We'll be passing `oparg + 1` to call_function, to
+ make it accept the `obj` as a first argument.
+ */
+ res = call_function(&sp, oparg + 1, NULL);
+ stack_pointer = sp;
+ }
+
+ PUSH(res);
+ if (res == NULL)
+ goto error;
+ DISPATCH();
+ }
+
PREDICTED(CALL_FUNCTION);
TARGET(CALL_FUNCTION) {
PyObject **sp, *res;