From 0340bb9c735fba1f6253d6c10490dc5c350a8309 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 21 Oct 2015 16:03:06 +0300 Subject: [PATCH] A new API to generate additional methods after dependency checking --- .../java/org/teavm/common/CachedMapper.java | 4 + .../AbstractDependencyListener.java | 4 + .../org/teavm/dependency/DependencyAgent.java | 16 ++- .../teavm/dependency/DependencyChecker.java | 106 ++++++++++++++++-- .../dependency/DependencyGraphBuilder.java | 2 + .../org/teavm/dependency/DependencyInfo.java | 6 +- .../teavm/dependency/DependencyListener.java | 2 + .../org/teavm/dependency/DependencyNode.java | 14 +++ .../org/teavm/dependency/FieldDependency.java | 2 +- .../teavm/dependency/MethodDependency.java | 8 +- .../org/teavm/model/emit/ProgramEmitter.java | 16 ++- .../model/util/MissingItemsProcessor.java | 6 +- core/src/main/java/org/teavm/vm/TeaVM.java | 8 +- .../plugin/EnumDependencySupport.java | 2 +- .../resources/archetype-resources/pom.xml | 2 +- 15 files changed, 161 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/org/teavm/common/CachedMapper.java b/core/src/main/java/org/teavm/common/CachedMapper.java index 00ead5c50..027d43556 100644 --- a/core/src/main/java/org/teavm/common/CachedMapper.java +++ b/core/src/main/java/org/teavm/common/CachedMapper.java @@ -61,6 +61,10 @@ public class CachedMapper implements Mapper { return wrapper.value; } + public void replace(T preimage, R value) { + cache.get(preimage).value = value; + } + public void invalidate(T preimage) { cache.remove(preimage); } diff --git a/core/src/main/java/org/teavm/dependency/AbstractDependencyListener.java b/core/src/main/java/org/teavm/dependency/AbstractDependencyListener.java index 00cf56fce..241a60dd4 100644 --- a/core/src/main/java/org/teavm/dependency/AbstractDependencyListener.java +++ b/core/src/main/java/org/teavm/dependency/AbstractDependencyListener.java @@ -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) { + } } diff --git a/core/src/main/java/org/teavm/dependency/DependencyAgent.java b/core/src/main/java/org/teavm/dependency/DependencyAgent.java index 28658cf74..6e4d3477b 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAgent.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAgent.java @@ -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 getAchievableMethods() { - return checker.getAchievableMethods(); + public Collection getReachableMethods() { + return checker.getReachableMethods(); } @Override - public Collection getAchievableFields() { - return checker.getAchievableFields(); + public Collection getReachableFields() { + return checker.getReachableFields(); } @Override - public Collection getAchievableClasses() { - return checker.getAchievableClasses(); + public Collection getReachableClasses() { + return checker.getReachableClasses(); } @Override diff --git a/core/src/main/java/org/teavm/dependency/DependencyChecker.java b/core/src/main/java/org/teavm/dependency/DependencyChecker.java index 12dba39d6..27a742e0b 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -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 nodes = new ArrayList<>(); List typeBitSets = new ArrayList<>(); Map 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 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 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("", void.class)); + MethodReader method = reader.getMethod(new MethodDescriptor("", 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 getAchievableMethods() { + public Collection getReachableMethods() { return methodCache.getCachedPreimages(); } @Override - public Collection getAchievableFields() { + public Collection getReachableFields() { return fieldCache.getCachedPreimages(); } @Override - public Collection getAchievableClasses() { + public Collection getReachableClasses() { return classCache.getCachedPreimages(); } private Set 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 getService(Class type) { return services.getService(type); } diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 06cc57e8a..d08be0772 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -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); } diff --git a/core/src/main/java/org/teavm/dependency/DependencyInfo.java b/core/src/main/java/org/teavm/dependency/DependencyInfo.java index 064e5bbb6..4be420aaa 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyInfo.java +++ b/core/src/main/java/org/teavm/dependency/DependencyInfo.java @@ -30,11 +30,11 @@ public interface DependencyInfo { ClassLoader getClassLoader(); - Collection getAchievableMethods(); + Collection getReachableMethods(); - Collection getAchievableFields(); + Collection getReachableFields(); - Collection getAchievableClasses(); + Collection getReachableClasses(); FieldDependencyInfo getField(FieldReference fieldRef); diff --git a/core/src/main/java/org/teavm/dependency/DependencyListener.java b/core/src/main/java/org/teavm/dependency/DependencyListener.java index b9ff5a601..ef573119c 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyListener.java +++ b/core/src/main/java/org/teavm/dependency/DependencyListener.java @@ -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); } diff --git a/core/src/main/java/org/teavm/dependency/DependencyNode.java b/core/src/main/java/org/teavm/dependency/DependencyNode.java index d36248d93..184114540 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -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; } diff --git a/core/src/main/java/org/teavm/dependency/FieldDependency.java b/core/src/main/java/org/teavm/dependency/FieldDependency.java index 6535bbc06..444d5378d 100644 --- a/core/src/main/java/org/teavm/dependency/FieldDependency.java +++ b/core/src/main/java/org/teavm/dependency/FieldDependency.java @@ -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; diff --git a/core/src/main/java/org/teavm/dependency/MethodDependency.java b/core/src/main/java/org/teavm/dependency/MethodDependency.java index 7fb7a38c1..003039a7f 100644 --- a/core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -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; diff --git a/core/src/main/java/org/teavm/model/emit/ProgramEmitter.java b/core/src/main/java/org/teavm/model/emit/ProgramEmitter.java index b34bfb353..2ffe29a18 100644 --- a/core/src/main/java/org/teavm/model/emit/ProgramEmitter.java +++ b/core/src/main/java/org/teavm/model/emit/ProgramEmitter.java @@ -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()); } diff --git a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java index de182f96e..d5c42826f 100644 --- a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java +++ b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java @@ -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) { diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index a4c57f160..774db97c7 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -312,11 +312,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } public Collection getClasses() { - return dependencyChecker.getAchievableClasses(); + return dependencyChecker.getReachableClasses(); } public Collection 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; diff --git a/platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java b/platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java index a9ee997fd..33f32aad7 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java +++ b/platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java @@ -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); } } diff --git a/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml b/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml index 5a2eebaa2..7a1e0f85c 100644 --- a/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml +++ b/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml @@ -41,7 +41,7 @@ - + maven-compiler-plugin 3.1