A new API to generate additional methods after dependency checking

This commit is contained in:
Alexey Andreev 2015-10-21 16:03:06 +03:00
parent 2640234391
commit 0340bb9c73
15 changed files with 161 additions and 37 deletions

View File

@ -61,6 +61,10 @@ public class CachedMapper<T, R> implements Mapper<T, R> {
return wrapper.value; return wrapper.value;
} }
public void replace(T preimage, R value) {
cache.get(preimage).value = value;
}
public void invalidate(T preimage) { public void invalidate(T preimage) {
cache.remove(preimage); cache.remove(preimage);
} }

View File

@ -37,4 +37,8 @@ public abstract class AbstractDependencyListener implements DependencyListener {
@Override @Override
public void fieldReached(DependencyAgent agent, FieldDependency field, CallLocation location) { public void fieldReached(DependencyAgent agent, FieldDependency field, CallLocation location) {
} }
@Override
public void completing(DependencyAgent agent) {
}
} }

View File

@ -48,6 +48,10 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
checker.submitClass(cls); checker.submitClass(cls);
} }
public void submitMethod(MethodReference method, Program program) {
checker.submitMethod(method, program);
}
public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) { public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) {
return checker.linkMethod(methodRef, callLocation); return checker.linkMethod(methodRef, callLocation);
} }
@ -80,18 +84,18 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
} }
@Override @Override
public Collection<MethodReference> getAchievableMethods() { public Collection<MethodReference> getReachableMethods() {
return checker.getAchievableMethods(); return checker.getReachableMethods();
} }
@Override @Override
public Collection<FieldReference> getAchievableFields() { public Collection<FieldReference> getReachableFields() {
return checker.getAchievableFields(); return checker.getReachableFields();
} }
@Override @Override
public Collection<String> getAchievableClasses() { public Collection<String> getReachableClasses() {
return checker.getAchievableClasses(); return checker.getReachableClasses();
} }
@Override @Override

View File

