Improve performance of dependency analyzer

This commit is contained in:
Alexey Andreev 2017-11-10 23:45:12 +03:00
parent e1c7acd8a1
commit d811e7edbb
8 changed files with 255 additions and 89 deletions

View File

@ -15,6 +15,9 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import com.carrotsearch.hppc.cursors.IntCursor;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -57,6 +60,7 @@ import org.teavm.model.util.ProgramUtils;
import org.teavm.parsing.Parser; import org.teavm.parsing.Parser;
public class DependencyChecker implements DependencyInfo { public class DependencyChecker implements DependencyInfo {
private static final int PROPAGATION_STACK_THRESHOLD = 50;
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true"); static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
private int classNameSuffix; private int classNameSuffix;
private DependencyClassSource classSource; private DependencyClassSource classSource;
@ -68,6 +72,7 @@ public class DependencyChecker implements DependencyInfo {
private CachedMapper<String, ClassDependency> classCache; private CachedMapper<String, ClassDependency> classCache;
private List<DependencyListener> listeners = new ArrayList<>(); private List<DependencyListener> listeners = new ArrayList<>();
private ServiceRepository services; private ServiceRepository services;
private Deque<DependencyNodeToNodeTransition> pendingTransitions = new ArrayDeque<>();
private Deque<Runnable> tasks = new ArrayDeque<>(); private Deque<Runnable> tasks = new ArrayDeque<>();
private Queue<Runnable> deferredTasks = new ArrayDeque<>(); private Queue<Runnable> deferredTasks = new ArrayDeque<>();
List<DependencyType> types = new ArrayList<>(); List<DependencyType> types = new ArrayList<>();
@ -79,6 +84,7 @@ public class DependencyChecker implements DependencyInfo {
private DependencyAgent agent; private DependencyAgent agent;
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>(); Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
private boolean completing; private boolean completing;
private Map<String, SuperClassFilter> superClassFilters = new HashMap<>();
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics) { Diagnostics diagnostics) {
@ -237,7 +243,7 @@ public class DependencyChecker implements DependencyInfo {
private int propagationDepth; private int propagationDepth;
void schedulePropagation(DependencyConsumer consumer, DependencyType type) { void schedulePropagation(DependencyConsumer consumer, DependencyType type) {
if (propagationDepth < 50) { if (propagationDepth < PROPAGATION_STACK_THRESHOLD) {
++propagationDepth; ++propagationDepth;
consumer.consume(type); consumer.consume(type);
--propagationDepth; --propagationDepth;
@ -246,6 +252,20 @@ public class DependencyChecker implements DependencyInfo {
} }
} }
void schedulePropagation(DependencyNodeToNodeTransition consumer, DependencyType type) {
if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD) {
++propagationDepth;
consumer.consume(type);
--propagationDepth;
} else {
if (consumer.pendingTypes == null) {
pendingTransitions.add(consumer);
consumer.pendingTypes = new IntOpenHashSet();
}
consumer.pendingTypes.add(type.index);
}
}
void schedulePropagation(DependencyNodeToNodeTransition consumer, DependencyType[] types) { void schedulePropagation(DependencyNodeToNodeTransition consumer, DependencyType[] types) {
if (types.length == 0) { if (types.length == 0) {
return; return;
@ -255,14 +275,18 @@ public class DependencyChecker implements DependencyInfo {
return; return;
} }
if (propagationDepth < 50) { if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD) {
++propagationDepth; ++propagationDepth;
consumer.consume(types); consumer.consume(types);
--propagationDepth; --propagationDepth;
} else { } else {
tasks.add(() -> { if (consumer.pendingTypes == null) {
consumer.consume(types); pendingTransitions.add(consumer);
}); consumer.pendingTypes = new IntOpenHashSet();
}
for (DependencyType type : types) {
consumer.pendingTypes.add(type.index);
}
} }
} }
@ -275,7 +299,7 @@ public class DependencyChecker implements DependencyInfo {
return; return;
} }
if (propagationDepth < 50) { if (propagationDepth < PROPAGATION_STACK_THRESHOLD) {
++propagationDepth; ++propagationDepth;
for (DependencyType type : types) { for (DependencyType type : types) {
consumer.consume(type); consumer.consume(type);
@ -486,7 +510,7 @@ public class DependencyChecker implements DependencyInfo {
} }
FieldDependency dep = new FieldDependency(node, field, fieldRef); FieldDependency dep = new FieldDependency(node, field, fieldRef);
if (!dep.isMissing()) { if (!dep.isMissing()) {
tasks.add(() -> linkClass(fieldRef.getClassName(), null).initClass(null)); deferredTasks.add(() -> linkClass(fieldRef.getClassName(), null).initClass(null));
} }
return dep; return dep;
} }
@ -538,9 +562,13 @@ public class DependencyChecker implements DependencyInfo {
return; return;
} }
int index = 0; int index = 0;
while (true) { while (!deferredTasks.isEmpty() || !tasks.isEmpty() || !pendingTransitions.isEmpty()) {
while (!tasks.isEmpty()) { while (true) {
tasks.removeLast().run(); processNodeToNodeTransitionQueue();
if (tasks.isEmpty()) {
break;
}
tasks.remove().run();
if (++index == 100) { if (++index == 100) {
if (interruptor != null && !interruptor.shouldContinue()) { if (interruptor != null && !interruptor.shouldContinue()) {
interrupted = true; interrupted = true;
@ -549,10 +577,31 @@ public class DependencyChecker implements DependencyInfo {
index = 0; index = 0;
} }
} }
if (deferredTasks.isEmpty()) {
break; propagationDepth = PROPAGATION_STACK_THRESHOLD;
while (!deferredTasks.isEmpty()) {
deferredTasks.remove().run();
}
propagationDepth = 0;
}
}
private void processNodeToNodeTransitionQueue() {
while (!pendingTransitions.isEmpty()) {
DependencyNodeToNodeTransition transition = pendingTransitions.remove();
IntSet pendingTypes = transition.pendingTypes;
transition.pendingTypes = null;
if (pendingTypes.size() == 1) {
DependencyType type = types.get(pendingTypes.iterator().next().value);
transition.consume(type);
} else {
DependencyType[] typesToPropagate = new DependencyType[pendingTypes.size()];
int index = 0;
for (IntCursor cursor : pendingTypes) {
typesToPropagate[index++] = types.get(cursor.value);
}
transition.consume(typesToPropagate);
} }
deferredTasks.poll().run();
} }
} }
@ -611,4 +660,8 @@ public class DependencyChecker implements DependencyInfo {
public void addBootstrapMethodSubstitutor(MethodReference method, BootstrapMethodSubstitutor substitutor) { public void addBootstrapMethodSubstitutor(MethodReference method, BootstrapMethodSubstitutor substitutor) {
bootstrapMethodSubstitutors.put(method, substitutor); bootstrapMethodSubstitutors.put(method, substitutor);
} }
SuperClassFilter getSuperClassFilter(String superClass) {
return superClassFilters.computeIfAbsent(superClass, s -> new SuperClassFilter(classSource, s));
}
} }

View File

@ -37,7 +37,7 @@ class DependencyClassSource implements ClassHolderSource {
private List<ClassHolderTransformer> transformers = new ArrayList<>(); private List<ClassHolderTransformer> transformers = new ArrayList<>();
private Map<String, ClassHolder> cache = new LinkedHashMap<>(); private Map<String, ClassHolder> cache = new LinkedHashMap<>();
public DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics) { DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics) {
this.innerSource = innerSource; this.innerSource = innerSource;
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
} }

View File

@ -17,6 +17,7 @@ package org.teavm.dependency;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -65,7 +66,7 @@ class DependencyGraphBuilder {
private TextLocation currentLocation; private TextLocation currentLocation;
private ExceptionConsumer currentExceptionConsumer; private ExceptionConsumer currentExceptionConsumer;
public DependencyGraphBuilder(DependencyChecker dependencyChecker) { DependencyGraphBuilder(DependencyChecker dependencyChecker) {
this.dependencyChecker = dependencyChecker; this.dependencyChecker = dependencyChecker;
} }
@ -303,9 +304,8 @@ class DependencyGraphBuilder {
} }
} }
private static class VirtualCallConsumer implements DependencyConsumer { static class VirtualCallConsumer implements DependencyConsumer {
private final DependencyNode node; private final DependencyNode node;
private final String filterClass;
private final MethodDescriptor methodDesc; private final MethodDescriptor methodDesc;
private final DependencyChecker checker; private final DependencyChecker checker;
private final DependencyNode[] parameters; private final DependencyNode[] parameters;
@ -313,14 +313,16 @@ class DependencyGraphBuilder {
private final DefaultCallGraphNode caller; private final DefaultCallGraphNode caller;
private final TextLocation location; private final TextLocation location;
private final Set<MethodReference> knownMethods = new HashSet<>(); private final Set<MethodReference> knownMethods = new HashSet<>();
private final BitSet knownTypes = new BitSet();
private ExceptionConsumer exceptionConsumer; private ExceptionConsumer exceptionConsumer;
private SuperClassFilter filter;
public VirtualCallConsumer(DependencyNode node, String filterClass, VirtualCallConsumer(DependencyNode node, String filterClass,
MethodDescriptor methodDesc, DependencyChecker checker, DependencyNode[] parameters, MethodDescriptor methodDesc, DependencyChecker checker, DependencyNode[] parameters,
DependencyNode result, DefaultCallGraphNode caller, TextLocation location, DependencyNode result, DefaultCallGraphNode caller, TextLocation location,
ExceptionConsumer exceptionConsumer) { ExceptionConsumer exceptionConsumer) {
this.node = node; this.node = node;
this.filterClass = filterClass; this.filter = checker.getSuperClassFilter(filterClass);
this.methodDesc = methodDesc; this.methodDesc = methodDesc;
this.checker = checker; this.checker = checker;
this.parameters = parameters; this.parameters = parameters;
@ -332,6 +334,11 @@ class DependencyGraphBuilder {
@Override @Override
public void consume(DependencyType type) { public void consume(DependencyType type) {
if (knownTypes.get(type.index)) {
return;
}
knownTypes.set(type.index);
String className = type.getName(); String className = type.getName();
if (DependencyChecker.shouldLog) { if (DependencyChecker.shouldLog) {
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". " System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". "
@ -339,10 +346,10 @@ class DependencyGraphBuilder {
} }
if (className.startsWith("[")) { if (className.startsWith("[")) {
className = "java.lang.Object"; className = "java.lang.Object";
type = checker.getType(className);
} }
ClassReaderSource classSource = checker.getClassSource(); if (!filter.match(type)) {
if (!classSource.isSuperType(filterClass, className).orElse(false)) {
return; return;
} }
MethodReference methodRef = new MethodReference(className, methodDesc); MethodReference methodRef = new MethodReference(className, methodDesc);
@ -351,8 +358,8 @@ class DependencyGraphBuilder {
methodDep.use(); methodDep.use();
DependencyNode[] targetParams = methodDep.getVariables(); DependencyNode[] targetParams = methodDep.getVariables();
if (parameters[0] != null && targetParams[0] != null) { if (parameters[0] != null && targetParams[0] != null) {
parameters[0].connect(targetParams[0], thisType -> classSource.isSuperType( parameters[0].connect(targetParams[0],
methodDep.getMethod().getOwnerName(), thisType.getName()).orElse(false)); checker.getSuperClassFilter(methodDep.getMethod().getOwnerName()));
} }
for (int i = 1; i < parameters.length; ++i) { for (int i = 1; i < parameters.length; ++i) {
if (parameters[i] != null && targetParams[i] != null) { if (parameters[i] != null && targetParams[i] != null) {
@ -429,14 +436,9 @@ class DependencyGraphBuilder {
if (targetType instanceof ValueType.Object) { if (targetType instanceof ValueType.Object) {
String targetClsName = ((ValueType.Object) targetType).getClassName(); String targetClsName = ((ValueType.Object) targetType).getClassName();
final ClassReader targetClass = classSource.get(targetClsName); final ClassReader targetClass = classSource.get(targetClsName);
if (targetClass != null) { if (targetClass != null && !(targetClass.getName().equals("java.lang.Object"))) {
if (valueNode != null && receiverNode != null) { if (valueNode != null && receiverNode != null) {
valueNode.connect(receiverNode, type -> { valueNode.connect(receiverNode, dependencyChecker.getSuperClassFilter(targetClass.getName()));
if (targetClass.getName().equals("java.lang.Object")) {
return true;
}
return classSource.isSuperType(targetClass.getName(), type.getName()).orElse(false);
});
} }
return; return;
} }

View File

@ -19,6 +19,7 @@ import java.util.*;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class DependencyNode implements ValueDependencyInfo { public class DependencyNode implements ValueDependencyInfo {
private static final int SMALL_TYPES_THRESHOLD = 6;
private DependencyChecker dependencyChecker; private DependencyChecker dependencyChecker;
private List<DependencyConsumer> followers; private List<DependencyConsumer> followers;
private int[] smallTypes; private int[] smallTypes;
@ -57,8 +58,8 @@ public class DependencyNode implements ValueDependencyInfo {
return false; return false;
} }
} }
if (smallTypes.length == 5) { if (smallTypes.length == SMALL_TYPES_THRESHOLD) {
types = new BitSet(); types = new BitSet(dependencyChecker.types.size() * 2);
for (int existingType : smallTypes) { for (int existingType : smallTypes) {
types.set(existingType); types.set(existingType);
} }
@ -84,9 +85,6 @@ public class DependencyNode implements ValueDependencyInfo {
} }
public void propagate(DependencyType type) { public void propagate(DependencyType type) {
if (type.getDependencyChecker() != dependencyChecker) {
throw new IllegalArgumentException("The given type does not belong to the same dependency checker");
}
if (degree > 2) { if (degree > 2) {
return; return;
} }
@ -94,29 +92,34 @@ public class DependencyNode implements ValueDependencyInfo {
if (DependencyChecker.shouldLog) { if (DependencyChecker.shouldLog) {
System.out.println(tag + " -> " + type.getName()); System.out.println(tag + " -> " + type.getName());
} }
if (followers != null) { scheduleSingleType(type);
for (DependencyConsumer consumer : followers.toArray(new DependencyConsumer[followers.size()])) { }
dependencyChecker.schedulePropagation(consumer, type); }
}
private void scheduleSingleType(DependencyType type) {
if (followers != null) {
for (DependencyConsumer consumer : followers.toArray(new DependencyConsumer[followers.size()])) {
dependencyChecker.schedulePropagation(consumer, type);
} }
if (transitions != null) { }
for (DependencyConsumer consumer : transitions.toArray(new DependencyConsumer[transitions.size()])) { if (transitions != null) {
dependencyChecker.schedulePropagation(consumer, type); for (DependencyNodeToNodeTransition consumer : transitions.toArray(
} new DependencyNodeToNodeTransition[transitions.size()])) {
dependencyChecker.schedulePropagation(consumer, type);
} }
} }
} }
public void propagate(DependencyType[] newTypes) { public void propagate(DependencyType[] newTypes) {
DependencyType[] types = new DependencyType[newTypes.length]; if (degree > 2) {
return;
}
int j = 0; int j = 0;
for (int i = 0; i < newTypes.length; ++i) { for (int i = 0; i < newTypes.length; ++i) {
DependencyType type = newTypes[i]; DependencyType type = newTypes[i];
if (type.getDependencyChecker() != dependencyChecker) {
throw new IllegalArgumentException("The given type does not belong to the same dependency checker");
}
if (addType(type)) { if (addType(type)) {
types[j++] = type; newTypes[j++] = type;
} }
} }
if (j == 0) { if (j == 0) {
@ -124,22 +127,29 @@ public class DependencyNode implements ValueDependencyInfo {
} }
if (DependencyChecker.shouldLog) { if (DependencyChecker.shouldLog) {
for (int i = 0; i < j; ++i) { for (int i = 0; i < j; ++i) {
System.out.println(tag + " -> " + types[i].getName()); System.out.println(tag + " -> " + newTypes[i].getName());
} }
} }
if (j < types.length && (followers != null || transitions != null)) { if (followers == null && transitions == null) {
types = Arrays.copyOf(types, j); return;
}
if (j < newTypes.length) {
if (j == 1) {
scheduleSingleType(newTypes[0]);
return;
}
newTypes = Arrays.copyOf(newTypes, j);
} }
if (followers != null) { if (followers != null) {
for (DependencyConsumer consumer : followers.toArray(new DependencyConsumer[followers.size()])) { for (DependencyConsumer consumer : followers.toArray(new DependencyConsumer[followers.size()])) {
dependencyChecker.schedulePropagation(consumer, types); dependencyChecker.schedulePropagation(consumer, newTypes);
} }
} }
if (transitions != null) { if (transitions != null) {
for (DependencyNodeToNodeTransition consumer : transitions.toArray( for (DependencyNodeToNodeTransition consumer : transitions.toArray(
new DependencyNodeToNodeTransition[transitions.size()])) { new DependencyNodeToNodeTransition[transitions.size()])) {
dependencyChecker.schedulePropagation(consumer, types); dependencyChecker.schedulePropagation(consumer, newTypes);
} }
} }
} }
@ -250,7 +260,7 @@ public class DependencyNode implements ValueDependencyInfo {
} }
return false; return false;
} }
return types != null && type.getDependencyChecker() == dependencyChecker && types.get(type.index); return types != null && types.get(type.index);
} }
@Override @Override

View File

@ -15,15 +15,18 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import com.carrotsearch.hppc.IntSet;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet;
class DependencyNodeToNodeTransition implements DependencyConsumer { class DependencyNodeToNodeTransition implements DependencyConsumer {
private DependencyNode source; private DependencyNode source;
DependencyNode destination; DependencyNode destination;
private DependencyTypeFilter filter; private DependencyTypeFilter filter;
private BitSet knownFilteredOffTypes;
IntSet pendingTypes;
public DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination, DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination, DependencyTypeFilter filter) {
DependencyTypeFilter filter) {
this.source = source; this.source = source;
this.destination = destination; this.destination = destination;
this.filter = filter; this.filter = filter;
@ -31,7 +34,7 @@ class DependencyNodeToNodeTransition implements DependencyConsumer {
@Override @Override
public void consume(DependencyType type) { public void consume(DependencyType type) {
if (filter != null && !filter.match(type)) { if (!filterType(type)) {
return; return;
} }
if (type.getName().startsWith("[")) { if (type.getName().startsWith("[")) {
@ -47,18 +50,20 @@ class DependencyNodeToNodeTransition implements DependencyConsumer {
} }
void consume(DependencyType[] types) { void consume(DependencyType[] types) {
DependencyType[] filtered = new DependencyType[types.length];
int j = 0; int j = 0;
for (DependencyType type : types) { for (DependencyType type : types) {
if (type.getName().startsWith("[")) { if (filterType(type)) {
source.getArrayItem().connect(destination.getArrayItem()); if (!destination.hasType(type)) {
destination.getArrayItem().connect(source.getArrayItem()); types[j++] = type;
} }
if (type.getName().equals("java.lang.Class")) {
source.getClassValueNode().connect(destination.getClassValueNode()); if (type.getName().startsWith("[")) {
} source.getArrayItem().connect(destination.getArrayItem());
if ((filter == null || filter.match(type)) && !destination.hasType(type)) { destination.getArrayItem().connect(source.getArrayItem());
filtered[j++] = type; }
if (type.getName().equals("java.lang.Class")) {
source.getClassValueNode().connect(destination.getClassValueNode());
}
} }
} }
@ -67,13 +72,32 @@ class DependencyNodeToNodeTransition implements DependencyConsumer {
} }
if (j == 1) { if (j == 1) {
destination.propagate(filtered[0]); destination.propagate(types[0]);
} else { } else {
if (j < filtered.length) { if (j < types.length) {
filtered = Arrays.copyOf(filtered, j); types = Arrays.copyOf(types, j);
} }
destination.propagate(filtered); destination.propagate(types);
} }
} }
private boolean filterType(DependencyType type) {
if (filter == null) {
return true;
}
if (knownFilteredOffTypes != null && knownFilteredOffTypes.get(type.index)) {
return false;
}
if (!filter.match(type)) {
if (knownFilteredOffTypes == null) {
knownFilteredOffTypes = new BitSet(64);
}
knownFilteredOffTypes.set(type.index);
return false;
}
return true;
}
} }

View File

@ -0,0 +1,41 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.dependency;
import com.carrotsearch.hppc.IntIntMap;
import com.carrotsearch.hppc.IntIntOpenHashMap;
import org.teavm.model.ClassReaderSource;
class SuperClassFilter implements DependencyTypeFilter {
private ClassReaderSource classSource;
private String superType;
private IntIntMap cache = new IntIntOpenHashMap();
SuperClassFilter(ClassReaderSource classSource, String superType) {
this.classSource = classSource;
this.superType = superType;
}
@Override
public boolean match(DependencyType type) {
int result = cache.getOrDefault(type.index, -1);
if (result < 0) {
result = classSource.isSuperType(superType, type.getName()).orElse(false) ? 1 : 0;
cache.put(type.index, result);
}
return result != 0;
}
}

View File

@ -113,24 +113,7 @@ public interface ClassReaderSource {
} }
default Optional<Boolean> isSuperType(String superType, String subType) { default Optional<Boolean> isSuperType(String superType, String subType) {
if (superType.equals(subType)) { return ClassReaderSourceHelper.isSuperType(this, superType, subType);
return Optional.of(true);
}
ClassReader cls = get(subType);
if (cls == null) {
return Optional.empty();
}
if (cls.getParent() != null) {
if (isSuperType(superType, cls.getParent()).orElse(false)) {
return Optional.of(true);
}
}
for (String iface : cls.getInterfaces()) {
if (isSuperType(superType, iface).orElse(false)) {
return Optional.of(true);
}
}
return Optional.of(false);
} }
default Optional<Boolean> isSuperType(ValueType superType, ValueType subType) { default Optional<Boolean> isSuperType(ValueType superType, ValueType subType) {

View File

@ -17,6 +17,7 @@ package org.teavm.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
final class ClassReaderSourceHelper { final class ClassReaderSourceHelper {
@ -59,4 +60,56 @@ final class ClassReaderSourceHelper {
return mostSpecificMethod; return mostSpecificMethod;
} }
static Optional<Boolean> isSuperType(ClassReaderSource classSource, String superType, String subType) {
if (superType.equals("java.lang.Object")) {
return Optional.of(true);
}
ClassReader cls = classSource.get(superType);
if (cls != null && !cls.hasModifier(ElementModifier.INTERFACE)) {
return isSuperTypeSimple(classSource, superType, subType);
}
return isSuperTypeInterface(classSource, superType, subType);
}
private static Optional<Boolean> isSuperTypeSimple(ClassReaderSource classSource,
String superType, String subType) {
while (!superType.equals(subType)) {
ClassReader cls = classSource.get(subType);
if (cls == null) {
return Optional.empty();
}
subType = cls.getParent();
if (subType == null) {
return Optional.of(false);
}
}
return Optional.of(true);
}
private static Optional<Boolean> isSuperTypeInterface(ClassReaderSource classSource,
String superType, String subType) {
if (superType.equals(subType)) {
return Optional.of(true);
}
ClassReader cls = classSource.get(subType);
if (cls == null) {
return Optional.empty();
}
if (cls.getParent() != null) {
if (isSuperTypeInterface(classSource, superType, cls.getParent()).orElse(false)) {
return Optional.of(true);
}
}
for (String iface : cls.getInterfaces()) {
if (isSuperTypeInterface(classSource, superType, iface).orElse(false)) {
return Optional.of(true);
}
}
return Optional.of(false);
}
} }