From 7c3738d2fb1bbbbbb70666c185e8163c019d6a72 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 10 Nov 2021 12:57:42 +0000 Subject: [PATCH 1/2] Move function object struct to an internal header. --- Include/cpython/funcobject.h | 80 +++---------------- Include/internal/pycore_ceval.h | 1 + Include/internal/pycore_frame.h | 2 + Include/internal/pycore_function.h | 63 +++++++++++++++ .../2021-11-10-13-08-41.bpo-45753.WEDzYA.rst | 4 + Objects/call.c | 1 + Python/bltinmodule.c | 1 + Python/ceval.c | 4 +- Python/specialize.c | 1 + 9 files changed, 85 insertions(+), 72 deletions(-) create mode 100644 Include/internal/pycore_function.h create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-11-10-13-08-41.bpo-45753.WEDzYA.rst diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 60b702218a1f40..bd508ebfb7df00 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -7,56 +7,7 @@ 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. - * They reference a code object in their __code__ attribute, which is a - * purely syntactic object, i.e. nothing more than a compiled version of some - * source code lines. There is one code object per source code "fragment", - * but each code object can be referenced by zero or many function objects - * depending only on how many times the 'def' statement in the source was - * executed so far. - */ - -typedef struct { - PyObject_HEAD - COMMON_FIELDS(func_) - PyObject *func_doc; /* The __doc__ attribute, can be anything */ - 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 */ - vectorcallfunc vectorcall; - /* Version number for use by specializer. - * Can set to non-zero when we want to specialize. - * Will be set to zero if any of these change: - * defaults - * kwdefaults (only if the object changes, not the contents of the dict) - * code - * annotations */ - uint32_t func_version; - - /* Invariant: - * func_closure contains the bindings for func_code->co_freevars, so - * PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code) - * (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0). - */ -} PyFunctionObject; +typedef struct _functionobject PyFunctionObject; PyAPI_DATA(PyTypeObject) PyFunction_Type; @@ -82,27 +33,14 @@ PyAPI_FUNC(PyObject *) _PyFunction_Vectorcall( size_t nargsf, PyObject *kwnames); -uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func); - -/* Macros for direct access to these values. Type checks are *not* - done, so use with care. */ -#define PyFunction_GET_CODE(func) \ - (((PyFunctionObject *)func) -> func_code) -#define PyFunction_GET_GLOBALS(func) \ - (((PyFunctionObject *)func) -> func_globals) -#define PyFunction_GET_MODULE(func) \ - (((PyFunctionObject *)func) -> func_module) -#define PyFunction_GET_DEFAULTS(func) \ - (((PyFunctionObject *)func) -> func_defaults) -#define PyFunction_GET_KW_DEFAULTS(func) \ - (((PyFunctionObject *)func) -> func_kwdefaults) -#define PyFunction_GET_CLOSURE(func) \ - (((PyFunctionObject *)func) -> func_closure) -#define PyFunction_GET_ANNOTATIONS(func) \ - (((PyFunctionObject *)func) -> func_annotations) - -#define PyFunction_AS_FRAME_CONSTRUCTOR(func) \ - ((PyFrameConstructor *)&((PyFunctionObject *)(func))->func_globals) +/* Macros for backwards compatibility. */ +#define PyFunction_GET_CODE(func) PyFunction_GetCode(func) +#define PyFunction_GET_GLOBALS(func) PyFunction_GetGlobals(func) +#define PyFunction_GET_MODULE(func) PyFunction_GetModule(func) +#define PyFunction_GET_DEFAULTS(func) PyFunction_GetDefaults(func) +#define PyFunction_GET_KW_DEFAULTS(func) PyFunction_GetKwDefaults(func) +#define PyFunction_GET_CLOSURE(func) PyFunction_GetClosure(func) +#define PyFunction_GET_ANNOTATIONS(func) PyFunction_GetAnnotations(func) /* The classmethod and staticmethod types lives here, too */ PyAPI_DATA(PyTypeObject) PyClassMethod_Type; diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 53580b99d33ac6..d5c15222ff3d0f 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -12,6 +12,7 @@ extern "C" { struct pyruntimestate; struct _ceval_runtime_state; +#include "pycore_function.h" // PyFrameConstructor #include "pycore_interp.h" // PyInterpreterState.eval_frame #include "pycore_pystate.h" // _PyThreadState_GET() diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index b025ca0b8d766c..a7b061a01e5adb 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -4,6 +4,8 @@ extern "C" { #endif +#include "pycore_function.h" // PyFrameConstructor + /* These values are chosen so that the inline functions below all * compare f_state to zero. */ diff --git a/Include/internal/pycore_function.h b/Include/internal/pycore_function.h new file mode 100644 index 00000000000000..9d8c61f32c9039 --- /dev/null +++ b/Include/internal/pycore_function.h @@ -0,0 +1,63 @@ +#ifndef PYCORE_FUNCTION_H +#define PYCORE_FUNCTION_H + +#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. + * They reference a code object in their __code__ attribute, which is a + * purely syntactic object, i.e. nothing more than a compiled version of some + * source code lines. There is one code object per source code "fragment", + * but each code object can be referenced by zero or many function objects + * depending only on how many times the 'def' statement in the source was + * executed so far. + */ + +typedef struct _functionobject { + PyObject_HEAD + COMMON_FIELDS(func_) + PyObject *func_doc; /* The __doc__ attribute, can be anything */ + 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 */ + vectorcallfunc vectorcall; + /* Version number for use by specializer. + * Can set to non-zero when we want to specialize. + * Will be set to zero if any of these change: + * defaults + * kwdefaults (only if the object changes, not the contents of the dict) + * code + * annotations */ + uint32_t func_version; + + /* Invariant: + * func_closure contains the bindings for func_code->co_freevars, so + * PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code) + * (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0). + */ +} PyFunctionObject; + +#define PyFunction_AS_FRAME_CONSTRUCTOR(func) \ + ((PyFrameConstructor *)&((PyFunctionObject *)(func))->func_globals) + +uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func); + + +#ifdef __cplusplus +} +#endif +#endif // PYCORE_FUNCTION_H diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-11-10-13-08-41.bpo-45753.WEDzYA.rst b/Misc/NEWS.d/next/Core and Builtins/2021-11-10-13-08-41.bpo-45753.WEDzYA.rst new file mode 100644 index 00000000000000..89bf2afe53e6fa --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-11-10-13-08-41.bpo-45753.WEDzYA.rst @@ -0,0 +1,4 @@ +Move the PyFunctionObject struct to an internal header. + +The meaning to the internal fields of the function was never defined and +could result in subtle bugs if misused. This move makes that clearer. diff --git a/Objects/call.c b/Objects/call.c index 5e55518b04cbfe..7e0c1e4bb876db 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -1,6 +1,7 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgsTstate() #include "pycore_ceval.h" // _PyEval_EvalFrame() +#include "pycore_function.h" // PyFrameConstructor #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pystate.h" // _PyThreadState_GET() diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 9e3b25c59a5505..c4938761b99de3 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -5,6 +5,7 @@ #include "pycore_ast.h" // _PyAST_Validate() #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_compile.h" // _PyAST_Compile() +#include "pycore_function.h" // PyFrameConstructor #include "pycore_object.h" // _Py_AddToAllObjects() #include "pycore_pyerrors.h" // _PyErr_NoMemory() #include "pycore_pystate.h" // _PyThreadState_GET() diff --git a/Python/ceval.c b/Python/ceval.c index c9f6638afa0e46..4e6dbb43a674d7 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -14,6 +14,7 @@ #include "pycore_call.h" // _PyObject_FastCallDictTstate() #include "pycore_ceval.h" // _PyEval_SignalAsyncExc() #include "pycore_code.h" +#include "pycore_function.h" #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_object.h" // _PyObject_GC_TRACK() @@ -4625,7 +4626,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr } // Check if the call can be inlined or not if (Py_TYPE(function) == &PyFunction_Type && tstate->interp->eval_frame == NULL) { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags; + PyFunctionObject *func = (PyFunctionObject *)function; + int code_flags = ((PyCodeObject*)func->func_code)->co_flags; int is_generator = code_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); if (!is_generator) { PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : PyFunction_GET_GLOBALS(function); diff --git a/Python/specialize.c b/Python/specialize.c index 162728314e1003..f180b786e914ca 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1,5 +1,6 @@ #include "Python.h" #include "pycore_code.h" +#include "pycore_function.h" #include "pycore_dict.h" #include "pycore_long.h" #include "pycore_moduleobject.h" From 2b0075c822da0e49a3c768467a54d16651ae2cf7 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 10 Nov 2021 13:15:01 +0000 Subject: [PATCH 2/2] Include core_frame and core_function headers in build files. --- Makefile.pre.in | 2 ++ PCbuild/pythoncore.vcxproj | 2 ++ PCbuild/pythoncore.vcxproj.filters | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/Makefile.pre.in b/Makefile.pre.in index 7e959b01906bc8..29f5f8a448e99c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1256,6 +1256,8 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_dtoa.h \ $(srcdir)/Include/internal/pycore_fileutils.h \ $(srcdir)/Include/internal/pycore_floatobject.h \ + $(srcdir)/Include/internal/pycore_frame.h \ + $(srcdir)/Include/internal/pycore_function.h \ $(srcdir)/Include/internal/pycore_format.h \ $(srcdir)/Include/internal/pycore_getopt.h \ $(srcdir)/Include/internal/pycore_gil.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index b65998186927b6..943e93d6f79e06 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -192,6 +192,8 @@ + + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 62aab5bccf9ef2..e5e7825cb75ac6 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -525,6 +525,12 @@ Include\internal + + Include\internal + + + Include\internal + Include\internal