Issue #26110: Add document for LOAD_METHOD and CALL_METHOD opcode.
Changed stack layout bit for "easy to explain."
This commit is contained in:
parent
510df6f272
commit
015bce64b3
|
@ -957,6 +957,28 @@ All of the following opcodes use their arguments.
|
||||||
value.
|
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)
|
.. opcode:: MAKE_FUNCTION (argc)
|
||||||
|
|
||||||
Pushes a new function object on the stack. From bottom to top, the consumed
|
Pushes a new function object on the stack. From bottom to top, the consumed
|
||||||
|
|
|
@ -170,3 +170,10 @@ Changes in the Python API
|
||||||
Assigning to them was deprecated in Python 3.5.
|
Assigning to them was deprecated in Python 3.5.
|
||||||
Use the :meth:`~http.cookies.Morsel.set` method for setting them.
|
Use the :meth:`~http.cookies.Morsel.set` method for setting them.
|
||||||
(Contributed by Serhiy Storchaka in :issue:`29192`.)
|
(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`.)
|
||||||
|
|
|
@ -3236,81 +3236,73 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
|
|
||||||
int meth_found = _PyObject_GetMethod(obj, name, &meth);
|
int meth_found = _PyObject_GetMethod(obj, name, &meth);
|
||||||
|
|
||||||
SET_TOP(meth); /* Replace `obj` on top; OK if NULL. */
|
|
||||||
if (meth == NULL) {
|
if (meth == NULL) {
|
||||||
/* Most likely attribute wasn't found. */
|
/* Most likely attribute wasn't found. */
|
||||||
Py_DECREF(obj);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meth_found) {
|
if (meth_found) {
|
||||||
/* The method object is now on top of the stack.
|
/* We can bypass temporary bound method object.
|
||||||
Push `obj` back to the stack, so that the stack
|
meth is unbound method and obj is self.
|
||||||
layout would be:
|
|
||||||
|
|
||||||
method | obj | arg1 | ... | argN
|
meth | self | arg1 | ... | argN
|
||||||
*/
|
*/
|
||||||
PUSH(obj);
|
SET_TOP(meth);
|
||||||
|
PUSH(obj); // self
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Not a method (but a regular attr, or something
|
/* meth is not an unbound method (but a regular attr, or
|
||||||
was returned by a descriptor protocol). Push
|
something was returned by a descriptor protocol). Set
|
||||||
NULL to the top of the stack, to signal
|
the second element of the stack to NULL, to signal
|
||||||
CALL_METHOD that it's not a method call.
|
CALL_METHOD that it's not a method call.
|
||||||
|
|
||||||
|
NULL | meth | arg1 | ... | argN
|
||||||
*/
|
*/
|
||||||
|
SET_TOP(NULL);
|
||||||
Py_DECREF(obj);
|
Py_DECREF(obj);
|
||||||
PUSH(NULL);
|
PUSH(meth);
|
||||||
}
|
}
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
TARGET(CALL_METHOD) {
|
TARGET(CALL_METHOD) {
|
||||||
/* Designed to work in tamdem with LOAD_METHOD. */
|
/* Designed to work in tamdem with LOAD_METHOD. */
|
||||||
PyObject **sp, *res, *obj;
|
PyObject **sp, *res, *meth;
|
||||||
|
|
||||||
sp = stack_pointer;
|
sp = stack_pointer;
|
||||||
|
|
||||||
obj = PEEK(oparg + 1);
|
meth = PEEK(oparg + 2);
|
||||||
if (obj == NULL) {
|
if (meth == NULL) {
|
||||||
/* `obj` is NULL when LOAD_METHOD thinks that it's not
|
/* `meth` is NULL when LOAD_METHOD thinks that it's not
|
||||||
a method call. Swap the NULL and callable.
|
a method call.
|
||||||
|
|
||||||
Stack layout:
|
Stack layout:
|
||||||
|
|
||||||
... | callable | NULL | arg1 | ... | argN
|
... | NULL | callable | arg1 | ... | argN
|
||||||
^- TOP()
|
^- TOP()
|
||||||
^- (-oparg)
|
^- (-oparg)
|
||||||
^- (-oparg-1)
|
^- (-oparg-1)
|
||||||
^- (-oparg-2)
|
^- (-oparg-2)
|
||||||
|
|
||||||
after the next line it will be:
|
`callable` will be POPed by call_funtion.
|
||||||
|
NULL will will be POPed manually later.
|
||||||
... | 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);
|
res = call_function(&sp, oparg, NULL);
|
||||||
stack_pointer = sp;
|
stack_pointer = sp;
|
||||||
(void)POP(); /* POP the left side callable. */
|
(void)POP(); /* POP the NULL. */
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* This is a method call. Stack layout:
|
/* This is a method call. Stack layout:
|
||||||
|
|
||||||
... | method | obj | arg1 | ... | argN
|
... | method | self | arg1 | ... | argN
|
||||||
^- TOP()
|
^- TOP()
|
||||||
^- (-oparg)
|
^- (-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
|
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);
|
res = call_function(&sp, oparg + 1, NULL);
|
||||||
stack_pointer = sp;
|
stack_pointer = sp;
|
||||||
|
|
Loading…
Reference in New Issue