mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Memoize result of MRO resolution
This commit is contained in:
@@ -62,6 +62,7 @@ import static com.intellij.openapi.util.text.StringUtil.notNullize;
|
||||
*/
|
||||
public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyClass {
|
||||
public static final PyClass[] EMPTY_ARRAY = new PyClassImpl[0];
|
||||
private static final Object EVALUATING = new Object();
|
||||
|
||||
private List<PyTargetExpression> myInstanceAttributes;
|
||||
private final NotNullLazyValue<CachedValue<Boolean>> myNewStyle = new NotNullLazyValue<CachedValue<Boolean>>() {
|
||||
@@ -323,7 +324,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
List<PyClassLikeType> result = new LinkedList<PyClassLikeType>(); // need to insert to 0th position on linearize
|
||||
while (true) {
|
||||
// filter blank sequences
|
||||
List<List<PyClassLikeType>> nonBlankSequences = new ArrayList<List<PyClassLikeType>>(sequences.size());
|
||||
final List<List<PyClassLikeType>> nonBlankSequences = new ArrayList<List<PyClassLikeType>>(sequences.size());
|
||||
for (List<PyClassLikeType> item : sequences) {
|
||||
if (item.size() > 0) nonBlankSequences.add(item);
|
||||
}
|
||||
@@ -333,14 +334,14 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
PyClassLikeType head = null; // to keep compiler happy; really head is assigned in the loop at least once.
|
||||
for (List<PyClassLikeType> seq : nonBlankSequences) {
|
||||
head = seq.get(0);
|
||||
boolean head_in_tails = false;
|
||||
for (List<PyClassLikeType> tail_seq : nonBlankSequences) {
|
||||
if (tail_seq.indexOf(head) > 0) { // -1 is not found, 0 is head, >0 is tail.
|
||||
head_in_tails = true;
|
||||
boolean headInTails = false;
|
||||
for (List<PyClassLikeType> tailSeq : nonBlankSequences) {
|
||||
if (tailSeq.indexOf(head) > 0) { // -1 is not found, 0 is head, >0 is tail.
|
||||
headInTails = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!head_in_tails) {
|
||||
if (!headInTails) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -363,28 +364,46 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
} // we either return inside the loop or die by assertion
|
||||
}
|
||||
|
||||
|
||||
private static List<PyClassLikeType> mroLinearize(@NotNull PyClassLikeType type, boolean addThisType, @NotNull TypeEvalContext context) {
|
||||
return mroLinearize(type, addThisType, context, new HashMap<PyClassLikeType, Object>());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PyClassLikeType> mroLinearize(@NotNull PyClassLikeType type, @NotNull Set<PyClassLikeType> seen, boolean addThisType,
|
||||
@NotNull TypeEvalContext context) {
|
||||
if (seen.contains(type)) {
|
||||
private static List<PyClassLikeType> mroLinearize(@NotNull PyClassLikeType type, boolean addThisType,
|
||||
@NotNull TypeEvalContext context,
|
||||
@NotNull Map<PyClassLikeType, Object> cache) {
|
||||
final Object computed = cache.get(type);
|
||||
if (computed == EVALUATING) {
|
||||
throw new IllegalStateException("Circular class inheritance");
|
||||
}
|
||||
final List<PyClassLikeType> bases = type.getSuperClassTypes(context);
|
||||
List<List<PyClassLikeType>> lines = new ArrayList<List<PyClassLikeType>>();
|
||||
for (PyClassLikeType base : bases) {
|
||||
if (base != null) {
|
||||
final Set<PyClassLikeType> newSeen = new HashSet<PyClassLikeType>(seen);
|
||||
newSeen.add(type);
|
||||
List<PyClassLikeType> lin = mroLinearize(base, newSeen, true, context);
|
||||
if (!lin.isEmpty()) lines.add(lin);
|
||||
if (computed != null) {
|
||||
//noinspection unchecked
|
||||
return (List<PyClassLikeType>)computed;
|
||||
}
|
||||
cache.put(type, EVALUATING);
|
||||
List<PyClassLikeType> result = null;
|
||||
try {
|
||||
final List<PyClassLikeType> bases = type.getSuperClassTypes(context);
|
||||
final List<List<PyClassLikeType>> lines = new ArrayList<List<PyClassLikeType>>();
|
||||
for (PyClassLikeType base : bases) {
|
||||
if (base != null) {
|
||||
final List<PyClassLikeType> baseClassMRO = mroLinearize(base, true, context, cache);
|
||||
if (!baseClassMRO.isEmpty()) {
|
||||
lines.add(baseClassMRO);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bases.isEmpty()) {
|
||||
lines.add(bases);
|
||||
}
|
||||
result = mroMerge(lines);
|
||||
if (addThisType) {
|
||||
result.add(0, type);
|
||||
}
|
||||
}
|
||||
if (!bases.isEmpty()) {
|
||||
lines.add(bases);
|
||||
}
|
||||
List<PyClassLikeType> result = mroMerge(lines);
|
||||
if (addThisType) {
|
||||
result.add(0, type);
|
||||
finally {
|
||||
cache.put(type, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1280,7 +1299,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
final PyType thisType = context.getType(this);
|
||||
if (thisType instanceof PyClassLikeType) {
|
||||
try {
|
||||
return mroLinearize((PyClassLikeType)thisType, new HashSet<PyClassLikeType>(), false, context);
|
||||
return mroLinearize((PyClassLikeType)thisType, false, context);
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
|
||||
class Class001(object):
|
||||
unique_attr = 1
|
||||
|
||||
|
||||
class Class002(Class001):
|
||||
unique_attr = 2
|
||||
|
||||
|
||||
class Class003(Class002, Class001):
|
||||
unique_attr = 3
|
||||
|
||||
|
||||
class Class004(Class003, Class002, Class001):
|
||||
unique_attr = 4
|
||||
|
||||
|
||||
class Class005(Class004, Class003, Class002):
|
||||
unique_attr = 5
|
||||
|
||||
|
||||
class Class006(Class005, Class004, Class003):
|
||||
unique_attr = 6
|
||||
|
||||
|
||||
class Class007(Class006, Class005, Class004):
|
||||
unique_attr = 7
|
||||
|
||||
|
||||
class Class008(Class007, Class006, Class005):
|
||||
unique_attr = 8
|
||||
|
||||
|
||||
class Class009(Class008, Class007, Class006):
|
||||
unique_attr = 9
|
||||
|
||||
|
||||
class Class010(Class009, Class008, Class007):
|
||||
unique_attr = 10
|
||||
|
||||
|
||||
class Class011(Class010, Class009, Class008):
|
||||
unique_attr = 11
|
||||
|
||||
|
||||
class Class012(Class011, Class010, Class009):
|
||||
unique_attr = 12
|
||||
|
||||
|
||||
class Class013(Class012, Class011, Class010):
|
||||
unique_attr = 13
|
||||
|
||||
|
||||
class Class014(Class013, Class012, Class011):
|
||||
unique_attr = 14
|
||||
|
||||
|
||||
class Class015(Class014, Class013, Class012):
|
||||
unique_attr = 15
|
||||
|
||||
|
||||
class Class016(Class015, Class014, Class013):
|
||||
unique_attr = 16
|
||||
|
||||
|
||||
class Class017(Class016, Class015, Class014):
|
||||
unique_attr = 17
|
||||
|
||||
|
||||
class Class018(Class017, Class016, Class015):
|
||||
unique_attr = 18
|
||||
|
||||
|
||||
class Class019(Class018, Class017, Class016):
|
||||
unique_attr = 19
|
||||
|
||||
|
||||
class Class020(Class019, Class018, Class017):
|
||||
unique_attr = 20
|
||||
|
||||
|
||||
class Class021(Class020, Class019, Class018):
|
||||
unique_attr = 21
|
||||
|
||||
class Class022(Class021, Class020, Class019):
|
||||
unique_attr = 22
|
||||
|
||||
class Class023(Class022, Class021, Class020):
|
||||
unique_attr = 23
|
||||
|
||||
|
||||
class Class024(Class023, Class022, Class021):
|
||||
unique_attr = 24
|
||||
|
||||
|
||||
class Class025(Class024, Class023, Class022):
|
||||
unique_attr = 25
|
||||
|
||||
|
||||
class Class026(Class025, Class024, Class023):
|
||||
unique_attr = 26
|
||||
|
||||
|
||||
class Class027(Class026, Class025, Class024):
|
||||
unique_attr = 27
|
||||
|
||||
|
||||
class Class028(Class027, Class026, Class025):
|
||||
unique_attr = 28
|
||||
|
||||
|
||||
class Class029(Class028, Class027, Class026):
|
||||
|
||||
unique_attr = 29
|
||||
|
||||
class Class030(Class029, Class028, Class027):
|
||||
unique_attr = 30
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.jetbrains.python.codeInsight;
|
||||
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.types.PyClassLikeType;
|
||||
@@ -71,6 +72,18 @@ public class PyClassMROTest extends PyTestCase {
|
||||
assertMRO(getClass("H"), "E", "F", "B", "G", "C", "D", "A", "object");
|
||||
}
|
||||
|
||||
public void testTangledInheritance() throws Exception {
|
||||
final List<String> expectedMRO = new ArrayList<String>();
|
||||
for (int i = 29; i >= 1; i--) {
|
||||
expectedMRO.add(String.format("Class%03d", i));
|
||||
}
|
||||
expectedMRO.add("object");
|
||||
final long startTime = System.currentTimeMillis();
|
||||
assertMRO(getClass("Class030"), ArrayUtil.toStringArray(expectedMRO));
|
||||
final long elapsed = System.currentTimeMillis() - startTime;
|
||||
assertTrue("Calculation of MRO takes too much time: " + elapsed + " ms", elapsed < 5000);
|
||||
}
|
||||
|
||||
public void assertMRO(@NotNull PyClass cls, @NotNull String... mro) {
|
||||
final List<PyClassLikeType> types = cls.getAncestorTypes(TypeEvalContext.codeInsightFallback());
|
||||
final List<String> classNames = new ArrayList<String>();
|
||||
|
||||
Reference in New Issue
Block a user