diff --git a/core/src/main/java/com/ibm/wala/ipa/cha/ClassHierarchy.java b/core/src/main/java/com/ibm/wala/ipa/cha/ClassHierarchy.java index d5dca66fc8..efeb3670eb 100644 --- a/core/src/main/java/com/ibm/wala/ipa/cha/ClassHierarchy.java +++ b/core/src/main/java/com/ibm/wala/ipa/cha/ClassHierarchy.java @@ -10,6 +10,7 @@ */ package com.ibm.wala.ipa.cha; +import com.google.gson.Gson; import com.ibm.wala.classLoader.ArrayClass; import com.ibm.wala.classLoader.BytecodeClass; import com.ibm.wala.classLoader.ClassLoaderFactory; @@ -24,6 +25,7 @@ import com.ibm.wala.core.util.ref.CacheReference; import com.ibm.wala.core.util.ref.ReferenceCleanser; import com.ibm.wala.core.util.strings.Atom; +import com.ibm.wala.core.util.strings.StringStuff; import com.ibm.wala.core.util.warnings.Warning; import com.ibm.wala.core.util.warnings.Warnings; import com.ibm.wala.ipa.callgraph.AnalysisScope; @@ -706,6 +708,56 @@ private void recursiveStringify(Node n, StringBuilder buffer) { } } + /** + * Converts ClassHierarchy to a JSON String, mapping each class name to a list of subclass names + */ + public String toJson() { + // Initialize the map to store the pairs + HashMap> classNameToSubclassNames = new HashMap<>(); + + // Start the recursive function from the root node + helperToJson(root, classNameToSubclassNames); + + // Use Gson to convert the map to a JSON string + Gson gson = new Gson(); + return gson.toJson(classNameToSubclassNames); + } + + /** helper function to toJson that performs recursion to go through all of the DAG */ + private void helperToJson(Node n, HashMap> hash) { + String key = nodeToString(n); + + // If the node is already processed, return to avoid infinite recursion + if (hash.containsKey(key)) { + return; + } + + // Initialize a set to store the names of the subclasses + Set subclassNames = new HashSet<>(); + + // Process all children of the current node + if (n.children.size() > 0) { + Iterator children = n.getChildren(); + while (children.hasNext()) { + Node temp = children.next(); + helperToJson(temp, hash); + + String childKey = nodeToString(temp); + subclassNames.add(childKey); + } + } + + // Put the current node and its subclasses in the map + hash.put(key, subclassNames); + } + + /** + * Removed unnecessary part of Node by turning it into a String (Made for toJson and helperToJson) + */ + private String nodeToString(Node n) { + return StringStuff.jvmToBinaryName(n.getJavaClass().getName().toString()); + } + /** * Number the class hierarchy tree to support efficient subclass tests. After numbering the tree, * n1 is a child of n2 iff n2.left <= n1.left ^ n1.left <= n2.right. Described as "relative diff --git a/core/src/test/java/com/ibm/wala/core/tests/callGraph/ClassConstantTest.java b/core/src/test/java/com/ibm/wala/core/tests/callGraph/ClassConstantTest.java index c6d4b4cc9d..6c876d1610 100644 --- a/core/src/test/java/com/ibm/wala/core/tests/callGraph/ClassConstantTest.java +++ b/core/src/test/java/com/ibm/wala/core/tests/callGraph/ClassConstantTest.java @@ -14,8 +14,12 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.ibm.wala.classLoader.IClass; import com.ibm.wala.core.tests.util.TestConstants; import com.ibm.wala.core.tests.util.WalaTestCase; +import com.ibm.wala.core.util.strings.StringStuff; import com.ibm.wala.ipa.callgraph.AnalysisCacheImpl; import com.ibm.wala.ipa.callgraph.AnalysisOptions; import com.ibm.wala.ipa.callgraph.AnalysisScope; @@ -31,6 +35,10 @@ import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.CancelException; import java.io.IOException; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.Set; import org.junit.jupiter.api.Test; @@ -79,4 +87,30 @@ public void testClassConstants() // make sure call to hashCode from main assertTrue(cg.hasEdge(mainMethodNode, hashCodeNodes.iterator().next())); } + + @Test + public void classHierarchyToJson() throws ClassHierarchyException, IOException { + AnalysisScope scope = + CallGraphTestUtil.makeJ2SEAnalysisScope( + TestConstants.WALA_TESTDATA, CallGraphTestUtil.REGRESSION_EXCLUSIONS); + ClassHierarchy cha = ClassHierarchyFactory.make(scope); + Gson gson = new Gson(); + Type type = new TypeToken>>() {}.getType(); + String json = cha.toJson(); + System.err.println(json); + HashMap> list = gson.fromJson(json, type); + assertTrue(list.containsKey(nodeToString(cha.getRootClass()))); + + Set subclassNames = new HashSet<>(); + Iterator children = cha.computeSubClasses(cha.getRootClass().getReference()).iterator(); + while (children.hasNext()) { + String temp = nodeToString(children.next()); + subclassNames.add(temp); + } + assertTrue(subclassNames.containsAll(list.get(nodeToString(cha.getRootClass())))); + } + + private String nodeToString(IClass klass) { + return StringStuff.jvmToBinaryName(klass.getName().toString()); + } }