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;
}
public void replace(T preimage, R value) {
cache.get(preimage).value = value;
}
public void invalidate(T preimage) {
cache.remove(preimage);
}

View File

@ -37,4 +37,8 @@ public abstract class AbstractDependencyListener implements DependencyListener {
@Override
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);
}
public void submitMethod(MethodReference method, Program program) {
checker.submitMethod(method, program);
}
public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) {
return checker.linkMethod(methodRef, callLocation);
}
@ -80,18 +84,18 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
}
@Override
public Collection<MethodReference> getAchievableMethods() {
return checker.getAchievableMethods();
public Collection<MethodReference> getReachableMethods() {
return checker.getReachableMethods();
}
@Override
public Collection<FieldReference> getAchievableFields() {
return checker.getAchievableFields();
public Collection<FieldReference> getReachableFields() {
return checker.getReachableFields();
}
@Override
public Collection<String> getAchievableClasses() {
return checker.getAchievableClasses();
public Collection<String> getReachableClasses() {
return checker.getReachableClasses();
}
@Override

View File

@ -38,6 +38,7 @@ import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;
@ -46,8 +47,10 @@ import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
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<BitSet> typeBitSets = new ArrayList<>();
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
private boolean completing;
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics) {
@ -153,9 +157,36 @@ public class DependencyChecker implements DependencyInfo {
}
public void submitClass(ClassHolder cls) {
if (completing) {
throw new IllegalStateException("Can't submit class during completion phase");
}
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) {
listeners.add(listener);
listener.started(agent);
@ -193,7 +224,10 @@ public class DependencyChecker implements DependencyInfo {
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);
boolean added = true;
if (callLocation != null && callLocation.getMethod() != null) {
@ -247,6 +281,9 @@ public class DependencyChecker implements DependencyInfo {
private Set<MethodReference> methodsAddedByRoot = new HashSet<>();
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) {
throw new IllegalArgumentException();
}
@ -272,9 +309,9 @@ public class DependencyChecker implements DependencyInfo {
return graph;
}
void initClass(ClassDependency cls, final CallLocation callLocation) {
void initClass(ClassDependency cls, CallLocation callLocation) {
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) {
tasks.add(() -> linkMethod(method.getReference(), callLocation).use());
}
@ -286,6 +323,7 @@ public class DependencyChecker implements DependencyInfo {
DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1];
for (int i = 0; i < parameterNodes.length; ++i) {
parameterNodes[i] = createNode();
parameterNodes[i].method = methodRef;
if (shouldLog) {
parameterNodes[i].setTag(methodRef + ":" + i);
}
@ -295,15 +333,17 @@ public class DependencyChecker implements DependencyInfo {
resultNode = null;
} else {
resultNode = createNode();
resultNode.method = methodRef;
if (shouldLog) {
resultNode.setTag(methodRef + ":RESULT");
}
}
DependencyNode thrown = createNode();
thrown.method = methodRef;
if (shouldLog) {
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);
if (method != null) {
tasks.add(() -> {
@ -314,7 +354,7 @@ public class DependencyChecker implements DependencyInfo {
return dep;
}
void scheduleMethodAnalysis(final MethodDependency dep) {
void scheduleMethodAnalysis(MethodDependency dep) {
tasks.add(() -> {
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyChecker.this);
graphBuilder.buildGraph(dep);
@ -322,23 +362,26 @@ public class DependencyChecker implements DependencyInfo {
}
@Override
public Collection<MethodReference> getAchievableMethods() {
public Collection<MethodReference> getReachableMethods() {
return methodCache.getCachedPreimages();
}
@Override
public Collection<FieldReference> getAchievableFields() {
public Collection<FieldReference> getReachableFields() {
return fieldCache.getCachedPreimages();
}
@Override
public Collection<String> getAchievableClasses() {
public Collection<String> getReachableClasses() {
return classCache.getCachedPreimages();
}
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;
if (location != null) {
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;
}
public void processDependencies() {
interrupted = false;
private void processQueue() {
if (interrupted) {
return;
}
int index = 0;
while (!tasks.isEmpty()) {
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) {
return services.getService(type);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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