@@ -94,20 +94,62 @@ framelocalsproxy_hasval(_PyInterpreterFrame *frame, PyCodeObject *co, int i)
9494 return true;
9595}
9696
97+ /* *seen is NULL when dedup is known to be unnecessary.
98+ Otherwise it maps each localsplus name to Py_True if a value was already
99+ kept, or Py_False if the name was only seen on an empty slot. An empty
100+ slot must not count as the kept value, but it still proves the name is
101+ repeated, so it is recorded while _co_localsplus_dedup is unknown.
102+ Returns -1 if the map cannot be created. */
97103static int
98- framelocalsproxy_is_first_occurrence ( PyObject * seen , PyObject * name )
104+ framelocalsproxy_prepare_dedup ( PyCodeObject * co , PyObject * * seen )
99105{
100- int found = PySet_Contains (seen , name );
101- if (found < 0 ) {
106+ * seen = NULL ;
107+ if (co -> _co_localsplus_dedup == _PYCODE_LOCALSPLUS_DEDUP_UNIQUE ) {
108+ return 0 ;
109+ }
110+ * seen = PyDict_New ();
111+ if (* seen == NULL ) {
102112 return -1 ;
103113 }
104- if (found ) {
114+ return 0 ;
115+ }
116+
117+ /* Returns 1 if this slot's value should be kept, 0 if not, -1 on error.
118+ An empty slot is skipped once _co_localsplus_dedup is known. A repeated
119+ name stores _PYCODE_LOCALSPLUS_DEDUP_NEEDED. UNIQUE is stored by the
120+ caller only after the walk finishes without one. */
121+ static int
122+ framelocalsproxy_visit_name (PyCodeObject * co , int i , PyObject * seen ,
123+ int has_value )
124+ {
125+ if (seen == NULL ) {
126+ return has_value ;
127+ }
128+ if (!has_value &&
129+ co -> _co_localsplus_dedup != _PYCODE_LOCALSPLUS_DEDUP_UNKNOWN ) {
105130 return 0 ;
106131 }
107- if (PySet_Add (seen , name ) < 0 ) {
108- return -1 ;
132+ PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
133+ PyObject * prev = PyDict_GetItemWithError (seen , name );
134+ if (prev == NULL ) {
135+ if (PyErr_Occurred ()) {
136+ return -1 ;
137+ }
138+ if (PyDict_SetItem (seen , name , has_value ? Py_True : Py_False ) < 0 ) {
139+ return -1 ;
140+ }
141+ return has_value ;
142+ }
143+ else {
144+ co -> _co_localsplus_dedup = _PYCODE_LOCALSPLUS_DEDUP_NEEDED ;
145+ if (!has_value || prev == Py_True ) {
146+ return 0 ;
147+ }
148+ if (PyDict_SetItem (seen , name , Py_True ) < 0 ) {
149+ return -1 ;
150+ }
151+ return 1 ;
109152 }
110- return 1 ;
111153}
112154
113155static int
@@ -397,27 +439,30 @@ framelocalsproxy_keys(PyObject *self, PyObject *Py_UNUSED(ignored))
397439 return NULL ;
398440 }
399441 // An inlined comprehension cell can share a name with a free var.
400- PyObject * seen = PySet_New (NULL );
401- if (seen == NULL ) {
442+ // Assume dedup may be needed until a walk of seen shows otherwise.
443+ PyObject * seen = NULL ;
444+ if (framelocalsproxy_prepare_dedup (co , & seen ) < 0 ) {
402445 Py_DECREF (names );
403446 return NULL ;
404447 }
405448
406449 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
407- if (framelocalsproxy_hasval (frame -> f_frame , co , i )) {
450+ int has = framelocalsproxy_hasval (frame -> f_frame , co , i );
451+ int emit = framelocalsproxy_visit_name (co , i , seen , has );
452+ if (emit < 0 ) {
453+ goto error ;
454+ }
455+ if (emit ) {
408456 PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
409- int first = framelocalsproxy_is_first_occurrence (seen , name );
410- if (first < 0 ) {
457+ if (PyList_Append (names , name ) < 0 ) {
411458 goto error ;
412459 }
413- if (first ) {
414- if (PyList_Append (names , name ) < 0 ) {
415- goto error ;
416- }
417- }
418460 }
419461 }
420- Py_DECREF (seen );
462+ if (co -> _co_localsplus_dedup == _PYCODE_LOCALSPLUS_DEDUP_UNKNOWN ) {
463+ co -> _co_localsplus_dedup = _PYCODE_LOCALSPLUS_DEDUP_UNIQUE ;
464+ }
465+ Py_XDECREF (seen );
421466
422467 // Iterate through the extra locals
423468 if (frame -> f_extra_locals ) {
@@ -438,7 +483,7 @@ framelocalsproxy_keys(PyObject *self, PyObject *Py_UNUSED(ignored))
438483 return names ;
439484
440485error :
441- Py_DECREF (seen );
486+ Py_XDECREF (seen );
442487 Py_DECREF (names );
443488 return NULL ;
444489}
@@ -622,30 +667,30 @@ framelocalsproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored))
622667 if (values == NULL ) {
623668 return NULL ;
624669 }
625- PyObject * seen = PySet_New ( NULL ) ;
626- if (seen == NULL ) {
670+ PyObject * seen = NULL ;
671+ if (framelocalsproxy_prepare_dedup ( co , & seen ) < 0 ) {
627672 Py_DECREF (values );
628673 return NULL ;
629674 }
630675
631676 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
632677 PyObject * value = framelocalsproxy_getval (frame -> f_frame , co , i );
633- if (value ) {
634- PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
635- int first = framelocalsproxy_is_first_occurrence (seen , name );
636- if (first == 1 ) {
637- if (PyList_Append (values , value ) < 0 ) {
638- Py_DECREF (value );
639- goto error ;
640- }
641- }
678+ int has = value != NULL ;
679+ int emit = framelocalsproxy_visit_name (co , i , seen , has );
680+ if (emit < 0 ) {
681+ Py_XDECREF (value );
682+ goto error ;
683+ }
684+ if (emit && PyList_Append (values , value ) < 0 ) {
642685 Py_DECREF (value );
643- if (first < 0 ) {
644- goto error ;
645- }
686+ goto error ;
646687 }
688+ Py_XDECREF (value );
689+ }
690+ if (co -> _co_localsplus_dedup == _PYCODE_LOCALSPLUS_DEDUP_UNKNOWN ) {
691+ co -> _co_localsplus_dedup = _PYCODE_LOCALSPLUS_DEDUP_UNIQUE ;
647692 }
648- Py_DECREF (seen );
693+ Py_XDECREF (seen );
649694
650695 // Iterate through the extra locals
651696 if (frame -> f_extra_locals ) {
@@ -663,7 +708,7 @@ framelocalsproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored))
663708 return values ;
664709
665710error :
666- Py_DECREF (seen );
711+ Py_XDECREF (seen );
667712 Py_DECREF (values );
668713 return NULL ;
669714}
@@ -677,36 +722,37 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored))
677722 if (items == NULL ) {
678723 return NULL ;
679724 }
680- PyObject * seen = PySet_New ( NULL ) ;
681- if (seen == NULL ) {
725+ PyObject * seen = NULL ;
726+ if (framelocalsproxy_prepare_dedup ( co , & seen ) < 0 ) {
682727 Py_DECREF (items );
683728 return NULL ;
684729 }
685730
686731 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
687- PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
688732 PyObject * value = framelocalsproxy_getval (frame -> f_frame , co , i );
689-
690- if (value ) {
691- int first = framelocalsproxy_is_first_occurrence (seen , name );
692- if (first == 1 ) {
693- PyObject * pair = _PyTuple_FromPairSteal (Py_NewRef (name ), value );
694- if (pair == NULL ) {
695- goto error ;
696- }
697- if (_PyList_AppendTakeRef ((PyListObject * )items , pair ) < 0 ) {
698- goto error ;
699- }
700- }
701- else {
702- Py_DECREF (value );
703- if (first < 0 ) {
704- goto error ;
705- }
706- }
733+ int has = value != NULL ;
734+ int emit = framelocalsproxy_visit_name (co , i , seen , has );
735+ if (emit < 0 ) {
736+ Py_XDECREF (value );
737+ goto error ;
707738 }
739+ if (!emit ) {
740+ Py_XDECREF (value );
741+ continue ;
742+ }
743+ PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
744+ PyObject * pair = _PyTuple_FromPairSteal (Py_NewRef (name ), value );
745+ if (pair == NULL ) {
746+ goto error ;
747+ }
748+ if (_PyList_AppendTakeRef ((PyListObject * )items , pair ) < 0 ) {
749+ goto error ;
750+ }
751+ }
752+ if (co -> _co_localsplus_dedup == _PYCODE_LOCALSPLUS_DEDUP_UNKNOWN ) {
753+ co -> _co_localsplus_dedup = _PYCODE_LOCALSPLUS_DEDUP_UNIQUE ;
708754 }
709- Py_DECREF (seen );
755+ Py_XDECREF (seen );
710756 seen = NULL ;
711757
712758 // Iterate through the extra locals
@@ -746,24 +792,25 @@ framelocalsproxy_length(PyObject *self)
746792 size += PyDict_Size (frame -> f_extra_locals );
747793 }
748794
749- PyObject * seen = PySet_New ( NULL ) ;
750- if (seen == NULL ) {
795+ PyObject * seen = NULL ;
796+ if (framelocalsproxy_prepare_dedup ( co , & seen ) < 0 ) {
751797 return -1 ;
752798 }
753799 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
754- if (framelocalsproxy_hasval (frame -> f_frame , co , i )) {
755- PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
756- int first = framelocalsproxy_is_first_occurrence (seen , name );
757- if (first < 0 ) {
758- Py_DECREF (seen );
759- return -1 ;
760- }
761- else if (first ) {
762- size ++ ;
763- }
800+ int has = framelocalsproxy_hasval (frame -> f_frame , co , i );
801+ int emit = framelocalsproxy_visit_name (co , i , seen , has );
802+ if (emit < 0 ) {
803+ Py_XDECREF (seen );
804+ return -1 ;
764805 }
806+ if (emit ) {
807+ size ++ ;
808+ }
809+ }
810+ if (co -> _co_localsplus_dedup == _PYCODE_LOCALSPLUS_DEDUP_UNKNOWN ) {
811+ co -> _co_localsplus_dedup = _PYCODE_LOCALSPLUS_DEDUP_UNIQUE ;
765812 }
766- Py_DECREF (seen );
813+ Py_XDECREF (seen );
767814 return size ;
768815}
769816
0 commit comments