mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-08 16:04:10 -08:00
Improve performance of dependency analyzer
This commit is contained in:
parent
e1c7acd8a1
commit
d811e7edbb
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
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.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -57,6 +60,7 @@ import org.teavm.model.util.ProgramUtils;
|
|||
import org.teavm.parsing.Parser;
|
||||
|
||||
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");
|
||||
private int classNameSuffix;
|
||||
private DependencyClassSource classSource;
|
||||
|
@ -68,6 +72,7 @@ public class DependencyChecker implements DependencyInfo {
|
|||
private CachedMapper<String, ClassDependency> classCache;
|
||||
private List<DependencyListener> listeners = new ArrayList<>();
|
||||
private ServiceRepository services;
|
||||
private Deque<DependencyNodeToNodeTransition> pendingTransitions = new ArrayDeque<>();
|
||||
private Deque<Runnable> tasks = new ArrayDeque<>();
|
||||
private Queue<Runnable> deferredTasks = new ArrayDeque<>();
|
||||
List<DependencyType> types = new ArrayList<>();
|
||||
|
@ -79,6 +84,7 @@ public class DependencyChecker implements DependencyInfo {
|
|||
private DependencyAgent agent;
|
||||
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
|
||||
private boolean completing;
|
||||
private Map<String, SuperClassFilter> superClassFilters = new HashMap<>();
|
||||
|
||||
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||
Diagnostics diagnostics) {
|
||||
|
@ -237,7 +243,7 @@ public class DependencyChecker implements DependencyInfo {
|
|||
private int propagationDepth;
|
||||
|
||||
void schedulePropagation(DependencyConsumer consumer, DependencyType type) {
|
||||
if (propagationDepth < 50) {
|
||||
if (propagationDepth < PROPAGATION_STACK_THRESHOLD) {
|
||||
++propagationDepth;
|
||||
consumer.consume(type);
|
||||
--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) {
|
||||
if (types.length == 0) {
|
||||
return;
|
||||
|
@ -255,14 +275,18 @@ public class DependencyChecker implements DependencyInfo {
|
|||
return;
|
||||
}
|
||||
|
||||
if (propagationDepth < 50) {
|
||||
if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD) {
|
||||
++propagationDepth;
|
||||
consumer.consume(types);
|
||||
--propagationDepth;
|
||||
} else {
|
||||
tasks.add(() -> {
|
||||
consumer.consume(types);
|
||||
});
|
||||
if (consumer.pendingTypes == null) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (propagationDepth < 50) {
|
||||
if (propagationDepth < PROPAGATION_STACK_THRESHOLD) {
|
||||
++propagationDepth;
|
||||
for (DependencyType type : types) {
|
||||
consumer.consume(type);
|
||||
|
@ -486,7 +510,7 @@ public class DependencyChecker implements DependencyInfo {
|
|||
}
|
||||
FieldDependency dep = new FieldDependency(node, field, fieldRef);
|
||||
if (!dep.isMissing()) {
|
||||
tasks.add(() -> linkClass(fieldRef.getClassName(), null).initClass(null));
|
||||
deferredTasks.add(() -> linkClass(fieldRef.getClassName(), null).initClass(null));
|
||||
}
|
||||
return dep;
|
||||
}
|
||||
|
@ -538,9 +562,13 @@ public class DependencyChecker implements DependencyInfo {
|
|||
return;
|
||||
}
|
||||
int index = 0;
|
||||
while (true) {
|
||||
while (!tasks.isEmpty()) {
|
||||
tasks.removeLast().run();
|
||||
while (!deferredTasks.isEmpty() || !tasks.isEmpty() || !pendingTransitions.isEmpty()) {
|
||||
while (true) {
|
||||
processNodeToNodeTransitionQueue();
|
||||
if (tasks.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
tasks.remove().run();
|
||||
if (++index == 100) {
|
||||
if (interruptor != null && !interruptor.shouldContinue()) {
|
||||
interrupted = true;
|
||||
|
@ -549,10 +577,31 @@ public class DependencyChecker implements DependencyInfo {
|
|||
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) {
|
||||
bootstrapMethodSubstitutors.put(method, substitutor);
|
||||
}
|
||||
|
||||
SuperClassFilter getSuperClassFilter(String superClass) {
|
||||
return superClassFilters.computeIfAbsent(superClass, s -> new SuperClassFilter(classSource, s));
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
private List<ClassHolderTransformer> transformers = new ArrayList<>();
|
||||
private Map<String, ClassHolder> cache = new LinkedHashMap<>();
|
||||
|
||||
public DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
this.innerSource = innerSource;
|
||||
this.diagnostics = diagnostics;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.dependency;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -65,7 +66,7 @@ class DependencyGraphBuilder {
|
|||
private TextLocation currentLocation;
|
||||
private ExceptionConsumer currentExceptionConsumer;
|
||||
|
||||
public DependencyGraphBuilder(DependencyChecker dependencyChecker) {
|
||||
DependencyGraphBuilder(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 String filterClass;
|
||||
private final MethodDescriptor methodDesc;
|
||||
private final DependencyChecker checker;
|
||||
private final DependencyNode[] parameters;
|
||||
|
@ -313,14 +313,16 @@ class DependencyGraphBuilder {
|
|||
private final DefaultCallGraphNode caller;
|
||||
private final TextLocation location;
|
||||
private final Set<MethodReference> knownMethods = new HashSet<>();
|
||||
private final BitSet knownTypes = new BitSet();
|
||||
private ExceptionConsumer exceptionConsumer;
|
||||
private SuperClassFilter filter;
|
||||
|
||||
public VirtualCallConsumer(DependencyNode node, String filterClass,
|
||||
VirtualCallConsumer(DependencyNode node, String filterClass,
|
||||
MethodDescriptor methodDesc, DependencyChecker checker, DependencyNode[] parameters,
|
||||
DependencyNode result, DefaultCallGraphNode caller, TextLocation location,
|
||||
ExceptionConsumer exceptionConsumer) {
|
||||
this.node = node;
|
||||
this.filterClass = filterClass;
|
||||
this.filter = checker.getSuperClassFilter(filterClass);
|
||||
this.methodDesc = methodDesc;
|
||||
this.checker = checker;
|
||||
this.parameters = parameters;
|
||||
|
@ -332,6 +334,11 @@ class DependencyGraphBuilder {
|
|||
|
||||
@Override
|
||||
public void consume(DependencyType type) {
|
||||
if (knownTypes.get(type.index)) {
|
||||
return;
|
||||
}
|
||||
knownTypes.set(type.index);
|
||||
|
||||
String className = type.getName();
|
||||
if (DependencyChecker.shouldLog) {
|
||||
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". "
|
||||
|
@ -339,10 +346,10 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
if (className.startsWith("[")) {
|
||||
className = "java.lang.Object";
|
||||
type = checker.getType(className);
|
||||
}
|
||||
|
||||
ClassReaderSource classSource = checker.getClassSource();
|
||||
if (!classSource.isSuperType(filterClass, className).orElse(false)) {
|
||||
if (!filter.match(type)) {
|
||||
return;
|
||||
}
|
||||
MethodReference methodRef = new MethodReference(className, methodDesc);
|
||||
|
@ -351,8 +358,8 @@ class DependencyGraphBuilder {
|
|||
methodDep.use();
|
||||
DependencyNode[] targetParams = methodDep.getVariables();
|
||||
if (parameters[0] != null && targetParams[0] != null) {
|
||||
parameters[0].connect(targetParams[0], thisType -> classSource.isSuperType(
|
||||
methodDep.getMethod().getOwnerName(), thisType.getName()).orElse(false));
|
||||
parameters[0].connect(targetParams[0],
|
||||
checker.getSuperClassFilter(methodDep.getMethod().getOwnerName()));
|
||||
}
|
||||
for (int i = 1; i < parameters.length; ++i) {
|
||||
if (parameters[i] != null && targetParams[i] != null) {
|
||||
|
@ -429,14 +436,9 @@ class DependencyGraphBuilder {
|
|||
if (targetType instanceof ValueType.Object) {
|
||||
String targetClsName = ((ValueType.Object) targetType).getClassName();
|
||||
final ClassReader targetClass = classSource.get(targetClsName);
|
||||
if (targetClass != null) {
|
||||
if (targetClass != null && !(targetClass.getName().equals("java.lang.Object"))) {
|
||||
if (valueNode != null && receiverNode != null) {
|
||||
valueNode.connect(receiverNode, type -> {
|
||||
if (targetClass.getName().equals("java.lang.Object")) {
|
||||
return true;
|
||||
}
|
||||
return classSource.isSuperType(targetClass.getName(), type.getName()).orElse(false);
|
||||
});
|
||||
valueNode.connect(receiverNode, dependencyChecker.getSuperClassFilter(targetClass.getName()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.*;
|
|||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class DependencyNode implements ValueDependencyInfo {
|
||||
private static final int SMALL_TYPES_THRESHOLD = 6;
|
||||
private DependencyChecker dependencyChecker;
|
||||
private List<DependencyConsumer> followers;
|
||||
private int[] smallTypes;
|
||||
|
@ -57,8 +58,8 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (smallTypes.length == 5) {
|
||||
types = new BitSet();
|
||||
if (smallTypes.length == SMALL_TYPES_THRESHOLD) {
|
||||
types = new BitSet(dependencyChecker.types.size() * 2);
|
||||
for (int existingType : smallTypes) {
|
||||
types.set(existingType);
|
||||
}
|
||||
|
@ -84,9 +85,6 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
@ -94,29 +92,34 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
if (DependencyChecker.shouldLog) {
|
||||
System.out.println(tag + " -> " + type.getName());
|
||||
}
|
||||
if (followers != null) {
|
||||
for (DependencyConsumer consumer : followers.toArray(new DependencyConsumer[followers.size()])) {
|
||||
dependencyChecker.schedulePropagation(consumer, type);
|
||||
}
|
||||
scheduleSingleType(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()])) {
|
||||
dependencyChecker.schedulePropagation(consumer, type);
|
||||
}
|
||||
}
|
||||
if (transitions != null) {
|
||||
for (DependencyNodeToNodeTransition consumer : transitions.toArray(
|
||||
new DependencyNodeToNodeTransition[transitions.size()])) {
|
||||
dependencyChecker.schedulePropagation(consumer, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void propagate(DependencyType[] newTypes) {
|
||||
DependencyType[] types = new DependencyType[newTypes.length];
|
||||
if (degree > 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < newTypes.length; ++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)) {
|
||||
types[j++] = type;
|
||||
newTypes[j++] = type;
|
||||
}
|
||||
}
|
||||
if (j == 0) {
|
||||
|
@ -124,22 +127,29 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
if (DependencyChecker.shouldLog) {
|
||||
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)) {
|
||||
types = Arrays.copyOf(types, j);
|
||||
if (followers == null && transitions == null) {
|
||||
return;
|
||||
}
|
||||
if (j < newTypes.length) {
|
||||
if (j == 1) {
|
||||
scheduleSingleType(newTypes[0]);
|
||||
return;
|
||||
}
|
||||
newTypes = Arrays.copyOf(newTypes, j);
|
||||
}
|
||||
if (followers != null) {
|
||||
for (DependencyConsumer consumer : followers.toArray(new DependencyConsumer[followers.size()])) {
|
||||
dependencyChecker.schedulePropagation(consumer, types);
|
||||
dependencyChecker.schedulePropagation(consumer, newTypes);
|
||||
}
|
||||
}
|
||||
if (transitions != null) {
|
||||
for (DependencyNodeToNodeTransition consumer : transitions.toArray(
|
||||
new DependencyNodeToNodeTransition[transitions.size()])) {
|
||||
dependencyChecker.schedulePropagation(consumer, types);
|
||||
dependencyChecker.schedulePropagation(consumer, newTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +260,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
return types != null && type.getDependencyChecker() == dependencyChecker && types.get(type.index);
|
||||
return types != null && types.get(type.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,15 +15,18 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
|
||||
class DependencyNodeToNodeTransition implements DependencyConsumer {
|
||||
private DependencyNode source;
|
||||
DependencyNode destination;
|
||||
private DependencyTypeFilter filter;
|
||||
private BitSet knownFilteredOffTypes;
|
||||
IntSet pendingTypes;
|
||||
|
||||
public DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination,
|
||||
DependencyTypeFilter filter) {
|
||||
DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination, DependencyTypeFilter filter) {
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
this.filter = filter;
|
||||
|
@ -31,7 +34,7 @@ class DependencyNodeToNodeTransition implements DependencyConsumer {
|
|||
|
||||
@Override
|
||||
public void consume(DependencyType type) {
|
||||
if (filter != null && !filter.match(type)) {
|
||||
if (!filterType(type)) {
|
||||
return;
|
||||
}
|
||||
if (type.getName().startsWith("[")) {
|
||||
|
@ -47,18 +50,20 @@ class DependencyNodeToNodeTransition implements DependencyConsumer {
|
|||
}
|
||||
|
||||
void consume(DependencyType[] types) {
|
||||
DependencyType[] filtered = new DependencyType[types.length];
|
||||
int j = 0;
|
||||
for (DependencyType type : types) {
|
||||
if (type.getName().startsWith("[")) {
|
||||
source.getArrayItem().connect(destination.getArrayItem());
|
||||
destination.getArrayItem().connect(source.getArrayItem());
|
||||
}
|
||||
if (type.getName().equals("java.lang.Class")) {
|
||||
source.getClassValueNode().connect(destination.getClassValueNode());
|
||||
}
|
||||
if ((filter == null || filter.match(type)) && !destination.hasType(type)) {
|
||||
filtered[j++] = type;
|
||||
if (filterType(type)) {
|
||||
if (!destination.hasType(type)) {
|
||||
types[j++] = type;
|
||||
}
|
||||
|
||||
if (type.getName().startsWith("[")) {
|
||||
source.getArrayItem().connect(destination.getArrayItem());
|
||||
destination.getArrayItem().connect(source.getArrayItem());
|
||||
}
|
||||
if (type.getName().equals("java.lang.Class")) {
|
||||
source.getClassValueNode().connect(destination.getClassValueNode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,13 +72,32 @@ class DependencyNodeToNodeTransition implements DependencyConsumer {
|
|||
}
|
||||
|
||||
if (j == 1) {
|
||||
destination.propagate(filtered[0]);
|
||||
destination.propagate(types[0]);
|
||||
} else {
|
||||
if (j < filtered.length) {
|
||||
filtered = Arrays.copyOf(filtered, j);
|
||||
if (j < types.length) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -113,24 +113,7 @@ public interface ClassReaderSource {
|
|||
}
|
||||
|
||||
default Optional<Boolean> isSuperType(String superType, String subType) {
|
||||
if (superType.equals(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);
|
||||
return ClassReaderSourceHelper.isSuperType(this, superType, subType);
|
||||
}
|
||||
|
||||
default Optional<Boolean> isSuperType(ValueType superType, ValueType subType) {
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.model;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
final class ClassReaderSourceHelper {
|
||||
|
@ -59,4 +60,56 @@ final class ClassReaderSourceHelper {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user