Skip to content

Commit 1b7dcfe

Browse files
committed
invoke the mro() method during MRO computation
1 parent b63feb5 commit 1b7dcfe

File tree

7 files changed

+112
-16
lines changed

7 files changed

+112
-16
lines changed

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class C(A, B):
6565
assert C.__base__ == B
6666

6767
#-----------------------------------------------
68-
68+
6969
class A(object):
7070
pass
7171

@@ -79,9 +79,9 @@ class C(A, B):
7979

8080
C = type('C', (B, int), {'spam': lambda self: 'spam%s' % self})
8181
assert C.__base__ == int
82-
82+
8383
#-----------------------------------------------
84-
84+
8585
class A (object): pass
8686

8787
class BB (A): pass
@@ -96,7 +96,7 @@ class D (B, C): pass
9696
assert D.__base__ == C
9797

9898
#-----------------------------------------------
99-
99+
100100
class A: pass
101101

102102
class B: pass
@@ -110,7 +110,7 @@ class C(A): pass
110110
assert B.__subclasses__() == [C]
111111

112112
#-----------------------------------------------
113-
113+
114114
class A: pass
115115

116116
class B: pass
@@ -133,7 +133,7 @@ class C(A, B): pass
133133
assert B.__subclasses__() == [C]
134134

135135
#-----------------------------------------------
136-
136+
137137
# class A: pass
138138
#
139139
# class B: pass
@@ -155,5 +155,16 @@ class MyStr(str):
155155
A = type('A', (), {
156156
MyStr("x"): 42
157157
})
158-
assert A.x == 42
159158
assert any(type(k) == MyStr for k in A.__dict__.keys())
159+
160+
def test_mro():
161+
class M(type):
162+
def mro(cls):
163+
assert type.mro(cls) == [cls, A, B, object]
164+
return [cls, B, A, object]
165+
166+
class A: pass
167+
class B: pass
168+
class C(A, B, metaclass = M): pass
169+
170+
assert C.__mro__ == (C, B, A, object)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonManagedClass.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@
3434
import java.util.WeakHashMap;
3535

