mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
A new API to generate additional methods after dependency checking
This commit is contained in:
parent
2640234391
commit
0340bb9c73
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user