@@ -53,22 +53,27 @@ static int
5353framelocalsproxy_getkeyindex (PyFrameObject * frame , PyObject * key , bool read )
5454{
5555 /*
56- * Returns the fast locals index of the key
56+ * Returns -2 (!) if an error occurred; exception will be set.
57+ * Returns the fast locals index of the key on success:
5758 * - if read == true, returns the index if the value is not NULL
5859 * - if read == false, returns the index if the value is not hidden
60+ * Otherwise returns -1.
5961 */
6062
61- assert (PyUnicode_CheckExact (key ));
62-
6363 PyCodeObject * co = _PyFrame_GetCode (frame -> f_frame );
64- int found_key = false;
64+
65+ // Ensure that the key is hashable.
66+ Py_hash_t key_hash = PyObject_Hash (key );
67+ if (key_hash == -1 ) {
68+ return -2 ;
69+ }
70+ bool found = false;
6571
6672 // We do 2 loops here because it's highly possible the key is interned
6773 // and we can do a pointer comparison.
6874 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
6975 PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
7076 if (name == key ) {
71- found_key = true;
7277 if (read ) {
7378 if (framelocalsproxy_getval (frame -> f_frame , co , i ) != NULL ) {
7479 return i ;
@@ -78,23 +83,35 @@ framelocalsproxy_getkeyindex(PyFrameObject *frame, PyObject* key, bool read)
7883 return i ;
7984 }
8085 }
86+ found = true;
8187 }
8288 }
83-
84- if (!found_key ) {
85- // This is unlikely, but we need to make sure. This means the key
86- // is not interned.
87- for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
88- PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
89- if (_PyUnicode_EQ (name , key )) {
90- if (read ) {
91- if (framelocalsproxy_getval (frame -> f_frame , co , i ) != NULL ) {
92- return i ;
93- }
94- } else {
95- if (!(_PyLocals_GetKind (co -> co_localspluskinds , i ) & CO_FAST_HIDDEN )) {
96- return i ;
97- }
89+ if (found ) {
90+ // This is an attempt to read an unset local variable or
91+ // write to a variable that is hidden from regular write operations
92+ return -1 ;
93+ }
94+ // This is unlikely, but we need to make sure. This means the key
95+ // is not interned.
96+ for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
97+ PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
98+ Py_hash_t name_hash = PyObject_Hash (name );
99+ assert (name_hash != -1 ); // keys are exact unicode
100+ if (name_hash != key_hash ) {
101+ continue ;
102+ }
103+ int same = PyObject_RichCompareBool (name , key , Py_EQ );
104+ if (same < 0 ) {
105+ return -2 ;
106+ }
107+ if (same ) {
108+ if (read ) {
109+ if (framelocalsproxy_getval (frame -> f_frame , co , i ) != NULL ) {
110+ return i ;
111+ }
112+ } else {
113+ if (!(_PyLocals_GetKind (co -> co_localspluskinds , i ) & CO_FAST_HIDDEN )) {
114+ return i ;
98115 }
99116 }
100117 }
@@ -109,13 +126,14 @@ framelocalsproxy_getitem(PyObject *self, PyObject *key)
109126 PyFrameObject * frame = ((PyFrameLocalsProxyObject * )self )-> frame ;
110127 PyCodeObject * co = _PyFrame_GetCode (frame -> f_frame );
111128
112- if (PyUnicode_CheckExact (key )) {
113- int i = framelocalsproxy_getkeyindex (frame , key , true);
114- if (i >= 0 ) {
115- PyObject * value = framelocalsproxy_getval (frame -> f_frame , co , i );
116- assert (value != NULL );
117- return Py_NewRef (value );
118- }
129+ int i = framelocalsproxy_getkeyindex (frame , key , true);
130+ if (i == -2 ) {
131+ return NULL ;
132+ }
133+ if (i >= 0 ) {
134+ PyObject * value = framelocalsproxy_getval (frame -> f_frame , co , i );
135+ assert (value != NULL );
136+ return Py_NewRef (value );
119137 }
120138
121139 // Okay not in the fast locals, try extra locals
@@ -145,37 +163,38 @@ framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value)
145163 return -1 ;
146164 }
147165
148- if (PyUnicode_CheckExact (key )) {
149- int i = framelocalsproxy_getkeyindex (frame , key , false);
150- if (i >= 0 ) {
151- _Py_Executors_InvalidateDependency (PyInterpreterState_Get (), co , 1 );
152-
153- _PyLocals_Kind kind = _PyLocals_GetKind (co -> co_localspluskinds , i );
154- _PyStackRef oldvalue = fast [i ];
155- PyObject * cell = NULL ;
156- if (kind == CO_FAST_FREE ) {
157- // The cell was set when the frame was created from
158- // the function's closure.
159- assert (oldvalue .bits != 0 && PyCell_Check (PyStackRef_AsPyObjectBorrow (oldvalue )));
160- cell = PyStackRef_AsPyObjectBorrow (oldvalue );
161- } else if (kind & CO_FAST_CELL && oldvalue .bits != 0 ) {
162- PyObject * as_obj = PyStackRef_AsPyObjectBorrow (oldvalue );
163- if (PyCell_Check (as_obj )) {
164- cell = as_obj ;
165- }
166+ int i = framelocalsproxy_getkeyindex (frame , key , false);
167+ if (i == -2 ) {
168+ return -1 ;
169+ }
170+ if (i >= 0 ) {
171+ _Py_Executors_InvalidateDependency (PyInterpreterState_Get (), co , 1 );
172+
173+ _PyLocals_Kind kind = _PyLocals_GetKind (co -> co_localspluskinds , i );
174+ _PyStackRef oldvalue = fast [i ];
175+ PyObject * cell = NULL ;
176+ if (kind == CO_FAST_FREE ) {
177+ // The cell was set when the frame was created from
178+ // the function's closure.
179+ assert (oldvalue .bits != 0 && PyCell_Check (PyStackRef_AsPyObjectBorrow (oldvalue )));
180+ cell = PyStackRef_AsPyObjectBorrow (oldvalue );
181+ } else if (kind & CO_FAST_CELL && oldvalue .bits != 0 ) {
182+ PyObject * as_obj = PyStackRef_AsPyObjectBorrow (oldvalue );
183+ if (PyCell_Check (as_obj )) {
184+ cell = as_obj ;
166185 }
167- if (cell != NULL ) {
168- PyObject * oldvalue_o = PyCell_GET (cell );
169- if (value != oldvalue_o ) {
170- PyCell_SET (cell , Py_XNewRef (value ));
171- Py_XDECREF (oldvalue_o );
172- }
173- } else if (value != PyStackRef_AsPyObjectBorrow (oldvalue )) {
174- PyStackRef_XCLOSE (fast [i ]);
175- fast [i ] = PyStackRef_FromPyObjectNew (value );
186+ }
187+ if (cell != NULL ) {
188+ PyObject * oldvalue_o = PyCell_GET (cell );
189+ if (value != oldvalue_o ) {
190+ PyCell_SET (cell , Py_XNewRef (value ));
191+ Py_XDECREF (oldvalue_o );
176192 }
177- return 0 ;
193+ } else if (value != PyStackRef_AsPyObjectBorrow (oldvalue )) {
194+ PyStackRef_XCLOSE (fast [i ]);
195+ fast [i ] = PyStackRef_FromPyObjectNew (value );
178196 }
197+ return 0 ;
179198 }
180199
181200 // Okay not in the fast locals, try extra locals
@@ -545,11 +564,12 @@ framelocalsproxy_contains(PyObject *self, PyObject *key)
545564{
546565 PyFrameObject * frame = ((PyFrameLocalsProxyObject * )self )-> frame ;
547566
548- if (PyUnicode_CheckExact (key )) {
549- int i = framelocalsproxy_getkeyindex (frame , key , true);
550- if (i >= 0 ) {
551- return 1 ;
552- }
567+ int i = framelocalsproxy_getkeyindex (frame , key , true);
568+ if (i == -2 ) {
569+ return -1 ;
570+ }
571+ if (i >= 0 ) {
572+ return 1 ;
553573 }
554574
555575 PyObject * extra = ((PyFrameObject * )frame )-> f_extra_locals ;
0 commit comments