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
diff --git a/Objects/call.c b/Objects/call.c
index 1fb85ef..7972693 100644
--- a/Objects/call.c
+++ b/Objects/call.c
@@ -331,16 +331,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;
     }
@@ -381,14 +381,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)) {
@@ -397,34 +396,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);
 }
 
 
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 4c5eaa2..45a275b 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -22,7 +22,6 @@ static PyMemberDef frame_memberlist[] = {
     {NULL}      /* Sentinel */
 };
 
-
 static struct _Py_frame_state *
 get_frame_state(void)
 {
@@ -816,54 +815,12 @@ 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;
@@ -871,18 +828,14 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
 #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;
@@ -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;
     }
@@ -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;
@@ -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;
+}
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index e7961b3..f839d7b 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -3,6 +3,7 @@
 
 #include "Python.h"
 #include "pycore_object.h"
+#include "frameobject.h"
 #include "code.h"
 #include "structmember.h"         // PyMemberDef
 
@@ -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 */
@@ -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;
 }
 
@@ -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);