diff --git a/core/build.gradle.kts b/core/build.gradle.kts index b505e41d7b..f23e4c12eb 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -59,6 +59,7 @@ dependencies { api(projects.util) { because("public interface CallGraph extends interface NumberedGraph") } testFixturesImplementation(libs.ant) testFixturesImplementation(libs.junit.platform.launcher) + implementation(libs.gson) testImplementation(libs.assertj.core) testImplementation(libs.hamcrest) testRuntimeOnly(sourceSets["testSubjects"].output.classesDirs) diff --git a/core/src/main/java/com/ibm/wala/ipa/callgraph/AnalysisScope.java b/core/src/main/java/com/ibm/wala/ipa/callgraph/AnalysisScope.java index 5a36b459f2..b3e740925f 100644 --- a/core/src/main/java/com/ibm/wala/ipa/callgraph/AnalysisScope.java +++ b/core/src/main/java/com/ibm/wala/ipa/callgraph/AnalysisScope.java @@ -10,6 +10,7 @@ */ package com.ibm.wala.ipa.callgraph; +import com.google.gson.Gson; import com.ibm.wala.classLoader.ArrayClassLoader; import com.ibm.wala.classLoader.BinaryDirectoryTreeModule; import com.ibm.wala.classLoader.ClassFileModule; @@ -345,6 +346,42 @@ public String toString() { return result.toString(); } + /** + * An AnalysisScope is converted to a JSON formatted variable using the loaders and exclusions + * hierarchy using ToJson. (Loaders) Primordial, Extension, Application, and Synthetic are the + * loaders keys; each one contains an arraylist of Strings. The exclusions contains an arraylist + * of strings. + * + * @return json variable containing contents of the AnalysisScope + */ + public String toJson() { + LinkedHashMap res = new LinkedHashMap<>(); + LinkedHashMap> loaders = new LinkedHashMap<>(); + for (ClassLoaderReference loader : loadersByName.values()) { + ArrayList arr = new ArrayList<>(); + for (Module m : getModules(loader)) { + arr.add(m.toString()); + } + loaders.put(loader.getName().toString(), arr); + } + res.put("Loaders", loaders); + ArrayList arr2 = new ArrayList<>(); + if (getExclusions() == null) { + res.put("Exclusions", arr2); + } else { + String[] exclusions = getExclusions().toString().split("\\|"); + for (int i = 0; i < exclusions.length; i++) { + String word = exclusions[i]; + word = word.replace("(", ""); + word = word.replace(")", ""); + arr2.add(word); + } + res.put("Exclusions", arr2); + } + Gson gson = new Gson(); + return gson.toJson(res); + } + /** * @return a String that describes exclusions from the analysis scope. */ diff --git a/core/src/test/java/com/ibm/wala/core/tests/cha/AnalysisScopeTest.java b/core/src/test/java/com/ibm/wala/core/tests/cha/AnalysisScopeTest.java index 151b32dc4f..8c008c1703 100644 --- a/core/src/test/java/com/ibm/wala/core/tests/cha/AnalysisScopeTest.java +++ b/core/src/test/java/com/ibm/wala/core/tests/cha/AnalysisScopeTest.java @@ -1,8 +1,14 @@ package com.ibm.wala.core.tests.cha; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import com.ibm.wala.core.tests.util.TestConstants; import com.ibm.wala.core.util.config.AnalysisScopeReader; import com.ibm.wala.core.util.io.FileProvider; @@ -10,12 +16,21 @@ import com.ibm.wala.ipa.cha.ClassHierarchy; import com.ibm.wala.ipa.cha.ClassHierarchyException; import com.ibm.wala.ipa.cha.ClassHierarchyFactory; +import com.ibm.wala.properties.WalaProperties; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.TypeReference; import java.io.FileInputStream; import java.io.IOException; +import java.lang.reflect.Type; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; import org.junit.jupiter.api.Test; public class AnalysisScopeTest { @@ -55,4 +70,63 @@ public void testBaseScope() throws IOException, ClassHierarchyException { ClassLoaderReference.Application, "Ljava/awt/AlphaComposite")), "found unexpected class"); } + + @Test + public void testToJson() throws IOException { + AnalysisScope scope = + AnalysisScopeReader.instance.readJavaScope( + TestConstants.WALA_TESTDATA, + new FileProvider().getFile("GUIExclusions.txt"), + AnalysisScopeTest.class.getClassLoader()); + Gson gson = new Gson(); + Type type = new TypeToken>() {}.getType(); + LinkedHashMap map = gson.fromJson(scope.toJson(), type); + assertEquals( + List.of("java\\/awt\\/.*", "javax\\/swing\\/.*", "sun\\/awt\\/.*", "sun\\/swing\\/.*"), + map.get("Exclusions")); + + @SuppressWarnings("unchecked") + Map> loaders = (Map>) map.get("Loaders"); + Set loaderKeys = + new HashSet<>(List.of("Primordial", "Extension", "Application", "Synthetic")); + assertEquals(loaders.keySet(), loaderKeys); + assertEquals(2, loaders.get("Primordial").size()); + assertThat(loaders.get("Primordial"), hasItem("Nested Jar File:primordial.jar.model")); + assertEquals(1, loaders.get("Application").size()); + assertThat( + loaders.get("Application").get(0), containsString("com.ibm.wala.core.testdata_1.0.0.jar")); + assertEquals(0, loaders.get("Extension").size()); + assertEquals(0, loaders.get("Synthetic").size()); + } + + @Test + public void testToJsonCustom() throws IOException { + AnalysisScope scope; + scope = AnalysisScope.createJavaAnalysisScope(); + String[] stdlibs = WalaProperties.getJ2SEJarFiles(); + Arrays.sort(stdlibs); + for (String stdlib : stdlibs) { + scope.addToScope(ClassLoaderReference.Primordial, new JarFile(stdlib)); + scope.addToScope(ClassLoaderReference.Application, new JarFile(stdlib)); + } + scope.setExclusions(null); + Gson gson = new Gson(); + Type type = new TypeToken>() {}.getType(); + LinkedHashMap map = gson.fromJson(scope.toJson(), type); + assertEquals(List.of(), map.get("Exclusions")); + + @SuppressWarnings("unchecked") + Map> loaders = (Map>) map.get("Loaders"); + Set loaderKeys = + new HashSet<>(List.of("Primordial", "Extension", "Application", "Synthetic")); + assertEquals(loaders.keySet(), loaderKeys); + assertEquals(stdlibs.length, loaders.get("Primordial").size()); + assertEquals(stdlibs.length, loaders.get("Application").size()); + for (int i = 0; i < stdlibs.length; i++) { + assertThat(loaders.get("Primordial"), hasItem("JarFileModule:" + stdlibs[i])); + assertThat(loaders.get("Application"), hasItem("JarFileModule:" + stdlibs[i])); + } + assertEquals(0, loaders.get("Extension").size()); + assertEquals(0, loaders.get("Synthetic").size()); + } }