2121 * objects.
2222 */
2323
24+ #include <stdbool.h>
25+
2426#include "Python.h"
2527#include "pycore_ast.h" // _PyAST_GetDocString()
2628#include "pycore_compile.h" // _PyFuture_FromAST()
@@ -7377,6 +7379,39 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts);
73777379static int
73787380ensure_exits_have_lineno (struct compiler * c );
73797381
7382+ static int *
7383+ build_cellfixedoffsets (struct compiler * c )
7384+ {
7385+ int nlocals = (int )PyDict_GET_SIZE (c -> u -> u_varnames );
7386+ int ncellvars = (int )PyDict_GET_SIZE (c -> u -> u_cellvars );
7387+ int nfreevars = (int )PyDict_GET_SIZE (c -> u -> u_freevars );
7388+
7389+ int noffsets = ncellvars + nfreevars ;
7390+ int * fixed = PyMem_New (int , noffsets );
7391+ if (fixed == NULL ) {
7392+ PyErr_NoMemory ();
7393+ return NULL ;
7394+ }
7395+ for (int i = 0 ; i < noffsets ; i ++ ) {
7396+ fixed [i ] = nlocals + i ;
7397+ }
7398+
7399+ PyObject * varname , * cellindex ;
7400+ Py_ssize_t pos = 0 ;
7401+ while (PyDict_Next (c -> u -> u_cellvars , & pos , & varname , & cellindex )) {
7402+ PyObject * varindex = PyDict_GetItem (c -> u -> u_varnames , varname );
7403+ if (varindex != NULL ) {
7404+ assert (PyLong_AS_LONG (cellindex ) < INT_MAX );
7405+ assert (PyLong_AS_LONG (varindex ) < INT_MAX );
7406+ int oldindex = (int )PyLong_AS_LONG (cellindex );
7407+ int argoffset = (int )PyLong_AS_LONG (varindex );
7408+ fixed [oldindex ] = argoffset ;
7409+ }
7410+ }
7411+
7412+ return fixed ;
7413+ }
7414+
73807415static inline int
73817416insert_instruction (basicblock * block , int pos , struct instr * instr ) {
73827417 if (compiler_next_instr (block ) < 0 ) {
@@ -7390,29 +7425,48 @@ insert_instruction(basicblock *block, int pos, struct instr *instr) {
73907425}
73917426
73927427static int
7393- insert_prefix_instructions (struct compiler * c , basicblock * entryblock ) {
7428+ insert_prefix_instructions (struct compiler * c , basicblock * entryblock ,
7429+ int * fixed )
7430+ {
73947431
73957432 int flags = compute_code_flags (c );
73967433 if (flags < 0 ) {
73977434 return -1 ;
73987435 }
73997436
74007437 /* Set up cells for any variable that escapes, to be put in a closure. */
7401- PyObject * k , * v ;
7402- Py_ssize_t pos = 0 ;
7403- while (PyDict_Next (c -> u -> u_cellvars , & pos , & k , & v )) {
7404- assert (PyLong_AS_LONG (v ) < INT_MAX );
7405- int cellindex = (int )PyLong_AS_LONG (v );
7406- struct instr make_cell = {
7407- .i_opcode = MAKE_CELL ,
7408- // This will get fixed in offset_derefs().
7409- .i_oparg = cellindex ,
7410- .i_lineno = -1 ,
7411- .i_target = NULL ,
7412- };
7413- if (insert_instruction (entryblock , (int )(pos - 1 ), & make_cell ) < 0 ) {
7438+ const int ncellvars = (int )PyDict_GET_SIZE (c -> u -> u_cellvars );
7439+ if (ncellvars ) {
7440+ // c->u->u_cellvars has the cells out of order so we sort them
7441+ // before adding the MAKE_CELL instructions. Note that we
7442+ // adjust for arg cells, which come first.
7443+ const int nvars = ncellvars + (int )PyDict_GET_SIZE (c -> u -> u_varnames );
7444+ int * sorted = PyMem_RawCalloc (nvars , sizeof (int ));
7445+ if (sorted == NULL ) {
7446+ PyErr_NoMemory ();
74147447 return -1 ;
74157448 }
7449+ for (int i = 0 ; i < ncellvars ; i ++ ) {
7450+ sorted [fixed [i ]] = i + 1 ;
7451+ }
7452+ for (int i = 0 , ncellsused = 0 ; ncellsused < ncellvars ; i ++ ) {
7453+ int oldindex = sorted [i ] - 1 ;
7454+ if (oldindex == -1 ) {
7455+ continue ;
7456+ }
7457+ struct instr make_cell = {
7458+ .i_opcode = MAKE_CELL ,
7459+ // This will get fixed in offset_derefs().
7460+ .i_oparg = oldindex ,
7461+ .i_lineno = -1 ,
7462+ .i_target = NULL ,
7463+ };
7464+ if (insert_instruction (entryblock , ncellsused , & make_cell ) < 0 ) {
7465+ return -1 ;
7466+ }
7467+ ncellsused += 1 ;
7468+ }
7469+ PyMem_RawFree (sorted );
74167470 }
74177471
74187472 /* Add the generator prefix instructions. */
@@ -7471,42 +7525,16 @@ guarantee_lineno_for_exits(struct assembler *a, int firstlineno) {
74717525}
74727526
74737527static int
7474- fix_cell_offsets (struct compiler * c , basicblock * entryblock )
7528+ fix_cell_offsets (struct compiler * c , basicblock * entryblock , int * fixedmap )
74757529{
7476- assert (PyDict_GET_SIZE (c -> u -> u_varnames ) < INT_MAX );
7477- assert (PyDict_GET_SIZE (c -> u -> u_cellvars ) < INT_MAX );
7478- assert (PyDict_GET_SIZE (c -> u -> u_freevars ) < INT_MAX );
74797530 int nlocals = (int )PyDict_GET_SIZE (c -> u -> u_varnames );
74807531 int ncellvars = (int )PyDict_GET_SIZE (c -> u -> u_cellvars );
74817532 int nfreevars = (int )PyDict_GET_SIZE (c -> u -> u_freevars );
7482- assert (INT_MAX - nlocals - ncellvars - nfreevars > 0 );
7483- int nlocalsplus = nlocals + ncellvars + nfreevars ;
7533+ int noffsets = ncellvars + nfreevars ;
74847534
7485- int maxoldoffset = ncellvars + nfreevars ;
7486- int * fixedmap = PyMem_New (int , maxoldoffset + 1 );
7487- if (fixedmap == NULL ) {
7488- PyErr_NoMemory ();
7489- return -1 ;
7490- }
7491- for (int i = 0 ; i < maxoldoffset ; i ++ ) {
7492- fixedmap [i ] = nlocals + i ;
7493- }
7494-
7495- // First map cell vars to args.
7496- PyObject * varname , * cellindex ;
7497- Py_ssize_t pos = 0 ;
7498- while (PyDict_Next (c -> u -> u_cellvars , & pos , & varname , & cellindex )) {
7499- PyObject * varindex = PyDict_GetItem (c -> u -> u_varnames , varname );
7500- if (varindex != NULL ) {
7501- assert (PyLong_AS_LONG (cellindex ) < INT_MAX );
7502- assert (PyLong_AS_LONG (varindex ) < INT_MAX );
7503- int oldindex = (int )PyLong_AS_LONG (cellindex );
7504- int argoffset = (int )PyLong_AS_LONG (varindex );
7505- fixedmap [oldindex ] = argoffset ;
7506- }
7507- }
7535+ // First deal with duplicates (arg cells).
75087536 int numdropped = 0 ;
7509- for (int i = 0 ; i < maxoldoffset ; i ++ ) {
7537+ for (int i = 0 ; i < noffsets ; i ++ ) {
75107538 if (fixedmap [i ] == i + nlocals ) {
75117539 fixedmap [i ] -= numdropped ;
75127540 }
@@ -7516,7 +7544,7 @@ fix_cell_offsets(struct compiler *c, basicblock *entryblock)
75167544 }
75177545 }
75187546
7519- // Then update offsets, either relative to locals or by cell2var .
7547+ // Then update offsets, either relative to locals or by cell2arg .
75207548 for (basicblock * b = entryblock ; b != NULL ; b = b -> b_next ) {
75217549 for (int i = 0 ; i < b -> b_iused ; i ++ ) {
75227550 struct instr * inst = & b -> b_instr [i ];
@@ -7530,13 +7558,15 @@ fix_cell_offsets(struct compiler *c, basicblock *entryblock)
75307558 case STORE_DEREF :
75317559 case DELETE_DEREF :
75327560 case LOAD_CLASSDEREF :
7561+ assert (oldoffset >= 0 );
7562+ assert (oldoffset < noffsets );
7563+ assert (fixedmap [oldoffset ] >= 0 );
75337564 inst -> i_oparg = fixedmap [oldoffset ];
75347565 }
75357566 }
75367567 }
75377568
7538- PyMem_Free (fixedmap );
7539- return nlocalsplus - numdropped ;
7569+ return numdropped ;
75407570}
75417571
75427572static PyCodeObject *
@@ -7577,8 +7607,22 @@ assemble(struct compiler *c, int addNone)
75777607 }
75787608 assert (entryblock != NULL );
75797609
7610+ assert (PyDict_GET_SIZE (c -> u -> u_varnames ) < INT_MAX );
7611+ assert (PyDict_GET_SIZE (c -> u -> u_cellvars ) < INT_MAX );
7612+ assert (PyDict_GET_SIZE (c -> u -> u_freevars ) < INT_MAX );
7613+ int nlocals = (int )PyDict_GET_SIZE (c -> u -> u_varnames );
7614+ int ncellvars = (int )PyDict_GET_SIZE (c -> u -> u_cellvars );
7615+ int nfreevars = (int )PyDict_GET_SIZE (c -> u -> u_freevars );
7616+ assert (INT_MAX - nlocals - ncellvars > 0 );
7617+ assert (INT_MAX - nlocals - ncellvars - nfreevars > 0 );
7618+ int nlocalsplus = nlocals + ncellvars + nfreevars ;
7619+ int * cellfixedoffsets = build_cellfixedoffsets (c );
7620+ if (cellfixedoffsets == NULL ) {
7621+ goto error ;
7622+ }
7623+
75807624 // This must be called before fix_cell_offsets().
7581- if (insert_prefix_instructions (c , entryblock )) {
7625+ if (insert_prefix_instructions (c , entryblock , cellfixedoffsets )) {
75827626 goto error ;
75837627 }
75847628
@@ -7595,10 +7639,13 @@ assemble(struct compiler *c, int addNone)
75957639 a .a_entry = entryblock ;
75967640 a .a_nblocks = nblocks ;
75977641
7598- int nlocalsplus = fix_cell_offsets (c , entryblock );
7599- if (nlocalsplus < 0 ) {
7642+ int numdropped = fix_cell_offsets (c , entryblock , cellfixedoffsets );
7643+ PyMem_Free (cellfixedoffsets ); // At this point we're done with it.
7644+ cellfixedoffsets = NULL ;
7645+ if (numdropped < 0 ) {
76007646 goto error ;
76017647 }
7648+ nlocalsplus -= numdropped ;
76027649
76037650 consts = consts_dict_keys_inorder (c -> u -> u_consts );
76047651 if (consts == NULL ) {
@@ -7639,10 +7686,10 @@ assemble(struct compiler *c, int addNone)
76397686 }
76407687
76417688 if (!assemble_exception_table (& a )) {
7642- return 0 ;
7689+ goto error ;
76437690 }
76447691 if (!assemble_line_range (& a )) {
7645- return 0 ;
7692+ goto error ;
76467693 }
76477694
76487695 if (_PyBytes_Resize (& a .a_lnotab , a .a_lnotab_off ) < 0 ) {
@@ -7667,6 +7714,9 @@ assemble(struct compiler *c, int addNone)
76677714 error :
76687715 Py_XDECREF (consts );
76697716 assemble_free (& a );
7717+ if (cellfixedoffsets != NULL ) {
7718+ PyMem_Free (cellfixedoffsets );
7719+ }
76707720 return co ;
76717721}
76727722
0 commit comments