mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 22:02:27 +00:00
Backport METH_FASTCALL from Python 3.7 (#328)
This commit is contained in:
parent
70c97f598b
commit
cf73bbd678
102 changed files with 2896 additions and 3301 deletions
354
third_party/python/Python/ceval.c
vendored
354
third_party/python/Python/ceval.c
vendored
|
@ -13,6 +13,7 @@
|
|||
#include "third_party/python/Include/ceval.h"
|
||||
#include "third_party/python/Include/classobject.h"
|
||||
#include "third_party/python/Include/code.h"
|
||||
#include "third_party/python/Include/descrobject.h"
|
||||
#include "third_party/python/Include/dictobject.h"
|
||||
#include "third_party/python/Include/eval.h"
|
||||
#include "third_party/python/Include/frameobject.h"
|
||||
|
@ -21,6 +22,7 @@
|
|||
#include "third_party/python/Include/import.h"
|
||||
#include "third_party/python/Include/longobject.h"
|
||||
#include "third_party/python/Include/object.h"
|
||||
#include "third_party/python/Include/objimpl.h"
|
||||
#include "third_party/python/Include/opcode.h"
|
||||
#include "third_party/python/Include/pydtrace.h"
|
||||
#include "third_party/python/Include/pyerrors.h"
|
||||
|
@ -50,6 +52,7 @@
|
|||
#define CHECKEXC 1 /* Double-check exception checking */
|
||||
#endif
|
||||
|
||||
extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **);
|
||||
typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *);
|
||||
|
||||
#ifdef LLTRACE
|
||||
|
@ -60,7 +63,6 @@ static int prtrace(PyObject *, const char *);
|
|||
static PyObject *call_function(PyObject ***, Py_ssize_t, PyObject *);
|
||||
static PyObject *cmp_outcome(int, PyObject *, PyObject *);
|
||||
static PyObject *do_call_core(PyObject *, PyObject *, PyObject *);
|
||||
static PyObject *fast_function(PyObject *, PyObject **, Py_ssize_t, PyObject *);
|
||||
static PyObject *import_from(PyObject *, PyObject *);
|
||||
static PyObject *import_name(PyFrameObject *, PyObject *, PyObject *, PyObject *);
|
||||
static PyObject *special_lookup(PyObject *, _Py_Identifier *);
|
||||
|
@ -730,7 +732,7 @@ PyObject *
|
|||
return tstate->interp->eval_frame(f, throwflag);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyObject * _Py_HOT_FUNCTION
|
||||
_PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||
{
|
||||
#ifdef DXPAIRS
|
||||
|
@ -3312,6 +3314,92 @@ _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);
|
||||
|
||||
if (meth == NULL) {
|
||||
/* Most likely attribute wasn't found. */
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (meth_found) {
|
||||
/* 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 {
|
||||
/* 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(meth);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(CALL_METHOD) {
|
||||
/* Designed to work in tamdem with LOAD_METHOD. */
|
||||
PyObject **sp, *res, *meth;
|
||||
|
||||
sp = stack_pointer;
|
||||
|
||||
meth = PEEK(oparg + 2);
|
||||
if (meth == NULL) {
|
||||
/* `meth` is NULL when LOAD_METHOD thinks that it's not
|
||||
a method call.
|
||||
|
||||
Stack layout:
|
||||
|
||||
... | NULL | callable | arg1 | ... | argN
|
||||
^- TOP()
|
||||
^- (-oparg)
|
||||
^- (-oparg-1)
|
||||
^- (-oparg-2)
|
||||
|
||||
`callable` will be POPed by call_function.
|
||||
NULL will will be POPed manually later.
|
||||
*/
|
||||
res = call_function(&sp, oparg, NULL);
|
||||
stack_pointer = sp;
|
||||
(void)POP(); /* POP the NULL. */
|
||||
}
|
||||
else {
|
||||
/* This is a method call. Stack layout:
|
||||
|
||||
... | method | self | arg1 | ... | argN
|
||||
^- TOP()
|
||||
^- (-oparg)
|
||||
^- (-oparg-1)
|
||||
^- (-oparg-2)
|
||||
|
||||
`self` and `method` will be POPed by call_function.
|
||||
We'll be passing `oparg + 1` to call_function, to
|
||||
make it accept the `self` 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;
|
||||
|
@ -3876,7 +3964,7 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount,
|
|||
/* This is gonna seem *real weird*, but if you put some other code between
|
||||
PyEval_EvalFrame() and _PyEval_EvalFrameDefault() you will need to adjust
|
||||
the test in the if statements in Misc/gdbinit (pystack and pystackv). */
|
||||
static PyObject *
|
||||
PyObject *
|
||||
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
||||
PyObject **args, Py_ssize_t argcount,
|
||||
PyObject **kwnames, PyObject **kwargs,
|
||||
|
@ -3902,7 +3990,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
|||
/* Create the frame */
|
||||
tstate = PyThreadState_GET();
|
||||
assert(tstate != NULL);
|
||||
f = PyFrame_New(tstate, co, globals, locals);
|
||||
f = _PyFrame_New_NoTrack(tstate, co, globals, locals);
|
||||
if (f == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -4124,9 +4212,15 @@ fail: /* Jump here from prelude on failure */
|
|||
so recursion_depth must be boosted for the duration.
|
||||
*/
|
||||
assert(tstate != NULL);
|
||||
++tstate->recursion_depth;
|
||||
Py_DECREF(f);
|
||||
--tstate->recursion_depth;
|
||||
if (Py_REFCNT(f) > 1) {
|
||||
Py_DECREF(f);
|
||||
_PyObject_GC_TRACK(f);
|
||||
}
|
||||
else {
|
||||
++tstate->recursion_depth;
|
||||
Py_DECREF(f);
|
||||
--tstate->recursion_depth;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -4662,35 +4756,6 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf)
|
|||
}
|
||||
|
||||
|
||||
/* External interface to call any callable object.
|
||||
The arg must be a tuple or NULL. The kw must be a dict or NULL. */
|
||||
PyObject *
|
||||
PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
#ifdef Py_DEBUG
|
||||
/* PyEval_CallObjectWithKeywords() must not be called with an exception
|
||||
set. It raises a new exception if parameters are invalid or if
|
||||
PyTuple_New() fails, and so the original exception is lost. */
|
||||
assert(!PyErr_Occurred());
|
||||
#endif
|
||||
if (args != NULL && !PyTuple_Check(args)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"argument list must be a tuple");
|
||||
return NULL;
|
||||
}
|
||||
if (kwargs != NULL && !PyDict_Check(kwargs)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"keyword list must be a dictionary");
|
||||
return NULL;
|
||||
}
|
||||
if (args == NULL) {
|
||||
return _PyObject_FastCallDict(func, NULL, 0, kwargs);
|
||||
}
|
||||
else {
|
||||
return PyObject_Call(func, args, kwargs);
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
PyEval_GetFuncName(PyObject *func)
|
||||
{
|
||||
|
@ -4748,7 +4813,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
|
|||
x = call; \
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
forceinline PyObject * _Py_HOT_FUNCTION
|
||||
call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
|
||||
{
|
||||
PyObject **pfunc = (*pp_stack) - oparg - 1;
|
||||
|
@ -4756,16 +4821,41 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
|
|||
PyObject *x, *w;
|
||||
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
||||
Py_ssize_t nargs = oparg - nkwargs;
|
||||
PyObject **stack;
|
||||
PyObject **stack = (*pp_stack) - nargs - nkwargs;
|
||||
/* Always dispatch PyCFunction first, because these are
|
||||
presumed to be the most frequent callable object.
|
||||
*/
|
||||
if (PyCFunction_Check(func)) {
|
||||
PyThreadState *tstate = PyThreadState_GET();
|
||||
PCALL(PCALL_CFUNCTION);
|
||||
stack = (*pp_stack) - nargs - nkwargs;
|
||||
C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames));
|
||||
}
|
||||
else if (Py_TYPE(func) == &PyMethodDescr_Type) {
|
||||
PyThreadState *tstate = PyThreadState_GET();
|
||||
if (nargs > 0 && tstate->use_tracing) {
|
||||
/* We need to create a temporary bound method as argument
|
||||
for profiling.
|
||||
|
||||
If nargs == 0, then this cannot work because we have no
|
||||
"self". In any case, the call itself would raise
|
||||
TypeError (foo needs an argument), so we just skip
|
||||
profiling. */
|
||||
PyObject *self = stack[0];
|
||||
func = Py_TYPE(func)->tp_descr_get(func, self, (PyObject*)Py_TYPE(self));
|
||||
if (func != NULL) {
|
||||
C_TRACE(x, _PyCFunction_FastCallKeywords(func,
|
||||
stack+1, nargs-1,
|
||||
kwnames));
|
||||
Py_DECREF(func);
|
||||
}
|
||||
else {
|
||||
x = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
x = _PyMethodDescr_FastCallKeywords(func, stack, nargs, kwnames);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
|
||||
/* optimize access to bound methods */
|
||||
|
@ -4777,13 +4867,13 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
|
|||
Py_INCREF(func);
|
||||
Py_SETREF(*pfunc, self);
|
||||
nargs++;
|
||||
stack--;
|
||||
}
|
||||
else {
|
||||
Py_INCREF(func);
|
||||
}
|
||||
stack = (*pp_stack) - nargs - nkwargs;
|
||||
if (PyFunction_Check(func)) {
|
||||
x = fast_function(func, stack, nargs, kwnames);
|
||||
x = _PyFunction_FastCallKeywords(func, stack, nargs, kwnames);
|
||||
}
|
||||
else {
|
||||
x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
|
||||
|
@ -4791,10 +4881,7 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
|
|||
Py_DECREF(func);
|
||||
}
|
||||
assert((x != NULL) ^ (PyErr_Occurred() != NULL));
|
||||
/* Clear the stack of the function object. Also removes
|
||||
the arguments in case they weren't consumed already
|
||||
(fast_function() and err_args() leave them on the stack).
|
||||
*/
|
||||
/* Clear the stack of the function object. */
|
||||
while ((*pp_stack) > pfunc) {
|
||||
w = EXT_POP(*pp_stack);
|
||||
Py_DECREF(w);
|
||||
|
@ -4803,183 +4890,6 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
|
|||
return x;
|
||||
}
|
||||
|
||||
/* The fast_function() function optimize calls for which no argument
|
||||
tuple is necessary; the objects are passed directly from the stack.
|
||||
For the simplest case -- a function that takes only positional
|
||||
arguments and is called with only positional arguments -- it
|
||||
inlines the most primitive frame setup code from
|
||||
PyEval_EvalCodeEx(), which vastly reduces the checks that must be
|
||||
done before evaluating the frame.
|
||||
*/
|
||||
static PyObject*
|
||||
_PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs,
|
||||
PyObject *globals)
|
||||
{
|
||||
PyFrameObject *f;
|
||||
PyThreadState *tstate = PyThreadState_GET();
|
||||
PyObject **fastlocals;
|
||||
Py_ssize_t i;
|
||||
PyObject *result;
|
||||
PCALL(PCALL_FASTER_FUNCTION);
|
||||
assert(globals != NULL);
|
||||
/* XXX Perhaps we should create a specialized
|
||||
PyFrame_New() that doesn't take locals, but does
|
||||
take builtins without sanity checking them.
|
||||
*/
|
||||
assert(tstate != NULL);
|
||||
f = PyFrame_New(tstate, co, globals, NULL);
|
||||
if (f == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
fastlocals = f->f_localsplus;
|
||||
for (i = 0; i < nargs; i++) {
|
||||
Py_INCREF(*args);
|
||||
fastlocals[i] = *args++;
|
||||
}
|
||||
result = PyEval_EvalFrameEx(f,0);
|
||||
++tstate->recursion_depth;
|
||||
Py_DECREF(f);
|
||||
--tstate->recursion_depth;
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
fast_function(PyObject *func, PyObject **stack,
|
||||
Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
|
||||
PyObject *globals = PyFunction_GET_GLOBALS(func);
|
||||
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
|
||||
PyObject *kwdefs, *closure, *name, *qualname;
|
||||
PyObject **d;
|
||||
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
||||
Py_ssize_t nd;
|
||||
assert(PyFunction_Check(func));
|
||||
assert(nargs >= 0);
|
||||
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
||||
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
||||
/* kwnames must only contains str strings, no subclass, and all keys must
|
||||
be unique */
|
||||
PCALL(PCALL_FUNCTION);
|
||||
PCALL(PCALL_FAST_FUNCTION);
|
||||
if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
|
||||
co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
|
||||
{
|
||||
if (argdefs == NULL && co->co_argcount == nargs) {
|
||||
return _PyFunction_FastCall(co, stack, nargs, globals);
|
||||
}
|
||||
else if (nargs == 0 && argdefs != NULL
|
||||
&& co->co_argcount == Py_SIZE(argdefs)) {
|
||||
/* function called with no arguments, but all parameters have
|
||||
a default value: use default values as arguments .*/
|
||||
stack = &PyTuple_GET_ITEM(argdefs, 0);
|
||||
return _PyFunction_FastCall(co, stack, Py_SIZE(argdefs), globals);
|
||||
}
|
||||
}
|
||||
kwdefs = PyFunction_GET_KW_DEFAULTS(func);
|
||||
closure = PyFunction_GET_CLOSURE(func);
|
||||
name = ((PyFunctionObject *)func) -> func_name;
|
||||
qualname = ((PyFunctionObject *)func) -> func_qualname;
|
||||
if (argdefs != NULL) {
|
||||
d = &PyTuple_GET_ITEM(argdefs, 0);
|
||||
nd = Py_SIZE(argdefs);
|
||||
}
|
||||
else {
|
||||
d = NULL;
|
||||
nd = 0;
|
||||
}
|
||||
return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
|
||||
stack, nargs,
|
||||
nkwargs ? &PyTuple_GET_ITEM(kwnames, 0) : NULL,
|
||||
stack + nargs,
|
||||
nkwargs, 1,
|
||||
d, (int)nd, kwdefs,
|
||||
closure, name, qualname);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyFunction_FastCallKeywords(PyObject *func, PyObject **stack,
|
||||
Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
return fast_function(func, stack, nargs, kwnames);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
|
||||
PyObject *kwargs)
|
||||
{
|
||||
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
|
||||
PyObject *globals = PyFunction_GET_GLOBALS(func);
|
||||
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
|
||||
PyObject *kwdefs, *closure, *name, *qualname;
|
||||
PyObject *kwtuple, **k;
|
||||
PyObject **d;
|
||||
Py_ssize_t nd, nk;
|
||||
PyObject *result;
|
||||
assert(func != NULL);
|
||||
assert(nargs >= 0);
|
||||
assert(nargs == 0 || args != NULL);
|
||||
assert(kwargs == NULL || PyDict_Check(kwargs));
|
||||
PCALL(PCALL_FUNCTION);
|
||||
PCALL(PCALL_FAST_FUNCTION);
|
||||
if (co->co_kwonlyargcount == 0 &&
|
||||
(kwargs == NULL || PyDict_Size(kwargs) == 0) &&
|
||||
co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
|
||||
{
|
||||
/* Fast paths */
|
||||
if (argdefs == NULL && co->co_argcount == nargs) {
|
||||
return _PyFunction_FastCall(co, args, nargs, globals);
|
||||
}
|
||||
else if (nargs == 0 && argdefs != NULL
|
||||
&& co->co_argcount == Py_SIZE(argdefs)) {
|
||||
/* function called with no arguments, but all parameters have
|
||||
a default value: use default values as arguments .*/
|
||||
args = &PyTuple_GET_ITEM(argdefs, 0);
|
||||
return _PyFunction_FastCall(co, args, Py_SIZE(argdefs), globals);
|
||||
}
|
||||
}
|
||||
if (kwargs != NULL) {
|
||||
Py_ssize_t pos, i;
|
||||
nk = PyDict_Size(kwargs);
|
||||
kwtuple = PyTuple_New(2 * nk);
|
||||
if (kwtuple == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
k = &PyTuple_GET_ITEM(kwtuple, 0);
|
||||
pos = i = 0;
|
||||
while (PyDict_Next(kwargs, &pos, &k[i], &k[i+1])) {
|
||||
Py_INCREF(k[i]);
|
||||
Py_INCREF(k[i+1]);
|
||||
i += 2;
|
||||
}
|
||||
nk = i / 2;
|
||||
}
|
||||
else {
|
||||
kwtuple = NULL;
|
||||
k = NULL;
|
||||
nk = 0;
|
||||
}
|
||||
kwdefs = PyFunction_GET_KW_DEFAULTS(func);
|
||||
closure = PyFunction_GET_CLOSURE(func);
|
||||
name = ((PyFunctionObject *)func) -> func_name;
|
||||
qualname = ((PyFunctionObject *)func) -> func_qualname;
|
||||
if (argdefs != NULL) {
|
||||
d = &PyTuple_GET_ITEM(argdefs, 0);
|
||||
nd = Py_SIZE(argdefs);
|
||||
}
|
||||
else {
|
||||
d = NULL;
|
||||
nd = 0;
|
||||
}
|
||||
result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
|
||||
args, nargs,
|
||||
k, k != NULL ? k + 1 : NULL, nk, 2,
|
||||
d, nd, kwdefs,
|
||||
closure, name, qualname);
|
||||
Py_XDECREF(kwtuple);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue