Skip to content

Commit 353e9ca

Browse files
committed
avoid FrameLocalsProxy duplicate-name dedup when obviously not needed
1 parent 68d86eb commit 353e9ca

1 file changed

Lines changed: 92 additions & 50 deletions

File tree

‎Objects/frameobject.c‎

Lines changed: 92 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
97101
static int
98102
framelocalsproxy_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+
113151
static int
114152
framelocalsproxy_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

440479
error:
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

665707
error:
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

731773
error:
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

Comments
 (0)