mirror of https://github.com/python/cpython
GH-125868: Fix STORE_ATTR_WITH_HINT specialization (GH-125876)
This commit is contained in:
parent
c35b33bfb7
commit
b61fece852
|
@ -778,8 +778,10 @@ def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=N
|
|||
|
||||
if caches:
|
||||
cache_info = []
|
||||
cache_offset = offset
|
||||
for name, size in _cache_format[opname[deop]].items():
|
||||
data = code[offset + 2: offset + 2 + 2 * size]
|
||||
data = code[cache_offset + 2: cache_offset + 2 + 2 * size]
|
||||
cache_offset += size * 2
|
||||
cache_info.append((name, size, data))
|
||||
else:
|
||||
cache_info = None
|
||||
|
|
|
@ -1155,6 +1155,50 @@ class TestInstanceDict(unittest.TestCase):
|
|||
{'a':1, 'b':2}
|
||||
)
|
||||
|
||||
def test_125868(self):
|
||||
|
||||
def make_special_dict():
|
||||
"""Create a dictionary an object with a this table:
|
||||
index | key | value
|
||||
----- | --- | -----
|
||||
0 | 'b' | 'value'
|
||||
1 | 'b' | NULL
|
||||
"""
|
||||
class A:
|
||||
pass
|
||||
a = A()
|
||||
a.a = 1
|
||||
a.b = 2
|
||||
d = a.__dict__.copy()
|
||||
del d['a']
|
||||
del d['b']
|
||||
d['b'] = "value"
|
||||
return d
|
||||
|
||||
class NoInlineAorB:
|
||||
pass
|
||||
for i in range(ord('c'), ord('z')):
|
||||
setattr(NoInlineAorB(), chr(i), i)
|
||||
|
||||
c = NoInlineAorB()
|
||||
c.a = 0
|
||||
c.b = 1
|
||||
self.assertFalse(_testinternalcapi.has_inline_values(c))
|
||||
|
||||
def f(o, n):
|
||||
for i in range(n):
|
||||
o.b = i
|
||||
# Prime f to store to dict slot 1
|
||||
f(c, 100)
|
||||
|
||||
test_obj = NoInlineAorB()
|
||||
test_obj.__dict__ = make_special_dict()
|
||||
self.assertEqual(test_obj.b, "value")
|
||||
|
||||
#This should set x.b = 0
|
||||
f(test_obj, 1)
|
||||
self.assertEqual(test_obj.b, 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
It was possible in 3.14.0a1 only for attribute lookup to give the wrong
|
||||
value. This was due to an incorrect specialization in very specific
|
||||
circumstances. This is fixed in 3.14.0a2.
|
|
@ -2303,17 +2303,16 @@ dummy_func(
|
|||
assert(PyDict_CheckExact((PyObject *)dict));
|
||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
||||
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries);
|
||||
PyObject *old_value;
|
||||
DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys));
|
||||
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
|
||||
DEOPT_IF(ep->me_key != name);
|
||||
PyObject *old_value = ep->me_value;
|
||||
DEOPT_IF(old_value == NULL);
|
||||
/* Ensure dict is GC tracked if it needs to be */
|
||||
if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(PyStackRef_AsPyObjectBorrow(value))) {
|
||||
_PyObject_GC_TRACK(dict);
|
||||
}
|
||||
old_value = ep->me_value;
|
||||
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
||||
_PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||
_PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
||||
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
||||
// when dict only holds the strong reference to value in ep->me_value.
|
||||
|
|
|
@ -2815,7 +2815,6 @@
|
|||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
PyObject *old_value;
|
||||
if (!DK_IS_UNICODE(dict->ma_keys)) {
|
||||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
|
@ -2825,14 +2824,17 @@
|
|||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
PyObject *old_value = ep->me_value;
|
||||
if (old_value == NULL) {
|
||||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
/* Ensure dict is GC tracked if it needs to be */
|
||||
if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(PyStackRef_AsPyObjectBorrow(value))) {
|
||||
_PyObject_GC_TRACK(dict);
|
||||
}
|
||||
old_value = ep->me_value;
|
||||
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
_PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||
_PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
||||
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
||||
|
|
|
@ -7443,18 +7443,17 @@
|
|||
assert(PyDict_CheckExact((PyObject *)dict));
|
||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
|
||||
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
|
||||
PyObject *old_value;
|
||||
DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys), STORE_ATTR);
|
||||
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
|
||||
DEOPT_IF(ep->me_key != name, STORE_ATTR);
|
||||
PyObject *old_value = ep->me_value;
|
||||
DEOPT_IF(old_value == NULL, STORE_ATTR);
|
||||
/* Ensure dict is GC tracked if it needs to be */
|
||||
if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(PyStackRef_AsPyObjectBorrow(value))) {
|
||||
_PyObject_GC_TRACK(dict);
|
||||
}
|
||||
old_value = ep->me_value;
|
||||
PyDict_WatchEvent event = old_value == NULL ? PyDict_EVENT_ADDED : PyDict_EVENT_MODIFIED;
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
_PyDict_NotifyEvent(tstate->interp, event, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||
_PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value));
|
||||
stack_pointer = _PyFrame_GetStackPointer(frame);
|
||||
ep->me_value = PyStackRef_AsPyObjectSteal(value);
|
||||
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
|
||||
|
|
Loading…
Reference in New Issue