Skip to content

Commit

Permalink
Add ability to only analyze a subset of standard lib from running JVM (
Browse files Browse the repository at this point in the history
  • Loading branch information
msridhar authored Jul 5, 2023
1 parent 248dbe2 commit 976bdbf
Show file tree
Hide file tree
Showing 18 changed files with 123 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,13 @@ public void processScopeDefLine(AnalysisScope scope, ClassLoader javaLoader, Str
} else if ("loaderImpl".equals(entryType)) {
scope.setLoaderImpl(walaLoader, entryPathname);
} else if ("stdlib".equals(entryType)) {
String[] stdlibs = WalaProperties.getJ2SEJarFiles();
boolean justBase = entryPathname.equals("base");
String[] stdlibs = WalaProperties.getJDKLibraryFiles(justBase);
for (String stdlib : stdlibs) {
scope.addToScope(walaLoader, new JarFile(stdlib, false));
}
} else if ("jdkModule".equals(entryType)) {
scope.addJDKModuleToScope(entryPathname);
} else if (!handleInSubclass(scope, walaLoader, language, entryType, entryPathname)) {
Assertions.UNREACHABLE();
}
Expand All @@ -238,13 +241,29 @@ protected boolean handleInSubclass(
}

/**
* Creates an AnalysisScope containing only the JDK standard libraries. If no explicit JDK library
* paths are given in the WALA properties file, the scope contains all library modules for the
* running JVM.
*
* @param exclusionsFile file holding class hierarchy exclusions. may be null
* @throws IllegalStateException if there are problmes reading wala properties
*/
public AnalysisScope makePrimordialScope(File exclusionsFile) throws IOException {
return readJavaScope(BASIC_FILE, exclusionsFile, MY_CLASSLOADER);
}

/**
* Creates an AnalysisScope containing only the JDK standard libraries. If no explicit JDK library
* paths are given in the WALA properties file, the scope contains only the {@code java.base}
* module for the running JVM.
*
* @param exclusionsFile file holding class hierarchy exclusions. may be null
* @throws IllegalStateException if there are problmes reading wala properties
*/
public AnalysisScope makeBasePrimordialScope(File exclusionsFile) throws IOException {
return readJavaScope("primordial-base.txt", exclusionsFile, MY_CLASSLOADER);
}

/**
* @param classPath class path to analyze, delimited by {@link File#pathSeparator}
* @param exclusionsFile file holding class hierarchy exclusions. may be null
Expand Down
16 changes: 16 additions & 0 deletions core/src/main/java/com/ibm/wala/ipa/callgraph/AnalysisScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.PlatformUtil;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
Expand All @@ -40,6 +41,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.NotSerializableException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -194,6 +197,19 @@ public void addClassFileToScope(ClassLoaderReference loader, File file)
s.add(new ClassFileModule(file, null));
}

/**
* Adds a module from the Java standard library to the analysis scope.
*
* @param moduleName the name of the module, e.g., {@code "java.sql"}
* @throws IOException if a module by that name cannot successfully be loaded
*/
public void addJDKModuleToScope(String moduleName) throws IOException {
Path path = PlatformUtil.getPathForJDKModule(moduleName);
if (!Files.exists(path)) {
throw new IOException("cannot find jmod file for module " + moduleName + ", tried " + path);
}
addToScope(ClassLoaderReference.Primordial, new JarFile(path.toString(), false));
}
/**
* Add a jar file to the scope via an {@link InputStream}. NOTE: The InputStream should *not* be a
* {@link java.util.jar.JarInputStream}; it should be a regular {@link InputStream} for the raw
Expand Down
26 changes: 20 additions & 6 deletions core/src/main/java/com/ibm/wala/properties/WalaProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,29 +51,43 @@ public final class WalaProperties {
/**
* Determine the classpath noted in wala.properties for J2SE standard libraries
*
* <p>If wala.properties cannot be loaded, returns jar files in boot classpath.
* <p>If wala.properties cannot be loaded, returns library files in boot classpath.
*
* @throws IllegalStateException if jar files cannot be discovered
* @see PlatformUtil#getBootClassPathJars()
* @throws IllegalStateException if library files cannot be discovered
* @see PlatformUtil#getJDKModules(boolean)
*/
public static String[] getJ2SEJarFiles() {
return getJDKLibraryFiles(false);
}

/**
* Determine the classpath noted in wala.properties for J2SE standard libraries
*
* <p>If wala.properties cannot be loaded, returns library files in boot classpath.
*
* @param justBase Only relevant if wala.properties cannot be loaded. If {@code true}, only
* returns the {@code java.base} library from boot classpath. Otherwise, returns all library
* modules from boot classpath.
* @see PlatformUtil#getJDKModules(boolean)
*/
public static String[] getJDKLibraryFiles(boolean justBase) {
Properties p = null;
try {
p = WalaProperties.loadProperties();
} catch (WalaException e) {
return PlatformUtil.getBootClassPathJars();
return PlatformUtil.getJDKModules(justBase);
}

String dir = p.getProperty(WalaProperties.J2SE_DIR);
if (dir == null) {
return PlatformUtil.getBootClassPathJars();
return PlatformUtil.getJDKModules(justBase);
}
if (!new File(dir).isDirectory()) {
System.err.println(
"WARNING: java_runtime_dir "
+ dir
+ " in wala.properties is invalid. Using boot class path instead.");
return PlatformUtil.getBootClassPathJars();
return PlatformUtil.getJDKModules(justBase);
}
return getJarsInDirectory(dir);
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/resources/primordial-base.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ public void testIO()
throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException {
AnalysisScope scope =
CallGraphTestUtil.makeJ2SEAnalysisScope(
"primordial.txt", CallGraphTestUtil.REGRESSION_EXCLUSIONS);
"primordial-base.txt", CallGraphTestUtil.REGRESSION_EXCLUSIONS);
ClassHierarchy cha = ClassHierarchyFactory.make(scope);
Iterable<Entrypoint> entrypoints = makePrimordialPublicEntrypoints(cha, "java/io");
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public void testKawaChess()
CallGraph CG =
testKawa(
new ResourceJarFileModule(getClass().getClassLoader().getResource("kawachess.jar")),
"baseAndDesktop.txt",
"main");

Set<CGNode> status =
Expand Down Expand Up @@ -89,6 +90,7 @@ public void testKawaTest()
CallGraph CG =
testKawa(
new ResourceJarFileModule(getClass().getClassLoader().getResource("kawatest.jar")),
"base.txt",
"test");

Set<CGNode> nodes = getNodes(CG, "Ltest", "plusish$V", "(Lgnu/lists/LList;)Ljava/lang/Object;");
Expand All @@ -113,14 +115,15 @@ private static Set<CGNode> getNodes(CallGraph CG, String cls, String method, Str
* #MAX_ITERATIONS} runs of the outer fixed point loop of call graph construction.
*
* @param code the module
* @param scopeFile the scope file to use
* @param main entrypoint method for the call graph
* @return the call graph
*/
private CallGraph testKawa(Module code, String main)
private CallGraph testKawa(Module code, String scopeFile, String main)
throws ClassHierarchyException, IllegalArgumentException, IOException, SecurityException {
AnalysisScope scope =
CallGraphTestUtil.makeJ2SEAnalysisScope(
"base.txt", CallGraphTestUtil.REGRESSION_EXCLUSIONS_FOR_GUI);
scopeFile, CallGraphTestUtil.REGRESSION_EXCLUSIONS_FOR_GUI);
scope.addToScope(
ClassLoaderReference.Application,
new ResourceJarFileModule(getClass().getClassLoader().getResource("kawa.jar")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,21 @@ public void testJarInputStream() throws IOException, ClassHierarchyException {
TypeReference.findOrCreate(
ClassLoaderReference.Application, "Lorg/apache/bcel/verifier/Verifier")));
}

@Test
public void testBaseScope() throws IOException, ClassHierarchyException {
AnalysisScope scope =
AnalysisScopeReader.instance.readJavaScope(
"primordial-base.txt", null, AnalysisScopeTest.class.getClassLoader());
ClassHierarchy cha = ClassHierarchyFactory.make(scope);
Assert.assertNotNull(
"couldn't find expected class",
cha.lookupClass(
TypeReference.findOrCreate(ClassLoaderReference.Application, "Ljava/util/ArrayList")));
Assert.assertNull(
"found unexpected class",
cha.lookupClass(
TypeReference.findOrCreate(
ClassLoaderReference.Application, "Ljava/awt/AlphaComposite")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static void beforeClass() throws Exception {
TestConstants.WALA_TESTDATA,
new FileProvider().getFile("J2SEClassHierarchyExclusions.txt"),
MY_CLASSLOADER);

scope.addJDKModuleToScope("java.sql");
ClassLoaderFactory factory = new ClassLoaderFactoryImpl(scope.getExclusions());

try {
Expand Down
2 changes: 1 addition & 1 deletion core/src/test/resources/base.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Primordial,Java,stdlib,none
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
3 changes: 3 additions & 0 deletions core/src/test/resources/baseAndDesktop.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Primordial,Java,stdlib,base
Primordial,Java,jdkModule,java.desktop
Primordial,Java,jarFile,primordial.jar.model
2 changes: 1 addition & 1 deletion core/src/test/resources/hello.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Primordial,Java,stdlib,none
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
Application,Java,classFile,hello/Hello.class
Application,Java,sourceFile,hello/Hello.java
2 changes: 1 addition & 1 deletion core/src/test/resources/ocaml_compr.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Primordial,Java,stdlib,none
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
Application,Java,jarFile,compr
2 changes: 1 addition & 1 deletion core/src/test/resources/ocaml_hello_hash.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Primordial,Java,stdlib,none
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
Application,Java,jarFile,hello_hash.jar
2 changes: 1 addition & 1 deletion core/src/test/resources/wala.testdata.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Primordial,Java,stdlib,none
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
Application,Java,jarFile,com.ibm.wala.core.testdata_1.0.0.jar
2 changes: 1 addition & 1 deletion core/src/testFixtures/resources/JLex.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Primordial,Java,stdlib,none
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
Application,Java,jarFile,JLex.jar
2 changes: 1 addition & 1 deletion core/src/testFixtures/resources/bcel.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Primordial,Java,stdlib,none
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
Application,Java,jarFile,bcel-5.2.jar
2 changes: 1 addition & 1 deletion core/src/testFixtures/resources/java_cup.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Primordial,Java,stdlib,none
Primordial,Java,stdlib,base
Primordial,Java,jarFile,primordial.jar.model
Application,Java,jarFile,java-cup-11a.jar
55 changes: 30 additions & 25 deletions util/src/main/java/com/ibm/wala/util/PlatformUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@
*/
package com.ibm.wala.util;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -47,38 +46,44 @@ public static boolean onIKVM() {
}

/**
* get the jars in the boot classpath. TODO test on more JVMs
* Gets the standard JDK modules shipped with the running JDK
*
* @throws IllegalStateException if boot classpath cannot be found
* @param justBase if {@code true}, only include the file corresponding to the {@code java.base}
* module
* @return array of {@code .jmod} module files
* @throws IllegalStateException if modules cannot be found
*/
public static String[] getBootClassPathJars() {
String classpath = null;
String javaVersion = System.getProperty("java.specification.version");
if (!javaVersion.equals("1.8")) {
// java11 support for jmod files
public static String[] getJDKModules(boolean justBase) {
List<String> jmods;
if (justBase) {
Path basePath = Paths.get(System.getProperty("java.home"), "jmods", "java.base.jmod");
if (!Files.exists(basePath)) {
throw new IllegalStateException("could not find java.base.jmod");
}
jmods = List.of(basePath.toString());
} else {
try (Stream<Path> stream = Files.list(Paths.get(System.getProperty("java.home"), "jmods"))) {
classpath =
String.join(
File.pathSeparator, stream.map(Path::toString).collect(Collectors.toList()));
jmods =
stream
.map(Path::toString)
.filter(p -> p.endsWith(".jmod"))
.collect(Collectors.toList());
} catch (IOException e) {
throw new IllegalStateException(e);
}
} else {
classpath = System.getProperty("sun.boot.class.path");
}
if (classpath == null) {
throw new IllegalStateException("could not find boot classpath");
}
String[] jars = classpath.split(File.pathSeparator);
ArrayList<String> result = new ArrayList<>();
for (String jar : jars) {
if ((jar.endsWith(".jar") || jar.endsWith(".jmod")) && new File(jar).exists()) {
result.add(jar);
}
}
return result.toArray(new String[0]);
return jmods.toArray(new String[0]);
}

/**
* Returns the filesystem path for a JDK module from the running JVM
*
* @param moduleName name of the module, e.g., {@code "java.sql"}
* @return path to the module
*/
public static Path getPathForJDKModule(String moduleName) {
return Paths.get(System.getProperty("java.home"), "jmods", moduleName + ".jmod");
}
/** @return the major version of the Java runtime we are running on. */
public static int getJavaRuntimeVersion() {
String version = System.getProperty("java.version");
Expand Down

0 comments on commit 976bdbf

Please sign in to comment.