From 015bce64b35fb9de199f41eb5a3e5b7d9d20e3c1 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Mon, 16 Jan 2017 17:23:30 +0900 Subject: [PATCH] Issue #26110: Add document for LOAD_METHOD and CALL_METHOD opcode. Changed stack layout bit for "easy to explain." --- Doc/library/dis.rst | 22 ++++++++++++++ Doc/whatsnew/3.7.rst | 7 +++++ Python/ceval.c | 72 ++++++++++++++++++++------------------------ 3 files changed, 61 insertions(+), 40 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index a15690ba489..694d5642fb0 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -957,6 +957,28 @@ All of the following opcodes use their arguments. value. +.. opcode:: LOAD_METHOD (namei) + + Loads a method named ``co_names[namei]`` from TOS object. TOS is popped and + method and TOS are pushed when interpreter can call unbound method directly. + TOS will be uesd as the first argument (``self``) by :opcode:`CALL_METHOD`. + Otherwise, ``NULL`` and method is pushed (method is bound method or + something else). + + .. versionadded:: 3.7 + + +.. opcode:: CALL_METHOD (argc) + + Calls a method. *argc* is number of positional arguments. + Keyword arguments are not supported. This opcode is designed to be used + with :opcode:`LOAD_METHOD`. Positional arguments are on top of the stack. + Below them, two items described in :opcode:`LOAD_METHOD` on the stack. + All of them are popped and return value is pushed. + + .. versionadded:: 3.7 + + .. opcode:: MAKE_FUNCTION (argc) Pushes a new function object on the stack. From bottom to top, the consumed diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index ca7c2c5381b..fe03defa65f 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -170,3 +170,10 @@ Changes in the Python API Assigning to them was deprecated in Python 3.5. Use the :meth:`~http.cookies.Morsel.set` method for setting them. (Contributed by Serhiy Storchaka in :issue:`29192`.) + + +CPython bytecode changes +------------------------ + +* Added two new opcodes: :opcode:`LOAD_METHOD`` and :opcode:`CALL_METHOD`. + (Contributed by Yury Selivanov and INADA Naoki in :issue:`26110`.) diff --git a/Python/ceval.c b/Python/ceval.c index b970ece4e92..1b8cdfc663a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3236,81 +3236,73 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) 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); + /* We can bypass temporary bound method object. + meth is unbound method and obj is self. + + meth | self | arg1 | ... | argN + */ + SET_TOP(meth); + PUSH(obj); // self } 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 + /* meth is not an unbound method (but a regular attr, or + something was returned by a descriptor protocol). Set + the second element of the stack to NULL, to signal CALL_METHOD that it's not a method call. + + NULL | meth | arg1 | ... | argN */ + SET_TOP(NULL); Py_DECREF(obj); - PUSH(NULL); + PUSH(meth); } DISPATCH(); } TARGET(CALL_METHOD) { /* Designed to work in tamdem with LOAD_METHOD. */ - PyObject **sp, *res, *obj; + PyObject **sp, *res, *meth; 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. + meth = PEEK(oparg + 2); + if (meth == NULL) { + /* `meth` is NULL when LOAD_METHOD thinks that it's not + a method call. Stack layout: - ... | callable | NULL | arg1 | ... | argN - ^- TOP() - ^- (-oparg) - ^- (-oparg-1) - ^- (-oparg-2) + ... | NULL | callable | 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.) + `callable` will be POPed by call_funtion. + NULL will will be POPed manually later. */ - SET_VALUE(oparg + 1, PEEK(oparg + 2)); res = call_function(&sp, oparg, NULL); stack_pointer = sp; - (void)POP(); /* POP the left side callable. */ + (void)POP(); /* POP the NULL. */ } else { /* This is a method call. Stack layout: - ... | method | obj | arg1 | ... | argN + ... | method | self | arg1 | ... | argN ^- TOP() ^- (-oparg) - ^- (-oparg-1) + ^- (-oparg-1) + ^- (-oparg-2) - `obj` and `method` will be POPed by call_function. + `self` 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. + make it accept the `self` as a first argument. */ res = call_function(&sp, oparg + 1, NULL); stack_pointer = sp;