diff --git a/com.ibm.wala.cast.java.test.data/.classpath b/com.ibm.wala.cast.java.test.data/.classpath index e8ea977a6..4f83b2397 100644 --- a/com.ibm.wala.cast.java.test.data/.classpath +++ b/com.ibm.wala.cast.java.test.data/.classpath @@ -2,6 +2,6 @@ - + diff --git a/com.ibm.wala.cast.java.test.data/.settings/org.eclipse.jdt.core.prefs b/com.ibm.wala.cast.java.test.data/.settings/org.eclipse.jdt.core.prefs index 4c8bbd5ba..d5a912066 100644 --- a/com.ibm.wala.cast.java.test.data/.settings/org.eclipse.jdt.core.prefs +++ b/com.ibm.wala.cast.java.test.data/.settings/org.eclipse.jdt.core.prefs @@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -106,7 +106,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disa org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.source=1.8 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 diff --git a/com.ibm.wala.core.testdata/src/dynamicCG/CallbacksMainClass.java b/com.ibm.wala.core.testdata/src/dynamicCG/CallbacksMainClass.java new file mode 100644 index 000000000..ad61873f6 --- /dev/null +++ b/com.ibm.wala.core.testdata/src/dynamicCG/CallbacksMainClass.java @@ -0,0 +1,33 @@ +package dynamicCG; + +import java.util.HashSet; +import java.util.Set; + +public class CallbacksMainClass { + + private static CallbacksMainClass instance; + + static { + callSomethingStatic(); + } + + public static void main(String[] args) { + Set junk = new HashSet(); + junk.add(instance); + System.err.println(junk.iterator().next().toString()); + } + + private static void callSomethingStatic() { + instance = new CallbacksMainClass(); + } + + @Override + public String toString() { + return callSomething(); + } + + private String callSomething() { + return "string"; + } + +} diff --git a/com.ibm.wala.core.tests/dat/Java60RegressionExclusions.txt b/com.ibm.wala.core.tests/dat/Java60RegressionExclusions.txt index f2bc72dea..0a868f5a8 100644 --- a/com.ibm.wala.core.tests/dat/Java60RegressionExclusions.txt +++ b/com.ibm.wala.core.tests/dat/Java60RegressionExclusions.txt @@ -1,5 +1,3 @@ -apple\/.* -com\/apple\/.* java\/awt\/.* javax\/swing\/.* sun\/awt\/.* @@ -13,6 +11,8 @@ com\/ibm\/security\/.* org\/apache\/xerces\/.* dalvik\/.* java\/io\/ObjectStreamClass* +apple\/.* +com\/apple\/.* jdk\/.* org\/omg\/.* org\/w3c\/.* diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTest.java index 559615a66..94fd48710 100644 --- a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTest.java +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTest.java @@ -32,13 +32,11 @@ import com.ibm.wala.util.CancelException; public class DynamicCallGraphTest extends DynamicCallGraphTestBase { private static String testJarLocation = getClasspathEntry("com.ibm.wala.core.testdata"); - - private static String testMain = "dynamicCG.MainClass"; - - private CallGraph staticCG(String exclusionsFile) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException { + + private CallGraph staticCG(String mainClass, String exclusionsFile) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException { AnalysisScope scope = CallGraphTestUtil.makeJ2SEAnalysisScope(TestConstants.WALA_TESTDATA, exclusionsFile != null? exclusionsFile: CallGraphTestUtil.REGRESSION_EXCLUSIONS); ClassHierarchy cha = ClassHierarchy.make(scope); - Iterable entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha, "LdynamicCG/MainClass"); + Iterable entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha, mainClass); AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints); return CallGraphTestUtil.buildZeroOneCFA(options, new AnalysisCache(), cha, scope, false); } @@ -46,16 +44,24 @@ public class DynamicCallGraphTest extends DynamicCallGraphTestBase { @Test public void testGraph() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassHierarchyException, CancelException, InterruptedException { instrument(testJarLocation); - run(testMain, null); - CallGraph staticCG = staticCG(null); + run("dynamicCG.MainClass", null); + CallGraph staticCG = staticCG("LdynamicCG/MainClass", null); + checkEdges(staticCG); + } + + @Test + public void testCallbacks() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassHierarchyException, CancelException, InterruptedException { + instrument(testJarLocation); + run("dynamicCG.CallbacksMainClass", null); + CallGraph staticCG = staticCG("LdynamicCG/CallbacksMainClass", null); checkEdges(staticCG); } @Test public void testExclusions() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassHierarchyException, CancelException, InterruptedException { instrument(testJarLocation); - run(testMain, "ShrikeTestExclusions.txt"); - CallGraph staticCG = staticCG("ShrikeTestExclusions.txt"); + run("dynamicCG.MainClass", "ShrikeTestExclusions.txt"); + CallGraph staticCG = staticCG("LdynamicCG/MainClass", "ShrikeTestExclusions.txt"); checkEdges(staticCG); } diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTestBase.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTestBase.java index 8dbcb8c61..038e0505c 100644 --- a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTestBase.java +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTestBase.java @@ -61,9 +61,9 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase { private boolean instrumentedJarBuilt = false; - private static String instrumentedJarLocation = System.getProperty("java.io.tmpdir") + File.separator + "test.jar"; + private String instrumentedJarLocation = System.getProperty("java.io.tmpdir") + File.separator + "test.jar"; - private static String cgLocation = System.getProperty("java.io.tmpdir") + File.separator + "cg.txt"; + private String cgLocation = System.getProperty("java.io.tmpdir") + File.separator + "cg.txt"; protected void instrument(String testJarLocation) throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException { if (! instrumentedJarBuilt) { @@ -116,8 +116,14 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase { childJvm.setFailonerror(true); childJvm.setFork(true); + if (new File(cgLocation).exists()) { + new File(cgLocation).delete(); + } + childJvm.init(); - Process x = Runtime.getRuntime().exec(childJvm.getCommandLine().toString()); + String commandLine = childJvm.getCommandLine().toString(); + System.err.println(commandLine); + Process x = Runtime.getRuntime().exec(commandLine); x.waitFor(); Assert.assertTrue("expected to create call graph", new File(cgLocation).exists()); @@ -149,7 +155,7 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase { Pair x = Pair.make(caller, callee); if (! edges.contains(x)) { edges.add(x); - System.err.println("found expected edge" + caller + " --> " + callee); + System.err.println("found expected edge " + caller + " --> " + callee); } } } @@ -189,6 +195,8 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase { String callerClass = edge.nextToken(); if ("root".equals(callerClass)) { caller = staticCG.getFakeRootNode(); + } else if ("callbacks".equals(callerClass)) { + continue loop; } else { String callerMethod = edge.nextToken(); MethodReference callerRef = MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + callerClass), Selector.make(callerMethod)); diff --git a/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java b/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java index e4139f823..293e4697b 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java +++ b/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java @@ -51,7 +51,6 @@ import com.ibm.wala.util.functions.Function; import com.ibm.wala.util.intset.IntSet; import com.ibm.wala.util.intset.IntSetUtil; -@SuppressWarnings("deprecation") public class MethodHandles { private static final IntSet self = IntSetUtil.make(new int[0]); diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/cha/CHACallGraph.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/cha/CHACallGraph.java index 7cd857cde..2e8287cc9 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/cha/CHACallGraph.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/cha/CHACallGraph.java @@ -14,6 +14,7 @@ import java.lang.ref.SoftReference; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.Stack; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.IMethod; @@ -32,9 +33,9 @@ import com.ibm.wala.shrikeBT.IInvokeInstruction; import com.ibm.wala.ssa.DefUse; import com.ibm.wala.ssa.IR; import com.ibm.wala.util.CancelException; +import com.ibm.wala.util.Predicate; import com.ibm.wala.util.collections.ComposedIterator; import com.ibm.wala.util.collections.EmptyIterator; -import com.ibm.wala.util.Predicate; import com.ibm.wala.util.collections.FilterIterator; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.collections.HashSetFactory; @@ -116,7 +117,8 @@ public class CHACallGraph extends BasicCallGraph { for(Entrypoint e : entrypoints) { root.addTarget(e.makeSite(programCounter++), null); } - closure(root, true); + newNodes.push(root); + closure(); isInitialized = true; } @@ -206,24 +208,6 @@ public class CHACallGraph extends BasicCallGraph { protected CGNode makeFakeWorldClinitNode() throws CancelException { return new CHARootNode(new FakeWorldClinitMethod(cha, options, cache), Everywhere.EVERYWHERE); } - - private void closure(CGNode n, boolean fromRoot) throws CancelException { - for(Iterator sites = n.iterateCallSites(); sites.hasNext(); ) { - Iterator methods = getPossibleTargets(sites.next()); - while (methods.hasNext()) { - IMethod target = methods.next(); - if (!target.isAbstract()) { - CGNode callee = getNode(target, Everywhere.EVERYWHERE); - if (callee == null) { - callee = findOrCreateNode(target, Everywhere.EVERYWHERE); - if (fromRoot) { - registerEntrypoint(callee); - } - } - } - } - } - } private int clinitPC = 0; @@ -247,12 +231,35 @@ public class CHACallGraph extends BasicCallGraph { return n; } + private Stack newNodes = new Stack(); + + private void closure() throws CancelException { + while (! newNodes.isEmpty()) { + CGNode n = newNodes.pop(); + for(Iterator sites = n.iterateCallSites(); sites.hasNext(); ) { + Iterator methods = getPossibleTargets(sites.next()); + while (methods.hasNext()) { + IMethod target = methods.next(); + if (!target.isAbstract()) { + CGNode callee = getNode(target, Everywhere.EVERYWHERE); + if (callee == null) { + callee = findOrCreateNode(target, Everywhere.EVERYWHERE); + if (n == getFakeRootNode()) { + registerEntrypoint(callee); + } + } + } + } + } + } + } + private CGNode makeNewNode(IMethod method, Context C) throws CancelException { CGNode n; Key k = new Key(method, C); n = new CHANode(method, C); registerNode(k, n); - closure(n, false); + newNodes.push(n); return n; } diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java index 369cdd8c6..0ff3b85da 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java @@ -260,11 +260,8 @@ public abstract class PropagationCallGraphBuilder implements CallGraphBuilder { /** BEGIN Custom change: throw exception on empty entry points. This is a severe issue that should not go undetected! */ if (entrypointCallSites.isEmpty()) { - throw new IllegalStateException("Could not create a entrypoint callsites." - + " This happens when some parameters of the method can not be generated automatically " - + "(e.g. when they refer to an interface or an abstract class)."); - } - + throw new IllegalStateException("Could not create a entrypoint callsites: " + Warnings.asString()); + } /** END Custom change: throw exception on empty entry points. This is a severe issue that should not go undetected! */ customInit(); diff --git a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DroidBenchCGTest.java b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DroidBenchCGTest.java index bbb75e388..a59d36f5d 100644 --- a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DroidBenchCGTest.java +++ b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DroidBenchCGTest.java @@ -146,17 +146,19 @@ public abstract class DroidBenchCGTest extends DalvikCallGraphTestBase { skipTests.add("Button2.apk"); } - public static Collection generateData(final URI[] androidLibs, final File androidJavaJar, final String filter) { - String f = walaProperties.getProperty("droidbench.root"); - if (f == null || !new File(f).exists()) { - f = "/tmp/DroidBench"; - } - - System.err.println("Use " + f + " as droid bench root"); - assert new File(f).exists() : "Use " + f + " as droid bench root"; - assert new File(f + "/apk/").exists() : "Use " + f + " as droid bench root"; - String droidBenchRoot = f; - + public static Collection generateData(final URI[] androidLibs, final File androidJavaJar, final String filter) { + String f = walaProperties.getProperty("droidbench.root"); + if (f == null || !new File(f).exists()) { + f = "/tmp/DroidBench"; + } + + System.err.println("Use " + f + " as droid bench root"); + assert new File(f).exists() : "Use " + f + " as droid bench root"; + assert new File(f + "/apk/").exists() : "Use " + f + " as droid bench root"; + return generateData(f, androidLibs, androidJavaJar, filter); + } + + public static Collection generateData(String droidBenchRoot, final URI[] androidLibs, final File androidJavaJar, final String filter) { final List files = new LinkedList(); FileUtil.recurseFiles(new VoidFunction() { @Override diff --git a/com.ibm.wala.ide.jsdt.tests/launchers/com.ibm.wala.ide.jsdt.tests.launch b/com.ibm.wala.ide.jsdt.tests/launchers/com.ibm.wala.ide.jsdt.tests.launch new file mode 100644 index 000000000..6b196b84a --- /dev/null +++ b/com.ibm.wala.ide.jsdt.tests/launchers/com.ibm.wala.ide.jsdt.tests.launch @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/Runtime.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/Runtime.java index 56f40c8fb..f527de2ef 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/Runtime.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/Runtime.java @@ -23,12 +23,42 @@ import com.ibm.wala.util.config.FileOfClasses; import com.ibm.wala.util.config.SetOfClasses; public class Runtime { + public interface Policy { + void callback(StackTraceElement[] stack, String klass, String method, Object receiver); + } + + private static class DefaultCallbackPolicy implements Policy { + // if not found: + // look up the stack for expected caller + // if found: + // (callback case) + // record real target of expected caller (or not) + // policy-based edge for call to current method + // if not found: + // (async system edge) + // policy-based edge for call to current method + @Override + public void callback(StackTraceElement[] stack, String klass, String method, Object receiver) { + // stack frames: Runtime.execution(0), callee(1), caller(2) + String root = "".equals(stack[1].getMethodName())? "clinit": "callbacks"; + String line = root + "\t" + bashToDescriptor(klass) + "\t" + String.valueOf(method) + "\n"; + synchronized (runtime) { + if (runtime.output != null) { + runtime.output.printf(line); + runtime.output.flush(); + } + } + } + } + private static final Runtime runtime = - new Runtime(System.getProperty("dynamicCGFile"), System.getProperty("dynamicCGFilter")); + new Runtime(System.getProperty("dynamicCGFile"), + System.getProperty("dynamicCGFilter"), + System.getProperty("policyClass", "com.ibm.wala.shrike.cg.Runtime$DefaultPolicy")); private PrintWriter output; private SetOfClasses filter; - private boolean handleUninstrumentedCode = false; + private Policy handleCallback; private ThreadLocal> callStacks = new ThreadLocal>() { @@ -41,7 +71,7 @@ public class Runtime { }; - private Runtime(String fileName, String filterFileName) { + private Runtime(String fileName, String filterFileName, String policyClassName) { try { filter = new FileOfClasses(new FileInputStream(filterFileName)); } catch (Exception e) { @@ -54,7 +84,11 @@ public class Runtime { output = new PrintWriter(System.err); } - handleUninstrumentedCode = Boolean.parseBoolean(System.getProperty("dynamicCGHandleMissing", "false")); + try { + handleCallback = (Policy) Class.forName(policyClassName).newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + handleCallback = new DefaultCallbackPolicy(); + } java.lang.Runtime.getRuntime().addShutdownHook(new Thread() { @Override @@ -96,13 +130,17 @@ public class Runtime { String caller = runtime.callStacks.get().peek(); checkValid: { - if (runtime.handleUninstrumentedCode) { + // + // check for expected caller + // + if (runtime.handleCallback != null) { StackTraceElement[] stack = (new Throwable()).getStackTrace(); if (stack.length > 2) { - // frames: me(0), callee(1), caller(2) + // frames: Runtime.execution(0), callee(1), caller(2) StackTraceElement callerFrame = stack[2]; if (! caller.contains(callerFrame.getMethodName()) || ! caller.contains(bashToDescriptor(callerFrame.getClassName()))) { + runtime.handleCallback.callback(stack, klass, method, receiver); break checkValid; } }