@@ -94,6 +94,10 @@ framelocalsproxy_hasval(_PyInterpreterFrame *frame, PyCodeObject *co, int i)
9494 return true;
9595}
9696
97+ typedef struct {
98+ PyObject * seen ;
99+ } FrameLocalsDedupCtx ;
100+
97101static int
98102framelocalsproxy_is_first_occurrence (PyObject * seen , PyObject * name )
99103{
@@ -110,6 +114,40 @@ framelocalsproxy_is_first_occurrence(PyObject *seen, PyObject *name)
110114 return 1 ;
111115}
112116
117+ static int
118+ framelocalsproxy_dedup_begin (PyCodeObject * co , FrameLocalsDedupCtx * ctx )
119+ {
120+ if (co -> co_ncellvars > 0 && co -> co_nfreevars > 0 ) {
121+ ctx -> seen = PySet_New (NULL );
122+ if (ctx -> seen == NULL ) {
123+ return -1 ;
124+ }
125+ }
126+ else {
127+ ctx -> seen = NULL ;
128+ }
129+ return 0 ;
130+ }
131+
132+ static inline int
133+ framelocalsproxy_dedup_should_skip (FrameLocalsDedupCtx * ctx , PyObject * name )
134+ {
135+ if (ctx -> seen == NULL ) {
136+ return 0 ;
137+ }
138+ int first = framelocalsproxy_is_first_occurrence (ctx -> seen , name );
139+ if (first < 0 ) {
140+ return -1 ;
141+ }
142+ return !first ;
143+ }
144+
145+ static inline void
146+ framelocalsproxy_dedup_clear (FrameLocalsDedupCtx * ctx )
147+ {
148+ Py_CLEAR (ctx -> seen );
149+ }
150+
113151static int
114152framelocalsproxy_getkeyindex (PyFrameObject * frame , PyObject * key , bool read , PyObject * * value_ptr )
115153{
@@ -396,28 +434,29 @@ framelocalsproxy_keys(PyObject *self, PyObject *Py_UNUSED(ignored))
396434 if (names == NULL ) {
397435 return NULL ;
398436 }
399- // An inlined comprehension cell can share a name with a free var.
400- PyObject * seen = PySet_New (NULL );
401- if (seen == NULL ) {
437+ FrameLocalsDedupCtx dedup ;
438+ if (framelocalsproxy_dedup_begin (co , & dedup ) < 0 ) {
402439 Py_DECREF (names );
403440 return NULL ;
404441 }
442+ // An inlined comprehension cell can share a name with a free var.
405443
406444 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
445+ PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
407446 if (framelocalsproxy_hasval (frame -> f_frame , co , i )) {
408- PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
409- int first = framelocalsproxy_is_first_occurrence (seen , name );
410- if (first < 0 ) {
447+ int skip = framelocalsproxy_dedup_should_skip (& dedup , name );
448+ if (skip < 0 ) {
411449 goto error ;
412450 }
413- if (first ) {
414- if (PyList_Append (names , name ) < 0 ) {
415- goto error ;
416- }
451+ if (skip ) {
452+ continue ;
453+ }
454+ if (PyList_Append (names , name ) < 0 ) {
455+ goto error ;
417456 }
418457 }
419458 }
420- Py_DECREF ( seen );
459+ framelocalsproxy_dedup_clear ( & dedup );
421460
422461 // Iterate through the extra locals
423462 if (frame -> f_extra_locals ) {
@@ -438,7 +477,7 @@ framelocalsproxy_keys(PyObject *self, PyObject *Py_UNUSED(ignored))
438477 return names ;
439478
440479error :
441- Py_DECREF ( seen );
480+ framelocalsproxy_dedup_clear ( & dedup );
442481 Py_DECREF (names );
443482 return NULL ;
444483}
@@ -622,8 +661,8 @@ framelocalsproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored))
622661 if (values == NULL ) {
623662 return NULL ;
624663 }
625- PyObject * seen = PySet_New ( NULL ) ;
626- if (seen == NULL ) {
664+ FrameLocalsDedupCtx dedup ;
665+ if (framelocalsproxy_dedup_begin ( co , & dedup ) < 0 ) {
627666 Py_DECREF (values );
628667 return NULL ;
629668 }
@@ -632,20 +671,23 @@ framelocalsproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored))
632671 PyObject * value = framelocalsproxy_getval (frame -> f_frame , co , i );
633672 if (value ) {
634673 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- }
674+ int skip = framelocalsproxy_dedup_should_skip (& dedup , name );
675+ if (skip < 0 ) {
676+ Py_DECREF (value );
677+ goto error ;
641678 }
642- Py_DECREF (value );
643- if (first < 0 ) {
679+ if (skip ) {
680+ Py_DECREF (value );
681+ continue ;
682+ }
683+ if (PyList_Append (values , value ) < 0 ) {
684+ Py_DECREF (value );
644685 goto error ;
645686 }
687+ Py_DECREF (value );
646688 }
647689 }
648- Py_DECREF ( seen );
690+ framelocalsproxy_dedup_clear ( & dedup );
649691
650692 // Iterate through the extra locals
651693 if (frame -> f_extra_locals ) {
@@ -663,7 +705,7 @@ framelocalsproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored))
663705 return values ;
664706
665707error :
666- Py_DECREF ( seen );
708+ framelocalsproxy_dedup_clear ( & dedup );
667709 Py_DECREF (values );
668710 return NULL ;
669711}
@@ -677,8 +719,8 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored))
677719 if (items == NULL ) {
678720 return NULL ;
679721 }
680- PyObject * seen = PySet_New ( NULL ) ;
681- if (seen == NULL ) {
722+ FrameLocalsDedupCtx dedup ;
723+ if (framelocalsproxy_dedup_begin ( co , & dedup ) < 0 ) {
682724 Py_DECREF (items );
683725 return NULL ;
684726 }
@@ -688,26 +730,26 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored))
688730 PyObject * value = framelocalsproxy_getval (frame -> f_frame , co , i );
689731
690732 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- }
733+ int skip = framelocalsproxy_dedup_should_skip (& dedup , name );
734+ if (skip < 0 ) {
735+ Py_DECREF (value );
736+ goto error ;
700737 }
701- else {
738+ if ( skip ) {
702739 Py_DECREF (value );
703- if (first < 0 ) {
704- goto error ;
705- }
740+ continue ;
741+ }
742+ PyObject * pair = _PyTuple_FromPairSteal (Py_NewRef (name ), value );
743+ if (pair == NULL ) {
744+ goto error ;
745+ }
746+ if (_PyList_AppendTakeRef ((PyListObject * )items , pair ) < 0 ) {
747+ Py_DECREF (value );
748+ goto error ;
706749 }
707750 }
708751 }
709- Py_DECREF (seen );
710- seen = NULL ;
752+ framelocalsproxy_dedup_clear (& dedup );
711753
712754 // Iterate through the extra locals
713755 if (frame -> f_extra_locals ) {
@@ -729,7 +771,7 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored))
729771 return items ;
730772
731773error :
732- Py_XDECREF ( seen );
774+ framelocalsproxy_dedup_clear ( & dedup );
733775 Py_DECREF (items );
734776 return NULL ;
735777}
@@ -746,24 +788,24 @@ framelocalsproxy_length(PyObject *self)
746788 size += PyDict_Size (frame -> f_extra_locals );
747789 }
748790
749- PyObject * seen = PySet_New ( NULL ) ;
750- if (seen == NULL ) {
791+ FrameLocalsDedupCtx dedup ;
792+ if (framelocalsproxy_dedup_begin ( co , & dedup ) < 0 ) {
751793 return -1 ;
752794 }
753795 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
796+ PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
754797 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 );
798+ int skip = framelocalsproxy_dedup_should_skip (& dedup , name );
799+ if (skip < 0 ) {
800+ framelocalsproxy_dedup_clear (& dedup );
759801 return -1 ;
760802 }
761- else if (first ) {
803+ if (! skip ) {
762804 size ++ ;
763805 }
764806 }
765807 }
766- Py_DECREF ( seen );
808+ framelocalsproxy_dedup_clear ( & dedup );
767809 return size ;
768810}
769811
0 commit comments