mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
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).
This commit is contained in:
parent
e6bb53bf61
commit
f2392133eb
13 changed files with 747 additions and 483 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue