Skip to content

Commit d6c33fb

Browse files
authored
bpo-42990: Introduce 'frame constructor' struct to simplify API for PyEval_CodeEval and friends (GH-24298)
* Introduce 'frame constructor' to simplify API for frame creation * Embed struct using a macro to conform to PEP 7
1 parent 23a567c commit d6c33fb

File tree

8 files changed

+144
-156
lines changed

8 files changed

+144
-156
lines changed

Include/cpython/frameobject.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,
7272

7373
/* only internal use */
7474
PyFrameObject* _PyFrame_New_NoTrack(PyThreadState *, PyCodeObject *,
75-
PyObject *, PyObject *);
75+
PyObject *, PyObject *, PyObject *);
7676

7777

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

9494
PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);
95+
96+
PyObject *_PyEval_BuiltinsFromGlobals(PyObject *globals);

Include/funcobject.h

+19-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@
77
extern "C" {
88
#endif
99

10+
11+
#define COMMON_FIELDS(PREFIX) \
12+
PyObject *PREFIX ## globals; \
13+
PyObject *PREFIX ## builtins; \
14+
PyObject *PREFIX ## name; \
15+
PyObject *PREFIX ## qualname; \
16+
PyObject *PREFIX ## code; /* A code object, the __code__ attribute */ \
17+
PyObject *PREFIX ## defaults; /* NULL or a tuple */ \
18+
PyObject *PREFIX ## kwdefaults; /* NULL or a dict */ \
19+
PyObject *PREFIX ## closure; /* NULL or a tuple of cell objects */
20+
21+
typedef struct {
22+
COMMON_FIELDS(fc_)
23+
} PyFrameConstructor;
24+
1025
/* Function objects and code objects should not be confused with each other:
1126
*
1227
* Function objects are created by the execution of the 'def' statement.
@@ -20,18 +35,12 @@ extern "C" {
2035

2136
typedef struct {
2237
PyObject_HEAD
23-
PyObject *func_code; /* A code object, the __code__ attribute */
24-
PyObject *func_globals; /* A dictionary (other mappings won't do) */
25-
PyObject *func_defaults; /* NULL or a tuple */
26-
PyObject *func_kwdefaults; /* NULL or a dict */
27-
PyObject *func_closure; /* NULL or a tuple of cell objects */
38+
COMMON_FIELDS(func_)
2839
PyObject *func_doc; /* The __doc__ attribute, can be anything */
29-
PyObject *func_name; /* The __name__ attribute, a string object */
3040
PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
3141
PyObject *func_weakreflist; /* List of weak references */
3242
PyObject *func_module; /* The __module__ attribute, can be anything */
3343
PyObject *func_annotations; /* Annotations, a dict or NULL */
34-
PyObject *func_qualname; /* The qualified name */
3544
vectorcallfunc vectorcall;
3645

3746
/* Invariant:
@@ -84,6 +93,9 @@ PyAPI_FUNC(PyObject *) _PyFunction_Vectorcall(
8493
#define PyFunction_GET_ANNOTATIONS(func) \
8594
(((PyFunctionObject *)func) -> func_annotations)
8695

96+
#define PyFunction_AS_FRAME_CONSTRUCTOR(func) \
97+
((PyFrameConstructor *)&((PyFunctionObject *)(func))->func_globals)
98+
8799
/* The classmethod and staticmethod types lives here, too */
88100
PyAPI_DATA(PyTypeObject) PyClassMethod_Type;
89101
PyAPI_DATA(PyTypeObject) PyStaticMethod_Type;

Include/internal/pycore_ceval.h

+2-5
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,10 @@ _PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag)
4242

4343
extern PyObject *_PyEval_EvalCode(
4444
PyThreadState *tstate,
45-
PyObject *_co, PyObject *globals, PyObject *locals,
45+
PyFrameConstructor *desc, PyObject *locals,
4646
PyObject *const *args, Py_ssize_t argcount,
4747
PyObject *const *kwnames, PyObject *const *kwargs,
48-
Py_ssize_t kwcount, int kwstep,
49-
PyObject *const *defs, Py_ssize_t defcount,
50-
PyObject *kwdefs, PyObject *closure,
51-
PyObject *name, PyObject *qualname);
48+
Py_ssize_t kwcount, int kwstep);
5249

5350
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
5451
extern int _PyEval_ThreadsInitialized(PyInterpreterState *interp);

Lib/test/test_sys.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,7 @@ class C(object): pass
12801280
check(x, vsize('4Pi2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
12811281
# function
12821282
def func(): pass
1283-
check(func, size('13P'))
1283+
check(func, size('14P'))
12841284
class c():
12851285
@staticmethod
12861286
def foo():

Objects/call.c

+7-26
Original file line numberDiff line numberDiff line change
@@ -331,16 +331,16 @@ PyCFunction_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
331331
static PyObject* _Py_HOT_FUNCTION
332332
function_code_fastcall(PyThreadState *tstate, PyCodeObject *co,
333333
PyObject *const *args, Py_ssize_t nargs,
334-
PyObject *globals)
334+
PyFunctionObject *func)
335335
{
336336
assert(tstate != NULL);
337-
assert(globals != NULL);
337+
assert(func != NULL);
338338

339339
/* XXX Perhaps we should create a specialized
340340
_PyFrame_New_NoTrack() that doesn't take locals, but does
341341
take builtins without sanity checking them.
342342
*/
343-
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, globals, NULL);
343+
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, func->func_globals, func->func_builtins, NULL);
344344
if (f == NULL) {
345345
return NULL;
346346
}
@@ -381,14 +381,13 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
381381

382382
PyThreadState *tstate = _PyThreadState_GET();
383383
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
384-
PyObject *globals = PyFunction_GET_GLOBALS(func);
385384
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
386385

387386
if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
388387
(co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
389388
{
390389
if (argdefs == NULL && co->co_argcount == nargs) {
391-
return function_code_fastcall(tstate, co, stack, nargs, globals);
390+
return function_code_fastcall(tstate, co, stack, nargs, (PyFunctionObject *)func);
392391
}
393392
else if (nargs == 0 && argdefs != NULL
394393
&& co->co_argcount == PyTuple_GET_SIZE(argdefs)) {
@@ -397,34 +396,16 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
397396
stack = _PyTuple_ITEMS(argdefs);
398397
return function_code_fastcall(tstate, co,
399398
stack, PyTuple_GET_SIZE(argdefs),
400-
globals);
399+
(PyFunctionObject *)func);
401400
}
402401
}
403402

404-
PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func);
405-
PyObject *closure = PyFunction_GET_CLOSURE(func);
406-
PyObject *name = ((PyFunctionObject *)func) -> func_name;
407-
PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname;
408-
409-
PyObject **d;
410-
Py_ssize_t nd;
411-
if (argdefs != NULL) {
412-
d = _PyTuple_ITEMS(argdefs);
413-
nd = PyTuple_GET_SIZE(argdefs);
414-
assert(nd <= INT_MAX);
415-
}
416-
else {
417-
d = NULL;
418-
nd = 0;
419-
}
420403
return _PyEval_EvalCode(tstate,
421-
(PyObject*)co, globals, (PyObject *)NULL,
404+
PyFunction_AS_FRAME_CONSTRUCTOR(func), (PyObject *)NULL,
422405
stack, nargs,
423406
nkwargs ? _PyTuple_ITEMS(kwnames) : NULL,
424407
stack + nargs,
425-
nkwargs, 1,
426-
d, (int)nd, kwdefs,
427-
closure, name, qualname);
408+
nkwargs, 1);
428409
}
429410

430411

Objects/frameobject.c

+33-52
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ static PyMemberDef frame_memberlist[] = {
2222
{NULL} /* Sentinel */
2323
};
2424

25-
2625
static struct _Py_frame_state *
2726
get_frame_state(void)
2827
{
@@ -816,73 +815,27 @@ frame_alloc(PyCodeObject *code)
816815
}
817816

818817

819-
static inline PyObject *
820-
frame_get_builtins(PyFrameObject *back, PyObject *globals)
821-
{
822-
PyObject *builtins;
823-
824-
if (back != NULL && back->f_globals == globals) {
825-
/* If we share the globals, we share the builtins.
826-
Save a lookup and a call. */
827-
builtins = back->f_builtins;
828-
assert(builtins != NULL);
829-
Py_INCREF(builtins);
830-
return builtins;
831-
}
832-
833-
builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
834-
if (builtins != NULL && PyModule_Check(builtins)) {
835-
builtins = PyModule_GetDict(builtins);
836-
assert(builtins != NULL);
837-
}
838-
if (builtins != NULL) {
839-
Py_INCREF(builtins);
840-
return builtins;
841-
}
842-
843-
if (PyErr_Occurred()) {
844-
return NULL;
845-
}
846-
847-
/* No builtins! Make up a minimal one.
848-
Give them 'None', at least. */
849-
builtins = PyDict_New();
850-
if (builtins == NULL) {
851-
return NULL;
852-
}
853-
if (PyDict_SetItemString(builtins, "None", Py_None) < 0) {
854-
Py_DECREF(builtins);
855-
return NULL;
856-
}
857-
return builtins;
858-
}
859-
860-
861818
PyFrameObject* _Py_HOT_FUNCTION
862819
_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
863-
PyObject *globals, PyObject *locals)
820+
PyObject *globals, PyObject *builtins, PyObject *locals)
864821
{
865822
#ifdef Py_DEBUG
866-
if (code == NULL || globals == NULL || !PyDict_Check(globals) ||
823+
if (code == NULL || globals == NULL || builtins == NULL ||
867824
(locals != NULL && !PyMapping_Check(locals))) {
868825
PyErr_BadInternalCall();
869826
return NULL;
870827
}
871828
#endif
872829

873830
PyFrameObject *back = tstate->frame;
874-
PyObject *builtins = frame_get_builtins(back, globals);
875-
if (builtins == NULL) {
876-
return NULL;
877-
}
878831

879832
PyFrameObject *f = frame_alloc(code);
880833
if (f == NULL) {
881-
Py_DECREF(builtins);
882834
return NULL;
883835
}
884836

885837
f->f_stackdepth = 0;
838+
Py_INCREF(builtins);
886839
f->f_builtins = builtins;
887840
Py_XINCREF(back);
888841
f->f_back = back;
@@ -902,8 +855,9 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
902855
f->f_locals = locals;
903856
}
904857
else {
905-
if (locals == NULL)
858+
if (locals == NULL) {
906859
locals = globals;
860+
}
907861
Py_INCREF(locals);
908862
f->f_locals = locals;
909863
}
@@ -925,7 +879,9 @@ PyFrameObject*
925879
PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
926880
PyObject *globals, PyObject *locals)
927881
{
928-
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, code, globals, locals);
882+
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
883+
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, code, globals, builtins, locals);
884+
Py_DECREF(builtins);
929885
if (f)
930886
_PyObject_GC_TRACK(f);
931887
return f;
@@ -1223,3 +1179,28 @@ PyFrame_GetBack(PyFrameObject *frame)
12231179
Py_XINCREF(back);
12241180
return back;
12251181
}
1182+
1183+
PyObject *_PyEval_BuiltinsFromGlobals(PyObject *globals) {
1184+
PyObject *builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
1185+
if (builtins) {
1186+
if (PyModule_Check(builtins)) {
1187+
builtins = PyModule_GetDict(builtins);
1188+
assert(builtins != NULL);
1189+
}
1190+
}
1191+
if (builtins == NULL) {
1192+
if (PyErr_Occurred()) {
1193+
return NULL;
1194+
}
1195+
/* No builtins! Make up a minimal one
1196+
Give them 'None', at least. */
1197+
builtins = PyDict_New();
1198+
if (builtins == NULL ||
1199+
PyDict_SetItemString(
1200+
builtins, "None", Py_None) < 0)
1201+
return NULL;
1202+
}
1203+
else
1204+
Py_INCREF(builtins);
1205+
return builtins;
1206+
}

Objects/funcobject.c

+11-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "Python.h"
55
#include "pycore_object.h"
6+
#include "frameobject.h"
67
#include "code.h"
78
#include "structmember.h" // PyMemberDef
89

@@ -40,8 +41,14 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
4041
op->func_weakreflist = NULL;
4142
Py_INCREF(code);
4243
op->func_code = code;
44+
assert(globals != NULL);
4345
Py_INCREF(globals);
4446
op->func_globals = globals;
47+
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
48+
if (builtins == NULL) {
49+
return NULL;
50+
}
51+
op->func_builtins = builtins;
4552
op->func_name = ((PyCodeObject *)code)->co_name;
4653
Py_INCREF(op->func_name);
4754
op->func_defaults = NULL; /* No default arguments */
@@ -592,15 +599,16 @@ func_clear(PyFunctionObject *op)
592599
{
593600
Py_CLEAR(op->func_code);
594601
Py_CLEAR(op->func_globals);
595-
Py_CLEAR(op->func_module);
602+
Py_CLEAR(op->func_builtins);
596603
Py_CLEAR(op->func_name);
604+
Py_CLEAR(op->func_qualname);
605+
Py_CLEAR(op->func_module);
597606
Py_CLEAR(op->func_defaults);
598607
Py_CLEAR(op->func_kwdefaults);
599608
Py_CLEAR(op->func_doc);
600609
Py_CLEAR(op->func_dict);
601610
Py_CLEAR(op->func_closure);
602611
Py_CLEAR(op->func_annotations);
603-
Py_CLEAR(op->func_qualname);
604612
return 0;
605613
}
606614

@@ -627,6 +635,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg)
627635
{
628636
Py_VISIT(f->func_code);
629637
Py_VISIT(f->func_globals);
638+
Py_VISIT(f->func_builtins);
630639
Py_VISIT(f->func_module);
631640
Py_VISIT(f->func_defaults);
632641
Py_VISIT(f->func_kwdefaults);

0 commit comments

Comments
 (0)