summaryrefslogtreecommitdiff
path: root/Python/compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c169
1 files changed, 161 insertions, 8 deletions
diff --git a/Python/compile.c b/Python/compile.c
index beceeea86b..acb5cfe29b 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -160,6 +160,8 @@ struct compiler {
int c_interactive; /* true if in interactive mode */
int c_nestlevel;
+ PyObject *c_const_cache; /* Python dict holding all constants,
+ including names tuple */
struct compiler_unit *u; /* compiler state for current block */
PyObject *c_stack; /* Python list holding compiler_unit ptrs */
PyArena *c_arena; /* pointer to memory allocation arena */
@@ -285,9 +287,16 @@ compiler_init(struct compiler *c)
{
memset(c, 0, sizeof(struct compiler));
+ c->c_const_cache = PyDict_New();
+ if (!c->c_const_cache) {
+ return 0;
+ }
+
c->c_stack = PyList_New(0);
- if (!c->c_stack)
+ if (!c->c_stack) {
+ Py_CLEAR(c->c_const_cache);
return 0;
+ }
return 1;
}
@@ -387,6 +396,7 @@ compiler_free(struct compiler *c)
if (c->c_future)
PyObject_Free(c->c_future);
Py_XDECREF(c->c_filename);
+ Py_DECREF(c->c_const_cache);
Py_DECREF(c->c_stack);
}
@@ -1179,18 +1189,121 @@ compiler_add_o(struct compiler *c, PyObject *dict, PyObject *o)
return arg;
}
+// Merge const *o* recursively and return constant key object.
+static PyObject*
+merge_consts_recursive(struct compiler *c, PyObject *o)
+{
+ // None and Ellipsis are singleton, and key is the singleton.
+ // No need to merge object and key.
+ if (o == Py_None || o == Py_Ellipsis) {
+ Py_INCREF(o);
+ return o;
+ }
+
+ PyObject *key = _PyCode_ConstantKey(o);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ // t is borrowed reference
+ PyObject *t = PyDict_SetDefault(c->c_const_cache, key, key);
+ if (t != key) {
+ Py_INCREF(t);
+ Py_DECREF(key);
+ return t;
+ }
+
+ if (PyTuple_CheckExact(o)) {
+ Py_ssize_t i, len = PyTuple_GET_SIZE(o);
+ for (i = 0; i < len; i++) {
+ PyObject *item = PyTuple_GET_ITEM(o, i);
+ PyObject *u = merge_consts_recursive(c, item);
+ if (u == NULL) {
+ Py_DECREF(key);
+ return NULL;
+ }
+
+ // See _PyCode_ConstantKey()
+ PyObject *v; // borrowed
+ if (PyTuple_CheckExact(u)) {
+ v = PyTuple_GET_ITEM(u, 1);
+ }
+ else {
+ v = u;
+ }
+ if (v != item) {
+ Py_INCREF(v);
+ PyTuple_SET_ITEM(o, i, v);
+ Py_DECREF(item);
+ }
+
+ Py_DECREF(u);
+ }
+ }
+ else if (PyFrozenSet_CheckExact(o)) {
+ // We register items in the frozenset, but don't rewrite
+ // the frozenset when the item is already registered
+ // because frozenset is rare and difficult.
+
+ // *key* is tuple. And it's first item is frozenset of
+ // constant keys.
+ // See _PyCode_ConstantKey() for detail.
+ assert(PyTuple_CheckExact(key));
+ assert(PyTuple_GET_SIZE(key) == 2);
+
+ Py_ssize_t len = PySet_GET_SIZE(o);
+ if (len == 0) {
+ return key;
+ }
+ PyObject *tuple = PyTuple_New(len);
+ if (tuple == NULL) {
+ Py_DECREF(key);
+ return NULL;
+ }
+ Py_ssize_t i = 0, pos = 0;
+ PyObject *item;
+ Py_hash_t hash;
+ while (_PySet_NextEntry(o, &pos, &item, &hash)) {
+ PyObject *k = merge_consts_recursive(c, item);
+ if (k == NULL) {
+ Py_DECREF(tuple);
+ Py_DECREF(key);
+ return NULL;
+ }
+ PyObject *u;
+ if (PyTuple_CheckExact(k)) {
+ u = PyTuple_GET_ITEM(k, 1);
+ }
+ else {
+ u = k;
+ }
+ Py_INCREF(u);
+ PyTuple_SET_ITEM(tuple, i, u);
+ i++;
+ }
+
+ PyObject *new = PyFrozenSet_New(tuple);
+ Py_DECREF(tuple);
+ if (new == NULL) {
+ Py_DECREF(key);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(key, 1, new);
+ }
+
+ return key;
+}
+
static Py_ssize_t
compiler_add_const(struct compiler *c, PyObject *o)
{
- PyObject *t;
- Py_ssize_t arg;
-
- t = _PyCode_ConstantKey(o);
- if (t == NULL)
+ PyObject *key = merge_consts_recursive(c, o);
+ if (key == NULL) {
return -1;
+ }
- arg = compiler_add_o(c, c->u->u_consts, t);
- Py_DECREF(t);
+ Py_ssize_t arg = compiler_add_o(c, c->u->u_consts, key);
+ Py_DECREF(key);
return arg;
}
@@ -5380,6 +5493,35 @@ compute_code_flags(struct compiler *c)
return flags;
}
+// Merge *tuple* with constant cache.
+// Unlike merge_consts_recursive(), this function doesn't work recursively.
+static int
+merge_const_tuple(struct compiler *c, PyObject **tuple)
+{
+ assert(PyTuple_CheckExact(*tuple));
+
+ PyObject *key = _PyCode_ConstantKey(*tuple);
+ if (key == NULL) {
+ return 0;
+ }
+
+ // t is borrowed reference
+ PyObject *t = PyDict_SetDefault(c->c_const_cache, key, key);
+ Py_DECREF(key);
+ if (t == NULL) {
+ return 0;
+ }
+ if (t == key) { // tuple is new constant.
+ return 1;
+ }
+
+ PyObject *u = PyTuple_GET_ITEM(t, 1);
+ Py_INCREF(u);
+ Py_DECREF(*tuple);
+ *tuple = u;
+ return 1;
+}
+
static PyCodeObject *
makecode(struct compiler *c, struct assembler *a)
{
@@ -5410,6 +5552,14 @@ makecode(struct compiler *c, struct assembler *a)
if (!freevars)
goto error;
+ if (!merge_const_tuple(c, &names) ||
+ !merge_const_tuple(c, &varnames) ||
+ !merge_const_tuple(c, &cellvars) ||
+ !merge_const_tuple(c, &freevars))
+ {
+ goto error;
+ }
+
nlocals = PyDict_GET_SIZE(c->u->u_varnames);
assert(nlocals < INT_MAX);
nlocals_int = Py_SAFE_DOWNCAST(nlocals, Py_ssize_t, int);
@@ -5427,6 +5577,9 @@ makecode(struct compiler *c, struct assembler *a)
goto error;
Py_DECREF(consts);
consts = tmp;
+ if (!merge_const_tuple(c, &consts)) {
+ goto error;
+ }
argcount = Py_SAFE_DOWNCAST(c->u->u_argcount, Py_ssize_t, int);
kwonlyargcount = Py_SAFE_DOWNCAST(c->u->u_kwonlyargcount, Py_ssize_t, int);