From 15aecf8dd70f82eb507d74fae9662072a377bdc8 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 4 Jan 2023 15:41:39 +0000 Subject: [PATCH] GH-100719: Remove the `co_nplaincellvars` field from code objects. (GH-100721) --- Include/cpython/code.h | 6 +++++- .../2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst | 2 ++ Objects/codeobject.c | 14 ++++---------- Objects/frameobject.c | 2 +- Objects/typeobject.c | 2 +- Python/bytecodes.c | 2 +- Python/ceval.c | 2 +- Python/compile.c | 2 +- Python/generated_cases.c.h | 2 +- Tools/build/deepfreeze.py | 8 ++------ 10 files changed, 19 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 1c619322926ef4..0cf49f06c87732 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -87,7 +87,6 @@ typedef struct { int co_nlocalsplus; /* number of local + cell + free variables */ \ int co_framesize; /* Size of frame in words */ \ int co_nlocals; /* number of local variables */ \ - int co_nplaincellvars; /* number of non-arg cell variables */ \ int co_ncellvars; /* total number of cell variables */ \ int co_nfreevars; /* number of free variables */ \ uint32_t co_version; /* version number */ \ @@ -157,6 +156,11 @@ static inline Py_ssize_t PyCode_GetNumFree(PyCodeObject *op) { return op->co_nfreevars; } +static inline int PyCode_GetFirstFree(PyCodeObject *op) { + assert(PyCode_Check(op)); + return op->co_nlocalsplus - op->co_nfreevars; +} + #define _PyCode_CODE(CO) _Py_RVALUE((_Py_CODEUNIT *)(CO)->co_code_adaptive) #define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT)) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst new file mode 100644 index 00000000000000..bb5d5619c2d0a8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst @@ -0,0 +1,2 @@ +Removed the co_nplaincellvars field from the code object, as it is +redundant. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index e174c6fee9cc24..6facfef4c9b473 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -247,11 +247,10 @@ _Py_set_localsplus_info(int offset, PyObject *name, _PyLocals_Kind kind, static void get_localsplus_counts(PyObject *names, PyObject *kinds, - int *pnlocals, int *pnplaincellvars, int *pncellvars, + int *pnlocals, int *pncellvars, int *pnfreevars) { int nlocals = 0; - int nplaincellvars = 0; int ncellvars = 0; int nfreevars = 0; Py_ssize_t nlocalsplus = PyTuple_GET_SIZE(names); @@ -265,7 +264,6 @@ get_localsplus_counts(PyObject *names, PyObject *kinds, } else if (kind & CO_FAST_CELL) { ncellvars += 1; - nplaincellvars += 1; } else if (kind & CO_FAST_FREE) { nfreevars += 1; @@ -274,9 +272,6 @@ get_localsplus_counts(PyObject *names, PyObject *kinds, if (pnlocals != NULL) { *pnlocals = nlocals; } - if (pnplaincellvars != NULL) { - *pnplaincellvars = nplaincellvars; - } if (pncellvars != NULL) { *pncellvars = ncellvars; } @@ -351,7 +346,7 @@ _PyCode_Validate(struct _PyCodeConstructor *con) * here to avoid the possibility of overflow (however remote). */ int nlocals; get_localsplus_counts(con->localsplusnames, con->localspluskinds, - &nlocals, NULL, NULL, NULL); + &nlocals, NULL, NULL); int nplainlocals = nlocals - con->argcount - con->kwonlyargcount - @@ -371,9 +366,9 @@ static void init_code(PyCodeObject *co, struct _PyCodeConstructor *con) { int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames); - int nlocals, nplaincellvars, ncellvars, nfreevars; + int nlocals, ncellvars, nfreevars; get_localsplus_counts(con->localsplusnames, con->localspluskinds, - &nlocals, &nplaincellvars, &ncellvars, &nfreevars); + &nlocals, &ncellvars, &nfreevars); co->co_filename = Py_NewRef(con->filename); co->co_name = Py_NewRef(con->name); @@ -401,7 +396,6 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_nlocalsplus = nlocalsplus; co->co_nlocals = nlocals; co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE; - co->co_nplaincellvars = nplaincellvars; co->co_ncellvars = ncellvars; co->co_nfreevars = nfreevars; co->co_version = _Py_next_func_version; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 98f0b3838723da..8d7ee4b81eb7dc 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1119,7 +1119,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame) /* Free vars have not been initialized -- Do that */ PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; - int offset = co->co_nlocals + co->co_nplaincellvars; + int offset = PyCode_GetFirstFree(co); for (int i = 0; i < co->co_nfreevars; ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); frame->localsplus[offset + i] = Py_NewRef(o); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 43633f044751ca..f2d78cf50913ec 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9514,7 +9514,7 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co, // Look for __class__ in the free vars. PyTypeObject *type = NULL; - int i = co->co_nlocals + co->co_nplaincellvars; + int i = PyCode_GetFirstFree(co); for (; i < co->co_nlocalsplus; i++) { assert((_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_FREE) != 0); PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 839fac3fcd1176..dec122a78223fe 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1357,8 +1357,8 @@ dummy_func( PyCodeObject *co = frame->f_code; assert(PyFunction_Check(frame->f_funcobj)); PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; - int offset = co->co_nlocals + co->co_nplaincellvars; assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; for (int i = 0; i < oparg; ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); frame->localsplus[offset + i] = Py_NewRef(o); diff --git a/Python/ceval.c b/Python/ceval.c index 45f42800d7ce58..54df1c512502eb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3417,7 +3417,7 @@ format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg) if (_PyErr_Occurred(tstate)) return; name = PyTuple_GET_ITEM(co->co_localsplusnames, oparg); - if (oparg < co->co_nplaincellvars + co->co_nlocals) { + if (oparg < PyCode_GetFirstFree(co)) { format_exc_check_arg(tstate, PyExc_UnboundLocalError, UNBOUNDLOCAL_ERROR_MSG, name); } else { diff --git a/Python/compile.c b/Python/compile.c index cbbdfb9e946772..c2e77fefb084e8 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2260,7 +2260,7 @@ compiler_make_closure(struct compiler *c, location loc, qualname = co->co_name; if (co->co_nfreevars) { - int i = co->co_nlocals + co->co_nplaincellvars; + int i = PyCode_GetFirstFree(co); for (; i < co->co_nlocalsplus; ++i) { /* Bypass com_addop_varname because it will generate LOAD_DEREF but LOAD_CLOSURE is needed. diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ed89e90b7c564d..3218bd091433f9 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1575,8 +1575,8 @@ PyCodeObject *co = frame->f_code; assert(PyFunction_Check(frame->f_funcobj)); PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; - int offset = co->co_nlocals + co->co_nplaincellvars; assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; for (int i = 0; i < oparg; ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); frame->localsplus[offset + i] = Py_NewRef(o); diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py index e4b422820f7db7..511b26a5ce3dc7 100644 --- a/Tools/build/deepfreeze.py +++ b/Tools/build/deepfreeze.py @@ -61,7 +61,6 @@ def get_localsplus_counts(code: types.CodeType, names: Tuple[str, ...], kinds: bytes) -> Tuple[int, int, int, int]: nlocals = 0 - nplaincellvars = 0 ncellvars = 0 nfreevars = 0 assert len(names) == len(kinds) @@ -72,15 +71,13 @@ def get_localsplus_counts(code: types.CodeType, ncellvars += 1 elif kind & CO_FAST_CELL: ncellvars += 1 - nplaincellvars += 1 elif kind & CO_FAST_FREE: nfreevars += 1 assert nlocals == len(code.co_varnames) == code.co_nlocals, \ (nlocals, len(code.co_varnames), code.co_nlocals) assert ncellvars == len(code.co_cellvars) assert nfreevars == len(code.co_freevars) - assert len(names) == nlocals + nplaincellvars + nfreevars - return nlocals, nplaincellvars, ncellvars, nfreevars + return nlocals, ncellvars, nfreevars PyUnicode_1BYTE_KIND = 1 @@ -243,7 +240,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str: co_localsplusnames = self.generate(name + "_localsplusnames", localsplusnames) co_localspluskinds = self.generate(name + "_localspluskinds", localspluskinds) # Derived values - nlocals, nplaincellvars, ncellvars, nfreevars = \ + nlocals, ncellvars, nfreevars = \ get_localsplus_counts(code, localsplusnames, localspluskinds) co_code_adaptive = make_string_literal(code.co_code) self.write("static") @@ -268,7 +265,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.field(code, "co_firstlineno") self.write(f".co_nlocalsplus = {len(localsplusnames)},") self.field(code, "co_nlocals") - self.write(f".co_nplaincellvars = {nplaincellvars},") self.write(f".co_ncellvars = {ncellvars},") self.write(f".co_nfreevars = {nfreevars},") self.write(f".co_version = {next_code_version},")