diff --git a/core/src/main/java/org/teavm/callgraph/CallGraphNode.java b/core/src/main/java/org/teavm/callgraph/CallGraphNode.java index eba99593a..15a92f48a 100644 --- a/core/src/main/java/org/teavm/callgraph/CallGraphNode.java +++ b/core/src/main/java/org/teavm/callgraph/CallGraphNode.java @@ -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 getCallSites(); - /** - * Returns immutable collection of all call sites that call this method. - * - * @return call sites - */ Collection getCallerCallSites(); Collection getFieldAccessSites(); diff --git a/core/src/main/java/org/teavm/callgraph/CallSite.java b/core/src/main/java/org/teavm/callgraph/CallSite.java index bcf942681..7f5fafc9f 100644 --- a/core/src/main/java/org/teavm/callgraph/CallSite.java +++ b/core/src/main/java/org/teavm/callgraph/CallSite.java @@ -15,31 +15,13 @@ */ package org.teavm.callgraph; +import java.util.Collection; import org.teavm.model.TextLocation; -/** - *

Call site that represents exact place in the code that calls a method.

. - * @author Alexey Andreev - */ public interface CallSite { - /** - *

Gets location of the call site

. - * - * @return location of the call site or null if no debug information found for this call site. - */ - TextLocation getLocation(); + Collection getLocations(CallGraphNode caller); - /** - *

Gets a method that this call site invokes.

- * - * @return a node that represent methods being called - */ - CallGraphNode getCallee(); + Collection getCalledMethods(); - /** - *

Gets a method that contains this call site.

- * - * @return a node that represents methods's caller - */ - CallGraphNode getCaller(); + Collection getCallers(); } diff --git a/core/src/main/java/org/teavm/callgraph/DefaultCallSite.java b/core/src/main/java/org/teavm/callgraph/DefaultCallSite.java deleted file mode 100644 index 1e22993c7..000000000 --- a/core/src/main/java/org/teavm/callgraph/DefaultCallSite.java +++ /dev/null @@ -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); - } -} diff --git a/core/src/main/java/org/teavm/debugging/information/DebugInformation.java b/core/src/main/java/org/teavm/debugging/information/DebugInformation.java index 6e6592169..12a485cc4 100644 --- a/core/src/main/java/org/teavm/debugging/information/DebugInformation.java +++ b/core/src/main/java/org/teavm/debugging/information/DebugInformation.java @@ -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; diff --git a/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java b/core/src/main/java/org/teavm/dependency/DefaultCallGraph.java similarity index 79% rename from core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java rename to core/src/main/java/org/teavm/dependency/DefaultCallGraph.java index f6df3ea07..f0e0d2195 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java +++ b/core/src/main/java/org/teavm/dependency/DefaultCallGraph.java @@ -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 nodes = new HashMap<>(); Map> 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 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 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 mapNodes(int[] nodes) { + Set result = new LinkedHashSet<>(); + for (int i = 0; i < nodes.length; ++i) { + result.add(this.nodes.get(nodes[i])); + } + return result; + } } } diff --git a/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java b/core/src/main/java/org/teavm/dependency/DefaultCallGraphNode.java similarity index 60% rename from core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java rename to core/src/main/java/org/teavm/dependency/DefaultCallGraphNode.java index f150a8149..209c70bc2 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java +++ b/core/src/main/java/org/teavm/dependency/DefaultCallGraphNode.java @@ -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 callSites; + private Map callSiteMap; + private List callSites; private DefaultCallSite singleCallSite; - private Set safeCallSites; + private Collection safeCallSites; private DefaultCallSite singleCaller; private List callerCallSites; private List safeCallersCallSites; private Set fieldAccessSites = new LinkedHashSet<>(); private Set 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 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); diff --git a/core/src/main/java/org/teavm/dependency/DefaultCallSite.java b/core/src/main/java/org/teavm/dependency/DefaultCallSite.java new file mode 100644 index 000000000..e82381ccc --- /dev/null +++ b/core/src/main/java/org/teavm/dependency/DefaultCallSite.java @@ -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> locations; + private TextLocation singleLocation; + MethodReference method; + Set callers; + private DefaultCallGraphNode singleCaller; + Set calledMethods; + DefaultCallGraphNode singleCalledMethod; + Collection readonlyCalledMethods; + + DefaultCallSite(MethodReference method, Set 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 getLocations(CallGraphNode caller) { + if (singleLocation != null) { + return caller == this.singleCaller ? Collections.singleton(singleLocation) : Collections.emptySet(); + } + if (locations == null) { + return Collections.emptyList(); + } + Set 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 singleLocations = new LinkedHashSet<>(); + singleLocations.add(singleLocation); + locations.put(singleCaller, singleLocations); + } + } + locations.computeIfAbsent(caller, k -> new LinkedHashSet<>()).add(location); + } + + @Override + public Collection getCalledMethods() { + if (singleCalledMethod == null) { + return Collections.singletonList(singleCalledMethod); + } + if (readonlyCalledMethods == null) { + readonlyCalledMethods = Collections.unmodifiableCollection(calledMethods); + } + return readonlyCalledMethods; + } + + @Override + public Collection getCallers() { + return callers != null ? callers : Collections.singletonList(singleCaller); + } +} diff --git a/core/src/main/java/org/teavm/callgraph/DefaultFieldAccessSite.java b/core/src/main/java/org/teavm/dependency/DefaultFieldAccessSite.java similarity index 90% rename from core/src/main/java/org/teavm/callgraph/DefaultFieldAccessSite.java rename to core/src/main/java/org/teavm/dependency/DefaultFieldAccessSite.java index a7621719a..ac55e91a4 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultFieldAccessSite.java +++ b/core/src/main/java/org/teavm/dependency/DefaultFieldAccessSite.java @@ -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; diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index dfa133ed8..cdf13778f 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -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; diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index ae176ff44..0cb0b09ee 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -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; diff --git a/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java index cadd1c614..38b051d40 100644 --- a/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java @@ -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); }); diff --git a/core/src/main/java/org/teavm/dependency/FastInstructionAnalyzer.java b/core/src/main/java/org/teavm/dependency/FastInstructionAnalyzer.java index c6ace2756..f3a27616e 100644 --- a/core/src/main/java/org/teavm/dependency/FastInstructionAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/FastInstructionAnalyzer.java @@ -47,7 +47,8 @@ class FastInstructionAnalyzer extends AbstractInstructionAnalyzer { protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method, List arguments) { invokeGetClass(method); - dependencyAnalyzer.getVirtualCallConsumer(method).addLocation(impreciseLocation); + FastVirtualCallConsumer consumer = dependencyAnalyzer.getVirtualCallConsumer(method); + consumer.addLocation(impreciseLocation); } private void invokeGetClass(MethodReference method) { diff --git a/core/src/main/java/org/teavm/dependency/FastVirtualCallConsumer.java b/core/src/main/java/org/teavm/dependency/FastVirtualCallConsumer.java index 124227fa1..31f5fe4c7 100644 --- a/core/src/main/java/org/teavm/dependency/FastVirtualCallConsumer.java +++ b/core/src/main/java/org/teavm/dependency/FastVirtualCallConsumer.java @@ -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 callLocations = new LinkedHashMap<>(); private final Set 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); } } } diff --git a/core/src/main/java/org/teavm/dependency/FieldDependency.java b/core/src/main/java/org/teavm/dependency/FieldDependency.java index 5df70a43a..27ea3c7ad 100644 --- a/core/src/main/java/org/teavm/dependency/FieldDependency.java +++ b/core/src/main/java/org/teavm/dependency/FieldDependency.java @@ -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; diff --git a/core/src/main/java/org/teavm/dependency/MethodDependency.java b/core/src/main/java/org/teavm/dependency/MethodDependency.java index db0a60806..a32b0311f 100644 --- a/core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -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); diff --git a/core/src/main/java/org/teavm/callgraph/SerializableCallGraph.java b/core/src/main/java/org/teavm/dependency/SerializableCallGraph.java similarity index 76% rename from core/src/main/java/org/teavm/callgraph/SerializableCallGraph.java rename to core/src/main/java/org/teavm/dependency/SerializableCallGraph.java index d44c1aab6..558105af8 100644 --- a/core/src/main/java/org/teavm/callgraph/SerializableCallGraph.java +++ b/core/src/main/java/org/teavm/dependency/SerializableCallGraph.java @@ -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; + } } diff --git a/core/src/main/java/org/teavm/dependency/VirtualCallConsumer.java b/core/src/main/java/org/teavm/dependency/VirtualCallConsumer.java index 07f8f8316..e71fdbeee 100644 --- a/core/src/main/java/org/teavm/dependency/VirtualCallConsumer.java +++ b/core/src/main/java/org/teavm/dependency/VirtualCallConsumer.java @@ -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); diff --git a/core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java b/core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java index 5f5d3a47d..fe15aa7d3 100644 --- a/core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java +++ b/core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java @@ -46,7 +46,6 @@ public class AsyncMethodFinder { private Map asyncFamilyMethods = new HashMap<>(); private Set readonlyAsyncMethods = Collections.unmodifiableSet(asyncMethods); private Set readonlyAsyncFamilyMethods = Collections.unmodifiableSet(asyncFamilyMethods.keySet()); - private Map> 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)); + } } } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 1acb76054..65dce0550 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -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(); } diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java index e3285d7a9..eee637578 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java @@ -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 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 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());