Reduce memory used by call graph in dev server mode

This commit is contained in:
Alexey Andreev 2019-03-07 16:51:00 +03:00
parent 88dca1bd02
commit fcfa998e1c
20 changed files with 284 additions and 192 deletions

View File

@ -18,37 +18,13 @@ package org.teavm.callgraph;
import java.util.Collection;
import org.teavm.model.MethodReference;
/**
* Represents a method with information about what methods does it call and what method do call the method.
* @author Alexey Andreev
*/
public interface CallGraphNode {
/**
* Returns reference to entire call graph.
*
* @return graph
*/
CallGraph getGraph();
/**
* Returns the method that this node represents.
*
* @return method
*/
MethodReference getMethod();
/**
* Returns immutable collection of all call sites that are in the method.
*
* @return call site
*/
Collection<? extends CallSite> getCallSites();
/**
* Returns immutable collection of all call sites that call this method.
*
* @return call sites
*/
Collection<? extends CallSite> getCallerCallSites();
Collection<? extends FieldAccessSite> getFieldAccessSites();

View File

@ -15,31 +15,13 @@
*/
package org.teavm.callgraph;
import java.util.Collection;
import org.teavm.model.TextLocation;
/**
* <p>Call site that represents exact place in the code that calls a method.</p>.
* @author Alexey Andreev
*/
public interface CallSite {
/**
* <p>Gets location of the call site</p>.
*
* @return location of the call site or <code>null</code> if no debug information found for this call site.
*/
TextLocation getLocation();
Collection<? extends TextLocation> getLocations(CallGraphNode caller);
/**
* <p>Gets a method that this call site invokes.</p>
*
* @return a node that represent methods being called
*/
CallGraphNode getCallee();
Collection<? extends CallGraphNode> getCalledMethods();
/**
* <p>Gets a method that contains this call site.</p>
*
* @return a node that represents methods's caller
*/
CallGraphNode getCaller();
Collection<? extends CallGraphNode> getCallers();
}

View File

@ -1,65 +0,0 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.callgraph;
import java.io.Serializable;
import java.util.Objects;
import org.teavm.model.TextLocation;
public class DefaultCallSite implements CallSite, Serializable {
private TextLocation location;
private DefaultCallGraphNode callee;
private DefaultCallGraphNode caller;
DefaultCallSite(TextLocation location, DefaultCallGraphNode callee, DefaultCallGraphNode caller) {
this.location = location;
this.callee = callee;
this.caller = caller;
}
@Override
public TextLocation getLocation() {
return location;
}
@Override
public DefaultCallGraphNode getCallee() {
return callee;
}
@Override
public DefaultCallGraphNode getCaller() {
return caller;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DefaultCallSite)) {
return false;
}
DefaultCallSite other = (DefaultCallSite) obj;
return Objects.equals(callee.getMethod(), other.callee.getMethod())
&& Objects.equals(location, other.location);
}
@Override
public int hashCode() {
return Objects.hash(callee.getMethod(), location);
}
}

View File