@ -38,6 +38,7 @@ import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder; import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReader; import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
@ -46,8 +47,10 @@ import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.util.ModelUtils; import org.teavm.model.util.ModelUtils;
import org.teavm.model.util.ProgramUtils;
/** /**
* *
@ -76,6 +79,7 @@ public class DependencyChecker implements DependencyInfo {
List<DependencyNode> nodes = new ArrayList<>(); List<DependencyNode> nodes = new ArrayList<>();
List<BitSet> typeBitSets = new ArrayList<>(); List<BitSet> typeBitSets = new ArrayList<>();
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>(); Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
private boolean completing;
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics) { Diagnostics diagnostics) {
@ -153,9 +157,36 @@ public class DependencyChecker implements DependencyInfo {
} }
public void submitClass(ClassHolder cls) { public void submitClass(ClassHolder cls) {
if (completing) {
throw new IllegalStateException("Can't submit class during completion phase");
}
classSource.submit(ModelUtils.copyClass(cls)); classSource.submit(ModelUtils.copyClass(cls));
} }
public void submitMethod(MethodReference methodRef, Program program) {
if (!completing) {
throw new IllegalStateException("Can't submit class during check phase");
}
MethodDependency dep = getMethod(methodRef);
if (dep == null) {
throw new IllegalArgumentException("Method was not reached: " + methodRef);
}
MethodHolder method = dep.method;
if (!method.hasModifier(ElementModifier.NATIVE)) {
throw new IllegalArgumentException("Method is not native: " + methodRef);
}
method.getModifiers().remove(ElementModifier.NATIVE);
method.setProgram(ProgramUtils.copy(program));
dep.used = false;
lock(dep, false);
scheduleMethodAnalysis(dep);
processQueue();
}
public void addDependencyListener(DependencyListener listener) { public void addDependencyListener(DependencyListener listener) {
listeners.add(listener); listeners.add(listener);
listener.started(agent); listener.started(agent);
@ -193,7 +224,10 @@ public class DependencyChecker implements DependencyInfo {
private Set<String> classesAddedByRoot = new HashSet<>(); private Set<String> classesAddedByRoot = new HashSet<>();
public ClassDependency linkClass(final String className, final CallLocation callLocation) { public ClassDependency linkClass(String className, CallLocation callLocation) {
if (completing && getClass(className) == null) {
throw new IllegalStateException("Can't link class during completion phase");
}
ClassDependency dep = classCache.map(className); ClassDependency dep = classCache.map(className);
boolean added = true; boolean added = true;
if (callLocation != null && callLocation.getMethod() != null) { if (callLocation != null && callLocation.getMethod() != null) {
@ -247,6 +281,9 @@ public class DependencyChecker implements DependencyInfo {
private Set<MethodReference> methodsAddedByRoot = new HashSet<>(); private Set<MethodReference> methodsAddedByRoot = new HashSet<>();
public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) { public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) {
if (completing && getMethod(methodRef) == null) {
throw new IllegalStateException("Can't submit class during completion phase");
}
if (methodRef == null) { if (methodRef == null) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
@ -272,9 +309,9 @@ public class DependencyChecker implements DependencyInfo {
return graph; return graph;
} }
void initClass(ClassDependency cls, final CallLocation callLocation) { void initClass(ClassDependency cls, CallLocation callLocation) {
ClassReader reader = cls.getClassReader(); ClassReader reader = cls.getClassReader();
final MethodReader method = reader.getMethod(new MethodDescriptor("<clinit>", void.class)); MethodReader method = reader.getMethod(new MethodDescriptor("<clinit>", void.class));
if (method != null) { if (method != null) {
tasks.add(() -> linkMethod(method.getReference(), callLocation).use()); tasks.add(() -> linkMethod(method.getReference(), callLocation).use());
} }
@ -286,6 +323,7 @@ public class DependencyChecker implements DependencyInfo {
DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1]; DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1];
for (int i = 0; i < parameterNodes.length; ++i) { for (int i = 0; i < parameterNodes.length; ++i) {
parameterNodes[i] = createNode(); parameterNodes[i] = createNode();
parameterNodes[i].method = methodRef;
if (shouldLog) { if (shouldLog) {
parameterNodes[i].setTag(methodRef + ":" + i); parameterNodes[i].setTag(methodRef + ":" + i);
} }
@ -295,15 +333,17 @@ public class DependencyChecker implements DependencyInfo {
resultNode = null; resultNode = null;
} else { } else {
resultNode = createNode(); resultNode = createNode();
resultNode.method = methodRef;
if (shouldLog) { if (shouldLog) {
resultNode.setTag(methodRef + ":RESULT"); resultNode.setTag(methodRef + ":RESULT");
} }
} }
DependencyNode thrown = createNode(); DependencyNode thrown = createNode();
thrown.method = methodRef;
if (shouldLog) { if (shouldLog) {
thrown.setTag(methodRef + ":THROWN"); thrown.setTag(methodRef + ":THROWN");
} }
final MethodDependency dep = new MethodDependency(this, parameterNodes, paramCount, resultNode, thrown, MethodDependency dep = new MethodDependency(this, parameterNodes, paramCount, resultNode, thrown,
method, methodRef); method, methodRef);
if (method != null) { if (method != null) {
tasks.add(() -> { tasks.add(() -> {
@ -314,7 +354,7 @@ public class DependencyChecker implements DependencyInfo {
return dep; return dep;
} }
void scheduleMethodAnalysis(final MethodDependency dep) { void scheduleMethodAnalysis(MethodDependency dep) {
tasks.add(() -> { tasks.add(() -> {
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyChecker.this); DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyChecker.this);
graphBuilder.buildGraph(dep); graphBuilder.buildGraph(dep);
@ -322,23 +362,26 @@ public class DependencyChecker implements DependencyInfo {
} }
@Override @Override
public Collection<MethodReference> getAchievableMethods() { public Collection<MethodReference> getReachableMethods() {
return methodCache.getCachedPreimages(); return methodCache.getCachedPreimages();
} }
@Override @Override
public Collection<FieldReference> getAchievableFields() { public Collection<FieldReference> getReachableFields() {
return fieldCache.getCachedPreimages(); return fieldCache.getCachedPreimages();
} }
@Override @Override
public Collection<String> getAchievableClasses() { public Collection<String> getReachableClasses() {
return classCache.getCachedPreimages(); return classCache.getCachedPreimages();
} }
private Set<FieldReference> fieldsAddedByRoot = new HashSet<>(); private Set<FieldReference> fieldsAddedByRoot = new HashSet<>();
public FieldDependency linkField(final FieldReference fieldRef, final CallLocation location) { public FieldDependency linkField(FieldReference fieldRef, CallLocation location) {
if (completing) {
throw new IllegalStateException("Can't submit class during completion phase");
}
boolean added = true; boolean added = true;
if (location != null) { if (location != null) {
added = callGraph.getNode(location.getMethod()).addFieldAccess(fieldRef, location.getSourceLocation()); added = callGraph.getNode(location.getMethod()).addFieldAccess(fieldRef, location.getSourceLocation());
@ -421,8 +464,10 @@ public class DependencyChecker implements DependencyInfo {
return method != null ? methodCache.getKnown(method.getReference()) : null; return method != null ? methodCache.getKnown(method.getReference()) : null;
} }
public void processDependencies() { private void processQueue() {
interrupted = false; if (interrupted) {
return;
}
int index = 0; int index = 0;
while (!tasks.isEmpty()) { while (!tasks.isEmpty()) {
tasks.poll().run(); tasks.poll().run();
@ -436,6 +481,45 @@ public class DependencyChecker implements DependencyInfo {
} }
} }
public void processDependencies() {
interrupted = false;
processQueue();
if (!interrupted) {
completing = true;
lock();
for (DependencyListener listener : listeners) {
listener.completing(agent);
}
}
}
private void lock() {
for (MethodReference method : getReachableMethods()) {
lock(getMethod(method), true);
}
for (FieldReference field : getReachableFields()) {
lock(getField(field));
}
}
private void lock(MethodDependency dep, boolean lock) {
for (DependencyNode node : dep.variableNodes) {
if (node != null) {
node.locked = lock;
}
}
if (dep.resultNode != null) {
dep.resultNode.locked = lock;
}
if (dep.thrown != null) {
dep.thrown.locked = lock;
}
}
private void lock(FieldDependency dep) {
dep.value.locked = true;
}
public <T> T getService(Class<T> type) { public <T> T getService(Class<T> type) {
return services.getService(type); return services.getService(type);
} }

View File

@ -115,8 +115,10 @@ class DependencyGraphBuilder {
nodeClassCount = Math.max(nodeClassCount, nodeMapping[i] + 1); nodeClassCount = Math.max(nodeClassCount, nodeMapping[i] + 1);
} }
DependencyNode[] nodeClasses = Arrays.copyOf(dep.getVariables(), nodeClassCount); DependencyNode[] nodeClasses = Arrays.copyOf(dep.getVariables(), nodeClassCount);
MethodReference ref = method.getReference();
for (int i = dep.getVariableCount(); i < nodeClasses.length; ++i) { for (int i = dep.getVariableCount(); i < nodeClasses.length; ++i) {
nodeClasses[i] = dependencyChecker.createNode(); nodeClasses[i] = dependencyChecker.createNode();
nodeClasses[i].method = ref;
if (DependencyChecker.shouldLog) { if (DependencyChecker.shouldLog) {
nodeClasses[i].setTag(dep.getMethod().getReference() + ":" + i); nodeClasses[i].setTag(dep.getMethod().getReference() + ":" + i);
} }

View File

@ -30,11 +30,11 @@ public interface DependencyInfo {
ClassLoader getClassLoader(); ClassLoader getClassLoader();
Collection<MethodReference> getAchievableMethods(); Collection<MethodReference> getReachableMethods();
Collection<FieldReference> getAchievableFields(); Collection<FieldReference> getReachableFields();
Collection<String> getAchievableClasses(); Collection<String> getReachableClasses();
FieldDependencyInfo getField(FieldReference fieldRef); FieldDependencyInfo getField(FieldReference fieldRef);

View File

@ -29,4 +29,6 @@ public interface DependencyListener {
void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location); void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location);
void fieldReached(DependencyAgent agent, FieldDependency field, CallLocation location); void fieldReached(DependencyAgent agent, FieldDependency field, CallLocation location);
void completing(DependencyAgent agent);
} }

View File

@ -16,6 +16,7 @@
package org.teavm.dependency; package org.teavm.dependency;
import java.util.*; import java.util.*;
import org.teavm.model.MethodReference;
/** /**
* *
@ -31,6 +32,8 @@ public class DependencyNode implements ValueDependencyInfo {
private DependencyNode arrayItemNode; private DependencyNode arrayItemNode;
private int degree; private int degree;
int index; int index;
boolean locked;
MethodReference method;
DependencyNode(DependencyChecker dependencyChecker, int index) { DependencyNode(DependencyChecker dependencyChecker, int index) {
this(dependencyChecker, index, 0); this(dependencyChecker, index, 0);
@ -45,6 +48,10 @@ public class DependencyNode implements ValueDependencyInfo {
private boolean addType(DependencyType type) { private boolean addType(DependencyType type) {
if (types == null) { if (types == null) {
if (smallTypes == null) { if (smallTypes == null) {
if (locked) {
throw new IllegalStateException("Error propagating type " + type.getName()
+ " to node in " + method);
}
smallTypes = new int[] { type.index }; smallTypes = new int[] { type.index };
return true; return true;
} }
@ -62,12 +69,19 @@ public class DependencyNode implements ValueDependencyInfo {
} }
smallTypes = null; smallTypes = null;
} else { } else {
if (locked) {
throw new IllegalStateException("Error propagating type " + type.getName() + " to node in method "
+ method);
}
smallTypes = Arrays.copyOf(smallTypes, smallTypes.length + 1); smallTypes = Arrays.copyOf(smallTypes, smallTypes.length + 1);
smallTypes[smallTypes.length - 1] = type.index; smallTypes[smallTypes.length - 1] = type.index;
return true; return true;
} }
} }
if (!types.get(type.index)) { if (!types.get(type.index)) {
if (locked) {
throw new IllegalStateException("Error propagating type " + type.getName() + " to node " + tag);
}
types.set(type.index); types.set(type.index);
return true; return true;
} }

View File

@ -23,7 +23,7 @@ import org.teavm.model.FieldReference;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class FieldDependency implements FieldDependencyInfo { public class FieldDependency implements FieldDependencyInfo {
private DependencyNode value; DependencyNode value;
private FieldReader field; private FieldReader field;
private FieldReference reference; private FieldReference reference;

View File

@ -26,13 +26,13 @@ import org.teavm.model.MethodReference;
*/ */
public class MethodDependency implements MethodDependencyInfo { public class MethodDependency implements MethodDependencyInfo {
private DependencyChecker dependencyChecker; private DependencyChecker dependencyChecker;
private DependencyNode[] variableNodes; DependencyNode[] variableNodes;
private int parameterCount; private int parameterCount;
private DependencyNode resultNode; DependencyNode resultNode;
private DependencyNode thrown; DependencyNode thrown;
MethodHolder method; MethodHolder method;
private MethodReference reference; private MethodReference reference;
private boolean used; boolean used;
DependencyPlugin dependencyPlugin; DependencyPlugin dependencyPlugin;
boolean dependencyPluginAttached; boolean dependencyPluginAttached;

View File

@ -22,6 +22,7 @@ import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.InstructionLocation; import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Phi; import org.teavm.model.Phi;
@ -146,6 +147,10 @@ public final class ProgramEmitter {
return var(var, type); return var(var, type);
} }
public ValueEmitter constantNull(Class<?> type) {
return constantNull(ValueType.parse(type));
}
public ValueEmitter defaultValue(ValueType type) { public ValueEmitter defaultValue(ValueType type) {
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) { switch (((ValueType.Primitive) type).getKind()) {
@ -387,8 +392,13 @@ public final class ProgramEmitter {
} }
public static ProgramEmitter create(MethodHolder method, ClassReaderSource classSource) { public static ProgramEmitter create(MethodHolder method, ClassReaderSource classSource) {
ProgramEmitter pe = create(method.getDescriptor(), classSource);
method.setProgram(pe.getProgram());
return pe;
}
public static ProgramEmitter create(MethodDescriptor method, ClassReaderSource classSource) {
Program program = new Program(); Program program = new Program();
method.setProgram(program);
BasicBlock zeroBlock = program.createBasicBlock(); BasicBlock zeroBlock = program.createBasicBlock();
BasicBlock block = program.createBasicBlock(); BasicBlock block = program.createBasicBlock();
@ -440,13 +450,13 @@ public final class ProgramEmitter {
return phi(ValueType.object(cls.getName())); return phi(ValueType.object(cls.getName()));
} }
public ChooseEmitter choise(ValueEmitter value) { public ChooseEmitter choice(ValueEmitter value) {
SwitchInstruction insn = new SwitchInstruction(); SwitchInstruction insn = new SwitchInstruction();
insn.setCondition(value.getVariable()); insn.setCondition(value.getVariable());
return new ChooseEmitter(this, insn, prepareBlock()); return new ChooseEmitter(this, insn, prepareBlock());
} }
public StringChooseEmitter stringChoise(ValueEmitter value) { public StringChooseEmitter stringChoice(ValueEmitter value) {
SwitchInstruction insn = new SwitchInstruction(); SwitchInstruction insn = new SwitchInstruction();
return new StringChooseEmitter(this, value, insn, prepareBlock()); return new StringChooseEmitter(this, value, insn, prepareBlock());
} }

View File

@ -41,9 +41,9 @@ public class MissingItemsProcessor {
public MissingItemsProcessor(DependencyInfo dependencyInfo, Diagnostics diagnostics) { public MissingItemsProcessor(DependencyInfo dependencyInfo, Diagnostics diagnostics) {
this.dependencyInfo = dependencyInfo; this.dependencyInfo = dependencyInfo;
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
achievableClasses = dependencyInfo.getAchievableClasses(); achievableClasses = dependencyInfo.getReachableClasses();
achievableMethods = dependencyInfo.getAchievableMethods(); achievableMethods = dependencyInfo.getReachableMethods();
achievableFields = dependencyInfo.getAchievableFields(); achievableFields = dependencyInfo.getReachableFields();
} }
public void processClass(ClassHolder cls) { public void processClass(ClassHolder cls) {

View File

@ -312,11 +312,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
public Collection<String> getClasses() { public Collection<String> getClasses() {
return dependencyChecker.getAchievableClasses(); return dependencyChecker.getReachableClasses();
} }
public Collection<MethodReference> getMethods() { public Collection<MethodReference> getMethods() {
return dependencyChecker.getAchievableMethods(); return dependencyChecker.getReachableMethods();
} }
public DependencyInfo getDependencyInfo() { public DependencyInfo getDependencyInfo() {
@ -474,7 +474,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
public ListableClassHolderSource link(DependencyInfo dependency) { public ListableClassHolderSource link(DependencyInfo dependency) {
reportPhase(TeaVMPhase.LINKING, dependency.getAchievableClasses().size()); reportPhase(TeaVMPhase.LINKING, dependency.getReachableClasses().size());
Linker linker = new Linker(); Linker linker = new Linker();
MutableClassHolderSource cutClasses = new MutableClassHolderSource(); MutableClassHolderSource cutClasses = new MutableClassHolderSource();
MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependency, diagnostics); MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependency, diagnostics);
@ -482,7 +482,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return cutClasses; return cutClasses;
} }
int index = 0; int index = 0;
for (String className : dependency.getAchievableClasses()) { for (String className : dependency.getReachableClasses()) {
ClassReader clsReader = dependency.getClassSource().get(className); ClassReader clsReader = dependency.getClassSource().get(className);
if (clsReader == null) { if (clsReader == null) {
continue; continue;

View File

@ -63,7 +63,7 @@ public class EnumDependencySupport extends AbstractDependencyListener {
} }
}); });
method.getResult().propagate(agent.getType("[java.lang.Enum")); method.getResult().propagate(agent.getType("[java.lang.Enum"));
for (String cls : agent.getAchievableClasses()) { for (String cls : agent.getReachableClasses()) {
classReached(agent, cls, location); classReached(agent, cls, location);
} }
} }

View File

@ -41,7 +41,7 @@
<build> <build>
<plugins> <plugins>
<!-- Configure Java compiler to use Java 7 syntax --> <!-- Configure Java compiler to use Java 8 syntax -->
<plugin> <plugin>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version> <version>3.1</version>