mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-31 12:24:10 -08:00
Improve performance and stability of development server and incremental compilation
This commit is contained in:
parent
c9c9c15dfe
commit
bab0cd59a6
|
@ -70,7 +70,7 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
||||||
MarkerKind kind;
|
MarkerKind kind;
|
||||||
if (instruction instanceof InvokeInstruction) {
|
if (instruction instanceof InvokeInstruction) {
|
||||||
MethodReference methodRef = ((InvokeInstruction) instruction).getMethod();
|
MethodReference methodRef = ((InvokeInstruction) instruction).getMethod();
|
||||||
MethodReader method = hierarchy.getClassSource().resolveImplementation(methodRef);
|
MethodReader method = hierarchy.resolve(methodRef);
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
||||||
receiver = ((InvokeInstruction) instruction).getReceiver();
|
receiver = ((InvokeInstruction) instruction).getReceiver();
|
||||||
} else if (instruction instanceof GetFieldInstruction) {
|
} else if (instruction instanceof GetFieldInstruction) {
|
||||||
FieldReference fieldRef = ((GetFieldInstruction) instruction).getField();
|
FieldReference fieldRef = ((GetFieldInstruction) instruction).getField();
|
||||||
FieldReader field = hierarchy.getClassSource().resolve(fieldRef);
|
FieldReader field = hierarchy.resolve(fieldRef);
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,21 +18,26 @@ package org.teavm.ast;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public class FieldNode {
|
public class FieldNode {
|
||||||
private String name;
|
private FieldReference reference;
|
||||||
private ValueType type;
|
private ValueType type;
|
||||||
private Set<ElementModifier> modifiers = EnumSet.noneOf(ElementModifier.class);
|
private Set<ElementModifier> modifiers = EnumSet.noneOf(ElementModifier.class);
|
||||||
private Object initialValue;
|
private Object initialValue;
|
||||||
|
|
||||||
public FieldNode(String name, ValueType type) {
|
public FieldNode(FieldReference reference, ValueType type) {
|
||||||
this.name = name;
|
this.reference = reference;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FieldReference getReference() {
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return reference.getFieldName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ElementModifier> getModifiers() {
|
public Set<ElementModifier> getModifiers() {
|
||||||
|
|
|
@ -199,7 +199,7 @@ public class Decompiler {
|
||||||
public ClassNode decompile(ClassHolder cls) {
|
public ClassNode decompile(ClassHolder cls) {
|
||||||
ClassNode clsNode = new ClassNode(cls.getName(), cls.getParent());
|
ClassNode clsNode = new ClassNode(cls.getName(), cls.getParent());
|
||||||
for (FieldHolder field : cls.getFields()) {
|
for (FieldHolder field : cls.getFields()) {
|
||||||
FieldNode fieldNode = new FieldNode(field.getName(), field.getType());
|
FieldNode fieldNode = new FieldNode(field.getReference(), field.getType());
|
||||||
fieldNode.getModifiers().addAll(field.getModifiers());
|
fieldNode.getModifiers().addAll(field.getModifiers());
|
||||||
fieldNode.setInitialValue(field.getInitialValue());
|
fieldNode.setInitialValue(field.getInitialValue());
|
||||||
clsNode.getFields().add(fieldNode);
|
clsNode.getFields().add(fieldNode);
|
||||||
|
|
|
@ -25,8 +25,8 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
private final Map<String, String> aliases = new HashMap<>();
|
private final Map<String, String> aliases = new HashMap<>();
|
||||||
private final Map<String, String> privateAliases = new HashMap<>();
|
private final Map<String, String> privateAliases = new HashMap<>();
|
||||||
private final Map<String, String> classAliases = new HashMap<>();
|
private final Map<String, String> classAliases = new HashMap<>();
|
||||||
private final Map<String, String> fieldAliases = new HashMap<>();
|
private final Map<FieldReference, String> fieldAliases = new HashMap<>();
|
||||||
private final Map<String, String> staticFieldAliases = new HashMap<>();
|
private final Map<FieldReference, String> staticFieldAliases = new HashMap<>();
|
||||||
private final Map<String, String> functionAliases = new HashMap<>();
|
private final Map<String, String> functionAliases = new HashMap<>();
|
||||||
private final Map<String, String> classInitAliases = new HashMap<>();
|
private final Map<String, String> classInitAliases = new HashMap<>();
|
||||||
|
|
||||||
|
@ -75,27 +75,32 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNameFor(FieldReference field) {
|
public String getNameFor(FieldReference field) {
|
||||||
String realCls = getRealFieldOwner(field.getClassName(), field.getFieldName());
|
String alias = fieldAliases.get(field);
|
||||||
if (!realCls.equals(field.getClassName())) {
|
if (alias == null) {
|
||||||
String alias = getNameFor(new FieldReference(realCls, field.getFieldName()));
|
FieldReference realField = getRealField(field);
|
||||||
fieldAliases.put(field.getClassName() + "#" + field, alias);
|
if (realField.equals(field)) {
|
||||||
return alias;
|
alias = aliasProvider.getFieldAlias(realField);
|
||||||
} else {
|
} else {
|
||||||
return fieldAliases.computeIfAbsent(realCls + "#" + field, key -> aliasProvider.getFieldAlias(field));
|
alias = getNameFor(realField);
|
||||||
|
}
|
||||||
|
fieldAliases.put(field, alias);
|
||||||
}
|
}
|
||||||
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFullNameFor(FieldReference field) {
|
public String getFullNameFor(FieldReference field) {
|
||||||
String realCls = getRealFieldOwner(field.getClassName(), field.getFieldName());
|
String alias = staticFieldAliases.get(field);
|
||||||
if (!realCls.equals(field.getClassName())) {
|
if (alias == null) {
|
||||||
String alias = getNameFor(new FieldReference(realCls, field.getFieldName()));
|
FieldReference realField = getRealField(field);
|
||||||
staticFieldAliases.put(field.getClassName() + "#" + field, alias);
|
if (realField.equals(field)) {
|
||||||
return alias;
|
alias = aliasProvider.getStaticFieldAlias(realField);
|
||||||
} else {
|
} else {
|
||||||
return staticFieldAliases.computeIfAbsent(realCls + "#" + field,
|
alias = getNameFor(realField);
|
||||||
key -> aliasProvider.getStaticFieldAlias(field));
|
}
|
||||||
|
staticFieldAliases.put(field, alias);
|
||||||
}
|
}
|
||||||
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -127,20 +132,19 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRealFieldOwner(String cls, String field) {
|
private FieldReference getRealField(FieldReference fieldRef) {
|
||||||
String initialCls = cls;
|
String initialCls = fieldRef.getClassName();
|
||||||
while (!fieldExists(cls, field)) {
|
String cls = fieldRef.getClassName();
|
||||||
ClassReader clsHolder = classSource.get(cls);
|
while (cls != null) {
|
||||||
if (clsHolder == null || clsHolder.getParent() == null) {
|
ClassReader clsReader = classSource.get(cls);
|
||||||
return initialCls;
|
if (clsReader != null) {
|
||||||
|
FieldReader fieldReader = clsReader.getField(fieldRef.getFieldName());
|
||||||
|
if (fieldReader != null) {
|
||||||
|
return fieldReader.getReference();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cls = clsHolder.getParent();
|
cls = clsReader.getParent();
|
||||||
}
|
}
|
||||||
return cls;
|
return fieldRef;
|
||||||
}
|
|
||||||
|
|
||||||
private boolean fieldExists(String cls, String field) {
|
|
||||||
ClassReader classHolder = classSource.get(cls);
|
|
||||||
return classHolder != null && classHolder.getField(field) != null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import com.carrotsearch.hppc.ObjectByteMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public final class AnnotationAwareCacheStatus implements CacheStatus {
|
public final class AnnotationAwareCacheStatus implements CacheStatus {
|
||||||
|
@ -32,10 +34,13 @@ public final class AnnotationAwareCacheStatus implements CacheStatus {
|
||||||
private IncrementalDependencyProvider dependencyProvider;
|
private IncrementalDependencyProvider dependencyProvider;
|
||||||
private List<Predicate<String>> synthesizedClasses = new ArrayList<>();
|
private List<Predicate<String>> synthesizedClasses = new ArrayList<>();
|
||||||
private ObjectByteMap<String> classStatusCache = new ObjectByteHashMap<>();
|
private ObjectByteMap<String> classStatusCache = new ObjectByteHashMap<>();
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
|
||||||
public AnnotationAwareCacheStatus(CacheStatus underlyingStatus, IncrementalDependencyProvider dependencyProvider) {
|
public AnnotationAwareCacheStatus(CacheStatus underlyingStatus, IncrementalDependencyProvider dependencyProvider,
|
||||||
|
ClassReaderSource classSource) {
|
||||||
this.underlyingStatus = underlyingStatus;
|
this.underlyingStatus = underlyingStatus;
|
||||||
this.dependencyProvider = dependencyProvider;
|
this.dependencyProvider = dependencyProvider;
|
||||||
|
this.classSource = classSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSynthesizedClasses(Predicate<String> synthesizedClasses) {
|
public void addSynthesizedClasses(Predicate<String> synthesizedClasses) {
|
||||||
|
@ -72,6 +77,18 @@ public final class AnnotationAwareCacheStatus implements CacheStatus {
|
||||||
return STALE;
|
return STALE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClassReader cls = classSource.get(className);
|
||||||
|
if (cls != null) {
|
||||||
|
if (cls.getParent() != null && getClassStatus(cls.getParent()) == STALE) {
|
||||||
|
return STALE;
|
||||||
|
}
|
||||||
|
for (String itf : cls.getInterfaces()) {
|
||||||
|
if (getClassStatus(cls.getParent()) == STALE) {
|
||||||
|
return STALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return FRESH;
|
return FRESH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,9 @@ import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class InMemoryMethodNodeCache implements MethodNodeCache {
|
public class InMemoryMethodNodeCache implements MethodNodeCache {
|
||||||
private Map<MethodReference, RegularItem> cache = new HashMap<>();
|
private Map<MethodReference, RegularItem> cache = new HashMap<>();
|
||||||
|
private Map<MethodReference, RegularItem> newItems = new HashMap<>();
|
||||||
private Map<MethodReference, AsyncItem> asyncCache = new HashMap<>();
|
private Map<MethodReference, AsyncItem> asyncCache = new HashMap<>();
|
||||||
|
private Map<MethodReference, AsyncItem> newAsyncItems = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
|
public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) {
|
||||||
|
@ -43,7 +45,7 @@ public class InMemoryMethodNodeCache implements MethodNodeCache {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
|
public void store(MethodReference methodReference, RegularMethodNode node, Supplier<String[]> dependencies) {
|
||||||
cache.put(methodReference, new RegularItem(node, dependencies.get().clone()));
|
newItems.put(methodReference, new RegularItem(node, dependencies.get().clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,7 +64,19 @@ public class InMemoryMethodNodeCache implements MethodNodeCache {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeAsync(MethodReference methodReference, AsyncMethodNode node, Supplier<String[]> dependencies) {
|
public void storeAsync(MethodReference methodReference, AsyncMethodNode node, Supplier<String[]> dependencies) {
|
||||||
asyncCache.put(methodReference, new AsyncItem(node, dependencies.get().clone()));
|
newAsyncItems.put(methodReference, new AsyncItem(node, dependencies.get().clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void commit() {
|
||||||
|
cache.putAll(newItems);
|
||||||
|
asyncCache.putAll(newAsyncItems);
|
||||||
|
newItems.clear();
|
||||||
|
newAsyncItems.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void discard() {
|
||||||
|
newItems.clear();
|
||||||
|
newAsyncItems.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class RegularItem {
|
static final class RegularItem {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.teavm.model.ProgramCache;
|
||||||
|
|
||||||
public class InMemoryProgramCache implements ProgramCache {
|
public class InMemoryProgramCache implements ProgramCache {
|
||||||
private Map<MethodReference, Item> cache = new HashMap<>();
|
private Map<MethodReference, Item> cache = new HashMap<>();
|
||||||
|
private Map<MethodReference, Item> newItems = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Program get(MethodReference method, CacheStatus cacheStatus) {
|
public Program get(MethodReference method, CacheStatus cacheStatus) {
|
||||||
|
@ -42,7 +43,20 @@ public class InMemoryProgramCache implements ProgramCache {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(MethodReference method, Program program, Supplier<String[]> dependencies) {
|
public void store(MethodReference method, Program program, Supplier<String[]> dependencies) {
|
||||||
cache.put(method, new Item(program, dependencies.get().clone()));
|
newItems.put(method, new Item(program, dependencies.get().clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void commit() {
|
||||||
|
cache.putAll(newItems);
|
||||||
|
newItems.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPendingItemsCount() {
|
||||||
|
return newItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void discard() {
|
||||||
|
newItems.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Item {
|
static final class Item {
|
||||||
|
|
|
@ -88,7 +88,6 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
|
||||||
return analyzer.getClassLoader();
|
return analyzer.getClassLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassHierarchy getClassHierarchy() {
|
public ClassHierarchy getClassHierarchy() {
|
||||||
return analyzer.getClassHierarchy();
|
return analyzer.getClassHierarchy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,7 +214,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
||||||
return classSource.isGeneratedClass(className);
|
return classSource.isGeneratedClass(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassHierarchy getClassHierarchy() {
|
public ClassHierarchy getClassHierarchy() {
|
||||||
return classHierarchy;
|
return classHierarchy;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package org.teavm.dependency;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
import org.teavm.model.ClassHierarchy;
|
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -27,8 +26,6 @@ public interface DependencyInfo {
|
||||||
|
|
||||||
ClassLoader getClassLoader();
|
ClassLoader getClassLoader();
|
||||||
|
|
||||||
ClassHierarchy getClassHierarchy();
|
|
||||||
|
|
||||||
Collection<MethodReference> getReachableMethods();
|
Collection<MethodReference> getReachableMethods();
|
||||||
|
|
||||||
Collection<FieldReference> getReachableFields();
|
Collection<FieldReference> getReachableFields();
|
||||||
|
|
|
@ -22,7 +22,7 @@ import org.teavm.model.VariableReader;
|
||||||
|
|
||||||
class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
|
class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
|
||||||
private FastDependencyAnalyzer dependencyAnalyzer;
|
private FastDependencyAnalyzer dependencyAnalyzer;
|
||||||
private MethodReference callerMethod;
|
private CallLocation impreciseLocation;
|
||||||
|
|
||||||
FastInstructionAnalyzer(FastDependencyAnalyzer dependencyAnalyzer) {
|
FastInstructionAnalyzer(FastDependencyAnalyzer dependencyAnalyzer) {
|
||||||
this.dependencyAnalyzer = dependencyAnalyzer;
|
this.dependencyAnalyzer = dependencyAnalyzer;
|
||||||
|
@ -31,7 +31,7 @@ class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
|
||||||
@Override
|
@Override
|
||||||
protected void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
|
protected void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
List<? extends VariableReader> arguments) {
|
List<? extends VariableReader> arguments) {
|
||||||
CallLocation callLocation = getCallLocation();
|
CallLocation callLocation = impreciseLocation;
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
dependencyAnalyzer.linkClass(method.getClassName()).initClass(callLocation);
|
dependencyAnalyzer.linkClass(method.getClassName()).initClass(callLocation);
|
||||||
}
|
}
|
||||||
|
@ -43,19 +43,14 @@ class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
|
||||||
@Override
|
@Override
|
||||||
protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
|
protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
List<? extends VariableReader> arguments) {
|
List<? extends VariableReader> arguments) {
|
||||||
dependencyAnalyzer.getVirtualCallConsumer(method).addLocation(getCallLocation());
|
dependencyAnalyzer.getVirtualCallConsumer(method).addLocation(impreciseLocation);
|
||||||
|
|
||||||
dependencyAnalyzer.getClassSource().overriddenMethods(method).forEach(methodImpl -> {
|
|
||||||
dependencyAnalyzer.linkMethod(methodImpl.getReference()).addLocation(getCallLocation());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cloneArray(VariableReader receiver, VariableReader array) {
|
public void cloneArray(VariableReader receiver, VariableReader array) {
|
||||||
DependencyNode arrayNode = getNode(array);
|
DependencyNode arrayNode = getNode(array);
|
||||||
MethodDependency cloneDep = getAnalyzer().linkMethod(CLONE_METHOD);
|
MethodDependency cloneDep = getAnalyzer().linkMethod(CLONE_METHOD);
|
||||||
cloneDep.addLocation(getCallLocation());
|
cloneDep.addLocation(impreciseLocation);
|
||||||
cloneDep.use();
|
cloneDep.use();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,4 +63,15 @@ class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
|
||||||
protected DependencyAnalyzer getAnalyzer() {
|
protected DependencyAnalyzer getAnalyzer() {
|
||||||
return dependencyAnalyzer;
|
return dependencyAnalyzer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CallLocation getCallLocation() {
|
||||||
|
return impreciseLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCaller(MethodReference caller) {
|
||||||
|
super.setCaller(caller);
|
||||||
|
impreciseLocation = new CallLocation(caller);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ class FastVirtualCallConsumer implements DependencyConsumer {
|
||||||
private final MethodDescriptor methodDesc;
|
private final MethodDescriptor methodDesc;
|
||||||
private final DependencyAnalyzer analyzer;
|
private final DependencyAnalyzer analyzer;
|
||||||
private final Map<MethodReference, CallLocation> callLocations = new LinkedHashMap<>();
|
private final Map<MethodReference, CallLocation> callLocations = new LinkedHashMap<>();
|
||||||
private final Set<MethodDependency> methods = new LinkedHashSet<>();
|
private final Set<MethodDependency> methods = new LinkedHashSet<>(100, 0.5f);
|
||||||
|
|
||||||
FastVirtualCallConsumer(DependencyNode node, MethodDescriptor methodDesc, DependencyAnalyzer analyzer) {
|
FastVirtualCallConsumer(DependencyNode node, MethodDescriptor methodDesc, DependencyAnalyzer analyzer) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
|
|
|
@ -19,11 +19,14 @@ import com.carrotsearch.hppc.ObjectByteHashMap;
|
||||||
import com.carrotsearch.hppc.ObjectByteMap;
|
import com.carrotsearch.hppc.ObjectByteMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import org.teavm.common.OptionalPredicate;
|
import org.teavm.common.OptionalPredicate;
|
||||||
|
|
||||||
public class ClassHierarchy {
|
public class ClassHierarchy {
|
||||||
private final ClassReaderSource classSource;
|
private final ClassReaderSource classSource;
|
||||||
private final Map<String, OptionalPredicate<String>> superclassPredicateCache = new HashMap<>();
|
private final Map<String, OptionalPredicate<String>> superclassPredicateCache = new HashMap<>();
|
||||||
|
private final Map<String, Map<MethodDescriptor, Optional<MethodReader>>> resolveMethodCache = new HashMap<>();
|
||||||
|
private final Map<String, Map<String, Optional<FieldReader>>> resolveFieldCache = new HashMap<>();
|
||||||
|
|
||||||
public ClassHierarchy(ClassReaderSource classSource) {
|
public ClassHierarchy(ClassReaderSource classSource) {
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
|
@ -62,6 +65,67 @@ public class ClassHierarchy {
|
||||||
return getSuperclassPredicate(superType).test(subType, defaultValue);
|
return getSuperclassPredicate(superType).test(subType, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MethodReader resolve(MethodReference method) {
|
||||||
|
return resolve(method.getClassName(), method.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodReader resolve(String className, MethodDescriptor method) {
|
||||||
|
Map<MethodDescriptor, Optional<MethodReader>> cache = resolveMethodCache.computeIfAbsent(className,
|
||||||
|
k -> new HashMap<>());
|
||||||
|
Optional<MethodReader> opt = cache.get(method);
|
||||||
|
if (opt == null) {
|
||||||
|
MethodReader reader = null;
|
||||||
|
ClassReader cls = classSource.get(className);
|
||||||
|
if (cls != null) {
|
||||||
|
reader = cls.getMethod(method);
|
||||||
|
if (reader == null && cls.getParent() != null) {
|
||||||
|
reader = resolve(cls.getParent(), method);
|
||||||
|
}
|
||||||
|
if (reader == null) {
|
||||||
|
for (String itf : cls.getInterfaces()) {
|
||||||
|
reader = resolve(itf, method);
|
||||||
|
if (reader != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt = Optional.ofNullable(reader);
|
||||||
|
cache.put(method, opt);
|
||||||
|
}
|
||||||
|
return opt.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldReader resolve(FieldReference field) {
|
||||||
|
return resolve(field.getClassName(), field.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldReader resolve(String className, String fieldName) {
|
||||||
|
Map<String, Optional<FieldReader>> cache = resolveFieldCache.computeIfAbsent(className,
|
||||||
|
k -> new HashMap<>());
|
||||||
|
Optional<FieldReader> opt = cache.get(fieldName);
|
||||||
|
if (opt == null) {
|
||||||
|
FieldReader reader = null;
|
||||||
|
ClassReader cls = classSource.get(className);
|
||||||
|
if (cls != null) {
|
||||||
|
if (cls.getParent() != null) {
|
||||||
|
reader = resolve(cls.getParent(), fieldName);
|
||||||
|
}
|
||||||
|
if (reader == null) {
|
||||||
|
for (String itf : cls.getInterfaces()) {
|
||||||
|
reader = resolve(itf, fieldName);
|
||||||
|
if (reader != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt = Optional.ofNullable(reader);
|
||||||
|
cache.put(fieldName, opt);
|
||||||
|
}
|
||||||
|
return opt.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
public OptionalPredicate<String> getSuperclassPredicate(String superclass) {
|
public OptionalPredicate<String> getSuperclassPredicate(String superclass) {
|
||||||
return superclassPredicateCache.computeIfAbsent(superclass, SuperclassPredicate::new);
|
return superclassPredicateCache.computeIfAbsent(superclass, SuperclassPredicate::new);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
|
||||||
public class ClassInference {
|
public class ClassInference {
|
||||||
private DependencyInfo dependencyInfo;
|
private DependencyInfo dependencyInfo;
|
||||||
|
private ClassHierarchy hierarchy;
|
||||||
private Graph assignmentGraph;
|
private Graph assignmentGraph;
|
||||||
private Graph cloneGraph;
|
private Graph cloneGraph;
|
||||||
private Graph arrayGraph;
|
private Graph arrayGraph;
|
||||||
|
@ -89,8 +90,9 @@ public class ClassInference {
|
||||||
|
|
||||||
private static final int MAX_DEGREE = 3;
|
private static final int MAX_DEGREE = 3;
|
||||||
|
|
||||||
public ClassInference(DependencyInfo dependencyInfo) {
|
public ClassInference(DependencyInfo dependencyInfo, ClassHierarchy hierarchy) {
|
||||||
this.dependencyInfo = dependencyInfo;
|
this.dependencyInfo = dependencyInfo;
|
||||||
|
this.hierarchy = hierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void infer(Program program, MethodReference methodReference) {
|
public void infer(Program program, MethodReference methodReference) {
|
||||||
|
@ -403,8 +405,6 @@ public class ClassInference {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propagateAlongCasts() {
|
private void propagateAlongCasts() {
|
||||||
ClassHierarchy hierarchy = dependencyInfo.getClassHierarchy();
|
|
||||||
|
|
||||||
for (ValueCast cast : casts) {
|
for (ValueCast cast : casts) {
|
||||||
int fromNode = nodeMapping[packNodeAndDegree(cast.fromVariable, 0)];
|
int fromNode = nodeMapping[packNodeAndDegree(cast.fromVariable, 0)];
|
||||||
if (!formerNodeChanged[fromNode] && !nodeChanged[fromNode]) {
|
if (!formerNodeChanged[fromNode] && !nodeChanged[fromNode]) {
|
||||||
|
@ -500,8 +500,6 @@ public class ClassInference {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propagateException(String thrownTypeName, BasicBlock block) {
|
private void propagateException(String thrownTypeName, BasicBlock block) {
|
||||||
ClassHierarchy hierarchy = dependencyInfo.getClassHierarchy();
|
|
||||||
|
|
||||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||||
String expectedType = tryCatch.getExceptionType();
|
String expectedType = tryCatch.getExceptionType();
|
||||||
if (expectedType == null || hierarchy.isSuperType(expectedType, thrownTypeName, false)) {
|
if (expectedType == null || hierarchy.isSuperType(expectedType, thrownTypeName, false)) {
|
||||||
|
|
|
@ -175,7 +175,7 @@ public final class ProgramEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueEmitter getField(FieldReference field, ValueType type) {
|
public ValueEmitter getField(FieldReference field, ValueType type) {
|
||||||
FieldReader resolvedField = classSource.resolve(field);
|
FieldReader resolvedField = hierarchy.resolve(field);
|
||||||
if (resolvedField != null) {
|
if (resolvedField != null) {
|
||||||
field = resolvedField.getReference();
|
field = resolvedField.getReference();
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ public final class ProgramEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgramEmitter setField(FieldReference field, ValueEmitter value) {
|
public ProgramEmitter setField(FieldReference field, ValueEmitter value) {
|
||||||
FieldReader resolvedField = classSource.resolve(field);
|
FieldReader resolvedField = hierarchy.resolve(field);
|
||||||
if (resolvedField != null) {
|
if (resolvedField != null) {
|
||||||
field = resolvedField.getReference();
|
field = resolvedField.getReference();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.Incoming;
|
import org.teavm.model.Incoming;
|
||||||
|
@ -55,16 +56,24 @@ public class Inlining {
|
||||||
private static final int MAX_DEPTH = 7;
|
private static final int MAX_DEPTH = 7;
|
||||||
private IntArrayList depthsByBlock;
|
private IntArrayList depthsByBlock;
|
||||||
private Set<Instruction> instructionsToSkip;
|
private Set<Instruction> instructionsToSkip;
|
||||||
|
private ClassHierarchy hierarchy;
|
||||||
|
private ClassReaderSource classes;
|
||||||
|
private DependencyInfo dependencyInfo;
|
||||||
|
|
||||||
public void apply(Program program, MethodReference method, ClassReaderSource classes,
|
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo) {
|
||||||
DependencyInfo dependencyInfo) {
|
this.hierarchy = hierarchy;
|
||||||
|
this.classes = hierarchy.getClassSource();
|
||||||
|
this.dependencyInfo = dependencyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(Program program, MethodReference method) {
|
||||||
depthsByBlock = new IntArrayList(program.basicBlockCount());
|
depthsByBlock = new IntArrayList(program.basicBlockCount());
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
depthsByBlock.add(0);
|
depthsByBlock.add(0);
|
||||||
}
|
}
|
||||||
instructionsToSkip = new HashSet<>();
|
instructionsToSkip = new HashSet<>();
|
||||||
|
|
||||||
while (applyOnce(program, classes)) {
|
while (applyOnce(program)) {
|
||||||
devirtualize(program, method, dependencyInfo);
|
devirtualize(program, method, dependencyInfo);
|
||||||
}
|
}
|
||||||
depthsByBlock = null;
|
depthsByBlock = null;
|
||||||
|
@ -73,8 +82,8 @@ public class Inlining {
|
||||||
new UnreachableBasicBlockEliminator().optimize(program);
|
new UnreachableBasicBlockEliminator().optimize(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean applyOnce(Program program, ClassReaderSource classSource) {
|
private boolean applyOnce(Program program) {
|
||||||
List<PlanEntry> plan = buildPlan(program, classSource, 0);
|
List<PlanEntry> plan = buildPlan(program, 0);
|
||||||
if (plan.isEmpty()) {
|
if (plan.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -210,7 +219,7 @@ public class Inlining {
|
||||||
execPlan(program, planEntry.innerPlan, firstInlineBlock.getIndex());
|
execPlan(program, planEntry.innerPlan, firstInlineBlock.getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<PlanEntry> buildPlan(Program program, ClassReaderSource classSource, int depth) {
|
private List<PlanEntry> buildPlan(Program program, int depth) {
|
||||||
if (depth >= MAX_DEPTH) {
|
if (depth >= MAX_DEPTH) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
@ -243,7 +252,7 @@ public class Inlining {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodReader invokedMethod = getMethod(classSource, invoke.getMethod());
|
MethodReader invokedMethod = getMethod(invoke.getMethod());
|
||||||
if (invokedMethod == null || invokedMethod.getProgram() == null
|
if (invokedMethod == null || invokedMethod.getProgram() == null
|
||||||
|| invokedMethod.getProgram().basicBlockCount() == 0) {
|
|| invokedMethod.getProgram().basicBlockCount() == 0) {
|
||||||
instructionsToSkip.add(insn);
|
instructionsToSkip.add(insn);
|
||||||
|
@ -264,7 +273,7 @@ public class Inlining {
|
||||||
entry.targetBlock = block.getIndex();
|
entry.targetBlock = block.getIndex();
|
||||||
entry.targetInstruction = insn;
|
entry.targetInstruction = insn;
|
||||||
entry.program = invokedProgram;
|
entry.program = invokedProgram;
|
||||||
entry.innerPlan.addAll(buildPlan(invokedProgram, classSource, depth + 1));
|
entry.innerPlan.addAll(buildPlan(invokedProgram, depth + 1));
|
||||||
entry.depth = depth;
|
entry.depth = depth;
|
||||||
plan.add(entry);
|
plan.add(entry);
|
||||||
}
|
}
|
||||||
|
@ -274,8 +283,8 @@ public class Inlining {
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodReader getMethod(ClassReaderSource classSource, MethodReference methodRef) {
|
private MethodReader getMethod(MethodReference methodRef) {
|
||||||
ClassReader cls = classSource.get(methodRef.getClassName());
|
ClassReader cls = classes.get(methodRef.getClassName());
|
||||||
return cls != null ? cls.getMethod(methodRef.getDescriptor()) : null;
|
return cls != null ? cls.getMethod(methodRef.getDescriptor()) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +317,7 @@ public class Inlining {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void devirtualize(Program program, MethodReference method, DependencyInfo dependencyInfo) {
|
private void devirtualize(Program program, MethodReference method, DependencyInfo dependencyInfo) {
|
||||||
ClassInference inference = new ClassInference(dependencyInfo);
|
ClassInference inference = new ClassInference(dependencyInfo, hierarchy);
|
||||||
inference.infer(program, method);
|
inference.infer(program, method);
|
||||||
|
|
||||||
for (BasicBlock block : program.getBasicBlocks()) {
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
|
|
@ -46,6 +46,7 @@ public class AsyncMethodFinder {
|
||||||
private Map<MethodReference, Boolean> asyncFamilyMethods = new HashMap<>();
|
private Map<MethodReference, Boolean> asyncFamilyMethods = new HashMap<>();
|
||||||
private Set<MethodReference> readonlyAsyncMethods = Collections.unmodifiableSet(asyncMethods);
|
private Set<MethodReference> readonlyAsyncMethods = Collections.unmodifiableSet(asyncMethods);
|
||||||
private Set<MethodReference> readonlyAsyncFamilyMethods = Collections.unmodifiableSet(asyncFamilyMethods.keySet());
|
private Set<MethodReference> readonlyAsyncFamilyMethods = Collections.unmodifiableSet(asyncFamilyMethods.keySet());
|
||||||
|
private Map<MethodReference, Set<MethodReference>> overiddenMethodsCache = new HashMap<>();
|
||||||
private CallGraph callGraph;
|
private CallGraph callGraph;
|
||||||
private Diagnostics diagnostics;
|
private Diagnostics diagnostics;
|
||||||
private ListableClassReaderSource classSource;
|
private ListableClassReaderSource classSource;
|
||||||
|
@ -209,7 +210,7 @@ public class AsyncMethodFinder {
|
||||||
if (cls == null) {
|
if (cls == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (MethodReference overriddenMethod : findOverriddenMethods(cls, methodRef)) {
|
for (MethodReference overriddenMethod : findOverriddenMethods(cls, methodRef.getDescriptor())) {
|
||||||
addOverriddenToFamily(overriddenMethod);
|
addOverriddenToFamily(overriddenMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,7 +233,7 @@ public class AsyncMethodFinder {
|
||||||
if (cls == null) {
|
if (cls == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (MethodReference overriddenMethod : findOverriddenMethods(cls, methodRef)) {
|
for (MethodReference overriddenMethod : findOverriddenMethods(cls, methodRef.getDescriptor())) {
|
||||||
if (addToFamily(overriddenMethod)) {
|
if (addToFamily(overriddenMethod)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -240,44 +241,44 @@ public class AsyncMethodFinder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MethodReference> findOverriddenMethods(ClassReader cls, MethodReference methodRef) {
|
private Set<MethodReference> findOverriddenMethods(ClassReader cls, MethodDescriptor methodDesc) {
|
||||||
List<String> parents = new ArrayList<>();
|
List<String> parents = new ArrayList<>();
|
||||||
if (cls.getParent() != null) {
|
if (cls.getParent() != null) {
|
||||||
parents.add(cls.getParent());
|
parents.add(cls.getParent());
|
||||||
}
|
}
|
||||||
parents.addAll(cls.getInterfaces());
|
parents.addAll(cls.getInterfaces());
|
||||||
|
|
||||||
Set<MethodReference> visited = new HashSet<>();
|
Set<String> visited = new HashSet<>();
|
||||||
Set<MethodReference> overridden = new HashSet<>();
|
Set<MethodReference> overridden = new HashSet<>();
|
||||||
for (String parent : parents) {
|
for (String parent : parents) {
|
||||||
findOverriddenMethods(new MethodReference(parent, methodRef.getDescriptor()), overridden, visited);
|
findOverriddenMethods(parent, methodDesc, overridden, visited);
|
||||||
}
|
}
|
||||||
return overridden;
|
return overridden;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findOverriddenMethods(MethodReference methodRef, Set<MethodReference> result,
|
private void findOverriddenMethods(String className, MethodDescriptor methodDesc, Set<MethodReference> result,
|
||||||
Set<MethodReference> visited) {
|
Set<String> visited) {
|
||||||
if (!visited.add(methodRef)) {
|
if (!visited.add(className)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (methodRef.getName().equals("<init>") || methodRef.getName().equals("<clinit>")) {
|
if (methodDesc.getName().equals("<init>") || methodDesc.getName().equals("<clinit>")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClassReader cls = classSource.get(methodRef.getClassName());
|
ClassReader cls = classSource.get(className);
|
||||||
if (cls == null) {
|
if (cls == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MethodReader method = cls.getMethod(methodRef.getDescriptor());
|
MethodReader method = cls.getMethod(methodDesc);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.FINAL)) {
|
if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.FINAL)) {
|
||||||
result.add(methodRef);
|
result.add(method.getReference());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (cls.getParent() != null) {
|
if (cls.getParent() != null) {
|
||||||
findOverriddenMethods(new MethodReference(cls.getParent(), methodRef.getDescriptor()), result, visited);
|
findOverriddenMethods(cls.getParent(), methodDesc, result, visited);
|
||||||
}
|
}
|
||||||
for (String iface : cls.getInterfaces()) {
|
for (String iface : cls.getInterfaces()) {
|
||||||
findOverriddenMethods(new MethodReference(iface, methodRef.getDescriptor()), result, visited);
|
findOverriddenMethods(iface, methodDesc, result, visited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||||
|
|
||||||
public class MissingItemsProcessor {
|
public class MissingItemsProcessor {
|
||||||
private DependencyInfo dependencyInfo;
|
private DependencyInfo dependencyInfo;
|
||||||
|
private ClassHierarchy hierarchy;
|
||||||
private Diagnostics diagnostics;
|
private Diagnostics diagnostics;
|
||||||
private List<Instruction> instructionsToAdd = new ArrayList<>();
|
private List<Instruction> instructionsToAdd = new ArrayList<>();
|
||||||
private MethodHolder methodHolder;
|
private MethodHolder methodHolder;
|
||||||
|
@ -35,9 +36,10 @@ public class MissingItemsProcessor {
|
||||||
private Collection<MethodReference> reachableMethods;
|
private Collection<MethodReference> reachableMethods;
|
||||||
private Collection<FieldReference> reachableFields;
|
private Collection<FieldReference> reachableFields;
|
||||||
|
|
||||||
public MissingItemsProcessor(DependencyInfo dependencyInfo, Diagnostics diagnostics) {
|
public MissingItemsProcessor(DependencyInfo dependencyInfo, ClassHierarchy hierarchy, Diagnostics diagnostics) {
|
||||||
this.dependencyInfo = dependencyInfo;
|
this.dependencyInfo = dependencyInfo;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
|
this.hierarchy = hierarchy;
|
||||||
reachableClasses = dependencyInfo.getReachableClasses();
|
reachableClasses = dependencyInfo.getReachableClasses();
|
||||||
reachableMethods = dependencyInfo.getReachableMethods();
|
reachableMethods = dependencyInfo.getReachableMethods();
|
||||||
reachableFields = dependencyInfo.getReachableFields();
|
reachableFields = dependencyInfo.getReachableFields();
|
||||||
|
@ -171,7 +173,7 @@ public class MissingItemsProcessor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dependencyInfo.getClassSource().resolve(method) != null) {
|
if (hierarchy.resolve(method) != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.diagnostics.AccumulationDiagnostics;
|
import org.teavm.diagnostics.AccumulationDiagnostics;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.diagnostics.ProblemProvider;
|
import org.teavm.diagnostics.ProblemProvider;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderSource;
|
import org.teavm.model.ClassHolderSource;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
|
@ -372,7 +373,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheStatus = new AnnotationAwareCacheStatus(rawCacheStatus, dependencyAnalyzer.getIncrementalDependencies());
|
cacheStatus = new AnnotationAwareCacheStatus(rawCacheStatus, dependencyAnalyzer.getIncrementalDependencies(),
|
||||||
|
dependencyAnalyzer.getClassSource());
|
||||||
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
|
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
|
@ -423,11 +425,12 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public ListableClassHolderSource link(DependencyInfo dependency) {
|
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
||||||
reportPhase(TeaVMPhase.LINKING, dependency.getReachableClasses().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,
|
||||||
|
dependency.getClassHierarchy(), diagnostics);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return cutClasses;
|
return cutClasses;
|
||||||
}
|
}
|
||||||
|
@ -446,7 +449,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
cutClasses.putClassHolder(cls);
|
cutClasses.putClassHolder(cls);
|
||||||
missingItemsProcessor.processClass(cls);
|
missingItemsProcessor.processClass(cls);
|
||||||
linker.link(dependency, cls);
|
linker.link(dependency, cls);
|
||||||
progressListener.progressReached(++index);
|
reportProgress(++index);
|
||||||
|
if (wasCancelled()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cutClasses;
|
return cutClasses;
|
||||||
}
|
}
|
||||||
|
@ -457,6 +463,12 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void reportProgress(int progress) {
|
||||||
|
if (progressListener.progressReached(progress) == TeaVMProgressFeedback.CANCEL) {
|
||||||
|
cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int devirtualize(int progress, ListableClassHolderSource classes, DependencyInfo dependency) {
|
private int devirtualize(int progress, ListableClassHolderSource classes, DependencyInfo dependency) {
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return progress;
|
return progress;
|
||||||
|
@ -470,7 +482,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
devirtualization.apply(method);
|
devirtualization.apply(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
progressListener.progressReached(++index);
|
reportProgress(++index);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -485,19 +497,19 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<MethodReference, Program> inlinedPrograms = new HashMap<>();
|
Map<MethodReference, Program> inlinedPrograms = new HashMap<>();
|
||||||
Inlining inlining = new Inlining();
|
Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyInfo);
|
||||||
for (String className : classes.getClassNames()) {
|
for (String className : classes.getClassNames()) {
|
||||||
ClassHolder cls = classes.get(className);
|
ClassHolder cls = classes.get(className);
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.getProgram() != null) {
|
if (method.getProgram() != null) {
|
||||||
Program program = ProgramUtils.copy(method.getProgram());
|
Program program = ProgramUtils.copy(method.getProgram());
|
||||||
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classes);
|
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classes);
|
||||||
inlining.apply(program, method.getReference(), classes, dependencyInfo);
|
inlining.apply(program, method.getReference());
|
||||||
new UnusedVariableElimination().optimize(context, program);
|
new UnusedVariableElimination().optimize(context, program);
|
||||||
inlinedPrograms.put(method.getReference(), program);
|
inlinedPrograms.put(method.getReference(), program);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
progressListener.progressReached(++progress);
|
reportProgress(++progress);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -521,7 +533,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
processMethod(method, classSource);
|
processMethod(method, classSource);
|
||||||
}
|
}
|
||||||
progressListener.progressReached(++progress);
|
reportProgress(++progress);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ public class MetaprogrammingDependencyListener extends AbstractDependencyListene
|
||||||
|
|
||||||
MetaprogrammingImpl.classLoader = proxyClassLoader;
|
MetaprogrammingImpl.classLoader = proxyClassLoader;
|
||||||
MetaprogrammingImpl.classSource = agent.getClassSource();
|
MetaprogrammingImpl.classSource = agent.getClassSource();
|
||||||
|
MetaprogrammingImpl.hierarchy = agent.getClassHierarchy();
|
||||||
MetaprogrammingImpl.incrementaDependencies = agent.getIncrementalCache();
|
MetaprogrammingImpl.incrementaDependencies = agent.getIncrementalCache();
|
||||||
MetaprogrammingImpl.agent = agent;
|
MetaprogrammingImpl.agent = agent;
|
||||||
MetaprogrammingImpl.reflectContext = new ReflectContext(agent.getClassHierarchy(), proxyClassLoader);
|
MetaprogrammingImpl.reflectContext = new ReflectContext(agent.getClassHierarchy(), proxyClassLoader);
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.teavm.metaprogramming.reflect.ReflectMethod;
|
||||||
import org.teavm.model.AccessLevel;
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
|
@ -63,6 +64,7 @@ public final class MetaprogrammingImpl {
|
||||||
static Map<String, Integer> proxySuffixGenerators = new HashMap<>();
|
static Map<String, Integer> proxySuffixGenerators = new HashMap<>();
|
||||||
static ClassLoader classLoader;
|
static ClassLoader classLoader;
|
||||||
static ClassReaderSource classSource;
|
static ClassReaderSource classSource;
|
||||||
|
static ClassHierarchy hierarchy;
|
||||||
static IncrementalDependencyRegistration incrementaDependencies;
|
static IncrementalDependencyRegistration incrementaDependencies;
|
||||||
static ReflectContext reflectContext;
|
static ReflectContext reflectContext;
|
||||||
static DependencyAgent agent;
|
static DependencyAgent agent;
|
||||||
|
@ -87,7 +89,7 @@ public final class MetaprogrammingImpl {
|
||||||
return var != null ? new ValueImpl<>(var, varContext, valueImpl.type) : null;
|
return var != null ? new ValueImpl<>(var, varContext, valueImpl.type) : null;
|
||||||
} else {
|
} else {
|
||||||
Fragment fragment = (Fragment) computation;
|
Fragment fragment = (Fragment) computation;
|
||||||
MethodReader method = classSource.resolve(fragment.method);
|
MethodReader method = hierarchy.resolve(fragment.method);
|
||||||
generator.addProgram(method.getProgram(), fragment.capturedValues);
|
generator.addProgram(method.getProgram(), fragment.capturedValues);
|
||||||
return new ValueImpl<>(generator.getResultVar(), varContext, fragment.method.getReturnType());
|
return new ValueImpl<>(generator.getResultVar(), varContext, fragment.method.getReturnType());
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ public final class MetaprogrammingImpl {
|
||||||
|
|
||||||
public static void emit(Action action) {
|
public static void emit(Action action) {
|
||||||
Fragment fragment = (Fragment) action;
|
Fragment fragment = (Fragment) action;
|
||||||
MethodReader method = classSource.resolve(fragment.method);
|
MethodReader method = hierarchy.resolve(fragment.method);
|
||||||
generator.addProgram(method.getProgram(), fragment.capturedValues);
|
generator.addProgram(method.getProgram(), fragment.capturedValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +123,7 @@ public final class MetaprogrammingImpl {
|
||||||
|
|
||||||
if (value instanceof Fragment) {
|
if (value instanceof Fragment) {
|
||||||
Fragment fragment = (Fragment) value;
|
Fragment fragment = (Fragment) value;
|
||||||
MethodReader method = classSource.resolve(fragment.method);
|
MethodReader method = hierarchy.resolve(fragment.method);
|
||||||
generator.addProgram(method.getProgram(), fragment.capturedValues);
|
generator.addProgram(method.getProgram(), fragment.capturedValues);
|
||||||
generator.blockIndex = generator.returnBlockIndex;
|
generator.blockIndex = generator.returnBlockIndex;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.teavm.backend.javascript.JavaScriptTarget;
|
||||||
import org.teavm.common.DisjointSet;
|
import org.teavm.common.DisjointSet;
|
||||||
import org.teavm.diagnostics.Problem;
|
import org.teavm.diagnostics.Problem;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolderSource;
|
import org.teavm.model.ClassHolderSource;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
@ -145,7 +146,8 @@ public class DependencyTest {
|
||||||
|
|
||||||
private void processAssertions(List<Assertion> assertions, MethodDependencyInfo methodDep,
|
private void processAssertions(List<Assertion> assertions, MethodDependencyInfo methodDep,
|
||||||
DependencyInfo dependencyInfo, Program program) {
|
DependencyInfo dependencyInfo, Program program) {
|
||||||
ClassInference classInference = new ClassInference(dependencyInfo);
|
ClassInference classInference = new ClassInference(dependencyInfo, new ClassHierarchy(
|
||||||
|
dependencyInfo.getClassSource()));
|
||||||
classInference.infer(program, methodDep.getReference());
|
classInference.infer(program, methodDep.getReference());
|
||||||
|
|
||||||
for (Assertion assertion : assertions) {
|
for (Assertion assertion : assertions) {
|
||||||
|
|
|
@ -213,6 +213,8 @@ public class IncrementalTest {
|
||||||
if (!problems.isEmpty()) {
|
if (!problems.isEmpty()) {
|
||||||
fail("Compiler error generating file '" + name + "'\n" + buildErrorMessage(vm));
|
fail("Compiler error generating file '" + name + "'\n" + buildErrorMessage(vm));
|
||||||
}
|
}
|
||||||
|
astCache.commit();
|
||||||
|
programCache.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildErrorMessage(TeaVM vm) {
|
private String buildErrorMessage(TeaVM vm) {
|
||||||
|
|
|
@ -114,6 +114,7 @@ public final class TeaVMDevServerRunner {
|
||||||
|
|
||||||
devServer.setIndicator(commandLine.hasOption("indicator"));
|
devServer.setIndicator(commandLine.hasOption("indicator"));
|
||||||
devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload"));
|
devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload"));
|
||||||
|
devServer.setVerbose(commandLine.hasOption('v'));
|
||||||
if (commandLine.hasOption("port")) {
|
if (commandLine.hasOption("port")) {
|
||||||
try {
|
try {
|
||||||
devServer.setPort(Integer.parseInt(commandLine.getOptionValue("port")));
|
devServer.setPort(Integer.parseInt(commandLine.getOptionValue("port")));
|
||||||
|
|
|
@ -43,6 +43,7 @@ public class FileSystemWatcher {
|
||||||
private WatchService watchService;
|
private WatchService watchService;
|
||||||
private Map<WatchKey, Path> keysToPath = new HashMap<>();
|
private Map<WatchKey, Path> keysToPath = new HashMap<>();
|
||||||
private Map<Path, WatchKey> pathsToKey = new HashMap<>();
|
private Map<Path, WatchKey> pathsToKey = new HashMap<>();
|
||||||
|
private Map<Path, Integer> refCount = new HashMap<>();
|
||||||
private Set<File> changedFiles = new LinkedHashSet<>();
|
private Set<File> changedFiles = new LinkedHashSet<>();
|
||||||
|
|
||||||
public FileSystemWatcher(String[] classPath) throws IOException {
|
public FileSystemWatcher(String[] classPath) throws IOException {
|
||||||
|
@ -51,9 +52,7 @@ public class FileSystemWatcher {
|
||||||
Path path = Paths.get(entry);
|
Path path = Paths.get(entry);
|
||||||
File file = path.toFile();
|
File file = path.toFile();
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
if (!file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
registerSingle(path.getParent());
|
|
||||||
} else {
|
|
||||||
register(path);
|
register(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +73,13 @@ public class FileSystemWatcher {
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
return FileVisitResult.CONTINUE;
|
return FileVisitResult.CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||||
|
Path parent = file.getParent();
|
||||||
|
refCount.put(parent, refCount.getOrDefault(parent, 0) + 1);
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
@ -83,6 +89,9 @@ public class FileSystemWatcher {
|
||||||
StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
|
StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
|
||||||
keysToPath.put(key, path);
|
keysToPath.put(key, path);
|
||||||
pathsToKey.put(path, key);
|
pathsToKey.put(path, key);
|
||||||
|
refCount.put(path, refCount.getOrDefault(path, 0) + 1);
|
||||||
|
Path parent = path.getParent();
|
||||||
|
refCount.put(parent, refCount.getOrDefault(parent, 0) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasChanges() throws IOException {
|
public boolean hasChanges() throws IOException {
|
||||||
|
@ -159,9 +168,6 @@ public class FileSystemWatcher {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
Path basePath = keysToPath.get(baseKey);
|
Path basePath = keysToPath.get(baseKey);
|
||||||
if (basePath == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
Path path = basePath.resolve((Path) event.context());
|
Path path = basePath.resolve((Path) event.context());
|
||||||
WatchKey key = pathsToKey.get(path);
|
WatchKey key = pathsToKey.get(path);
|
||||||
|
|
||||||
|
@ -170,9 +176,8 @@ public class FileSystemWatcher {
|
||||||
|
|
||||||
if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
|
if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
pathsToKey.remove(path);
|
|
||||||
keysToPath.remove(key);
|
|
||||||
key.cancel();
|
key.cancel();
|
||||||
|
releasePath(path);
|
||||||
}
|
}
|
||||||
} else if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
|
} else if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
|
||||||
if (Files.isDirectory(path)) {
|
if (Files.isDirectory(path)) {
|
||||||
|
@ -181,4 +186,18 @@ public class FileSystemWatcher {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void releasePath(Path path) {
|
||||||
|
refCount.put(path, refCount.getOrDefault(path, 0) - 1);
|
||||||
|
while (refCount.getOrDefault(path, 0) <= 0) {
|
||||||
|
WatchKey key = pathsToKey.get(path);
|
||||||
|
if (key == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pathsToKey.remove(path);
|
||||||
|
keysToPath.remove(key);
|
||||||
|
path = path.getParent();
|
||||||
|
refCount.put(path, refCount.getOrDefault(path, 0) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,10 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
@ -92,7 +94,10 @@ public class CodeServlet extends HttpServlet {
|
||||||
private final Map<String, byte[]> content = new HashMap<>();
|
private final Map<String, byte[]> content = new HashMap<>();
|
||||||
private MemoryBuildTarget buildTarget = new MemoryBuildTarget();
|
private MemoryBuildTarget buildTarget = new MemoryBuildTarget();
|
||||||
|
|
||||||
private CodeWsEndpoint wsEndpoint;
|
private final Set<CodeWsEndpoint> wsEndpoints = new LinkedHashSet<>();
|
||||||
|
private final Object statusLock = new Object();
|
||||||
|
private boolean compiling;
|
||||||
|
private double progress;
|
||||||
|
|
||||||
public CodeServlet(String mainClass, String[] classPath) {
|
public CodeServlet(String mainClass, String[] classPath) {
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
|
@ -129,14 +134,32 @@ public class CodeServlet extends HttpServlet {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWsEndpoint(CodeWsEndpoint wsEndpoint) {
|
|
||||||
this.wsEndpoint = wsEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAutomaticallyReloaded(boolean automaticallyReloaded) {
|
public void setAutomaticallyReloaded(boolean automaticallyReloaded) {
|
||||||
this.automaticallyReloaded = automaticallyReloaded;
|
this.automaticallyReloaded = automaticallyReloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addWsEndpoint(CodeWsEndpoint endpoint) {
|
||||||
|
synchronized (wsEndpoints) {
|
||||||
|
wsEndpoints.add(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
double progress;
|
||||||
|
synchronized (statusLock) {
|
||||||
|
if (!compiling) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progress = this.progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint.progress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeWsEndpoint(CodeWsEndpoint endpoint) {
|
||||||
|
synchronized (wsEndpoints) {
|
||||||
|
wsEndpoints.remove(endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
String path = req.getPathInfo();
|
String path = req.getPathInfo();
|
||||||
|
@ -289,8 +312,17 @@ public class CodeServlet extends HttpServlet {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
log.info("Changes detected. Recompiling.");
|
log.info("Changes detected. Recompiling.");
|
||||||
|
|
||||||
List<String> staleClasses = getChangedClasses(watcher.grabChangedFiles());
|
List<String> staleClasses = getChangedClasses(watcher.grabChangedFiles());
|
||||||
log.debug("Following classes changed: " + staleClasses);
|
if (staleClasses.size() > 15) {
|
||||||
|
List<String> displayedStaleClasses = staleClasses.subList(0, 10);
|
||||||
|
log.debug("Following classes changed (" + staleClasses.size() + "): "
|
||||||
|
+ String.join(", ", displayedStaleClasses) + " and more...");
|
||||||
|
} else {
|
||||||
|
log.debug("Following classes changed (" + staleClasses.size() + "): "
|
||||||
|
+ String.join(", ", staleClasses));
|
||||||
|
}
|
||||||
|
|
||||||
classSource.evict(staleClasses);
|
classSource.evict(staleClasses);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -358,9 +390,7 @@ public class CodeServlet extends HttpServlet {
|
||||||
log.info("Starting build");
|
log.info("Starting build");
|
||||||
progressListener.last = 0;
|
progressListener.last = 0;
|
||||||
progressListener.lastTime = System.currentTimeMillis();
|
progressListener.lastTime = System.currentTimeMillis();
|
||||||
if (wsEndpoint != null) {
|
reportProgress(0);
|
||||||
wsEndpoint.progress(0);
|
|
||||||
}
|
|
||||||
vm.build(buildTarget, fileName);
|
vm.build(buildTarget, fileName);
|
||||||
addIndicator();
|
addIndicator();
|
||||||
generateDebug(debugInformationBuilder);
|
generateDebug(debugInformationBuilder);
|
||||||
|
@ -419,19 +449,18 @@ public class CodeServlet extends HttpServlet {
|
||||||
|
|
||||||
private void postBuild(TeaVM vm, long startTime) {
|
private void postBuild(TeaVM vm, long startTime) {
|
||||||
if (!vm.wasCancelled()) {
|
if (!vm.wasCancelled()) {
|
||||||
|
log.info("Recompiled stale methods: " + programCache.getPendingItemsCount());
|
||||||
if (vm.getProblemProvider().getSevereProblems().isEmpty()) {
|
if (vm.getProblemProvider().getSevereProblems().isEmpty()) {
|
||||||
log.info("Build complete successfully");
|
log.info("Build complete successfully");
|
||||||
saveNewResult();
|
saveNewResult();
|
||||||
lastReachedClasses = vm.getDependencyInfo().getReachableClasses().size();
|
lastReachedClasses = vm.getDependencyInfo().getReachableClasses().size();
|
||||||
classSource.commit();
|
classSource.commit();
|
||||||
if (wsEndpoint != null) {
|
programCache.commit();
|
||||||
wsEndpoint.complete(true);
|
astCache.commit();
|
||||||
}
|
reportCompilationComplete(true);
|
||||||
} else {
|
} else {
|
||||||
log.info("Build complete with errors");
|
log.info("Build complete with errors");
|
||||||
if (wsEndpoint != null) {
|
reportCompilationComplete(false);
|
||||||
wsEndpoint.complete(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
printStats(vm, startTime);
|
printStats(vm, startTime);
|
||||||
TeaVMProblemRenderer.describeProblems(vm, log);
|
TeaVMProblemRenderer.describeProblems(vm, log);
|
||||||
|
@ -439,6 +468,8 @@ public class CodeServlet extends HttpServlet {
|
||||||
log.info("Build cancelled");
|
log.info("Build cancelled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
astCache.discard();
|
||||||
|
programCache.discard();
|
||||||
buildTarget.clear();
|
buildTarget.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,6 +536,43 @@ public class CodeServlet extends HttpServlet {
|
||||||
return new URLClassLoader(urls, CodeServlet.class.getClassLoader());
|
return new URLClassLoader(urls, CodeServlet.class.getClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void reportProgress(double progress) {
|
||||||
|
synchronized (statusLock) {
|
||||||
|
if (compiling && this.progress == progress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
compiling = true;
|
||||||
|
this.progress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeWsEndpoint[] endpoints;
|
||||||
|
synchronized (wsEndpoints) {
|
||||||
|
endpoints = wsEndpoints.toArray(new CodeWsEndpoint[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CodeWsEndpoint endpoint : endpoints) {
|
||||||
|
endpoint.progress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reportCompilationComplete(boolean success) {
|
||||||
|
synchronized (statusLock) {
|
||||||
|
if (!compiling) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
compiling = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeWsEndpoint[] endpoints;
|
||||||
|
synchronized (wsEndpoints) {
|
||||||
|
endpoints = wsEndpoints.toArray(new CodeWsEndpoint[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CodeWsEndpoint endpoint : endpoints) {
|
||||||
|
endpoint.complete(success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final ProgressListenerImpl progressListener = new ProgressListenerImpl();
|
private final ProgressListenerImpl progressListener = new ProgressListenerImpl();
|
||||||
|
|
||||||
class ProgressListenerImpl implements TeaVMProgressListener {
|
class ProgressListenerImpl implements TeaVMProgressListener {
|
||||||
|
@ -540,13 +608,13 @@ public class CodeServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TeaVMProgressFeedback progressReached(int progress) {
|
public TeaVMProgressFeedback progressReached(int progress) {
|
||||||
if (wsEndpoint != null && indicator) {
|
if (indicator) {
|
||||||
int current = start + Math.min(progress, phaseLimit) * (end - start) / phaseLimit;
|
int current = start + Math.min(progress, phaseLimit) * (end - start) / phaseLimit;
|
||||||
if (current != last) {
|
if (current != last) {
|
||||||
if (current - last > 10 || System.currentTimeMillis() - lastTime > 100) {
|
if (current - last > 10 || System.currentTimeMillis() - lastTime > 100) {
|
||||||
lastTime = System.currentTimeMillis();
|
lastTime = System.currentTimeMillis();
|
||||||
last = current;
|
last = current;
|
||||||
wsEndpoint.progress(current / 10.0);
|
reportProgress(current / 10.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.devserver;
|
package org.teavm.devserver;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import javax.websocket.OnClose;
|
||||||
import javax.websocket.OnOpen;
|
import javax.websocket.OnOpen;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
import javax.websocket.server.ServerEndpoint;
|
import javax.websocket.server.ServerEndpoint;
|
||||||
|
@ -23,38 +23,35 @@ import javax.websocket.server.ServerEndpoint;
|
||||||
@ServerEndpoint("/")
|
@ServerEndpoint("/")
|
||||||
public class CodeWsEndpoint {
|
public class CodeWsEndpoint {
|
||||||
private Session session;
|
private Session session;
|
||||||
private boolean compiling;
|
private CodeServlet servlet;
|
||||||
private double progress;
|
|
||||||
|
|
||||||
@OnOpen
|
@OnOpen
|
||||||
public void open(Session session) {
|
public void open(Session session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
@SuppressWarnings("unchecked")
|
servlet = (CodeServlet) session.getUserProperties().get("teavm.servlet");
|
||||||
Consumer<CodeWsEndpoint> consumer = (Consumer<CodeWsEndpoint>) session.getUserProperties().get("ws.consumer");
|
if (servlet != null) {
|
||||||
if (consumer != null) {
|
servlet.addWsEndpoint(this);
|
||||||
consumer.accept(this);
|
|
||||||
}
|
}
|
||||||
if (compiling) {
|
}
|
||||||
sendProgress(progress);
|
|
||||||
|
@OnClose
|
||||||
|
public void close() {
|
||||||
|
if (servlet != null) {
|
||||||
|
servlet.removeWsEndpoint(this);
|
||||||
}
|
}
|
||||||
|
servlet = null;
|
||||||
|
session = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void progress(double value) {
|
public void progress(double value) {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
sendProgress(value);
|
session.getAsyncRemote().sendText("{ \"command\": \"compiling\", \"progress\": " + value + " }");
|
||||||
}
|
}
|
||||||
compiling = true;
|
|
||||||
progress = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void complete(boolean success) {
|
public void complete(boolean success) {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
session.getAsyncRemote().sendText("{ \"command\": \"complete\", \"success\": " + success + " }");
|
session.getAsyncRemote().sendText("{ \"command\": \"complete\", \"success\": " + success + " }");
|
||||||
}
|
}
|
||||||
compiling = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendProgress(double value) {
|
|
||||||
session.getAsyncRemote().sendText("{ \"command\": \"compiling\", \"progress\": " + value + " }");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import javax.websocket.Decoder;
|
import javax.websocket.Decoder;
|
||||||
import javax.websocket.Encoder;
|
import javax.websocket.Encoder;
|
||||||
import javax.websocket.Extension;
|
import javax.websocket.Extension;
|
||||||
|
@ -42,7 +41,8 @@ public class DevServer {
|
||||||
private List<String> sourcePath = new ArrayList<>();
|
private List<String> sourcePath = new ArrayList<>();
|
||||||
private boolean indicator;
|
private boolean indicator;
|
||||||
private boolean reloadedAutomatically;
|
private boolean reloadedAutomatically;
|
||||||
private TeaVMToolLog log = new ConsoleTeaVMToolLog(false);
|
private boolean verbose;
|
||||||
|
private TeaVMToolLog log;
|
||||||
|
|
||||||
private Server server;
|
private Server server;
|
||||||
private int port = 9090;
|
private int port = 9090;
|
||||||
|
@ -85,11 +85,16 @@ public class DevServer {
|
||||||
this.reloadedAutomatically = reloadedAutomatically;
|
this.reloadedAutomatically = reloadedAutomatically;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setVerbose(boolean verbose) {
|
||||||
|
this.verbose = verbose;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getSourcePath() {
|
public List<String> getSourcePath() {
|
||||||
return sourcePath;
|
return sourcePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
log = new ConsoleTeaVMToolLog(verbose);
|
||||||
server = new Server();
|
server = new Server();
|
||||||
ServerConnector connector = new ServerConnector(server);
|
ServerConnector connector = new ServerConnector(server);
|
||||||
connector.setPort(port);
|
connector.setPort(port);
|
||||||
|
@ -110,7 +115,7 @@ public class DevServer {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
|
ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
|
||||||
wscontainer.addEndpoint(new DevServerEndpointConfig(servlet::setWsEndpoint));
|
wscontainer.addEndpoint(new DevServerEndpointConfig(servlet));
|
||||||
server.start();
|
server.start();
|
||||||
server.join();
|
server.join();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -129,8 +134,8 @@ public class DevServer {
|
||||||
private class DevServerEndpointConfig implements ServerEndpointConfig {
|
private class DevServerEndpointConfig implements ServerEndpointConfig {
|
||||||
private Map<String, Object> userProperties = new HashMap<>();
|
private Map<String, Object> userProperties = new HashMap<>();
|
||||||
|
|
||||||
public DevServerEndpointConfig(Consumer<CodeWsEndpoint> consumer) {
|
public DevServerEndpointConfig(CodeServlet servlet) {
|
||||||
userProperties.put("ws.consumer", consumer);
|
userProperties.put("teavm.servlet", servlet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue
Block a user