mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Reduce memory used by call graph in dev server mode
This commit is contained in:
parent
88dca1bd02
commit
fcfa998e1c
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
96
core/src/main/java/org/teavm/dependency/DefaultCallSite.java
Normal file
96
core/src/main/java/org/teavm/dependency/DefaultCallSite.java
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
@ -36,9 +36,11 @@ class SerializableCallGraph implements Serializable {
|
|||
}
|
||||
|
||||
static class CallSite implements Serializable {
|
||||
TextLocation location;
|
||||
int callee;
|
||||
int caller;
|
||||
MethodReference method;
|
||||
boolean virtual;
|
||||
Location[] locations;
|
||||
int[] calledMethods;
|
||||
int[] callers;
|
||||
}
|
||||
|
||||
static class FieldAccess implements Serializable {
|
||||
|
@ -46,4 +48,9 @@ class SerializableCallGraph implements Serializable {
|
|||
int callee;
|
||||
FieldReference field;
|
||||
}
|
||||
|
||||
static class Location {
|
||||
TextLocation value;
|
||||
int caller;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue
Block a user