summaryrefslogtreecommitdiffstats
path: root/Include
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2021-06-07 18:22:26 (GMT)
committerGitHub <noreply@github.com>2021-06-07 18:22:26 (GMT)
commit2ab27c4af4ddf7528e1375e77c787c7fbb09b5e6 (patch)
treed3983e5282f575560cb7449fae4785447fdfff14 /Include
parent001eb520b5757294dc455c900d94b7b153de6cdd (diff)
downloadcpython-2ab27c4af4ddf7528e1375e77c787c7fbb09b5e6.zip
cpython-2ab27c4af4ddf7528e1375e77c787c7fbb09b5e6.tar.gz
cpython-2ab27c4af4ddf7528e1375e77c787c7fbb09b5e6.tar.bz2
bpo-43693: Un-revert commits 2c1e258 and b2bf2bc. (gh-26577)
These were reverted in gh-26530 (commit 17c4edc) due to refleaks. * 2c1e258 - Compute deref offsets in compiler (gh-25152) * b2bf2bc - Add new internal code objects fields: co_fastlocalnames and co_fastlocalkinds. (gh-26388) This change fixes the refleaks. https://bugs.python.org/issue43693
Diffstat (limited to 'Include')
-rw-r--r--Include/cpython/code.h24
-rw-r--r--Include/internal/pycore_code.h63
2 files changed, 78 insertions, 9 deletions
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 98d728b..c81f9f3 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -3,6 +3,8 @@
#endif
typedef uint16_t _Py_CODEUNIT;
+// Each oparg must fit in the second half of _Py_CODEUNIT, hence 8 bits.
+#define _Py_MAX_OPARG 255
#ifdef WORDS_BIGENDIAN
# define _Py_OPCODE(word) ((word) >> 8)
@@ -16,6 +18,11 @@ typedef uint16_t _Py_CODEUNIT;
typedef struct _PyOpcache _PyOpcache;
+
+// These are duplicated from pycore_code.h.
+typedef unsigned char _PyLocalsPlusKind;
+typedef _PyLocalsPlusKind *_PyLocalsPlusKinds;
+
/* Bytecode object */
struct PyCodeObject {
PyObject_HEAD
@@ -47,7 +54,9 @@ struct PyCodeObject {
// The hottest fields (in the eval loop) are grouped here at the top.
PyObject *co_consts; /* list (constants used) */
PyObject *co_names; /* list of strings (names used) */
- _Py_CODEUNIT *co_firstinstr; /* Pointer to first instruction, used for quickening */
+ _Py_CODEUNIT *co_firstinstr; /* Pointer to first instruction, used for quickening.
+ Unlike the other "hot" fields, this one is
+ actually derived from co_code. */
PyObject *co_exceptiontable; /* Byte string encoding exception handling table */
int co_flags; /* CO_..., see below */
int co_warmup; /* Warmup counter for quickening */
@@ -59,9 +68,8 @@ struct PyCodeObject {
int co_stacksize; /* #entries needed for evaluation stack */
int co_firstlineno; /* first source line number */
PyObject *co_code; /* instruction opcodes */
- PyObject *co_varnames; /* tuple of strings (local variable names) */
- PyObject *co_cellvars; /* tuple of strings (cell variable names) */
- PyObject *co_freevars; /* tuple of strings (free variable names) */
+ PyObject *co_localsplusnames; /* tuple mapping offsets to names */
+ _PyLocalsPlusKinds co_localspluskinds; /* array mapping to local kinds */
PyObject *co_filename; /* unicode (where it was loaded from) */
PyObject *co_name; /* unicode (name, for reference) */
PyObject *co_linetable; /* string (encoding addr<->lineno mapping) See
@@ -70,11 +78,15 @@ struct PyCodeObject {
/* These fields are set with computed values on new code objects. */
int *co_cell2arg; /* Maps cell vars which are arguments. */
- // These are redundant but offer some performance benefit.
+ // redundant values (derived from co_localsplusnames and co_localspluskinds)
int co_nlocalsplus; /* number of local + cell + free variables */
int co_nlocals; /* number of local variables */
int co_ncellvars; /* number of cell variables */
int co_nfreevars; /* number of free variables */
+ // lazily-computed values
+ PyObject *co_varnames; /* tuple of strings (local variable names) */
+ PyObject *co_cellvars; /* tuple of strings (cell variable names) */
+ PyObject *co_freevars; /* tuple of strings (free variable names) */
/* The remaining fields are zeroed out on new code objects. */
@@ -152,7 +164,7 @@ struct PyCodeObject {
PyAPI_DATA(PyTypeObject) PyCode_Type;
#define PyCode_Check(op) Py_IS_TYPE(op, &PyCode_Type)
-#define PyCode_GetNumFree(op) (PyTuple_GET_SIZE((op)->co_freevars))
+#define PyCode_GetNumFree(op) ((op)->co_nfreevars)
/* Public interface */
PyAPI_FUNC(PyCodeObject *) PyCode_New(
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index cb72350..d1ff597 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -150,6 +150,58 @@ int _Py_Quicken(PyCodeObject *code);
extern Py_ssize_t _Py_QuickenedCount;
+
+/* "Locals plus" for a code object is the set of locals + cell vars +
+ * free vars. This relates to variable names as well as offsets into
+ * the "fast locals" storage array of execution frames. The compiler
+ * builds the list of names, their offsets, and the corresponding
+ * kind of local.
+ *
+ * Those kinds represent the source of the initial value and the
+ * variable's scope (as related to closures). A "local" is an
+ * argument or other variable defined in the current scope. A "free"
+ * variable is one that is defined in an outer scope and comes from
+ * the function's closure. A "cell" variable is a local that escapes
+ * into an inner function as part of a closure, and thus must be
+ * wrapped in a cell. Any "local" can also be a "cell", but the
+ * "free" kind is mutually exclusive with both.
+ */
+
+// We would use an enum if C let us specify the storage type.
+typedef unsigned char _PyLocalsPlusKind;
+/* Note that these all fit within _PyLocalsPlusKind, as do combinations. */
+// Later, we will use the smaller numbers to differentiate the different
+// kinds of locals (e.g. pos-only arg, varkwargs, local-only).
+#define CO_FAST_LOCAL 0x20
+#define CO_FAST_CELL 0x40
+#define CO_FAST_FREE 0x80
+
+typedef _PyLocalsPlusKind *_PyLocalsPlusKinds;
+
+static inline int
+_PyCode_InitLocalsPlusKinds(int num, _PyLocalsPlusKinds *pkinds)
+{
+ if (num == 0) {
+ *pkinds = NULL;
+ return 0;
+ }
+ _PyLocalsPlusKinds kinds = PyMem_NEW(_PyLocalsPlusKind, num);
+ if (kinds == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ *pkinds = kinds;
+ return 0;
+}
+
+static inline void
+_PyCode_ClearLocalsPlusKinds(_PyLocalsPlusKinds kinds)
+{
+ if (kinds != NULL) {
+ PyMem_Free(kinds);
+ }
+}
+
struct _PyCodeConstructor {
/* metadata */
PyObject *filename;
@@ -166,13 +218,13 @@ struct _PyCodeConstructor {
PyObject *names;
/* mapping frame offsets to information */
- PyObject *varnames;
- PyObject *cellvars;
- PyObject *freevars;
+ PyObject *localsplusnames;
+ _PyLocalsPlusKinds localspluskinds;
/* args (within varnames) */
int argcount;
int posonlyargcount;
+ // XXX Replace argcount with posorkwargcount (argcount - posonlyargcount).
int kwonlyargcount;
/* needed to create the frame */
@@ -199,6 +251,11 @@ PyAPI_FUNC(PyCodeObject *) _PyCode_New(struct _PyCodeConstructor *);
int _PyCode_InitOpcache(PyCodeObject *co);
+/* Getters for internal PyCodeObject data. */
+PyAPI_FUNC(PyObject *) _PyCode_GetVarnames(PyCodeObject *);
+PyAPI_FUNC(PyObject *) _PyCode_GetCellvars(PyCodeObject *);
+PyAPI_FUNC(PyObject *) _PyCode_GetFreevars(PyCodeObject *);
+
#ifdef __cplusplus
}