Skip to content

Commit b61fece

Browse files
authored
GH-125868: Fix STORE_ATTR_WITH_HINT specialization (GH-125876)
1 parent c35b33b commit b61fece

File tree

6 files changed

+62
-13
lines changed

6 files changed

+62
-13
lines changed

Lib/dis.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -778,8 +778,10 @@ def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=N
778778

779779
if caches:
780780
cache_info = []
781+
cache_offset = offset
781782
for name, size in _cache_format[opname[deop]].items():
782-
data = code[offset + 2: offset + 2 + 2 * size]
783+
data = code[cache_offset + 2: cache_offset + 2 + 2 * size]
784+
cache_offset += size * 2
783785
cache_info.append((name, size, data))
784786
else:
785787
cache_info = None

Lib/test/test_opcache.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,50 @@ class D(dict): pass
11551155
{'a':1, 'b':2}
11561156
)
11571157

1158+
def test_125868(self):
1159+
1160+
def make_special_dict():
1161+
"""Create a dictionary an object with a this table:
1162+
index | key | value
1163+
----- | --- | -----
1164+
0 | 'b' | 'value'
1165+
1 | 'b' | NULL
1166+
"""
1167+
class A:
1168+
pass
1169+
a = A()
1170+
a.a = 1
1171+
a.b = 2
1172+
d = a.__dict__.copy()
1173+
del d['a']
1174+
del d['b']
1175+
d['b'] = "value"
1176+
return d
1177+
1178+
class NoInlineAorB:
1179+
pass
1180+
for i in range(ord('c'), ord('z')):
1181+
setattr(NoInlineAorB(), chr(i), i)
1182+
1183+
c = NoInlineAorB()
1184+
c.a = 0
1185+
c.b = 1
1186+
self.assertFalse(_testinternalcapi.has_inline_values(c))
1187+
1188+
def f(o, n):
1189+
for i in range(n):
1190+
o.b = i
1191+
# Prime f to store to dict slot 1
1192+
f(c, 100)
1193+
1194+
test_obj = NoInlineAorB()
1195+
test_obj.__dict__ = make_special_dict()
1196+
self.assertEqual(test_obj.b, "value")
1197+
1198+
#This should set x.b = 0
1199+
f(test_obj, 1)
1200+
self.assertEqual(test_obj.b, 0)
1201+
11581202

11591203
if __name__ == "__main__":
11601204
unittest.main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
It was possible in 3.14.0a1 only for attribute lookup to give the wrong
2+
value. This was due to an incorrect specialization in very specific
3+
circumstances. This is fixed in 3.14.0a2.

Python/bytecodes.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,17 +2303,16 @@ dummy_func(
23032303
assert(PyDict_CheckExact((PyObject *)dict));
23042304
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
23052305
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries);
2306-
PyObject *old_value;
23072306
DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys));
23082307
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
23092308
DEOPT_IF(ep->me_key != name);
2309+
PyObject *old_value = ep->me_value;
2310+
DEOPT_IF(old_value == NULL);
23102311
/* Ensure dict is GC tracked if it needs to be */
23112312
if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(PyStackRef_AsPyObjectBorrow(value))) {
23122313
_PyObject_GC_TRACK(dict);
23132314
}
2314-
old_value = ep->me_value;
2315-
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
2316-
_PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
2315+
_PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value));
23172316
ep->me_value = PyStackRef_AsPyObjectSteal(value);
23182317
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
23192318
// when dict only holds the strong reference to value in ep->me_value.

Python/executor_cases.c.h

Lines changed: 6 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)