8000 bpo-42990: Introduce 'frame constructor' struct to simplify API for PyEval_CodeEval and friends by markshannon · Pull Request #24298 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-42990: Introduce 'frame constructor' struct to simplify API for PyEval_CodeEval and friends #24298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Include/cpython/frameobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,

/* only internal use */
PyFrameObject* _PyFrame_New_NoTrack(PyThreadState *, PyCodeObject *,
PyObject *, PyObject *);
PyObject *, PyObject *, PyObject *);


/* The rest of the interface is specific for frame objects */
Expand All @@ -92,3 +92,5 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out);

PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);

PyObject *_PyEval_BuiltinsFromGlobals(PyObject *globals);
26 changes: 19 additions & 7 deletions Include/funcobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@
extern "C" {
#endif


#define COMMON_FIELDS(PREFIX) \
PyObject *PREFIX ## globals; \
PyObject *PREFIX ## builtins; \
PyObject *PREFIX ## name; \
PyObject *PREFIX ## qualname; \
PyObject *PREFIX ## code; /* A code object, the __code__ attribute */ \
PyObject *PREFIX ## defaults; /* NULL or a tuple */ \
PyObject *PREFIX ## kwdefaults; /* NULL or a dict */ \
PyObject *PREFIX ## closure; /* NULL or a tuple of cell objects */

typedef struct {
COMMON_FIELDS(fc_)
} PyFrameConstructor;

/* Function objects and code objects should not be confused with each other:
*
* Function objects are created by the execution of the 'def' statement.
Expand All @@ -20,18 +35,12 @@ extern "C" {

typedef struct {
PyObject_HEAD
PyObject *func_code; /* A code object, the __code__ attribute */
PyObject *func_globals; /* A dictionary (other mappings won't do) */
PyObject *func_defaults; /* NULL or a tuple */
PyObject *func_kwdefaults; /* NULL or a dict */
PyObject *func_closure; /* NULL or a tuple of cell objects */
COMMON_FIELDS(func_)
PyObject *func_doc; /* The __doc__ attribute, can be anything */
PyObject *func_name; /* The __name__ attribute, a string object */
PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
PyObject *func_weakreflist; /* List of weak references */
PyObject *func_module; /* The __module__ attribute, can be anything */
PyObject *func_annotations; /* Annotations, a dict or NULL */
PyObject *func_qualname; /* The qualified name */
vectorcallfunc vectorcall;

/* Invariant:
Expand Down Expand Up @@ -84,6 +93,9 @@ PyAPI_FUNC(PyObject *) _PyFunction_Vectorcall(
#define PyFunction_GET_ANNOTATIONS(func) \
(((PyFunctionObject *)func) -> func_annotations)

#define PyFunction_AS_FRAME_CONSTRUCTOR(func) \
((PyFrameConstructor *)&((PyFunctionObject *)(func))->func_globals)

/* The classmethod and staticmethod types lives here, too */
PyAPI_DATA(PyTypeObject) PyClassMethod_Type;
PyAPI_DATA(PyTypeObject) PyStaticMethod_Type;
Expand Down
7 changes: 2 additions & 5 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,10 @@ _PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag)

extern PyObject *_PyEval_EvalCode(
PyThreadState *tstate,
PyObject *_co, PyObject *globals, PyObject *locals,
PyFrameConstructor *desc, 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);
Py_ssize_t kwcount, int kwstep);

#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
extern int _PyEval_ThreadsInitialized(PyInterpreterState *interp);
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1275,7 +1275,7 @@ class C(object): pass
check(x, vsize('4Pi2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
# function
def func(): pass
check(func, size('13P'))
check(func, size('14P'))
class c():
@staticmethod
def foo():
Expand Down
33 changes: 7 additions & 26 deletions Objects/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,16 +307,16 @@ PyCFunction_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
static PyObject* _Py_HOT_FUNCTION
function_code_fastcall(PyThreadState *tstate, PyCodeObject *co,
PyObject *const *args, Py_ssize_t nargs,
PyObject *globals)
PyFunctionObject *func)
{
assert(tstate != NULL);
assert(globals != 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, globals, NULL);
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, func->func_globals, func->func_builtins, NULL);
if (f == NULL) {
return NULL;
}
Expand Down Expand Up @@ -357,14 +357,13 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,

PyThreadState *tstate = _PyThreadState_GET();
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
PyObject *globals = PyFunction_GET_GLOBALS(func);
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);

if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
(co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
{
if (argdefs == NULL && co->co_argcount == nargs) {
return function_code_fastcall(tstate, co, stack, nargs, globals);
return function_code_fastcall(tstate, co, stack, nargs, (PyFunctionObject *)func);
}
else if (nargs == 0 && argdefs != NULL
&& co->co_argcount == PyTuple_GET_SIZE(argdefs)) {
Expand All @@ -373,34 +372,16 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
stack = _PyTuple_ITEMS(argdefs);
return function_code_fastcall(tstate, co,
stack, PyTuple_GET_SIZE(argdefs),
globals);
(PyFunctionObject *)func);
}
}

PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func);
PyObject *closure = PyFunction_GET_CLOSURE(func);
PyObject *name = ((PyFunctionObject *)func) -> func_name;
PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname;

PyObject **d;
Py_ssize_t nd;
if (argdefs != NULL) {
d = _PyTuple_ITEMS(argdefs);
nd = PyTuple_GET_SIZE(argdefs);
assert(nd <= INT_MAX);
}
else {
d = NULL;
nd = 0;
}
return _PyEval_EvalCode(tstate,
(PyObject*)co, globals, (PyObject *)NULL,
PyFunction_AS_FRAME_CONSTRUCTOR(func), (PyObject *)NULL,
stack, nargs,
nkwargs ? _PyTuple_ITEMS(kwnames) : NULL,
stack + nargs,
nkwargs, 1,
d, (int)nd, kwdefs,
closure, name, qualname);
nkwargs, 1);
}


