@@ -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,35 +163,36 @@ 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 );
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 );
152172
153- _PyLocals_Kind kind = _PyLocals_GetKind (co -> co_localspluskinds , i );
154- PyObject * 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 != NULL && PyCell_Check (oldvalue ));
173+ _PyLocals_Kind kind = _PyLocals_GetKind (co -> co_localspluskinds , i );
174+ PyObject * 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 != NULL && PyCell_Check (oldvalue ));
180+ cell = oldvalue ;
181+ } else if (kind & CO_FAST_CELL && oldvalue != NULL ) {
182+ if (PyCell_Check (oldvalue )) {
160183 cell = oldvalue ;
161- } else if (kind & CO_FAST_CELL && oldvalue != NULL ) {
162- if (PyCell_Check (oldvalue )) {
163- cell = oldvalue ;
164- }
165184 }
166- if (cell != NULL ) {
167- oldvalue = PyCell_GET (cell );
168- if (value != oldvalue ) {
169- PyCell_SET (cell , Py_XNewRef (value ));
170- Py_XDECREF (oldvalue );
171- }
172- } else if (value != oldvalue ) {
173- Py_XSETREF (fast [i ], Py_NewRef (value ));
185+ }
186+ if (cell != NULL ) {
187+ oldvalue = PyCell_GET (cell );
188+ if (value != oldvalue ) {
189+ PyCell_SET (cell , Py_XNewRef (value ));
190+ Py_XDECREF (oldvalue );
174191 }
175- return 0 ;
192+ } else if (value != oldvalue ) {
193+ Py_XSETREF (fast [i ], Py_NewRef (value ));
176194 }
195+ return 0 ;
177196 }
178197
179198 // Okay not in the fast locals, try extra locals
@@ -543,11 +562,12 @@ framelocalsproxy_contains(PyObject *self, PyObject *key)
543562{
544563 PyFrameObject * frame = ((PyFrameLocalsProxyObject * )self )-> frame ;
545564
546- if (PyUnicode_CheckExact (key )) {
547- int i = framelocalsproxy_getkeyindex (frame , key , true);
548- if (i >= 0 ) {
549- return 1 ;
550- }
565+ int i = framelocalsproxy_getkeyindex (frame , key , true);
566+ if (i == -2 ) {
567+ return -1 ;
568+ }
569+ if (i >= 0 ) {
570+ return 1 ;
551571 }
552572
553573 PyObject * extra = ((PyFrameObject * )frame )-> f_extra_locals ;
0 commit comments