3636
import com.oracle.graal.python.PythonLanguage;
37+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
3738
import com.oracle.graal.python.builtins.objects.PNone;
3839
import com.oracle.graal.python.builtins.objects.cext.PythonClassNativeWrapper;
3940
import com.oracle.graal.python.builtins.objects.object.PythonObject;
4041
import com.oracle.graal.python.builtins.objects.type.TypeNodes.ComputeMroNode;
4142
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetSubclassesNode;
43+
import com.oracle.graal.python.nodes.ErrorMessages;
4244
import com.oracle.graal.python.nodes.PGuards;
45+
import com.oracle.graal.python.nodes.PRaiseNode;
4346
import com.oracle.graal.python.runtime.exception.PException;
4447
import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage;
4548
import com.oracle.truffle.api.Assumption;
@@ -82,6 +85,7 @@ protected PythonManagedClass(Object typeClass, DynamicObject storage, Shape inst
8285

8386
// Compute MRO
8487
this.methodResolutionOrder.setInternalArrayObject(ComputeMroNode.doSlowPath(this));
88+
this.methodResolutionOrder.setInitialized();
8589
this.needsNativeAllocation = computeNeedsNativeAllocation();
8690

8791
setAttribute(__NAME__, getBaseName(name));
@@ -176,12 +180,16 @@ private void unsafeSetSuperClass(PythonAbstractClass... newBaseClasses) {
176180
assert getBaseClasses() == null || getBaseClasses().length == 0;
177181
this.baseClasses = newBaseClasses;
178182

183+
for (PythonAbstractClass base : getBaseClasses()) {
184+
if (base instanceof PythonManagedClass && !((PythonManagedClass) base).getMethodResolutionOrder().isInitialized()) {
185+
throw PRaiseNode.getUncached().raise(TypeError, ErrorMessages.CANNOT_EXTEND_INCOMPLETE_P, base);
186+
}
187+
}
179188
for (PythonAbstractClass base : getBaseClasses()) {
180189
if (base != null) {
181190
GetSubclassesNode.getUncached().execute(base).add(this);
182191
}
183192
}
184-
this.methodResolutionOrder.setInternalArrayObject(ComputeMroNode.doSlowPath(this));
185193
}
186194

187195
@TruffleBoundary

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
import com.oracle.graal.python.builtins.objects.type.TypeNodes.CheckCompatibleForAssigmentNode;
133133
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBaseClassNode;
134134
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBestBaseClassNode;
135+
import static com.oracle.graal.python.nodes.SpecialMethodNames.MRO;
135136
import com.oracle.truffle.api.profiles.BranchProfile;
136137
import com.oracle.truffle.api.profiles.ConditionProfile;
137138

@@ -177,12 +178,17 @@ private static String concat(Object moduleName, Object qualName) {
177178
abstract static class MroAttrNode extends PythonBuiltinNode {
178179
@Specialization
179180
Object doit(Object klass,
180-
@Cached("create()") TypeNodes.GetMroNode getMroNode) {
181-
return factory().createTuple(getMroNode.execute(klass));
181+
@Cached("create()") TypeNodes.GetMroNode getMroNode,
182+
@Cached TypeNodes.GetMroStorageNode getMroStorageNode) {
183+
if (klass instanceof PythonManagedClass && !getMroStorageNode.execute(klass).isInitialized()) {
184+
return PNone.NONE;
185+
}
186+
PythonAbstractClass[] mro = getMroNode.execute(klass);
187+
return factory().createTuple(mro);
182188
}
183189
}
184190

185-
@Builtin(name = "mro", minNumOfPositionalArgs = 1)
191+
@Builtin(name = MRO, minNumOfPositionalArgs = 1)
186192
@GenerateNodeFactory
187193
public abstract static class MroNode extends PythonBuiltinNode {
188194
@Specialization(guards = "lib.isLazyPythonClass(klass)")
@@ -196,7 +202,7 @@ Object doit(Object klass,
196202
@Specialization(guards = "!lib.isLazyPythonClass(object)")
197203
Object doit(Object object,
198204
@SuppressWarnings("unused") @CachedLibrary(limit = "2") PythonObjectLibrary lib) {
199-
throw raise(TypeError, ErrorMessages.DESCRIPTOR_REQUIRES_OBJ, "mro", "type", object);
205+
throw raise(TypeError, ErrorMessages.DESCRIPTOR_REQUIRES_OBJ, MRO, "type", object);
200206
}
201207
}
202208

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import com.oracle.graal.python.builtins.objects.common.SequenceNodesFactory.GetObjectArrayNodeGen;
7171
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetInternalObjectArrayNode;
7272
import com.oracle.graal.python.builtins.objects.dict.PDict;
73+
import com.oracle.graal.python.builtins.objects.function.PFunction;
7374
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
7475
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
7576
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass.FlagsContainer;
@@ -90,15 +91,18 @@
9091
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DICT__;
9192
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__SLOTS__;
9293
import com.oracle.graal.python.nodes.SpecialMethodNames;
94+
import static com.oracle.graal.python.nodes.SpecialMethodNames.MRO;
9395
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETATTRIBUTE__;
9496
import static com.oracle.graal.python.nodes.SpecialMethodNames.__NEW__;
9597
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
98+
import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
9699
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
97100
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
98101
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
99102
import com.oracle.graal.python.nodes.truffle.PythonTypes;
100103
import com.oracle.graal.python.runtime.PythonContext;
101104
import com.oracle.graal.python.runtime.exception.PException;
105+
import com.oracle.graal.python.runtime.sequence.PSequence;
102106
import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage;
103107
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
104108
import com.oracle.graal.python.util.PythonUtils;
@@ -232,7 +236,11 @@ public abstract static class GetMroStorageNode extends PNodeWithContext {
232236
public abstract MroSequenceStorage execute(Object obj);
233237

234238
@Specialization
235-
static MroSequenceStorage doPythonClass(PythonManagedClass obj) {
239+
static MroSequenceStorage doPythonClass(PythonManagedClass obj,
240+
@Cached("createBinaryProfile()") ConditionProfile notInitialized) {
241+
if (!notInitialized.profile(obj.getMethodResolutionOrder().isInitialized())) {
242+
obj.getMethodResolutionOrder().setInternalArrayObject(TypeNodes.ComputeMroNode.doSlowPath(obj, false));
243+
}
236244
return obj.getMethodResolutionOrder();
237245
}
238246

@@ -277,7 +285,7 @@ static MroSequenceStorage doNativeClass(PythonNativeClass obj,
277285
@TruffleBoundary
278286
static MroSequenceStorage doSlowPath(Object obj) {
279287
if (obj instanceof PythonManagedClass) {
280-
return ((PythonManagedClass) obj).getMethodResolutionOrder();
288+
return doPythonClass((PythonManagedClass) obj, ConditionProfile.getUncached());
281289
} else if (obj instanceof PythonBuiltinClassType) {
282290
return PythonLanguage.getCore().lookupType((PythonBuiltinClassType) obj).getMethodResolutionOrder();
283291
} else if (PGuards.isNativeClass(obj)) {
@@ -1085,14 +1093,29 @@ public abstract static class ComputeMroNode extends Node {
10851093

10861094
@TruffleBoundary
10871095
public static PythonAbstractClass[] doSlowPath(PythonAbstractClass cls) {
1088-
return computeMethodResolutionOrder(cls);
1096+
return doSlowPath(cls, true);
10891097
}
10901098

1091-
private static PythonAbstractClass[] computeMethodResolutionOrder(PythonAbstractClass cls) {
1099+
@TruffleBoundary
1100+
public static PythonAbstractClass[] doSlowPath(PythonAbstractClass cls, boolean invokeMro) {
1101+
return computeMethodResolutionOrder(cls, invokeMro);
1102+
}
1103+
1104+
private static PythonAbstractClass[] computeMethodResolutionOrder(PythonAbstractClass cls, boolean invokeMro) {
10921105
CompilerAsserts.neverPartOfCompilation();
10931106

10941107
PythonAbstractClass[] currentMRO = null;
10951108

1109+
Object type = PythonObjectLibrary.getUncached().getLazyPythonClass(cls);
1110+
if (invokeMro) {
1111+
if (type instanceof LazyPythonClass) {
1112+
PythonAbstractClass[] typeMRO = getMRO((LazyPythonClass) type, cls);
1113+
if (typeMRO != null) {
1114+
return typeMRO;
1115+
}
1116+
}
1117+
}
1118+
10961119
PythonAbstractClass[] baseClasses = GetBaseClassesNodeGen.getUncached().execute(cls);
10971120
if (baseClasses.length == 0) {
10981121
currentMRO = new PythonAbstractClass[]{cls};
@@ -1121,6 +1144,40 @@ private static PythonAbstractClass[] computeMethodResolutionOrder(PythonAbstract
11211144
return currentMRO;
11221145
}
11231146

1147+
private static PythonAbstractClass[] getMRO(LazyPythonClass type, PythonAbstractClass cls) {
1148+
if (type instanceof PythonClass) {
1149+
Object mroMeth = LookupAttributeInMRONode.Dynamic.getUncached().execute(type, MRO);
1150+
if (mroMeth instanceof PFunction) {
1151+
Object mroObj = CallUnaryMethodNode.getUncached().executeObject(mroMeth, cls);
1152+
if (mroObj instanceof PSequence) {
1153+
return mroCheck(cls, ((PSequence) mroObj).getSequenceStorage().getInternalArray());
1154+
}
1155+
throw PRaiseNode.getUncached().raise(TypeError, ErrorMessages.OBJ_NOT_ITERABLE, cls);
1156+
}
1157+
}
1158+
return null;
1159+
}
1160+
1161+
private static PythonAbstractClass[] mroCheck(LazyPythonClass cls, Object[] mro) {
1162+
List<PythonAbstractClass> resultMro = new ArrayList<>(mro.length);
1163+
for (int i = 0; i < mro.length; i++) {
1164+
Object object = mro[i];
1165+
if (object == null) {
1166+
continue;
1167+
}
1168+
if (!PythonObjectLibrary.getUncached().isLazyPythonClass(object)) {
1169+
throw PRaiseNode.getUncached().raise(TypeError, ErrorMessages.S_RETURNED_NON_CLASS, "mro()", object);
1170+
}
1171+
if (!IsSubtypeNode.getUncached().execute(cls, object)) {
1172+
// XXX typeobject.c/mro_check() does !PyType_IsSubtype(solid, solid_base(base))
1173+
// could reuse GetBestBaseClassNode.solidBase(), but need frame for that
1174+
throw PRaiseNode.getUncached().raise(TypeError, ErrorMessages.S_RETURNED_BASE_WITH_UNSUITABLE_LAYOUT, "mro()", object);
1175+
}
1176+
resultMro.add((PythonAbstractClass) object);
1177+
}
1178+
return resultMro.toArray(new PythonAbstractClass[resultMro.size()]);
1179+
}
1180+
11241181
private static PythonAbstractClass[] mergeMROs(MROMergeState[] toMerge, List<PythonAbstractClass> mro) {
11251182
int idx;
11261183
scan: for (idx = 0; idx < toMerge.length; idx++) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public abstract class ErrorMessages {
121121
public static final String CANNOT_CREATE_INSTANCES = "cannot create '%s' instances";
122122
public static final String CANNOT_CREATE_WEAK_REFERENCE_TO = "cannot create weak reference to '%p' object";
123123
public static final String CANNOT_DELETE_ATTRIBUTE = "can't delete %s.%s";
124+
public static final String CANNOT_EXTEND_INCOMPLETE_P = "Cannot extend an incomplete type '%p'";
124125
public static final String CANNOT_GET_SHAPE_OF_NATIVE_CLS = "cannot get shape of native class";
125126
public static final String CANNOT_GET_CONSISTEMT_METHOD_RESOLUTION = "Cannot create a consistent method resolution\norder (MRO) for bases %s";
126127
public static final String CANNOT_HANDLE_ZIP_FILE = "cannot handle Zip file: '%s'";
@@ -421,8 +422,10 @@ public abstract class ErrorMessages {
421422
public static final String REQUIRES_STRING_AS_LEFT_OPERAND = "'in <string>' requires string as left operand, not %P";
422423
public static final String RESULT_TOO_MANY_ITEMS = "%s result has too many items";
423424
public static final String RETURN_OUTSIDE_FUNC = "'return' outside function";
425+
public static final String S_RETURNED_BASE_WITH_UNSUITABLE_LAYOUT = "%s returned base with unsuitable layout ('%p')";
424426
public static final String RETURNED_NON_FLOAT = "%p.%s returned non-float (type %p)";
425427
public static final String RETURNED_NON_INT = "%s returned a non-int (type %p)";
428+
public static final String S_RETURNED_NON_CLASS = "%s returned a non-class ('%p')";
426429
public static final String P_S_RETURNED_NON_INT = "%p.%s returned a non int (type %p)";
427430
public static final String RETURNED_NON_INTEGER = "%s returned a non-integer";
428431
public static final String RETURNED_NON_INTEGRAL = "%s returned non-Integral (type %p)";

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/SpecialMethodNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ public abstract class SpecialMethodNames {
167167
public static final String __CLASS_GETITEM__ = "__class_getitem__";
168168
public static final String FILENO = "fileno";
169169
public static final String ISDISJOINT = "isdisjoint";
170+
public static final String MRO = "mro";
170171

171172
public static final String RICHCMP = "__truffle_richcompare__";
172173
public static final String TRUFFLE_SOURCE = "__truffle_source__";

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/MroSequenceStorage.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public final class MroSequenceStorage extends TypedSequenceStorage {
7373

7474
@CompilationFinal(dimensions = 1) private PythonAbstractClass[] values;
7575

76+
@CompilationFinal private boolean initialized = false;
77+
7678
@TruffleBoundary
7779
public MroSequenceStorage(String className, PythonAbstractClass[] elements) {
7880
this.className = className;
@@ -258,6 +260,14 @@ public void setInternalArrayObject(Object arrayObject) {
258260
this.capacity = classArray.length;
259261
}
260262

263+
public boolean isInitialized() {
264+
return initialized;
265+
}
266+
267+
public boolean setInitialized() {
268+
return initialized = true;
269+
}
270+
261271
@Override
262272
public ListStorageType getElementType() {
263273
return ListStorageType.Generic;

0 commit comments

Comments
 (0)