> methodCalls = new HashSet<>();
- private final JarMetadata jarMetadata;
+ private boolean isTestClass;
- public ClassVisitor(JavaClass jc, JarMetadata jarMetadata) {
+ public ClassVisitor(JavaClass jc, JarMetadata jarMetadata, boolean isTestClass) {
clazz = jc;
constants = new ConstantPoolGen(clazz.getConstantPool());
this.jarMetadata = jarMetadata;
+ this.isTestClass = isTestClass;
classReferenceFormat = "C:" + clazz.getClassName() + " %s";
}
@@ -72,18 +72,16 @@ public void visitJavaClass(JavaClass jc) {
public void visitConstantPool(ConstantPool constantPool) {
for (int i = 0; i < constantPool.getLength(); i++) {
Constant constant = constantPool.getConstant(i);
- if (constant == null)
- continue;
+ if (constant == null) continue;
if (constant.getTag() == 7) {
- String referencedClass =
- constantPool.constantToString(constant);
+ String referencedClass = constantPool.constantToString(constant);
}
}
}
public void visitMethod(Method method) {
MethodGen mg = new MethodGen(method, clazz.getClassName(), constants);
- MethodVisitor visitor = new MethodVisitor(mg, clazz, jarMetadata);
+ MethodVisitor visitor = new MethodVisitor(mg, clazz, jarMetadata, isTestClass);
methodCalls.addAll(visitor.start());
}
diff --git a/src/main/java/gr/gousiosg/javacg/stat/DynamicCallManager.java b/src/main/java/gr/gousiosg/javacg/stat/DynamicCallManager.java
index de8ce3b9..1f0fde1c 100644
--- a/src/main/java/gr/gousiosg/javacg/stat/DynamicCallManager.java
+++ b/src/main/java/gr/gousiosg/javacg/stat/DynamicCallManager.java
@@ -23,43 +23,37 @@
import java.util.regex.Pattern;
/**
- * {@link DynamicCallManager} provides facilities to retrieve information about
- * dynamic calls statically.
- *
- * Most of the time, call relationships are explicit, which allows to properly
- * build the call graph statically. But in the case of dynamic linking, i.e.
- * invokedynamic instructions, this relationship might be unknown
- * until the code is actually executed. Indeed, bootstrap methods are used to
- * dynamically link the code at first call. One can read details about the
- * invokedynamic
- * instruction to know more about this mechanism.
- *
- * Nested lambdas are particularly subject to such absence of concrete caller,
- * which lead us to produce method names like lambda$null$0, which
- * breaks the call graph. This information can however be retrieved statically
- * through the code of the bootstrap method called.
- *
- * In {@link #retrieveCalls(Method, JavaClass)}, we retrieve the (called,
- * caller) relationships by analyzing the code of the caller {@link Method}.
- * This information is then used in {@link #linkCalls(Method)} to rename the
- * called {@link Method} properly.
+ * {@link DynamicCallManager} provides facilities to retrieve information about dynamic calls
+ * statically.
+ *
+ *
Most of the time, call relationships are explicit, which allows to properly build the call
+ * graph statically. But in the case of dynamic linking, i.e. invokedynamic
+ * instructions, this relationship might be unknown until the code is actually executed. Indeed,
+ * bootstrap methods are used to dynamically link the code at first call. One can read details about
+ * the
+ * invokedynamic instruction to know more about this mechanism.
+ *
+ *
Nested lambdas are particularly subject to such absence of concrete caller, which lead us to
+ * produce method names like lambda$null$0, which breaks the call graph. This
+ * information can however be retrieved statically through the code of the bootstrap method called.
+ *
+ *
In {@link #retrieveCalls(Method, JavaClass)}, we retrieve the (called, caller) relationships
+ * by analyzing the code of the caller {@link Method}. This information is then used in {@link
+ * #linkCalls(Method)} to rename the called {@link Method} properly.
*
* @author Matthieu Vergne
*/
public class DynamicCallManager {
- private static Logger LOGGER = LoggerFactory.getLogger(DynamicCallManager.class);
-
- private static final Pattern BOOTSTRAP_CALL_PATTERN = Pattern
- .compile("invokedynamic\t(\\d+):\\S+ \\S+ \\(\\d+\\)");
+ private static final Pattern BOOTSTRAP_CALL_PATTERN =
+ Pattern.compile("invokedynamic\t(\\d+):\\S+ \\S+ \\(\\d+\\)");
private static final int CALL_HANDLE_INDEX_ARGUMENT = 1;
-
+ private static Logger LOGGER = LoggerFactory.getLogger(DynamicCallManager.class);
private final Map dynamicCallers = new HashMap<>();
/**
- * Retrieve dynamic call relationships based on the code of the provided
- * {@link Method}.
+ * Retrieve dynamic call relationships based on the code of the provided {@link Method}.
*
* @param method {@link Method} to analyze the code
* @param jc {@link JavaClass} info, which contains the bootstrap methods
@@ -92,7 +86,8 @@ public void retrieveCalls(Method method, JavaClass jc) {
private String getMethodNameFromHandleIndex(ConstantPool cp, int callIndex) {
ConstantMethodHandle handle = (ConstantMethodHandle) cp.getConstant(callIndex);
ConstantCP ref = (ConstantCP) cp.getConstant(handle.getReferenceIndex());
- ConstantNameAndType nameAndType = (ConstantNameAndType) cp.getConstant(ref.getNameAndTypeIndex());
+ ConstantNameAndType nameAndType =
+ (ConstantNameAndType) cp.getConstant(ref.getNameAndTypeIndex());
return nameAndType.getName(cp);
}
diff --git a/src/main/java/gr/gousiosg/javacg/stat/GraphUtils.java b/src/main/java/gr/gousiosg/javacg/stat/GraphUtils.java
deleted file mode 100644
index 83cd5a38..00000000
--- a/src/main/java/gr/gousiosg/javacg/stat/GraphUtils.java
+++ /dev/null
@@ -1,400 +0,0 @@
-package gr.gousiosg.javacg.stat;
-
-import gr.gousiosg.javacg.dyn.Pair;
-import gr.gousiosg.javacg.stat.support.IgnoredConstants;
-import gr.gousiosg.javacg.stat.support.JarMetadata;
-import gr.gousiosg.javacg.stat.support.coverage.ColoredNode;
-import org.apache.bcel.classfile.ClassParser;
-import org.jgrapht.Graph;
-import org.jgrapht.graph.DefaultDirectedGraph;
-import org.jgrapht.graph.DefaultEdge;
-import org.jgrapht.nio.Attribute;
-import org.jgrapht.nio.DefaultAttribute;
-import org.jgrapht.nio.dot.DOTExporter;
-import org.reflections.Reflections;
-import org.reflections.scanners.SubTypesScanner;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.*;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.*;
-import java.util.function.Function;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-/**
- * Provides graph utilities such as:
- * - Building a graph ({@link GraphUtils#buildGraph(Map)})
- * - Finding the reachability in a graph ({@link GraphUtils#reachability(Graph, String, Optional)})
- * - Finding the ancestry in a graph ({@link GraphUtils#ancestry(Graph, String, int)})
- */
-public class GraphUtils {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(GraphUtils.class);
-
- private static final String LABEL = "label";
- private static final String STYLE = "style";
- private static final String FILLCOLOR = "fillcolor";
- private static final String FILLED = "filled";
- private static final String NODE_DELIMITER = "\"";
-
- public static Graph reachability(Graph graph, String entrypoint, Optional maybeMaximumDepth) {
-
- if (!graph.containsVertex(entrypoint)) {
- LOGGER.error("---> " + entrypoint + "<---");
- LOGGER.error("The graph doesn't contain the vertex specified as the entry point!");
- throw new InputMismatchException("graph doesn't contain vertex " + entrypoint);
- }
-
- if (maybeMaximumDepth.isPresent() && (maybeMaximumDepth.get() < 0)) {
- LOGGER.error("Depth " + maybeMaximumDepth.get() + " must be greater than 0!");
- System.exit(1);
- }
-
- LOGGER.info("Starting reachability at entry point: " + entrypoint);
- maybeMaximumDepth.ifPresent(d -> LOGGER.info("Traversing to depth " + d));
-
- Graph subgraph = new DefaultDirectedGraph<>(DefaultEdge.class);
- int currentDepth = 0;
-
- Deque reachable = new ArrayDeque<>();
- reachable.push(entrypoint);
-
- Map subgraphNodes = new HashMap<>();
- Set seenBefore = new HashSet<>();
- Set nextLevel = new HashSet<>();
-
- while (!reachable.isEmpty()) {
-
- /* Stop once we've surpassed maximum depth */
- if (maybeMaximumDepth.isPresent() && (maybeMaximumDepth.get() < currentDepth)) {
- break;
- }
-
- while (!reachable.isEmpty()) {
- /* Visit reachable node */
- String source = reachable.pop();
- ColoredNode sourceNode = subgraphNodes.containsKey(source) ? subgraphNodes.get(source) : new ColoredNode(source);
-
- /* Keep track of who we've visited */
- seenBefore.add(source);
- if (!subgraphNodes.containsKey(source)) {
- subgraph.addVertex(sourceNode);
- subgraphNodes.put(source, sourceNode);
- }
-
- /* Check if we can add deeper edges or not */
- if (maybeMaximumDepth.isPresent() && (maybeMaximumDepth.get() == currentDepth)) {
- break;
- }
-
- graph.edgesOf(source).forEach(edge -> {
- String target = graph.getEdgeTarget(edge);
- ColoredNode targetNode = subgraphNodes.containsKey(target) ? subgraphNodes.get(target) : new ColoredNode(target);
-
- if (!subgraphNodes.containsKey(target)) {
- subgraphNodes.put(target, targetNode);
- subgraph.addVertex(targetNode);
- }
-
- if (graph.containsEdge(source, target) && !subgraph.containsEdge(sourceNode, targetNode)) {
- subgraph.addEdge(sourceNode, targetNode);
- }
-
- /* Have we visited this vertex before? */
- if (!seenBefore.contains(target)) {
- nextLevel.add(target);
- seenBefore.add(target);
- }
-
- });
- }
-
- currentDepth++;
- reachable.addAll(nextLevel);
- nextLevel.clear();
- }
-
- subgraphNodes.get(entrypoint).markEntryPoint();
- return subgraph;
- }
-
- public static Graph ancestry(Graph graph, String entrypoint, int ancestryDepth) {
-
- if (!graph.containsVertex(entrypoint)) {
- LOGGER.error("---> " + entrypoint + "<---");
- LOGGER.error("The graph doesn't contain the vertex specified as the entry point!");
- throw new InputMismatchException("graph doesn't contain vertex " + entrypoint);
- }
-
- LOGGER.info("Starting ancestry at entry point: " + entrypoint);
- LOGGER.info("Traversing to depth " + ancestryDepth);
-
- /* Book-keeping */
- Graph ancestry = new DefaultDirectedGraph<>(DefaultEdge.class);
- Map nodeMap = new HashMap<>();
- Deque parentsToInspect = new ArrayDeque<>();
- Set seenBefore = new HashSet<>();
- Set nextLevel = new HashSet<>();
-
- /* Add root node to ancestry graph */
- ColoredNode root = new ColoredNode(entrypoint);
- ancestry.addVertex(root);
- nodeMap.put(entrypoint, root);
- parentsToInspect.push(entrypoint);
-
- int currentDepth = 0;
- while (!parentsToInspect.isEmpty()) {
-
- if (ancestryDepth < currentDepth) {
- break;
- }
-
- /* Loop over all nodes that we haven't yet seen yet and are reachable at depth "currentDepth" */
- while (!parentsToInspect.isEmpty()) {
-
- /* Fetch next node */
- String child = parentsToInspect.pop();
- ColoredNode childNode = nodeMap.containsKey(child) ? nodeMap.get(child) : new ColoredNode(child);
-
- /* Keep track of who we've seen before */
- seenBefore.add(child);
- if (!nodeMap.containsKey(child)) {
- ancestry.addVertex(childNode);
- nodeMap.put(child, childNode);
- }
-
- graph.incomingEdgesOf(child).forEach(incomingEdge -> {
- String parent = graph.getEdgeSource(incomingEdge);
- ColoredNode parentNode = nodeMap.containsKey(parent) ? nodeMap.get(parent) : new ColoredNode(parent);
-
- if (!nodeMap.containsKey(parent)) {
- nodeMap.put(parent, parentNode);
- ancestry.addVertex(parentNode);
- }
-
- ancestry.addEdge(parentNode, childNode);
-
- /* Have we visited this vertex before? */
- if (!seenBefore.contains(parent)) {
- nextLevel.add(parent);
- seenBefore.add(parent);
- }
- });
- }
-
- currentDepth++;
- parentsToInspect.addAll(nextLevel);
- nextLevel.clear();
- }
-
- nodeMap.get(entrypoint).markEntryPoint();
- return ancestry;
- }
-
- public static void writeGraph(Graph graph, DOTExporter exporter, Optional maybeOutputName) {
- LOGGER.info("Attempting to store callgraph...");
-
- if (maybeOutputName.isEmpty()) {
- LOGGER.error("No output name specified!");
- return;
- }
-
- /* Write to .dot file in output directory */
- String path = JCallGraph.OUTPUT_DIRECTORY + maybeOutputName.get();
- try {
- Writer writer = new FileWriter(path);
- exporter.exportGraph(graph, writer);
- LOGGER.info("Graph written to " + path + "!");
- } catch (IOException e) {
- LOGGER.error("Unable to write callgraph to " + path);
- }
- }
-
- public static Graph staticCallgraph(List> jars) throws InputMismatchException {
- LOGGER.info("Beginning callgraph analysis...");
-
- /* Load JAR URLs */
- List urls = new ArrayList<>();
- try {
- for (Pair pair : jars) {
- URL url = new URL("jar:file:" + pair.first + "!/");
- urls.add(url);
- }
- } catch (MalformedURLException e) {
- LOGGER.error("Error loading URLs: " + e.getMessage());
- throw new InputMismatchException("Couldn't load provided JARs");
- }
-
- if (urls.isEmpty()) {
- LOGGER.error("No URLs to scan!");
- throw new InputMismatchException("There are no URLs to scan!");
- }
-
- /* Setup infrastructure for analysis */
- URLClassLoader cl = URLClassLoader.newInstance(urls.toArray(new URL[0]), ClassLoader.getSystemClassLoader());
- Reflections reflections = new Reflections(cl, new SubTypesScanner(false));
- JarMetadata jarMetadata = new JarMetadata(cl, reflections);
-
- /* Store method calls (caller -> receiver) */
- Map> calls = new HashMap<>();
-
- for (Pair pair : jars) {
- String jarPath = pair.first;
- File file = pair.second;
-
- try (JarFile jarFile = new JarFile(file)) {
- LOGGER.info("Analyzing: " + jarFile.getName());
- Stream entries = enumerationAsStream(jarFile.entries());
-
- Function getClassVisitor =
- (ClassParser cp) -> {
- try {
- return new ClassVisitor(cp.parse(), jarMetadata);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- };
-
- /* Analyze each jar entry to find callgraph */
- entries.flatMap(e -> {
- if (e.isDirectory() || !e.getName().endsWith(".class"))
- return Stream.of();
-
- /* Ignore specified JARs */
- if (shouldIgnoreEntry(e.getName().replace("/", "."))) {
- return Stream.of();
- } else {
- LOGGER.info("Inspecting " + e.getName());
- }
-
- ClassParser cp = new ClassParser(jarPath, e.getName());
- return getClassVisitor.apply(cp).start().methodCalls().stream();
- }).forEach(p -> {
- /* Create edges between nodes */
- calls.putIfAbsent((p.first), new HashSet<>());
- calls.get(p.first).add(p.second);
- });
-
- } catch (IOException e) {
- LOGGER.error("Error when analyzing JAR \"" + jarPath + "\": + e.getMessage()");
- e.printStackTrace();
- }
- }
-
- /* Convert calls into a graph */
- Graph graph = buildGraph(calls);
-
- /* Prune bridge methods from graph */
- jarMetadata.getBridgeMethods().forEach(bridgeMethod -> {
-
- /* Fetch the bridge method and make sure it has exactly one outgoing edge */
- String bridgeNode = formatNode(bridgeMethod);
- Optional maybeEdge = graph.outgoingEdgesOf(bridgeNode).stream().findFirst();
-
- if (graph.outDegreeOf(bridgeNode) != 1 || maybeEdge.isEmpty()) {
-
- graph.outgoingEdgesOf(bridgeNode).stream().forEach(e -> {
- LOGGER.error("\t" + graph.getEdgeSource(e) + " -> " + graph.getEdgeTarget(e));
- });
- LOGGER.error("Found a bridge method that doesn't have exactly 1 outgoing edge: " + bridgeMethod + " : " + graph.outDegreeOf(bridgeNode));
- System.exit(1);
- }
-
- /* Fetch the bridge method's target */
- String bridgeTarget = graph.getEdgeTarget(maybeEdge.get());
-
- /* Redirect all edges from the bridge method to its target */
- graph.incomingEdgesOf(bridgeNode).forEach(edge -> {
- String sourceNode = graph.getEdgeSource(edge);
- graph.addEdge(sourceNode, bridgeTarget);
- });
-
- /* Remove the bridge method from the graph */
- graph.removeVertex(bridgeNode);
- });
-
- return graph;
- }
-
- private static Graph buildGraph(Map> methodCalls) throws InputMismatchException {
- if (methodCalls.keySet().isEmpty()) {
- throw new InputMismatchException("There is no call graph to look at!");
- }
-
- /* initialize the graph */
- Graph graph = new DefaultDirectedGraph<>(DefaultEdge.class);
-
- /* fill the graph with vertices and edges */
- methodCalls.keySet().forEach(source -> {
- String sourceNode = formatNode(source);
- putIfAbsent(graph, sourceNode);
-
- methodCalls.get(source).forEach(destination -> {
- String destinationNode = formatNode(destination);
- putIfAbsent(graph, destinationNode);
- graph.addEdge(sourceNode, destinationNode);
- });
- });
-
- return graph;
- }
-
- private static void putIfAbsent(Graph graph, String vertex) {
- if (!graph.containsVertex(vertex)) {
- graph.addVertex(vertex);
- }
- }
-
- private static boolean shouldIgnoreEntry(String entry) {
- return IgnoredConstants.IGNORED_CALLING_PACKAGES.stream()
- .anyMatch(entry::startsWith);
- }
-
- public static String formatNode(String node) {
- return NODE_DELIMITER + node + NODE_DELIMITER;
- }
-
- public static Stream enumerationAsStream(Enumeration e) {
- return StreamSupport.stream(
- Spliterators.spliteratorUnknownSize(
- new Iterator() {
- public T next() {
- return e.nextElement();
- }
-
- public boolean hasNext() {
- return e.hasMoreElements();
- }
- },
- Spliterator.ORDERED), false);
- }
-
- public static DOTExporter defaultExporter() {
- DOTExporter exporter = new DOTExporter<>(id -> id);
- exporter.setVertexAttributeProvider((v) -> {
- Map map = new LinkedHashMap<>();
- map.put(LABEL, DefaultAttribute.createAttribute(v));
- return map;
- });
- return exporter;
- }
-
- public static DOTExporter coloredExporter() {
- DOTExporter exporter = new DOTExporter<>(ColoredNode::getLabel);
- exporter.setVertexAttributeProvider((v) -> {
- Map map = new LinkedHashMap<>();
- map.put(LABEL, DefaultAttribute.createAttribute(v.getLabel()));
- map.put(STYLE, DefaultAttribute.createAttribute(FILLED));
- map.put(FILLCOLOR, DefaultAttribute.createAttribute(v.getColor()));
- return map;
- });
- return exporter;
- }
-
-}
diff --git a/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java b/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
index 38818d5f..c8bacbaf 100644
--- a/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
+++ b/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
@@ -28,123 +28,535 @@
package gr.gousiosg.javacg.stat;
-import gr.gousiosg.javacg.stat.support.Arguments;
-import gr.gousiosg.javacg.stat.support.coverage.ColoredNode;
-import gr.gousiosg.javacg.stat.support.coverage.CoverageStatistics;
-import gr.gousiosg.javacg.stat.support.coverage.JacocoCoverage;
+import edu.uic.bitslab.callgraph.GetBest;
+import gr.gousiosg.javacg.dyn.Pair;
+import gr.gousiosg.javacg.stat.coverage.ColoredNode;
+import gr.gousiosg.javacg.stat.coverage.CoverageStatistics;
+import gr.gousiosg.javacg.stat.coverage.JacocoCoverage;
+import gr.gousiosg.javacg.stat.graph.*;
+import gr.gousiosg.javacg.stat.support.BuildArguments;
+import gr.gousiosg.javacg.stat.support.GitArguments;
+import gr.gousiosg.javacg.stat.support.RepoTool;
+import gr.gousiosg.javacg.stat.support.TestArguments;
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.generic.Type;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultEdge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.util.InputMismatchException;
-import java.util.Optional;
+import java.io.*;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+
+import static java.util.Map.entry;
/**
- * Constructs a callgraph out of a JAR archive. Can combine multiple archives
- * into a single call graph.
+ * Constructs a callgraph out of a JAR archive. Can combine multiple archives into a single call
+ * graph.
*
* @author Georgios Gousios
* @author Will Cygan
+ * @author Alekh Meka
*/
public class JCallGraph {
- private static final Logger LOGGER = LoggerFactory.getLogger(JCallGraph.class);
-
- public static final String OUTPUT_DIRECTORY = "./output/";
- private static final String REACHABILITY = "reachability";
- private static final String COVERAGE = "coverage";
- private static final String ANCESTRY = "ancestry";
- private static final String DELIMITER = "-";
- private static final String DOT_SUFFIX = ".dot";
- private static final String CSV_SUFFIX = ".csv";
-
- public static void main(String[] args) {
- try {
- LOGGER.info("Starting java-cg!");
- Arguments arguments = new Arguments(args);
- Graph graph = GraphUtils.staticCallgraph(arguments.getJars());
- JacocoCoverage jacocoCoverage = new JacocoCoverage(arguments.maybeCoverage());
-
- /* Should we store the graph in a file? */
- if (arguments.maybeOutput().isPresent()) {
- GraphUtils.writeGraph(graph, GraphUtils.defaultExporter(), arguments.maybeOutput().map(JCallGraph::asDot));
+ public static final String OUTPUT_DIRECTORY = "./output/";
+ private static final Logger LOGGER = LoggerFactory.getLogger(JCallGraph.class);
+ private static final String REACHABILITY = "reachability";
+ private static final String COVERAGE = "coverage";
+ private static final String ANCESTRY = "ancestry";
+ private static final String DELIMITER = "-";
+ private static final String DOT_SUFFIX = ".dot";
+ private static final String CSV_SUFFIX = ".csv";
+ private static final String SER_SUFFIX = ".ser";
+
+ public static void main(String[] args) {
+ try {
+ LOGGER.info("Starting java-cg!");
+ switch(args[0]){
+ case "manual-test": {
+ manualMain(args);
+ return;
+ }
+ case "git":{
+ GitArguments arguments = new GitArguments(args);
+ RepoTool rt = maybeObtainTool(arguments);
+ rt.cloneRepo();
+ rt.applyPatch();
+ rt.buildJars();
+ break;
+ }
+ case "build": {
+ // Build and serialize a staticcallgraph object with jar files provided
+ BuildArguments arguments = new BuildArguments(args);
+ StaticCallgraph callgraph = StaticCallgraph.build(arguments);
+ callgraph.JarEntry=arguments.getJars().get(0).first;
+ maybeSerializeStaticCallGraph(callgraph, arguments);
+ break;
+ }
+ case "buildyaml":{
+ DumperOptions options = new DumperOptions();
+ options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ options.setPrettyFlow(true);
+ Yaml yaml = new Yaml(options);
+
+ JarInputStream jarFileStream = new JarInputStream(new FileInputStream(args[1]));
+ JarFile jarFile = new JarFile(args[1]);
+
+ ArrayList listOfAllClasses = getAllClassesFromJar(jarFileStream);
+ ArrayList> nameEntryList = new ArrayList<>();
+ for (JarEntry entry : listOfAllClasses)
+ nameEntryList.addAll(fetchAllMethodSignaturesForyaml(jarFile,entry));
+ ArrayList