Skip to content

Commit bc10b90

Browse files
committed
[GR-10450] fix LookupAttributeInMRONode (remove caching on the attribute value, which can change - i.e., class attributes)
PullRequest: graalpython/86
2 parents 6ac6203 + 767d5b0 commit bc10b90

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_scope.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,11 +500,13 @@ def f2():
500500
return x
501501
f2()
502502

503+
del x
504+
503505
for i in range(100):
504506
f1()
505507

506508
# TODO: in cpython the value for count is 0 since "x" is gced after each f1() call
507-
assert Foo.count == 0 or Foo.count == 1
509+
assert Foo.count == 0 or Foo.count == 100
508510

509511

510512
# def test_class_and_global():
@@ -827,3 +829,24 @@ def func_2(x=environ):
827829
assert 'my-key' in val
828830
assert val['my-key'] == 'test-val'
829831
del environ['my-key']
832+
833+
834+
def test_class_attr():
835+
class MyClass(object):
836+
a_counter = 0
837+
838+
def register():
839+
MyClass.a_counter += 1
840+
841+
MyClass.a_counter += 1
842+
MyClass.a_counter += 1
843+
MyClass.a_counter += 1
844+
MyClass.a_counter += 1
845+
846+
for i in range(10):
847+
MyClass.a_counter += 1
848+
849+
for i in range(10):
850+
register()
851+
852+
assert MyClass.a_counter == 24

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import com.oracle.truffle.api.dsl.NodeChild;
4848
import com.oracle.truffle.api.dsl.NodeChildren;
4949
import com.oracle.truffle.api.dsl.Specialization;
50+
import com.oracle.truffle.api.nodes.ExplodeLoop;
5051

5152
@NodeChildren({@NodeChild(value = "klass", type = PNode.class), @NodeChild(value = "key", type = PNode.class)})
5253
public abstract class LookupAttributeInMRONode extends PNode {
@@ -62,19 +63,37 @@ public static LookupAttributeInMRONode create() {
6263
*/
6364
public abstract Object execute(Object klass, Object key);
6465

65-
@SuppressWarnings("unused")
66-
@Specialization(guards = {"klass == cachedKlass", "key.equals(cachedKey)"}, limit = "5", assumptions = "lookupStable")
67-
protected Object returnDirect(PythonClass klass, String key,
68-
@Cached("klass") PythonClass cachedKlass,
69-
@Cached("key") String cachedKey,
70-
@Cached("cachedKlass.getLookupStableAssumption()") Assumption lookupStable,
71-
@Cached("cachedKlass.getAttribute(cachedKey)") Object result) {
72-
return result;
66+
@Specialization(guards = {"klass == cachedKlass", "key.equals(cachedKey)", "mroLength < 32"}, limit = "5", assumptions = "lookupStable")
67+
@ExplodeLoop(kind = ExplodeLoop.LoopExplosionKind.FULL_EXPLODE_UNTIL_RETURN)
68+
protected Object lookupConstantMRO(@SuppressWarnings("unused") PythonClass klass, String key,
69+
@Cached("klass") @SuppressWarnings("unused") PythonClass cachedKlass,
70+
@Cached("key") @SuppressWarnings("unused") String cachedKey,
71+
@Cached("cachedKlass.getLookupStableAssumption()") @SuppressWarnings("unused") Assumption lookupStable,
72+
@Cached("create()") ReadAttributeFromObjectNode readAttrNode,
73+
@Cached(value = "cachedKlass.getMethodResolutionOrder()", dimensions = 1) PythonClass[] mro,
74+
@Cached("mro.length") @SuppressWarnings("unused") int mroLength) {
75+
for (int i = 0; i < mro.length; i++) {
76+
PythonClass kls = mro[i];
77+
Object value = readAttrNode.execute(kls, key);
78+
if (value != PNone.NO_VALUE) {
79+
return value;
80+
}
81+
}
82+
return PNone.NO_VALUE;
7383
}
7484

75-
@Specialization(replaces = "returnDirect")
76-
protected Object lookup(PythonClass klass, String key) {
77-
return klass.getAttribute(key);
85+
@Specialization
86+
protected Object lookup(PythonClass klass, String key,
87+
@Cached("create()") ReadAttributeFromObjectNode readAttrNode) {
88+
PythonClass[] mro = klass.getMethodResolutionOrder();
89+
for (int i = 0; i < mro.length; i++) {
90+
PythonClass kls = mro[i];
91+
Object value = readAttrNode.execute(kls, key);
92+
if (value != PNone.NO_VALUE) {
93+
return value;
94+
}
95+
}
96+
return PNone.NO_VALUE;
7897
}
7998

8099
@SuppressWarnings("unused")

0 commit comments

Comments
 (0)