Expand Down
85 changes: 33 additions & 52 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ static PyMemberDef frame_memberlist[] = {
{NULL} /* Sentinel */
};


static struct _Py_frame_state *
get_frame_state(void)
{
Expand Down Expand Up @@ -816,73 +815,27 @@ frame_alloc(PyCodeObject *code)
}


static inline PyObject *
frame_get_builtins(PyFrameObject *back, PyObject *globals)
{
PyObject *builtins;

if (back != NULL && back->f_globals == globals) {
/* If we share the globals, we share the builtins.
Save a lookup and a call. */
builtins = back->f_builtins;
assert(builtins != NULL);
Py_INCREF(builtins);
return builtins;
}

builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
if (builtins != NULL && PyModule_Check(builtins)) {
builtins = PyModule_GetDict(builtins);
assert(builtins != NULL);
}
if (builtins != NULL) {
Py_INCREF(builtins);
return builtins;
}

if (PyErr_Occurred()) {
return NULL;
}

/* No builtins! Make up a minimal one.
Give them 'None', at least. */
builtins = PyDict_New();
if (builtins == NULL) {
return NULL;
}
if (PyDict_SetItemString(builtins, "None", Py_None) < 0) {
Py_DECREF(builtins);
return NULL;
}
return builtins;
}


PyFrameObject* _Py_HOT_FUNCTION
_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
PyObject *globals, PyObject *locals)
PyObject *globals, PyObject *builtins, PyObject *locals)
{
#ifdef Py_DEBUG
if (code == NULL || globals == NULL || !PyDict_Check(globals) ||
if (code == NULL || globals == NULL || builtins == NULL ||
(locals != NULL && !PyMapping_Check(locals))) {
PyErr_BadInternalCall();
return NULL;
}
#endif

PyFrameObject *back = tstate->frame;
PyObject *builtins = frame_get_builtins(back, globals);
if (builtins == NULL) {
return NULL;
}

PyFrameObject *f = frame_alloc(code);
if (f == NULL) {
Py_DECREF(builtins);
return NULL;
}

f->f_stackdepth = 0;
Py_INCREF(builtins);
f->f_builtins = builtins;
Py_XINCREF(back);
f->f_back = back;
Expand All @@ -902,8 +855,9 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
f->f_locals = locals;
}
else {
if (locals == NULL)
if (locals == NULL) {
locals = globals;
}
Py_INCREF(locals);
f->f_locals = locals;
}
Expand All @@ -925,7 +879,9 @@ PyFrameObject*
PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
PyObject *globals, PyObject *locals)
{
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, code, globals, locals);
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, code, globals, builtins, locals);
Py_DECREF(builtins);
if (f)
_PyObject_GC_TRACK(f);
return f;
Expand Down Expand Up @@ -1223,3 +1179,28 @@ PyFrame_GetBack(PyFrameObject *frame)
Py_XINCREF(back);
return back;
}

PyObject *_PyEval_BuiltinsFromGlobals(PyObject *globals) {
PyObject *builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
if (builtins) {
if (PyModule_Check(builtins)) {
builtins = PyModule_GetDict(builtins);
assert(builtins != NULL);
}
}
if (builtins == NULL) {
if (PyErr_Occurred()) {
return NULL;
}
/* No builtins! Make up a minimal one
Give them 'None', at least. */
builtins = PyDict_New();
if (builtins == NULL ||
PyDict_SetItemString(
builtins, "None", Py_None) < 0)
return NULL;
}
else
Py_INCREF(builtins);
return builtins;
}
13 changes: 11 additions & 2 deletions Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "Python.h"
#include "pycore_object.h"
#include "frameobject.h"
#include "code.h"
#include "structmember.h" // PyMemberDef

Expand Down Expand Up @@ -40,8 +41,14 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
op->func_weakreflist = NULL;
Py_INCREF(code);
op->func_code = code;
assert(globals != NULL);
Py_INCREF(globals);
op->func_globals = globals;
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
if (builtins == NULL) {
return NULL;
}
op->func_builtins = builtins;
op->func_name = ((PyCodeObject *)code)->co_name;
Py_INCREF(op->func_name);
op->func_defaults = NULL; /* No default arguments */
Expand Down Expand Up @@ -592,15 +599,16 @@ func_clear(PyFunctionObject *op)
{
Py_CLEAR(op->func_code);
Py_CLEAR(op->func_globals);
Py_CLEAR(op->func_module);
Py_CLEAR(op->func_builtins);
Py_CLEAR(op->func_name);
Py_CLEAR(op->func_qualname);
Py_CLEAR(op->func_module);
Py_CLEAR(op->func_defaults);
Py_CLEAR(op->func_kwdefaults);
Py_CLEAR(op->func_doc);
Py_CLEAR(op->func_dict);
Py_CLEAR(op->func_closure);
Py_CLEAR(op->func_annotations);
Py_CLEAR(op->func_qualname);
return 0;
}

Expand All @@ -627,6 +635,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg)
{
Py_VISIT(f->func_code);
Py_VISIT(f->func_globals);
Py_VISIT(f->func_builtins);
Py_VISIT(f->func_module);
Py_VISIT(f->func_defaults);
Py_VISIT(f->func_kwdefaults);
Expand Down
Loading
0