diff --git a/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java b/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java index 865f37110..f907552ef 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java +++ b/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java @@ -15,98 +15,251 @@ */ package org.teavm.callgraph; +import com.carrotsearch.hppc.ObjectIntMap; +import com.carrotsearch.hppc.ObjectIntOpenHashMap; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; public class DefaultCallGraph implements CallGraph, Serializable { - private transient Map nodes = new HashMap<>(); - private List> nodeList; - private transient Map> fieldAccessSites = new HashMap<>(); - private List> fieldAccessSiteList; - private transient Map> classAccessSites = new HashMap<>(); - private List> classAccessSiteList; + Map nodes = new LinkedHashMap<>(); + Map> fieldAccessSites = new LinkedHashMap<>(); + Map> classAccessSites = new LinkedHashMap<>(); @Override public DefaultCallGraphNode getNode(MethodReference method) { - ensureDeserialized(); return nodes.computeIfAbsent(method, k -> new DefaultCallGraphNode(this, method)); } @Override public Collection getFieldAccess(FieldReference reference) { - ensureDeserialized(); Set resultSet = fieldAccessSites.get(reference); return resultSet != null ? Collections.unmodifiableSet(resultSet) : Collections.emptySet(); } void addFieldAccess(DefaultFieldAccessSite accessSite) { - ensureDeserialized(); fieldAccessSites.computeIfAbsent(accessSite.getField(), k -> new HashSet<>()).add(accessSite); } @Override public Collection getClassAccess(String className) { - ensureDeserialized(); Set resultSet = classAccessSites.get(className); return resultSet != null ? Collections.unmodifiableSet(resultSet) : Collections.emptySet(); } void addClassAccess(DefaultClassAccessSite accessSite) { - ensureDeserialized(); classAccessSites.computeIfAbsent(accessSite.getClassName(), k -> new HashSet<>()).add(accessSite); } - private void ensureDeserialized() { - if (nodes != null) { - return; - } - - nodes = new HashMap<>(); - for (Map.Entry entry : nodeList) { - nodes.put(entry.getKey(), entry.getValue()); - } - nodeList = null; - - fieldAccessSites = new HashMap<>(); - for (Map.Entry entry : fieldAccessSiteList) { - fieldAccessSites.computeIfAbsent(entry.getKey(), k -> new HashSet<>()).add(entry.getValue()); - } - fieldAccessSiteList = null; - - classAccessSites = new HashMap<>(); - for (Map.Entry entry : classAccessSiteList) { - classAccessSites.computeIfAbsent(entry.getKey(), k -> new HashSet<>()).add(entry.getValue()); - } - classAccessSiteList = null; - } - private void writeObject(ObjectOutputStream out) throws IOException { - ensureDeserialized(); - nodeList = new ArrayList<>(nodes.entrySet()); - - fieldAccessSiteList = new ArrayList<>(); - for (Map.Entry> entry : fieldAccessSites.entrySet()) { - for (DefaultFieldAccessSite site : entry.getValue()) { - fieldAccessSiteList.add(new AbstractMap.SimpleEntry<>(entry.getKey(), site)); - } - } - - classAccessSiteList = new ArrayList<>(); - for (Map.Entry> entry : classAccessSites.entrySet()) { - for (DefaultClassAccessSite site : entry.getValue()) { - classAccessSiteList.add(new AbstractMap.SimpleEntry<>(entry.getKey(), site)); - } - } - - out.defaultWriteObject(); + SerializableCallGraphBuilder builder = new SerializableCallGraphBuilder(); + out.writeObject(builder.build(this)); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); + SerializableCallGraph scg = (SerializableCallGraph) in.readObject(); + nodes = new LinkedHashMap<>(); + fieldAccessSites = new LinkedHashMap<>(); + classAccessSites = new LinkedHashMap<>(); + new CallGraphBuilder().build(scg, this); + } + + static class SerializableCallGraphBuilder { + List nodes = new ArrayList<>(); + ObjectIntMap nodeToIndex = new ObjectIntOpenHashMap<>(); + List callSites = new ArrayList<>(); + List originalCallSites = new ArrayList<>(); + ObjectIntMap callSiteToIndex = new ObjectIntOpenHashMap<>(); + List fieldAccessList = new ArrayList<>(); + ObjectIntMap fieldAccessToIndex = new ObjectIntOpenHashMap<>(); + List classAccessList = new ArrayList<>(); + ObjectIntMap classAccessToIndex = new ObjectIntOpenHashMap<>(); + List nodesToProcess = new ArrayList<>(); + List callSitesToProcess = new ArrayList<>(); + List fieldAccessToProcess = new ArrayList<>(); + List classAccessToProcess = new ArrayList<>(); + + SerializableCallGraph build(DefaultCallGraph cg) { + SerializableCallGraph scg = new SerializableCallGraph(); + + scg.nodeIndexes = cg.nodes.values().stream() + .mapToInt(this::getNode) + .toArray(); + scg.fieldAccessIndexes = cg.fieldAccessSites.values().stream() + .flatMapToInt(accessSites -> accessSites.stream().mapToInt(this::getFieldAccess)) + .toArray(); + scg.classAccessIndexes = cg.classAccessSites.values().stream() + .flatMapToInt(accessSites -> accessSites.stream().mapToInt(this::getClassAccess)) + .toArray(); + + while (step()) { + // just repeat + } + + scg.nodes = nodes.toArray(new SerializableCallGraph.Node[0]); + scg.callSites = callSites.toArray(new SerializableCallGraph.CallSite[0]); + scg.fieldAccessList = fieldAccessList.toArray(new SerializableCallGraph.FieldAccess[0]); + scg.classAccessList = classAccessList.toArray(new SerializableCallGraph.ClassAccess[0]); + + return scg; + } + + boolean step() { + return processNodes() | processCallSites() | processFieldAccess() | processClassAccess(); + } + + boolean processNodes() { + boolean hasAny = false; + for (DefaultCallGraphNode node : nodesToProcess.toArray(new DefaultCallGraphNode[0])) { + int index = nodeToIndex.get(node); + SerializableCallGraph.Node serializableNode = nodes.get(index); + serializableNode.method = node.getMethod(); + serializableNode.callSites = node.getCallSites().stream() + .mapToInt(this::getCallSite) + .toArray(); + serializableNode.callerCallSites = node.getCallerCallSites().stream() + .mapToInt(this::getCallSite) + .toArray(); + serializableNode.fieldAccessSites = node.getFieldAccessSites().stream() + .mapToInt(this::getFieldAccess) + .toArray(); + serializableNode.classAccessSites = node.getClassAccessSites().stream() + .mapToInt(this::getClassAccess) + .toArray(); + hasAny = true; + } + nodesToProcess.clear(); + return hasAny; + } + + boolean processCallSites() { + boolean hasAny = false; + 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()); + hasAny = true; + } + callSitesToProcess.clear(); + return hasAny; + } + + boolean processFieldAccess() { + boolean hasAny = false; + for (DefaultFieldAccessSite accessSite : fieldAccessToProcess.toArray(new DefaultFieldAccessSite[0])) { + int index = fieldAccessToIndex.get(accessSite); + SerializableCallGraph.FieldAccess sfa = fieldAccessList.get(index); + sfa.location = accessSite.getLocation(); + sfa.field = accessSite.getField(); + sfa.callee = getNode(accessSite.getCallee()); + hasAny = true; + } + fieldAccessToProcess.clear(); + return hasAny; + } + + boolean processClassAccess() { + boolean hasAny = false; + for (DefaultClassAccessSite accessSite : classAccessToProcess.toArray(new DefaultClassAccessSite[0])) { + int index = classAccessToIndex.get(accessSite); + SerializableCallGraph.ClassAccess sca = classAccessList.get(index); + sca.location = accessSite.getLocation(); + sca.className = accessSite.getClassName(); + sca.callee = getNode(accessSite.getCallee()); + hasAny = true; + } + classAccessToProcess.clear(); + return hasAny; + } + + private int getNode(DefaultCallGraphNode node) { + int index = nodeToIndex.getOrDefault(node, -1); + if (index < 0) { + index = nodeToIndex.size(); + nodeToIndex.put(node, index); + nodes.add(new SerializableCallGraph.Node()); + nodesToProcess.add(node); + } + return index; + } + + private int getCallSite(DefaultCallSite callSite) { + int index = callSiteToIndex.getOrDefault(callSite, -1); + if (index < 0) { + index = callSiteToIndex.size(); + callSiteToIndex.put(callSite, index); + callSites.add(new SerializableCallGraph.CallSite()); + callSitesToProcess.add(callSite); + } + return index; + } + + private int getFieldAccess(DefaultFieldAccessSite fieldAccessSite) { + int index = fieldAccessToIndex.getOrDefault(fieldAccessSite, -1); + if (index < 0) { + index = fieldAccessToIndex.size(); + fieldAccessToIndex.put(fieldAccessSite, index); + fieldAccessList.add(new SerializableCallGraph.FieldAccess()); + fieldAccessToProcess.add(fieldAccessSite); + } + return index; + } + + private int getClassAccess(DefaultClassAccessSite classAccessSite) { + int index = classAccessToIndex.getOrDefault(classAccessSite, -1); + if (index < 0) { + index = classAccessToIndex.size(); + classAccessToIndex.put(classAccessSite, index); + classAccessList.add(new SerializableCallGraph.ClassAccess()); + classAccessToProcess.add(classAccessSite); + } + return index; + } + } + + static class CallGraphBuilder { + List nodes = new ArrayList<>(); + List callSites = new ArrayList<>(); + List fieldAccessList = new ArrayList<>(); + List classAccessList = new ArrayList<>(); + + void build(SerializableCallGraph scg, DefaultCallGraph cg) { + for (SerializableCallGraph.Node serializableNode : scg.nodes) { + 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))); + } + for (SerializableCallGraph.FieldAccess sfa : scg.fieldAccessList) { + fieldAccessList.add(new DefaultFieldAccessSite(sfa.location, nodes.get(sfa.callee), sfa.field)); + } + for (SerializableCallGraph.ClassAccess sca : scg.classAccessList) { + classAccessList.add(new DefaultClassAccessSite(sca.location, nodes.get(sca.callee), sca.className)); + } + + for (int index : scg.nodeIndexes) { + DefaultCallGraphNode node = nodes.get(index); + cg.nodes.put(node.getMethod(), node); + } + for (int index : scg.fieldAccessIndexes) { + cg.addFieldAccess(fieldAccessList.get(index)); + } + for (int index : scg.classAccessIndexes) { + cg.addClassAccess(classAccessList.get(index)); + } + } } } diff --git a/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java b/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java index 33e153a30..5ebe3dffc 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java +++ b/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java @@ -15,10 +15,6 @@ */ package org.teavm.callgraph; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -29,20 +25,17 @@ import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; import org.teavm.model.TextLocation; -public class DefaultCallGraphNode implements CallGraphNode, Serializable { +public class DefaultCallGraphNode implements CallGraphNode { private DefaultCallGraph graph; private MethodReference method; - private transient Set callSites = new HashSet<>(); - private List callSiteList; - private transient Set safeCallSites; - private List callerCallSites = new ArrayList<>(); - private transient List safeCallersCallSites; - private transient Set fieldAccessSites = new HashSet<>(); - private List fieldAccessSiteList; - private transient Set safeFieldAccessSites; - private transient Set classAccessSites = new HashSet<>(); - private List classAccessSiteList; - private transient Set safeClassAccessSites; + Set callSites = new HashSet<>(); + private Set safeCallSites; + List callerCallSites = new ArrayList<>(); + private List safeCallersCallSites; + Set fieldAccessSites = new HashSet<>(); + private Set safeFieldAccessSites; + Set classAccessSites = new HashSet<>(); + private Set safeClassAccessSites; DefaultCallGraphNode(DefaultCallGraph graph, MethodReference method) { this.graph = graph; @@ -61,7 +54,6 @@ public class DefaultCallGraphNode implements CallGraphNode, Serializable { @Override public Collection getCallSites() { - ensureDeserialized(); if (safeCallSites == null) { safeCallSites = Collections.unmodifiableSet(callSites); } @@ -70,7 +62,6 @@ public class DefaultCallGraphNode implements CallGraphNode, Serializable { @Override public Collection getCallerCallSites() { - ensureDeserialized(); if (safeCallersCallSites == null) { safeCallersCallSites = Collections.unmodifiableList(callerCallSites); } @@ -78,7 +69,6 @@ public class DefaultCallGraphNode implements CallGraphNode, Serializable { } public boolean addCallSite(MethodReference method, TextLocation location) { - ensureDeserialized(); DefaultCallGraphNode callee = graph.getNode(method); DefaultCallSite callSite = new DefaultCallSite(location, callee, this); if (callSites.add(callSite)) { @@ -95,7 +85,6 @@ public class DefaultCallGraphNode implements CallGraphNode, Serializable { @Override public Collection getFieldAccessSites() { - ensureDeserialized(); if (safeFieldAccessSites == null) { safeFieldAccessSites = Collections.unmodifiableSet(fieldAccessSites); } @@ -103,7 +92,6 @@ public class DefaultCallGraphNode implements CallGraphNode, Serializable { } public boolean addFieldAccess(FieldReference field, TextLocation location) { - ensureDeserialized(); DefaultFieldAccessSite site = new DefaultFieldAccessSite(location, this, field); if (fieldAccessSites.add(site)) { graph.addFieldAccess(site); @@ -114,8 +102,7 @@ public class DefaultCallGraphNode implements CallGraphNode, Serializable { } @Override - public Collection getClassAccessSites() { - ensureDeserialized(); + public Collection getClassAccessSites() { if (safeClassAccessSites == null) { safeClassAccessSites = Collections.unmodifiableSet(classAccessSites); } @@ -123,7 +110,6 @@ public class DefaultCallGraphNode implements CallGraphNode, Serializable { } public boolean addClassAccess(String className, TextLocation location) { - ensureDeserialized(); DefaultClassAccessSite site = new DefaultClassAccessSite(location, this, className); if (classAccessSites.add(site)) { graph.addClassAccess(site); @@ -132,31 +118,4 @@ public class DefaultCallGraphNode implements CallGraphNode, Serializable { return false; } } - - private void ensureDeserialized() { - if (callSites != null) { - return; - } - - callSites = new HashSet<>(callSiteList); - callSiteList = null; - - fieldAccessSites = new HashSet<>(fieldAccessSiteList); - fieldAccessSiteList = null; - - classAccessSites = new HashSet<>(classAccessSiteList); - classAccessSiteList = null; - } - - private void writeObject(ObjectOutputStream out) throws IOException { - ensureDeserialized(); - callSiteList = new ArrayList<>(callSites); - fieldAccessSiteList = new ArrayList<>(fieldAccessSites); - classAccessSiteList = new ArrayList<>(classAccessSites); - out.defaultWriteObject(); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - } } diff --git a/core/src/main/java/org/teavm/callgraph/DefaultClassAccessSite.java b/core/src/main/java/org/teavm/callgraph/DefaultClassAccessSite.java index afa679a44..f9179923e 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultClassAccessSite.java +++ b/core/src/main/java/org/teavm/callgraph/DefaultClassAccessSite.java @@ -21,10 +21,10 @@ import org.teavm.model.TextLocation; public class DefaultClassAccessSite implements ClassAccessSite, Serializable { private TextLocation location; - private CallGraphNode callee; + private DefaultCallGraphNode callee; private String className; - DefaultClassAccessSite(TextLocation location, CallGraphNode callee, String className) { + DefaultClassAccessSite(TextLocation location, DefaultCallGraphNode callee, String className) { this.location = location; this.callee = callee; this.className = className; @@ -36,7 +36,7 @@ public class DefaultClassAccessSite implements ClassAccessSite, Serializable { } @Override - public CallGraphNode getCallee() { + public DefaultCallGraphNode getCallee() { return callee; } diff --git a/core/src/main/java/org/teavm/callgraph/DefaultFieldAccessSite.java b/core/src/main/java/org/teavm/callgraph/DefaultFieldAccessSite.java index b086e9d94..7a3196164 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultFieldAccessSite.java +++ b/core/src/main/java/org/teavm/callgraph/DefaultFieldAccessSite.java @@ -22,10 +22,10 @@ import org.teavm.model.TextLocation; public class DefaultFieldAccessSite implements FieldAccessSite, Serializable { private TextLocation location; - private CallGraphNode callee; + private DefaultCallGraphNode callee; private FieldReference field; - DefaultFieldAccessSite(TextLocation location, CallGraphNode callee, FieldReference field) { + DefaultFieldAccessSite(TextLocation location, DefaultCallGraphNode callee, FieldReference field) { this.location = location; this.callee = callee; this.field = field; @@ -37,8 +37,8 @@ public class DefaultFieldAccessSite implements FieldAccessSite, Serializable { } @Override - public CallGraphNode getCallee() { - return null; + public DefaultCallGraphNode getCallee() { + return callee; } @Override diff --git a/core/src/main/java/org/teavm/callgraph/SerializableCallGraph.java b/core/src/main/java/org/teavm/callgraph/SerializableCallGraph.java new file mode 100644 index 000000000..52fd70d5b --- /dev/null +++ b/core/src/main/java/org/teavm/callgraph/SerializableCallGraph.java @@ -0,0 +1,57 @@ +/* + * Copyright 2017 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 org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; +import org.teavm.model.TextLocation; + +class SerializableCallGraph implements Serializable { + int[] nodeIndexes; + int[] fieldAccessIndexes; + int[] classAccessIndexes; + Node[] nodes; + CallSite[] callSites; + FieldAccess[] fieldAccessList; + ClassAccess[] classAccessList; + + static class Node implements Serializable { + MethodReference method; + int[] callSites; + int[] callerCallSites; + int[] fieldAccessSites; + int[] classAccessSites; + } + + static class CallSite implements Serializable { + TextLocation location; + int callee; + int caller; + } + + static class FieldAccess implements Serializable { + TextLocation location; + int callee; + FieldReference field; + } + + static class ClassAccess implements Serializable { + TextLocation location; + int callee; + String className; + } +} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java index d57da2e2e..3266dc91a 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java @@ -119,6 +119,8 @@ public class InProcessBuildStrategy implements TeaVMBuildStrategy { tool.setDebugInformationGenerated(debugInformationGenerated); tool.setSourceFilesCopied(sourceFilesCopied); + tool.setMinifying(false); + for (SourceFileProvider fileProvider : sourceFileProviders) { tool.addSourceFileProvider(fileProvider); } diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java b/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java index bca429acb..66abe2d94 100644 --- a/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java +++ b/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java @@ -118,6 +118,8 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote tool.setDebugInformationGenerated(request.debugInformationGenerated); tool.setSourceFilesCopied(request.sourceFilesCopied); + tool.setMinifying(false); + for (String sourceDirectory : request.sourceDirectories) { tool.addSourceFileProvider(new DirectorySourceFileProvider(new File(sourceDirectory))); } @@ -162,15 +164,10 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote @Override public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) { - if ((System.currentTimeMillis() - lastReportedTime) > 100) { - lastReportedTime = System.currentTimeMillis(); - try { - return callback.phaseStarted(phase, count); - } catch (RemoteException e) { - return TeaVMProgressFeedback.CANCEL; - } - } else { - return TeaVMProgressFeedback.CONTINUE; + try { + return callback.phaseStarted(phase, count); + } catch (RemoteException e) { + throw new RuntimeException(e); } } @@ -181,7 +178,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote try { return callback.progressReached(progress); } catch (RemoteException e) { - return TeaVMProgressFeedback.CANCEL; + throw new RuntimeException(e); } } else { return TeaVMProgressFeedback.CONTINUE;