@ -294,8 +294,8 @@ public class DebugInformation {
private DebuggerCallSite getCallSite(int index) {
RecordArray.Record record = callSiteMapping.get(index);
int type = record.get(2);
int method = record.get(3);
int type = record.get(0);
int method = record.get(1);
switch (type) {
case DebuggerCallSite.NONE:
return null;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014 Alexey Andreev.
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.callgraph;
package org.teavm.dependency;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
@ -30,10 +30,12 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.callgraph.CallGraph;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation;
public class DefaultCallGraph implements CallGraph, Serializable {
class DefaultCallGraph implements CallGraph, Serializable {
public Map<MethodReference, DefaultCallGraphNode> nodes = new HashMap<>();
Map<FieldReference, Set<DefaultFieldAccessSite>> fieldAccessSites = new HashMap<>();
@ -127,9 +129,19 @@ public class DefaultCallGraph implements CallGraph, Serializable {
for (DefaultCallSite callSite : callSitesToProcess.toArray(new DefaultCallSite[0])) {
int index = callSiteToIndex.get(callSite);
SerializableCallGraph.CallSite scs = callSites.get(index);
scs.location = callSite.getLocation();
scs.caller = getNode(callSite.getCaller());
scs.callee = getNode(callSite.getCallee());
scs.virtual = callSite.callers != null;
List<SerializableCallGraph.Location> locations = new ArrayList<>();
for (DefaultCallGraphNode caller : callSite.getCallers()) {
for (TextLocation textLocation : callSite.getLocations(caller)) {
SerializableCallGraph.Location location = new SerializableCallGraph.Location();
location.caller = getNode(caller);
location.value = textLocation;
locations.add(location);
}
}
scs.locations = locations.toArray(new SerializableCallGraph.Location[0]);
scs.callers = getNodes(callSite.getCallers());
scs.calledMethods = getNodes(callSite.getCalledMethods());
hasAny = true;
}
callSitesToProcess.clear();
@ -161,6 +173,15 @@ public class DefaultCallGraph implements CallGraph, Serializable {
return index;
}
private int[] getNodes(Collection<? extends DefaultCallGraphNode> nodes) {
int[] result = new int[nodes.size()];
int index = 0;
for (DefaultCallGraphNode node : nodes) {
result[index++] = getNode(node);
}
return result;
}
private int getCallSite(DefaultCallSite callSite) {
int index = callSiteToIndex.getOrDefault(callSite, -1);
if (index < 0) {
@ -194,7 +215,17 @@ public class DefaultCallGraph implements CallGraph, Serializable {
nodes.add(new DefaultCallGraphNode(cg, serializableNode.method));
}
for (SerializableCallGraph.CallSite scs : scg.callSites) {
callSites.add(new DefaultCallSite(scs.location, nodes.get(scs.callee), nodes.get(scs.caller)));
DefaultCallSite callSite;
if (scs.virtual) {
callSite = new DefaultCallSite(scs.method, mapNodes(scs.callers));
callSite.calledMethods.addAll(mapNodes(scs.calledMethods));
} else {
callSite = new DefaultCallSite(nodes.get(scs.calledMethods[0]), nodes.get(scs.callers[0]));
}
for (SerializableCallGraph.Location location : scs.locations) {
callSite.addLocation(nodes.get(location.caller), location.value);
}
callSites.add(callSite);
}
for (SerializableCallGraph.FieldAccess sfa : scg.fieldAccessList) {
fieldAccessList.add(new DefaultFieldAccessSite(sfa.location, nodes.get(sfa.callee), sfa.field));
@ -208,5 +239,13 @@ public class DefaultCallGraph implements CallGraph, Serializable {
cg.addFieldAccess(fieldAccessList.get(index));
}
}
private Set<DefaultCallGraphNode> mapNodes(int[] nodes) {
Set<DefaultCallGraphNode> result = new LinkedHashSet<>();
for (int i = 0; i < nodes.length; ++i) {
result.add(this.nodes.get(nodes[i]));
}
return result;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014 Alexey Andreev.
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,29 +13,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.callgraph;
package org.teavm.dependency;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.callgraph.CallGraphNode;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation;
public class DefaultCallGraphNode implements CallGraphNode {
class DefaultCallGraphNode implements CallGraphNode {
private DefaultCallGraph graph;
private MethodReference method;
private Set<DefaultCallSite> callSites;
private Map<MethodReference, DefaultCallSite> callSiteMap;
private List<DefaultCallSite> callSites;
private DefaultCallSite singleCallSite;
private Set<DefaultCallSite> safeCallSites;
private Collection<DefaultCallSite> safeCallSites;
private DefaultCallSite singleCaller;
private List<DefaultCallSite> callerCallSites;
private List<DefaultCallSite> safeCallersCallSites;
private Set<DefaultFieldAccessSite> fieldAccessSites = new LinkedHashSet<>();
private Set<DefaultFieldAccessSite> safeFieldAccessSites;
private DefaultCallSite virtualCallSite;
DefaultCallGraphNode(DefaultCallGraph graph, MethodReference method) {
this.graph = graph;
@ -61,7 +66,7 @@ public class DefaultCallGraphNode implements CallGraphNode {
return Collections.emptyList();
}
if (safeCallSites == null) {
safeCallSites = Collections.unmodifiableSet(callSites);
safeCallSites = Collections.unmodifiableCollection(callSites);
}
return safeCallSites;
}
@ -80,28 +85,64 @@ public class DefaultCallGraphNode implements CallGraphNode {
return safeCallersCallSites;
}
public boolean addCallSite(MethodReference method, TextLocation location) {
DefaultCallSite addCallSite(MethodReference method) {
DefaultCallGraphNode callee = graph.getNode(method);
DefaultCallSite callSite = new DefaultCallSite(location, callee, this);
if (callSites == null) {
if (singleCallSite == null) {
singleCallSite = callSite;
callee.addCaller(callSite);
return true;
singleCallSite = new DefaultCallSite(callee, this);
callee.addCaller(singleCallSite);
return singleCallSite;
}
callSites = new LinkedHashSet<>();
if (singleCallSite != null) {
if (singleCallSite.singleCalledMethod.getMethod().equals(method)) {
return singleCallSite;
}
}
callSiteMap = new LinkedHashMap<>();
callSites = new ArrayList<>();
callSiteMap.put(singleCallSite.singleCalledMethod.getMethod(), singleCallSite);
callSites.add(singleCallSite);
singleCallSite = null;
}
if (callSites.add(callSite)) {
DefaultCallSite callSite = callSiteMap.get(method);
if (callSite == null) {
callSite = new DefaultCallSite(callee, this);
callee.addCaller(callSite);
return true;
} else {
return false;
callSiteMap.put(method, callSite);
callSites.add(callSite);
}
return callSite;
}
DefaultCallSite getVirtualCallSite() {
if (virtualCallSite == null) {
virtualCallSite = new DefaultCallSite(method, new LinkedHashSet<>());
}
return virtualCallSite;
}
void addVirtualCallSite(DefaultCallSite callSite) {
if (callSite.callers == null) {
throw new IllegalArgumentException("Call site is not virtual");
}
if (callSite.callers.add(this)) {
if (callSites == null) {
callSites = new ArrayList<>();
callSiteMap = new LinkedHashMap<>();
if (singleCallSite != null) {
callSites.add(singleCallSite);
callSiteMap.put(singleCallSite.method, singleCallSite);
singleCallSite = null;
}
}
callSites.add(callSite);
}
}
private void addCaller(DefaultCallSite caller) {
void addCaller(DefaultCallSite caller) {
if (callerCallSites == null) {
if (singleCaller == null) {
singleCaller = caller;
@ -114,10 +155,6 @@ public class DefaultCallGraphNode implements CallGraphNode {
callerCallSites.add(caller);
}
public boolean addCallSite(MethodReference method) {
return addCallSite(method, null);
}
@Override
public Collection<DefaultFieldAccessSite> getFieldAccessSites() {
if (safeFieldAccessSites == null) {
@ -126,7 +163,7 @@ public class DefaultCallGraphNode implements CallGraphNode {
return safeFieldAccessSites;
}
public boolean addFieldAccess(FieldReference field, TextLocation location) {
boolean addFieldAccess(FieldReference field, TextLocation location) {
DefaultFieldAccessSite site = new DefaultFieldAccessSite(location, this, field);
if (fieldAccessSites.add(site)) {
graph.addFieldAccess(site);

View File

@ -0,0 +1,96 @@
/*
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.dependency;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.teavm.callgraph.CallGraphNode;
import org.teavm.callgraph.CallSite;
import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation;
public class DefaultCallSite implements CallSite, Serializable {
private Map<CallGraphNode, Set<TextLocation>> locations;
private TextLocation singleLocation;
MethodReference method;
Set<DefaultCallGraphNode> callers;
private DefaultCallGraphNode singleCaller;
Set<DefaultCallGraphNode> calledMethods;
DefaultCallGraphNode singleCalledMethod;
Collection<? extends DefaultCallGraphNode> readonlyCalledMethods;
DefaultCallSite(MethodReference method, Set<DefaultCallGraphNode> callers) {
this.method = method;
this.callers = callers;
locations = new HashMap<>();
calledMethods = new LinkedHashSet<>();
}
DefaultCallSite(DefaultCallGraphNode callee, DefaultCallGraphNode caller) {
this.singleCalledMethod = callee;
this.singleCaller = caller;
}
@Override
public Collection<? extends TextLocation> getLocations(CallGraphNode caller) {
if (singleLocation != null) {
return caller == this.singleCaller ? Collections.singleton(singleLocation) : Collections.emptySet();
}
if (locations == null) {
return Collections.emptyList();
}
Set<TextLocation> result = locations.get(caller);
return result != null ? Collections.unmodifiableSet(result) : Collections.emptySet();
}
public void addLocation(DefaultCallGraphNode caller, TextLocation location) {
if (locations == null) {
if (singleLocation == null && callers == null) {
singleLocation = location;
return;
}
locations = new LinkedHashMap<>();
if (singleLocation != null) {
Set<TextLocation> singleLocations = new LinkedHashSet<>();
singleLocations.add(singleLocation);
locations.put(singleCaller, singleLocations);
}
}
locations.computeIfAbsent(caller, k -> new LinkedHashSet<>()).add(location);
}
@Override
public Collection<? extends DefaultCallGraphNode> getCalledMethods() {
if (singleCalledMethod == null) {
return Collections.singletonList(singleCalledMethod);
}
if (readonlyCalledMethods == null) {
readonlyCalledMethods = Collections.unmodifiableCollection(calledMethods);
}
return readonlyCalledMethods;
}
@Override
public Collection<? extends DefaultCallGraphNode> getCallers() {
return callers != null ? callers : Collections.singletonList(singleCaller);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014 Alexey Andreev.
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.callgraph;
package org.teavm.dependency;
import java.io.Serializable;
import java.util.Objects;
import org.teavm.callgraph.FieldAccessSite;
import org.teavm.model.FieldReference;
import org.teavm.model.TextLocation;
public class DefaultFieldAccessSite implements FieldAccessSite, Serializable {
class DefaultFieldAccessSite implements FieldAccessSite, Serializable {
private TextLocation location;
private DefaultCallGraphNode callee;
private FieldReference field;

View File

@ -36,7 +36,6 @@ import org.objectweb.asm.tree.ClassNode;
import org.teavm.cache.IncrementalDependencyProvider;
import org.teavm.cache.IncrementalDependencyRegistration;
import org.teavm.callgraph.CallGraph;
import org.teavm.callgraph.DefaultCallGraph;
import org.teavm.common.CachedMapper;
import org.teavm.common.Mapper;
import org.teavm.common.ServiceRepository;

View File

@ -22,7 +22,6 @@ import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_EXIT_SYNC
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.teavm.callgraph.DefaultCallGraphNode;
import org.teavm.model.BasicBlockReader;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassHierarchy;

View File

@ -177,7 +177,7 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
FastVirtualCallConsumer getVirtualCallConsumer(MethodReference method) {
return virtualCallConsumers.computeIfAbsent(method, key -> {
FastVirtualCallConsumer consumer = new FastVirtualCallConsumer(instancesNode, key.getDescriptor(), this);
FastVirtualCallConsumer consumer = new FastVirtualCallConsumer(instancesNode, key, this);
defer(() -> {
getSubtypeNode(method.getClassName()).addConsumer(consumer);
});

View File

@ -47,7 +47,8 @@ class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments) {
invokeGetClass(method);
dependencyAnalyzer.getVirtualCallConsumer(method).addLocation(impreciseLocation);
FastVirtualCallConsumer consumer = dependencyAnalyzer.getVirtualCallConsumer(method);
consumer.addLocation(impreciseLocation);
}
private void invokeGetClass(MethodReference method) {

View File

@ -20,41 +20,45 @@ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
class FastVirtualCallConsumer implements DependencyConsumer {
private final DependencyNode node;
private final MethodDescriptor methodDesc;
private final MethodReference methodRef;
private final DependencyAnalyzer analyzer;
private final Map<MethodReference, CallLocation> callLocations = new LinkedHashMap<>();
private final Set<MethodDependency> methods = new LinkedHashSet<>(100, 0.5f);
final DefaultCallSite callSite;
FastVirtualCallConsumer(DependencyNode node, MethodDescriptor methodDesc, DependencyAnalyzer analyzer) {
FastVirtualCallConsumer(DependencyNode node, MethodReference methodRef, DependencyAnalyzer analyzer) {
this.node = node;
this.methodDesc = methodDesc;
this.methodRef = methodRef;
this.analyzer = analyzer;
callSite = analyzer.callGraph.getNode(methodRef).getVirtualCallSite();
}
@Override
public void consume(DependencyType type) {
String className = type.getName();
if (DependencyAnalyzer.shouldLog) {
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". "
System.out.println("Virtual call of " + methodRef + " detected on " + node.getTag() + ". "
+ "Target class is " + className);
}
if (className.startsWith("[")) {
className = "java.lang.Object";
type = analyzer.getType(className);
}
MethodDependency methodDep = analyzer.linkMethod(className, methodDesc);
MethodDependency methodDep = analyzer.linkMethod(className, methodRef.getDescriptor());
if (!methods.add(methodDep)) {
return;
}
DefaultCallGraphNode calledMethodNode = analyzer.callGraph.getNode(methodDep.getReference());
callSite.calledMethods.add(calledMethodNode);
calledMethodNode.addCaller(callSite);
for (CallLocation location : callLocations.values()) {
methodDep.addLocation(location);
methodDep.addLocation(location, false);
}
if (!methodDep.isMissing()) {
@ -64,8 +68,13 @@ class FastVirtualCallConsumer implements DependencyConsumer {
void addLocation(CallLocation location) {
if (callLocations.putIfAbsent(location.getMethod(), location) == null) {
DefaultCallGraphNode caller = analyzer.callGraph.getNode(location.getMethod());
caller.addVirtualCallSite(callSite);
if (location.getSourceLocation() != null) {
callSite.addLocation(caller, location.getSourceLocation());
}
for (MethodDependency method : methods) {
method.addLocation(location);
method.addLocation(location, false);
}
}
}

View File

@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.teavm.callgraph.DefaultCallGraphNode;
import org.teavm.model.CallLocation;
import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;

View File

@ -20,7 +20,6 @@ import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.teavm.callgraph.DefaultCallGraphNode;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
@ -111,12 +110,21 @@ public class MethodDependency implements MethodDependencyInfo {
}
public MethodDependency addLocation(CallLocation location) {
return addLocation(location, true);
}
MethodDependency addLocation(CallLocation location, boolean addCallSite) {
DefaultCallGraphNode node = dependencyAnalyzer.callGraph.getNode(location.getMethod());
if (locations == null) {
locations = new LinkedHashSet<>();
}
if (locations.add(location)) {
node.addCallSite(reference, location.getSourceLocation());
if (addCallSite) {
DefaultCallSite callSite = node.addCallSite(reference);
if (location.getSourceLocation() != null) {
callSite.addLocation(node, location.getSourceLocation());
}
}
if (locationListeners != null) {
for (LocationListener listener : locationListeners.toArray(new LocationListener[0])) {
listener.locationAdded(location);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.callgraph;
package org.teavm.dependency;
import java.io.Serializable;
import org.teavm.model.FieldReference;
@ -35,15 +35,22 @@ class SerializableCallGraph implements Serializable {
int[] fieldAccessSites;
}
static class CallSite implements Serializable {
TextLocation location;
int callee;
int caller;
static class CallSite implements Serializable {
MethodReference method;
boolean virtual;
Location[] locations;
int[] calledMethods;
int[] callers;
}
static class FieldAccess implements Serializable {
static class FieldAccess implements Serializable {
TextLocation location;
int callee;
FieldReference field;
}
static class Location {
TextLocation value;
int caller;
}
}

View File

@ -62,7 +62,6 @@ class VirtualCallConsumer implements DependencyConsumer {
}
if (className.startsWith("[")) {
className = "java.lang.Object";
type = analyzer.getType(className);
}
MethodDependency methodDep = analyzer.linkMethod(className, methodDesc);

View File

@ -46,7 +46,6 @@ public class AsyncMethodFinder {
private Map<MethodReference, Boolean> asyncFamilyMethods = new HashMap<>();
private Set<MethodReference> readonlyAsyncMethods = Collections.unmodifiableSet(asyncMethods);
private Set<MethodReference> readonlyAsyncFamilyMethods = Collections.unmodifiableSet(asyncFamilyMethods.keySet());
private Map<MethodReference, Set<MethodReference>> overiddenMethodsCache = new HashMap<>();
private CallGraph callGraph;
private Diagnostics diagnostics;
private ListableClassReaderSource classSource;
@ -178,8 +177,9 @@ public class AsyncMethodFinder {
return;
}
for (CallSite callSite : node.getCallerCallSites()) {
MethodReference nextMethod = callSite.getCaller().getMethod();
add(nextMethod, new CallStack(nextMethod, stack));
for (CallGraphNode caller : callSite.getCallers()) {
add(caller.getMethod(), new CallStack(caller.getMethod(), stack));
}
}
}

View File

@ -125,7 +125,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
private static final MethodDescriptor MAIN_METHOD_DESC = new MethodDescriptor("main",
ValueType.arrayOf(ValueType.object("java.lang.String")), ValueType.VOID);
private final ClassReaderSource classSource;
private final DependencyAnalyzer dependencyAnalyzer;
private final AccumulationDiagnostics diagnostics = new AccumulationDiagnostics();
private final ClassLoader classLoader;
@ -155,9 +154,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
TeaVM(TeaVMBuilder builder) {
target = builder.target;
classSource = builder.classSource;
classLoader = builder.classLoader;
dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(this.classSource, classLoader,
dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(builder.classSource, classLoader,
this, diagnostics, builder.referenceCache);
progressListener = new TeaVMProgressListener() {
@Override public TeaVMProgressFeedback progressReached(int progress) {
@ -313,21 +311,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
preservedClasses.add(className);
}
/**
* Gets a {@link ClassReaderSource} which is used by this TeaVM instance. It is exactly what was
* passed to {@link TeaVMBuilder#setClassSource(ClassReaderSource)}.
*
* @return class source.
*/
public ClassReaderSource getClassSource() {
return classSource;
}
/**
* Gets a {@link ClassReaderSource} which is similar to that of {@link #getClassSource()},
* except that it also contains classes with applied transformations together with
* classes, generated via {@link org.teavm.dependency.DependencyAgent#submitClass(ClassHolder)}.
*/
public ClassReaderSource getDependencyClassSource() {
return dependencyAnalyzer.getClassSource();
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.tooling;
import java.util.Collection;
import java.util.Iterator;
import org.teavm.callgraph.CallGraph;
import org.teavm.callgraph.CallGraphNode;
@ -70,12 +71,33 @@ public final class TeaVMProblemRenderer {
}
CallSite callSite = callSites.next();
sb.append("\n at ");
renderCallLocation(callSite.getCaller().getMethod(), callSite.getLocation(), sb);
node = callSite.getCaller();
CallGraphNode caller = getCaller(callSite);
renderCallLocation(caller.getMethod(), getLocation(callSite, caller), sb);
node = caller;
}
}
}
private static CallGraphNode getCaller(CallSite callSite) {
Collection<? extends CallGraphNode> callers = callSite.getCallers();
if (callers.isEmpty()) {
return null;
}
return callers.iterator().next();
}
private static TextLocation getLocation(CallSite callSite, CallGraphNode caller) {
if (caller == null) {
return null;
}
Collection<? extends TextLocation> locations = callSite.getLocations(caller);
if (locations.isEmpty()) {
return null;
}
return locations.iterator().next();
}
public static void renderCallLocation(MethodReference method, TextLocation location, StringBuilder sb) {
if (method != null) {
sb.append(method.getClassName() + "." + method.getName());