mirror of https://github.com/python/cpython
bpo-42990: Further refactoring of PyEval_ functions. (GH-24368)
* Further refactoring of PyEval_EvalCode and friends. Break into make-frame, and eval-frame parts. * Simplify function vector call using new _PyEval_Vector. * Remove unused internal functions: _PyEval_EvalCodeWithName and _PyEval_EvalCode. * Don't use legacy function PyEval_EvalCodeEx.
This commit is contained in:
parent
49926cf2bc
commit
0332e569c1
|
@ -71,8 +71,8 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,
|
||||||
PyObject *, PyObject *);
|
PyObject *, PyObject *);
|
||||||
|
|
||||||
/* only internal use */
|
/* only internal use */
|
||||||
PyFrameObject* _PyFrame_New_NoTrack(PyThreadState *, PyCodeObject *,
|
PyFrameObject*
|
||||||
PyObject *, PyObject *, PyObject *);
|
_PyFrame_New_NoTrack(PyThreadState *, PyFrameConstructor *, PyObject *);
|
||||||
|
|
||||||
|
|
||||||
/* The rest of the interface is specific for frame objects */
|
/* The rest of the interface is specific for frame objects */
|
||||||
|
|
|
@ -18,16 +18,6 @@ PyAPI_FUNC(PyObject *) PyEval_EvalCodeEx(PyObject *co,
|
||||||
PyObject *kwdefs, PyObject *closure);
|
PyObject *kwdefs, PyObject *closure);
|
||||||
|
|
||||||
#ifndef Py_LIMITED_API
|
#ifndef Py_LIMITED_API
|
||||||
PyAPI_FUNC(PyObject *) _PyEval_EvalCodeWithName(
|
|
||||||
PyObject *co,
|
|
||||||
PyObject *globals, PyObject *locals,
|
|
||||||
PyObject *const *args, Py_ssize_t argcount,
|
|
||||||
PyObject *const *kwnames, PyObject *const *kwargs,
|
|
||||||
Py_ssize_t kwcount, int kwstep,
|
|
||||||
PyObject *const *defs, Py_ssize_t defcount,
|
|
||||||
PyObject *kwdefs, PyObject *closure,
|
|
||||||
PyObject *name, PyObject *qualname);
|
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) _PyEval_CallTracing(PyObject *func, PyObject *args);
|
PyAPI_FUNC(PyObject *) _PyEval_CallTracing(PyObject *func, PyObject *args);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -40,12 +40,11 @@ _PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
||||||
return tstate->interp->eval_frame(tstate, f, throwflag);
|
return tstate->interp->eval_frame(tstate, f, throwflag);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern PyObject *_PyEval_EvalCode(
|
extern PyObject *
|
||||||
PyThreadState *tstate,
|
_PyEval_Vector(PyThreadState *tstate,
|
||||||
PyFrameConstructor *desc, PyObject *locals,
|
PyFrameConstructor *desc, PyObject *locals,
|
||||||
PyObject *const *args, Py_ssize_t argcount,
|
PyObject* const* args, size_t argcount,
|
||||||
PyObject *const *kwnames, PyObject *const *kwargs,
|
PyObject *kwnames);
|
||||||
Py_ssize_t kwcount, int kwstep);
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
|
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
|
||||||
extern int _PyEval_ThreadsInitialized(PyInterpreterState *interp);
|
extern int _PyEval_ThreadsInitialized(PyInterpreterState *interp);
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Refactor the ``PyEval_`` family of functions.
|
||||||
|
|
||||||
|
* An new function ``_PyEval_Vector`` is added to simplify calls to Python from C.
|
||||||
|
* ``_PyEval_EvalCodeWithName`` is removed
|
||||||
|
* ``PyEval_EvalCodeEx`` is retained as part of the API, but is not used internally
|
|
@ -328,87 +328,24 @@ PyCFunction_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
|
||||||
|
|
||||||
/* --- PyFunction call functions ---------------------------------- */
|
/* --- PyFunction call functions ---------------------------------- */
|
||||||
|
|
||||||
static PyObject* _Py_HOT_FUNCTION
|
|
||||||
function_code_fastcall(PyThreadState *tstate, PyCodeObject *co,
|
|
||||||
PyObject *const *args, Py_ssize_t nargs,
|
|
||||||
PyFunctionObject *func)
|
|
||||||
{
|
|
||||||
assert(tstate != NULL);
|
|
||||||
assert(func != NULL);
|
|
||||||
|
|
||||||
/* XXX Perhaps we should create a specialized
|
|
||||||
_PyFrame_New_NoTrack() that doesn't take locals, but does
|
|
||||||
take builtins without sanity checking them.
|
|
||||||
*/
|
|
||||||
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, func->func_globals, func->func_builtins, NULL);
|
|
||||||
if (f == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject **fastlocals = f->f_localsplus;
|
|
||||||
|
|
||||||
for (Py_ssize_t i = 0; i < nargs; i++) {
|
|
||||||
Py_INCREF(*args);
|
|
||||||
fastlocals[i] = *args++;
|
|
||||||
}
|
|
||||||
PyObject *result = _PyEval_EvalFrame(tstate, f, 0);
|
|
||||||
|
|
||||||
if (Py_REFCNT(f) > 1) {
|
|
||||||
Py_DECREF(f);
|
|
||||||
_PyObject_GC_TRACK(f);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
++tstate->recursion_depth;
|
|
||||||
Py_DECREF(f);
|
|
||||||
--tstate->recursion_depth;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
|
_PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
|
||||||
size_t nargsf, PyObject *kwnames)
|
size_t nargsf, PyObject *kwnames)
|
||||||
{
|
{
|
||||||
assert(PyFunction_Check(func));
|
assert(PyFunction_Check(func));
|
||||||
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
PyFrameConstructor *f = PyFunction_AS_FRAME_CONSTRUCTOR(func);
|
||||||
|
|
||||||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||||
assert(nargs >= 0);
|
assert(nargs >= 0);
|
||||||
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
|
||||||
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
|
||||||
/* kwnames must only contain strings and all keys must be unique */
|
|
||||||
|
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
|
assert(nargs == 0 || stack != NULL);
|
||||||
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
|
if (((PyCodeObject *)f->fc_code)->co_flags & CO_OPTIMIZED) {
|
||||||
|
return _PyEval_Vector(tstate, f, NULL, stack, nargs, kwnames);
|
||||||
if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
|
}
|
||||||
(co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
|
else {
|
||||||
{
|
return _PyEval_Vector(tstate, f, f->fc_globals, stack, nargs, kwnames);
|
||||||
if (argdefs == NULL && co->co_argcount == nargs) {
|
|
||||||
return function_code_fastcall(tstate, co, stack, nargs, (PyFunctionObject *)func);
|
|
||||||
}
|
|
||||||
else if (nargs == 0 && argdefs != NULL
|
|
||||||
&& co->co_argcount == PyTuple_GET_SIZE(argdefs)) {
|
|
||||||
/* function called with no arguments, but all parameters have
|
|
||||||
a default value: use default values as arguments .*/
|
|
||||||
stack = _PyTuple_ITEMS(argdefs);
|
|
||||||
return function_code_fastcall(tstate, co,
|
|
||||||
stack, PyTuple_GET_SIZE(argdefs),
|
|
||||||
(PyFunctionObject *)func);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _PyEval_EvalCode(tstate,
|
|
||||||
PyFunction_AS_FRAME_CONSTRUCTOR(func), (PyObject *)NULL,
|
|
||||||
stack, nargs,
|
|
||||||
nkwargs ? _PyTuple_ITEMS(kwnames) : NULL,
|
|
||||||
stack + nargs,
|
|
||||||
nkwargs, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- More complex call functions -------------------------------- */
|
/* --- More complex call functions -------------------------------- */
|
||||||
|
|
||||||
/* External interface to call any callable object.
|
/* External interface to call any callable object.
|
||||||
|
|
|
@ -816,11 +816,10 @@ frame_alloc(PyCodeObject *code)
|
||||||
|
|
||||||
|
|
||||||
PyFrameObject* _Py_HOT_FUNCTION
|
PyFrameObject* _Py_HOT_FUNCTION
|
||||||
_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
_PyFrame_New_NoTrack(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals)
|
||||||
PyObject *globals, PyObject *builtins, PyObject *locals)
|
|
||||||
{
|
{
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
if (code == NULL || globals == NULL || builtins == NULL ||
|
if (con == NULL || con->fc_code == NULL ||
|
||||||
(locals != NULL && !PyMapping_Check(locals))) {
|
(locals != NULL && !PyMapping_Check(locals))) {
|
||||||
PyErr_BadInternalCall();
|
PyErr_BadInternalCall();
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -829,38 +828,21 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
||||||
|
|
||||||
PyFrameObject *back = tstate->frame;
|
PyFrameObject *back = tstate->frame;
|
||||||
|
|
||||||
PyFrameObject *f = frame_alloc(code);
|
PyFrameObject *f = frame_alloc((PyCodeObject *)con->fc_code);
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->f_stackdepth = 0;
|
f->f_stackdepth = 0;
|
||||||
Py_INCREF(builtins);
|
Py_INCREF(con->fc_builtins);
|
||||||
f->f_builtins = builtins;
|
f->f_builtins = con->fc_builtins;
|
||||||
Py_XINCREF(back);
|
Py_XINCREF(back);
|
||||||
f->f_back = back;
|
f->f_back = back;
|
||||||
Py_INCREF(code);
|
Py_INCREF(con->fc_code);
|
||||||
Py_INCREF(globals);
|
Py_INCREF(con->fc_globals);
|
||||||
f->f_globals = globals;
|
f->f_globals = con->fc_globals;
|
||||||
/* Most functions have CO_NEWLOCALS and CO_OPTIMIZED set. */
|
Py_XINCREF(locals);
|
||||||
if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) ==
|
f->f_locals = locals;
|
||||||
(CO_NEWLOCALS | CO_OPTIMIZED))
|
|
||||||
; /* f_locals = NULL; will be set by PyFrame_FastToLocals() */
|
|
||||||
else if (code->co_flags & CO_NEWLOCALS) {
|
|
||||||
locals = PyDict_New();
|
|
||||||
if (locals == NULL) {
|
|
||||||
Py_DECREF(f);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
f->f_locals = locals;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (locals == NULL) {
|
|
||||||
locals = globals;
|
|
||||||
}
|
|
||||||
Py_INCREF(locals);
|
|
||||||
f->f_locals = locals;
|
|
||||||
}
|
|
||||||
|
|
||||||
f->f_lasti = -1;
|
f->f_lasti = -1;
|
||||||
f->f_lineno = 0;
|
f->f_lineno = 0;
|
||||||
|
@ -875,12 +857,23 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Legacy API */
|
||||||
PyFrameObject*
|
PyFrameObject*
|
||||||
PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
|
PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
|
||||||
PyObject *globals, PyObject *locals)
|
PyObject *globals, PyObject *locals)
|
||||||
{
|
{
|
||||||
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
|
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
|
||||||
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, code, globals, builtins, locals);
|
PyFrameConstructor desc = {
|
||||||
|
.fc_globals = globals,
|
||||||
|
.fc_builtins = builtins,
|
||||||
|
.fc_name = code->co_name,
|
||||||
|
.fc_qualname = code->co_name,
|
||||||
|
.fc_code = (PyObject *)code,
|
||||||
|
.fc_defaults = NULL,
|
||||||
|
.fc_kwdefaults = NULL,
|
||||||
|
.fc_closure = NULL
|
||||||
|
};
|
||||||
|
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, &desc, locals);
|
||||||
Py_DECREF(builtins);
|
Py_DECREF(builtins);
|
||||||
if (f)
|
if (f)
|
||||||
_PyObject_GC_TRACK(f);
|
_PyObject_GC_TRACK(f);
|
||||||
|
|
|
@ -575,9 +575,9 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
|
||||||
|
|
||||||
newfunc = (PyFunctionObject *)PyFunction_New((PyObject *)code,
|
newfunc = (PyFunctionObject *)PyFunction_New((PyObject *)code,
|
||||||
globals);
|
globals);
|
||||||
if (newfunc == NULL)
|
if (newfunc == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
if (name != Py_None) {
|
if (name != Py_None) {
|
||||||
Py_INCREF(name);
|
Py_INCREF(name);
|
||||||
Py_SETREF(newfunc->func_name, name);
|
Py_SETREF(newfunc->func_name, name);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "pycore_pyerrors.h" // _PyErr_NoMemory()
|
#include "pycore_pyerrors.h" // _PyErr_NoMemory()
|
||||||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||||
#include "pycore_tuple.h" // _PyTuple_FromArray()
|
#include "pycore_tuple.h" // _PyTuple_FromArray()
|
||||||
|
#include "pycore_ceval.h" // _PyEval_Vector()
|
||||||
|
|
||||||
_Py_IDENTIFIER(__builtins__);
|
_Py_IDENTIFIER(__builtins__);
|
||||||
_Py_IDENTIFIER(__dict__);
|
_Py_IDENTIFIER(__dict__);
|
||||||
|
@ -219,9 +220,9 @@ builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
|
||||||
Py_TYPE(ns)->tp_name);
|
Py_TYPE(ns)->tp_name);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), ns,
|
PyFrameConstructor *f = PyFunction_AS_FRAME_CONSTRUCTOR(func);
|
||||||
NULL, 0, NULL, 0, NULL, 0, NULL,
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
PyFunction_GET_CLOSURE(func));
|
cell = _PyEval_Vector(tstate, f, ns, NULL, 0, NULL);
|
||||||
if (cell != NULL) {
|
if (cell != NULL) {
|
||||||
if (bases != orig_bases) {
|
if (bases != orig_bases) {
|
||||||
if (PyMapping_SetItemString(ns, "__orig_bases__", orig_bases) < 0) {
|
if (PyMapping_SetItemString(ns, "__orig_bases__", orig_bases) < 0) {
|
||||||
|
|
339
Python/ceval.c
339
Python/ceval.c
|
@ -890,12 +890,27 @@ static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **);
|
||||||
PyObject *
|
PyObject *
|
||||||
PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
|
PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
|
||||||
{
|
{
|
||||||
return PyEval_EvalCodeEx(co,
|
if (locals == NULL) {
|
||||||
globals, locals,
|
locals = globals;
|
||||||
(PyObject **)NULL, 0,
|
}
|
||||||
(PyObject **)NULL, 0,
|
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
|
||||||
(PyObject **)NULL, 0,
|
if (builtins == NULL) {
|
||||||
NULL, NULL);
|
return NULL;
|
||||||
|
}
|
||||||
|
PyFrameConstructor desc = {
|
||||||
|
.fc_globals = globals,
|
||||||
|
.fc_builtins = builtins,
|
||||||
|
.fc_name = ((PyCodeObject *)co)->co_name,
|
||||||
|
.fc_qualname = ((PyCodeObject *)co)->co_name,
|
||||||
|
.fc_code = co,
|
||||||
|
.fc_defaults = NULL,
|
||||||
|
.fc_kwdefaults = NULL,
|
||||||
|
.fc_closure = NULL
|
||||||
|
};
|
||||||
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
|
PyObject *res =_PyEval_Vector(tstate, &desc, locals, NULL, 0, NULL);
|
||||||
|
Py_DECREF(builtins);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4343,7 +4358,7 @@ too_many_positional(PyThreadState *tstate, PyCodeObject *co,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co,
|
positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co,
|
||||||
Py_ssize_t kwcount, PyObject* const* kwnames,
|
Py_ssize_t kwcount, PyObject* kwnames,
|
||||||
PyObject *qualname)
|
PyObject *qualname)
|
||||||
{
|
{
|
||||||
int posonly_conflicts = 0;
|
int posonly_conflicts = 0;
|
||||||
|
@ -4354,7 +4369,7 @@ positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co,
|
||||||
|
|
||||||
for (int k2=0; k2<kwcount; k2++){
|
for (int k2=0; k2<kwcount; k2++){
|
||||||
/* Compare the pointers first and fallback to PyObject_RichCompareBool*/
|
/* Compare the pointers first and fallback to PyObject_RichCompareBool*/
|
||||||
PyObject* kwname = kwnames[k2];
|
PyObject* kwname = PyTuple_GET_ITEM(kwnames, k2);
|
||||||
if (kwname == posonly_name){
|
if (kwname == posonly_name){
|
||||||
if(PyList_Append(posonly_names, kwname) != 0) {
|
if(PyList_Append(posonly_names, kwname) != 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -4403,26 +4418,21 @@ fail:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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). */
|
|
||||||
|
|
||||||
PyObject *
|
PyFrameObject *
|
||||||
_PyEval_EvalCode(PyThreadState *tstate,
|
_PyEval_MakeFrameVector(PyThreadState *tstate,
|
||||||
PyFrameConstructor *con, PyObject *locals,
|
PyFrameConstructor *con, PyObject *locals,
|
||||||
PyObject *const *args, Py_ssize_t argcount,
|
PyObject *const *args, Py_ssize_t argcount,
|
||||||
PyObject *const *kwnames, PyObject *const *kwargs,
|
PyObject *kwnames)
|
||||||
Py_ssize_t kwcount, int kwstep)
|
|
||||||
{
|
{
|
||||||
assert(is_tstate_valid(tstate));
|
assert(is_tstate_valid(tstate));
|
||||||
|
|
||||||
PyCodeObject *co = (PyCodeObject*)con->fc_code;
|
PyCodeObject *co = (PyCodeObject*)con->fc_code;
|
||||||
assert(con->fc_defaults == NULL || PyTuple_CheckExact(con->fc_defaults));
|
assert(con->fc_defaults == NULL || PyTuple_CheckExact(con->fc_defaults));
|
||||||
PyObject *retval = NULL;
|
|
||||||
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
|
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
|
||||||
|
|
||||||
/* Create the frame */
|
/* Create the frame */
|
||||||
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, con->fc_globals, con->fc_builtins, locals);
|
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, con, locals);
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -4469,74 +4479,76 @@ _PyEval_EvalCode(PyThreadState *tstate,
|
||||||
SETLOCAL(total_args, u);
|
SETLOCAL(total_args, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle keyword arguments passed as two strided arrays */
|
/* Handle keyword arguments */
|
||||||
kwcount *= kwstep;
|
if (kwnames != NULL) {
|
||||||
for (i = 0; i < kwcount; i += kwstep) {
|
Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames);
|
||||||
PyObject **co_varnames;
|
for (i = 0; i < kwcount; i++) {
|
||||||
PyObject *keyword = kwnames[i];
|
PyObject **co_varnames;
|
||||||
PyObject *value = kwargs[i];
|
PyObject *keyword = PyTuple_GET_ITEM(kwnames, i);
|
||||||
Py_ssize_t j;
|
PyObject *value = args[i+argcount];
|
||||||
|
Py_ssize_t j;
|
||||||
|
|
||||||
if (keyword == NULL || !PyUnicode_Check(keyword)) {
|
if (keyword == NULL || !PyUnicode_Check(keyword)) {
|
||||||
_PyErr_Format(tstate, PyExc_TypeError,
|
_PyErr_Format(tstate, PyExc_TypeError,
|
||||||
"%U() keywords must be strings",
|
"%U() keywords must be strings",
|
||||||
con->fc_qualname);
|
con->fc_qualname);
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Speed hack: do raw pointer compares. As names are
|
|
||||||
normally interned this should almost always hit. */
|
|
||||||
co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
|
|
||||||
for (j = co->co_posonlyargcount; j < total_args; j++) {
|
|
||||||
PyObject *varname = co_varnames[j];
|
|
||||||
if (varname == keyword) {
|
|
||||||
goto kw_found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Slow fallback, just in case */
|
|
||||||
for (j = co->co_posonlyargcount; j < total_args; j++) {
|
|
||||||
PyObject *varname = co_varnames[j];
|
|
||||||
int cmp = PyObject_RichCompareBool( keyword, varname, Py_EQ);
|
|
||||||
if (cmp > 0) {
|
|
||||||
goto kw_found;
|
|
||||||
}
|
|
||||||
else if (cmp < 0) {
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
assert(j >= total_args);
|
/* Speed hack: do raw pointer compares. As names are
|
||||||
if (kwdict == NULL) {
|
normally interned this should almost always hit. */
|
||||||
|
co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
|
||||||
|
for (j = co->co_posonlyargcount; j < total_args; j++) {
|
||||||
|
PyObject *varname = co_varnames[j];
|
||||||
|
if (varname == keyword) {
|
||||||
|
goto kw_found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (co->co_posonlyargcount
|
/* Slow fallback, just in case */
|
||||||
&& positional_only_passed_as_keyword(tstate, co,
|
for (j = co->co_posonlyargcount; j < total_args; j++) {
|
||||||
kwcount, kwnames,
|
PyObject *varname = co_varnames[j];
|
||||||
|
int cmp = PyObject_RichCompareBool( keyword, varname, Py_EQ);
|
||||||
|
if (cmp > 0) {
|
||||||
|
goto kw_found;
|
||||||
|
}
|
||||||
|
else if (cmp < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(j >= total_args);
|
||||||
|
if (kwdict == NULL) {
|
||||||
|
|
||||||
|
if (co->co_posonlyargcount
|
||||||
|
&& positional_only_passed_as_keyword(tstate, co,
|
||||||
|
kwcount, kwnames,
|
||||||
con->fc_qualname))
|
con->fc_qualname))
|
||||||
{
|
{
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
_PyErr_Format(tstate, PyExc_TypeError,
|
||||||
|
"%U() got an unexpected keyword argument '%S'",
|
||||||
|
con->fc_qualname, keyword);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
_PyErr_Format(tstate, PyExc_TypeError,
|
if (PyDict_SetItem(kwdict, keyword, value) == -1) {
|
||||||
"%U() got an unexpected keyword argument '%S'",
|
goto fail;
|
||||||
con->fc_qualname, keyword);
|
}
|
||||||
goto fail;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (PyDict_SetItem(kwdict, keyword, value) == -1) {
|
kw_found:
|
||||||
goto fail;
|
if (GETLOCAL(j) != NULL) {
|
||||||
}
|
_PyErr_Format(tstate, PyExc_TypeError,
|
||||||
continue;
|
"%U() got multiple values for argument '%S'",
|
||||||
|
|
||||||
kw_found:
|
|
||||||
if (GETLOCAL(j) != NULL) {
|
|
||||||
_PyErr_Format(tstate, PyExc_TypeError,
|
|
||||||
"%U() got multiple values for argument '%S'",
|
|
||||||
con->fc_qualname, keyword);
|
con->fc_qualname, keyword);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
Py_INCREF(value);
|
||||||
|
SETLOCAL(j, value);
|
||||||
}
|
}
|
||||||
Py_INCREF(value);
|
|
||||||
SETLOCAL(j, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check the number of positional arguments */
|
/* Check the number of positional arguments */
|
||||||
|
@ -4631,37 +4643,72 @@ _PyEval_EvalCode(PyThreadState *tstate,
|
||||||
freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
|
freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle generator/coroutine/asynchronous generator */
|
return f;
|
||||||
if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
|
|
||||||
PyObject *gen;
|
|
||||||
int is_coro = co->co_flags & CO_COROUTINE;
|
|
||||||
|
|
||||||
/* Don't need to keep the reference to f_back, it will be set
|
|
||||||
* when the generator is resumed. */
|
|
||||||
Py_CLEAR(f->f_back);
|
|
||||||
|
|
||||||
/* Create a new generator that owns the ready to run frame
|
|
||||||
* and return that as the value. */
|
|
||||||
if (is_coro) {
|
|
||||||
gen = PyCoro_New(f, con->fc_name, con->fc_qualname);
|
|
||||||
} else if (co->co_flags & CO_ASYNC_GENERATOR) {
|
|
||||||
gen = PyAsyncGen_New(f, con->fc_name, con->fc_qualname);
|
|
||||||
} else {
|
|
||||||
gen = PyGen_NewWithQualName(f, con->fc_name, con->fc_qualname);
|
|
||||||
}
|
|
||||||
if (gen == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_PyObject_GC_TRACK(f);
|
|
||||||
|
|
||||||
return gen;
|
|
||||||
}
|
|
||||||
|
|
||||||
retval = _PyEval_EvalFrame(tstate, f, 0);
|
|
||||||
|
|
||||||
fail: /* Jump here from prelude on failure */
|
fail: /* Jump here from prelude on failure */
|
||||||
|
|
||||||
|
/* decref'ing the frame can cause __del__ methods to get invoked,
|
||||||
|
which can call back into Python. While we're done with the
|
||||||
|
current Python frame (f), the associated C stack is still in use,
|
||||||
|
so recursion_depth must be boosted for the duration.
|
||||||
|
*/
|
||||||
|
if (Py_REFCNT(f) > 1) {
|
||||||
|
Py_DECREF(f);
|
||||||
|
_PyObject_GC_TRACK(f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
++tstate->recursion_depth;
|
||||||
|
Py_DECREF(f);
|
||||||
|
--tstate->recursion_depth;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
make_coro(PyFrameConstructor *con, PyFrameObject *f)
|
||||||
|
{
|
||||||
|
assert (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR));
|
||||||
|
PyObject *gen;
|
||||||
|
int is_coro = ((PyCodeObject *)con->fc_code)->co_flags & CO_COROUTINE;
|
||||||
|
|
||||||
|
/* Don't need to keep the reference to f_back, it will be set
|
||||||
|
* when the generator is resumed. */
|
||||||
|
Py_CLEAR(f->f_back);
|
||||||
|
|
||||||
|
/* Create a new generator that owns the ready to run frame
|
||||||
|
* and return that as the value. */
|
||||||
|
if (is_coro) {
|
||||||
|
gen = PyCoro_New(f, con->fc_name, con->fc_qualname);
|
||||||
|
} else if (((PyCodeObject *)con->fc_code)->co_flags & CO_ASYNC_GENERATOR) {
|
||||||
|
gen = PyAsyncGen_New(f, con->fc_name, con->fc_qualname);
|
||||||
|
} else {
|
||||||
|
gen = PyGen_NewWithQualName(f, con->fc_name, con->fc_qualname);
|
||||||
|
}
|
||||||
|
if (gen == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_PyObject_GC_TRACK(f);
|
||||||
|
|
||||||
|
return gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
_PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con,
|
||||||
|
PyObject *locals,
|
||||||
|
PyObject* const* args, size_t argcount,
|
||||||
|
PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyFrameObject *f = _PyEval_MakeFrameVector(
|
||||||
|
tstate, con, locals, args, argcount, kwnames);
|
||||||
|
if (f == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
|
||||||
|
return make_coro(con, f);
|
||||||
|
}
|
||||||
|
PyObject *retval = _PyEval_EvalFrame(tstate, f, 0);
|
||||||
|
|
||||||
/* decref'ing the frame can cause __del__ methods to get invoked,
|
/* decref'ing the frame can cause __del__ methods to get invoked,
|
||||||
which can call back into Python. While we're done with the
|
which can call back into Python. While we're done with the
|
||||||
current Python frame (f), the associated C stack is still in use,
|
current Python frame (f), the associated C stack is still in use,
|
||||||
|
@ -4681,14 +4728,13 @@ fail: /* Jump here from prelude on failure */
|
||||||
|
|
||||||
/* Legacy API */
|
/* Legacy API */
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
|
||||||
PyObject *const *args, Py_ssize_t argcount,
|
PyObject *const *args, int argcount,
|
||||||
PyObject *const *kwnames, PyObject *const *kwargs,
|
PyObject *const *kws, int kwcount,
|
||||||
Py_ssize_t kwcount, int kwstep,
|
PyObject *const *defs, int defcount,
|
||||||
PyObject *const *defs, Py_ssize_t defcount,
|
PyObject *kwdefs, PyObject *closure)
|
||||||
PyObject *kwdefs, PyObject *closure,
|
|
||||||
PyObject *name, PyObject *qualname)
|
|
||||||
{
|
{
|
||||||
|
PyObject *res;
|
||||||
PyObject *defaults = _PyTuple_FromArray(defs, defcount);
|
PyObject *defaults = _PyTuple_FromArray(defs, defcount);
|
||||||
if (defaults == NULL) {
|
if (defaults == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -4698,44 +4744,75 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
|
||||||
Py_DECREF(defaults);
|
Py_DECREF(defaults);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
PyCodeObject *code = (PyCodeObject *)_co;
|
||||||
|
assert ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) == 0);
|
||||||
|
if (locals == NULL) {
|
||||||
|
locals = globals;
|
||||||
|
}
|
||||||
|
PyObject *kwnames;
|
||||||
|
PyObject *const *allargs;
|
||||||
|
PyObject **newargs;
|
||||||
|
if (kwcount == 0) {
|
||||||
|
allargs = args;
|
||||||
|
kwnames = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kwnames = PyTuple_New(kwcount);
|
||||||
|
if (kwnames == NULL) {
|
||||||
|
res = NULL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
newargs = PyMem_Malloc(sizeof(PyObject *)*(kwcount+argcount));
|
||||||
|
if (newargs == NULL) {
|
||||||
|
res = NULL;
|
||||||
|
Py_DECREF(kwnames);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < argcount; i++) {
|
||||||
|
newargs[i] = args[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < kwcount; i++) {
|
||||||
|
Py_INCREF(kws[2*i]);
|
||||||
|
PyTuple_SET_ITEM(kwnames, i, kws[2*i]);
|
||||||
|
newargs[argcount+i] = kws[2*i+1];
|
||||||
|
}
|
||||||
|
allargs = newargs;
|
||||||
|
}
|
||||||
|
PyObject **kwargs = PyMem_Malloc(sizeof(PyObject *)*kwcount);
|
||||||
|
if (kwargs == NULL) {
|
||||||
|
res = NULL;
|
||||||
|
Py_DECREF(kwnames);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < kwcount; i++) {
|
||||||
|
Py_INCREF(kws[2*i]);
|
||||||
|
PyTuple_SET_ITEM(kwnames, i, kws[2*i]);
|
||||||
|
kwargs[i] = kws[2*i+1];
|
||||||
|
}
|
||||||
PyFrameConstructor constr = {
|
PyFrameConstructor constr = {
|
||||||
.fc_globals = globals,
|
.fc_globals = globals,
|
||||||
.fc_builtins = builtins,
|
.fc_builtins = builtins,
|
||||||
.fc_name = name,
|
.fc_name = ((PyCodeObject *)_co)->co_name,
|
||||||
.fc_qualname = qualname,
|
.fc_qualname = ((PyCodeObject *)_co)->co_name,
|
||||||
.fc_code = _co,
|
.fc_code = _co,
|
||||||
.fc_defaults = defaults,
|
.fc_defaults = defaults,
|
||||||
.fc_kwdefaults = kwdefs,
|
.fc_kwdefaults = kwdefs,
|
||||||
.fc_closure = closure
|
.fc_closure = closure
|
||||||
};
|
};
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
PyObject *res = _PyEval_EvalCode(tstate, &constr, locals,
|
res = _PyEval_Vector(tstate, &constr, locals,
|
||||||
args, argcount,
|
allargs, argcount,
|
||||||
kwnames, kwargs,
|
kwnames);
|
||||||
kwcount, kwstep);
|
if (kwcount) {
|
||||||
|
Py_DECREF(kwnames);
|
||||||
|
PyMem_Free(newargs);
|
||||||
|
}
|
||||||
|
fail:
|
||||||
Py_DECREF(defaults);
|
Py_DECREF(defaults);
|
||||||
Py_DECREF(builtins);
|
Py_DECREF(builtins);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Legacy API */
|
|
||||||
PyObject *
|
|
||||||
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
|
|
||||||
PyObject *const *args, int argcount,
|
|
||||||
PyObject *const *kws, int kwcount,
|
|
||||||
PyObject *const *defs, int defcount,
|
|
||||||
PyObject *kwdefs, PyObject *closure)
|
|
||||||
{
|
|
||||||
return _PyEval_EvalCodeWithName(
|
|
||||||
_co, globals, locals,
|
|
||||||
args, argcount,
|
|
||||||
kws, kws != NULL ? kws + 1 : NULL,
|
|
||||||
kwcount, 2,
|
|
||||||
defs, defcount,
|
|
||||||
kwdefs, closure,
|
|
||||||
((PyCodeObject *)_co)->co_name,
|
|
||||||
((PyCodeObject *)_co)->co_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
special_lookup(PyThreadState *tstate, PyObject *o, _Py_Identifier *id)
|
special_lookup(PyThreadState *tstate, PyObject *o, _Py_Identifier *id)
|
||||||
|
|
Loading…
Reference in New Issue