mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-23 23:04:50 -08:00
Improve performance of dependency analyzer
This commit is contained in:
parent
e1c7acd8a1
commit
d811e7edbb
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user