more support for Java 7:

analysis now understands and propagates MethodHandle objects
  fixes to Shrike InvokeDynamic instruction


Former-commit-id: fb826f124423bcbca08f729cee1794fbda711d16
This commit is contained in:
Julian Dolby 2014-08-07 16:02:48 -04:00
parent 53af910339
commit 26f5254e3e
50 changed files with 1454 additions and 291 deletions

View File

@ -175,6 +175,18 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
//
// ///////////////////////////////////////////////////////////////////////////
private final static FieldReference prototypeRef;
static {
FieldReference x = null;
try {
byte[] utf8 = "__proto__".getBytes("UTF-8");
x = FieldReference.findOrCreate(JavaScriptTypes.Root, Atom.findOrCreate(utf8, 0, utf8.length), JavaScriptTypes.Root);
} catch (UnsupportedEncodingException e) {
assert false;
}
prototypeRef = x;
}
private final GlobalObjectKey globalObject = new GlobalObjectKey(cha.lookupClass(JavaScriptTypes.Object));
public PointerKey getPointerKeyForGlobalVar(String varName) {
@ -280,7 +292,7 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
//
// ///////////////////////////////////////////////////////////////////////////
public static class JSPointerAnalysisImpl extends AstPointerAnalysisImpl {
public static class JSPointerAnalysisImpl extends AstSSAPropagationCallGraphBuilder.AstPointerAnalysisImpl {
JSPointerAnalysisImpl(PropagationCallGraphBuilder builder, CallGraph cg, PointsToMap pointsToMap,
MutableMapping<InstanceKey> instanceKeys, PointerKeyFactory pointerKeys, InstanceKeyFactory iKeyFactory) {
@ -913,19 +925,7 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
}
};
private final FieldReference prototypeRef;
{
FieldReference x = null;
try {
byte[] utf8 = "__proto__".getBytes("UTF-8");
x = FieldReference.findOrCreate(JavaScriptTypes.Root, Atom.findOrCreate(utf8, 0, utf8.length), JavaScriptTypes.Root);
} catch (UnsupportedEncodingException e) {
assert false;
}
prototypeRef = x;
}
@Override
public void visitSetPrototype(SetPrototype instruction) {
visitPutInternal(instruction.getUse(1), instruction.getUse(0), false, prototypeRef);

View File

@ -56,7 +56,7 @@ public class JSZeroOrOneXCFABuilder extends JSCFABuilder {
// _necessary_ for correctness (we rely on it when handling lexical scoping)
contextSelector = new JavaScriptConstructorContextSelector(contextSelector);
contextSelector = new OneLevelForLexicalAccessFunctions(contextSelector);
//contextSelector = new OneLevelForLexicalAccessFunctions(contextSelector);
if (USE_OBJECT_SENSITIVITY) {
contextSelector = new ObjectSensitivityContextSelector(contextSelector);

View File

@ -38,8 +38,8 @@ public class JavaScriptConstructorInstanceKeys implements InstanceKeyFactory {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return base.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return base.getInstanceKeyForMetadataObject(obj, objType);
}
@Override

View File

@ -26,6 +26,7 @@ import com.ibm.wala.ipa.callgraph.ContextItem;
import com.ibm.wala.ipa.callgraph.ContextKey;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.SelectiveCPAContext;
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey.SingleInstanceFilter;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ssa.DefUse;
@ -219,9 +220,6 @@ public class PropertyNameContextSelector implements ContextSelector {
// use a MarkerForInContext to clone based on the InstanceKey used in the caller context
// TODO the cast below isn't safe; fix
InstanceKey callerIk = ((PropNameContext)caller.getContext()).getInstanceKey();
if (caller.toString().contains("_mixin_for_each_fn")) {
System.err.println(callerIk + "@" + site);
}
return new MarkerForInContext(baseContext, callerIk);
} else {
return baseContext;

View File

@ -13,8 +13,6 @@ package com.ibm.wala.cast.js.loader;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -71,10 +69,8 @@ import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.LanguageImpl;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SourceModule;
import com.ibm.wala.classLoader.SourceURLModule;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction.IOperator;
import com.ibm.wala.shrikeBT.IComparisonInstruction.Operator;
@ -645,16 +641,12 @@ public class JavaScriptLoader extends CAstAbstractModuleLoader {
return false;
}
@Override
public TypeReference getMetadataType() {
return null;
}
@Override
public TypeReference getStringType() {
return JavaScriptTypes.String;
}
@SuppressWarnings("static-access")
@Override
public PrimitiveType getPrimitive(TypeReference reference) {
return JSPrimitiveType.getPrimitive(reference);

View File

@ -623,6 +623,9 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
System.err.println(("looking up lexical parent " + definer));
Set<CGNode> creators = getLexicalDefiners(node, Pair.make(name, definer));
System.err.println("definers " + creators.size());
for (CGNode n : creators) {
PointerKey funargKey = handleRootLexicalReference(name, definer, n);
action(funargKey, vn);

View File

@ -79,8 +79,8 @@ public class CrossLanguageInstanceKeys implements InstanceKeyFactory {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return getSelector(type).getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return getSelector(objType).getInstanceKeyForMetadataObject(obj, objType);
}
}

View File

@ -180,8 +180,8 @@ abstract public class ScopeMappingInstanceKeys implements InstanceKeyFactory {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return basic.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return basic.getInstanceKeyForMetadataObject(obj, objType);
}
public ScopeMappingInstanceKeys(PropagationCallGraphBuilder builder, InstanceKeyFactory basic) {

View File

@ -2,6 +2,7 @@ source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
hello_hash.jar,\
JLex.jar,\
bcel-5.2.jar,\
java-cup-11a.jar,\

View File

@ -14,15 +14,6 @@
<property name="compilerArg" value=""/>
<property name="javacSource" value="1.5"/>
<property name="javacTarget" value="1.5"/>
<!-- This property has been updated to correspond to the paths used by the latest Java update
on Mac OS X 10.6 (Java version 1.6.0_22). If you are not using this version of Mac OS X or Java,
try changing the value of the property to "${java.home}/../../../Classes" -->
<condition property="dir_bootclasspath" value="${java.home}/../Classes">
<and>
<os family="mac"/>
<equals arg1="${ant.java.version}" arg2="1.6" />
</and>
</condition>
<property name="dir_bootclasspath" value="${java.home}/lib"/>
<path id="path_bootclasspath">
<fileset dir="${dir_bootclasspath}">
@ -60,6 +51,32 @@
<available file="${plugin.destination}/bcel-5.2.jar" property="bcel.present"/>
</target>
<target name="OCamlPresent" depends="init">
<available file="${plugin.destination}/ocaml/ocamljava-2.0-alpha1/lib/ocamljava.jar" property="ocaml.present"/>
</target>
<target name="fetchOCaml" depends="OCamlPresent" unless="ocaml.present">
<delete dir="${temp.folder}"/>
<mkdir dir="${temp.folder}"/>
<get src="http://www.ocamljava.org/downloads/download-2.0-alpha1-bin.php" dest="${temp.folder}/ocamljava-2.0-alpha1-bin.tar.gz"/>
<gunzip src="${temp.folder}//ocamljava-2.0-alpha1-bin.tar.gz" dest="${temp.folder}/ocamljava-2.0-alpha1-bin.tar"/>
<untar src="${temp.folder}/ocamljava-2.0-alpha1-bin.tar" dest="${plugin.destination}/ocaml">
</untar>
<delete dir="${temp.folder}"/>
</target>
<target name="HelloHashPresent" depends="init">
<available file="${plugin.destination}/hello_hash.jar" property="hello.hsh.present"/>
</target>
<target name="buildOCamlHelloHash" unless="hello.hash.present" depends="HelloHashPresent,fetchOCaml">
<java jar="${plugin.destination}/ocaml/ocamljava-2.0-alpha1/lib/ocamljava.jar" fork="true">
<arg file="${basedir}/ocaml/hello_hash.ml"/>
<arg value="-o"/>
<arg file="${plugin.destination}/hello_hash.jar"/>
</java>
</target>
<target name="fetchBcel" depends="bcelPresent" unless="bcel.present">
<delete dir="${temp.folder}"/>
<mkdir dir="${temp.folder}"/>
@ -70,7 +87,7 @@
<delete dir="${temp.folder}"/>
</target>
<target name="copyJars" depends="fetchJLex,fetchJavaCup,fetchBcel">
<target name="copyJars" depends="fetchJLex,fetchJavaCup,fetchBcel,buildOCamlHelloHash">
<copy todir="${plugin.destination}/bin">
<fileset dir="${plugin.destination}" includes="*.jar" />
</copy>

View File

@ -0,0 +1,27 @@
(*******************************************************************************
* Copyright (c) 2014 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
******************************************************************************)
let method_hash = Hashtbl.create 1000;;
let print_hello arg = print_string (arg ^ "!\n");;
let bar arg =
print_string "before calling print_hello in bar\n";
print_hello arg;
print_string "after calling print_hello in bar\n";;
let foo arg =
print_string "before calling bar in foo\n";
bar arg;
print_string "after calling bar in foo\n";;
Hashtbl.add method_hash "var" (foo);;
(Hashtbl.find method_hash "var") "Hello World";;

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/tools/ClassPrinter.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.ibm.wala.shrikeBT.shrikeCT.tools.ClassPrinter"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="${workspace_loc}/JLex.jar"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.shrike"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx800M"/>
</launchConfiguration>

View File

@ -24,7 +24,8 @@
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;com.ibm.wala.core.testdata&quot; type=&quot;1&quot;/&gt;&#10;"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/Java SE 7"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.ibm.wala.core.tests"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx800M -verbose:gc -Dcom.ibm.wala.util.fixedpoint.impl.verbose=true"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx800M -verbose:gc -Dcom.ibm.wala.util.fixedpoint.impl.verbose=true -XX:-UseSplitVerifier"/>
</launchConfiguration>

View File

@ -20,7 +20,7 @@
<phase>test</phase>
<configuration>
<testClassesDirectory>${project.build.outputDirectory}</testClassesDirectory>
<argLine>-Xmx800M -Dcom.ibm.wala.junit.profile=short -Dcom.ibm.wala.junit.analyzingJar=true -ea</argLine>
<argLine>-XX:-UseSplitVerifier -Xmx800M -Dcom.ibm.wala.junit.profile=short -Dcom.ibm.wala.junit.analyzingJar=true -ea</argLine>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
</configuration>
<goals>

View File

@ -0,0 +1,59 @@
package com.ibm.wala.core.tests.callGraph;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import org.junit.Test;
import com.ibm.wala.analysis.reflection.java7.MethodHandles;
import com.ibm.wala.core.tests.shrike.DynamicCallGraphTestBase;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.impl.Util;
import com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.DelegatingSSAContextInterpreter;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.Predicate;
import com.ibm.wala.util.io.TemporaryFile;
public class Java7CallGraphTest extends DynamicCallGraphTestBase {
@Test public void testOcamlHelloHash() throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
AnalysisScope scope = CallGraphTestUtil.makeJ2SEAnalysisScope("ocaml_hello_hash.txt", CallGraphTestUtil.REGRESSION_EXCLUSIONS);
ClassHierarchy cha = ClassHierarchy.make(scope);
Iterable<Entrypoint> entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha, "Lpack/ocamljavaMain");
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);
AnalysisCache cache = new AnalysisCache();
SSAPropagationCallGraphBuilder builder = Util.makeZeroOneContainerCFABuilder(options, cache, cha, scope);
builder.setContextSelector(new MethodHandles.ContextSelectorImpl(builder.getContextSelector()));
builder.setContextInterpreter(new DelegatingSSAContextInterpreter(new MethodHandles.ContextInterpreterImpl(), builder.getCFAContextInterpreter()));
CallGraph cg = builder.makeCallGraph(options, null);
File F = TemporaryFile.urlToFile("hello_hash_test_jar.jar", getClass().getClassLoader().getResource("hello_hash.jar"));
F.deleteOnExit();
instrument(F.getAbsolutePath());
run("pack.ocamljavaMain", null);
checkNodes(cg, new Predicate<MethodReference>() {
@Override
public boolean test(MethodReference t) {
String s = t.toString();
return s.contains("Lpack/") || s.contains("Locaml/stdlib/");
}
});
}
}

View File

@ -1,108 +1,28 @@
package com.ibm.wala.core.tests.shrike;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.GZIPInputStream;
import org.junit.Assert;
import org.junit.Test;
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.core.tests.util.WalaTestCase;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.shrike.cg.DynamicCallGraph;
import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.io.TemporaryFile;
public class DynamicCallGraphTest extends WalaTestCase {
private static String getClasspathEntry(String elt) {
for (String s : System.getProperty("java.class.path").split(File.pathSeparator)) {
if (s.indexOf(elt) >= 0) {
File e = new File(s);
Assert.assertTrue(elt + " expected to exist", e.exists());
if (e.isDirectory() && !s.endsWith("/")) {
s = s + "/";
}
return s;
}
}
Assert.assertFalse("cannot find " + elt, true);
return null;
}
public class DynamicCallGraphTest extends DynamicCallGraphTestBase {
private static String testJarLocation = getClasspathEntry("com.ibm.wala.core.testdata");
private boolean instrumentedJarBuilt = false;
private static 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 void instrument() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException {
if (! instrumentedJarBuilt) {
System.err.println("core data jar to instrument: " + testJarLocation);
DynamicCallGraph.main(new String[]{testJarLocation, "-o", instrumentedJarLocation});
Assert.assertTrue("expected to create /tmp/test.jar", new File(instrumentedJarLocation).exists());
instrumentedJarBuilt = true;
}
}
private void run(String exclusionsFile) throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
String shrikeBin = getClasspathEntry("com.ibm.wala.shrike");
String utilBin = getClasspathEntry("com.ibm.wala.util");
URLClassLoader jcl = new URLClassLoader(new URL[]{ new URL("file://" + instrumentedJarLocation), new URL("file://" + shrikeBin), new URL("file://" + utilBin) }, DynamicCallGraphTest.class.getClassLoader().getParent());
Class<?> testClass = jcl.loadClass("dynamicCG.MainClass");
Assert.assertNotNull(testClass);
Method testMain = testClass.getDeclaredMethod("main", String[].class);
Assert.assertNotNull(testMain);
System.setProperty("dynamicCGFile", cgLocation);
if (exclusionsFile != null) {
File tmpFile = TemporaryFile.urlToFile("exclusions.txt", getClass().getClassLoader().getResource(exclusionsFile));
System.setProperty("dynamicCGFilter", tmpFile.getCanonicalPath());
}
try {
testMain.invoke(null, (Object)new String[0]);
} catch (Throwable e) {
// exceptions here are from program being instrumented
// this is fine, since we are collecting its call graph
// and exceptions are possible behavior.
}
// the VM is not exiting, so stop tracing explicitly
Class<?> runtimeClass = jcl.loadClass("com.ibm.wala.shrike.cg.Runtime");
Assert.assertNotNull(runtimeClass);
Method endTrace = runtimeClass.getDeclaredMethod("endTrace");
Assert.assertNotNull(endTrace);
endTrace.invoke(null);
Assert.assertTrue("expected to create call graph", new File(System.getProperty("dynamicCGFile")).exists());
}
private static String testMain = "dynamicCG.MainClass";
private CallGraph staticCG(String exclusionsFile) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException {
AnalysisScope scope = CallGraphTestUtil.makeJ2SEAnalysisScope(TestConstants.WALA_TESTDATA, exclusionsFile != null? exclusionsFile: CallGraphTestUtil.REGRESSION_EXCLUSIONS);
@ -111,54 +31,21 @@ public class DynamicCallGraphTest extends WalaTestCase {
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);
return CallGraphTestUtil.buildZeroOneCFA(options, new AnalysisCache(), cha, scope, false);
}
private void check(CallGraph staticCG) throws IOException {
BufferedReader dynamicEdgesFile = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(System.getProperty("dynamicCGFile")))));
String line;
int lines = 0;
while ((line = dynamicEdgesFile.readLine()) != null) {
lines++;
StringTokenizer edge = new StringTokenizer(line, "\t");
CGNode caller;
String callerClass = edge.nextToken();
if ("root".equals(callerClass)) {
caller = staticCG.getFakeRootNode();
} else {
String callerMethod = edge.nextToken();
Set<CGNode> nodes = staticCG.getNodes(MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + callerClass), Selector.make(callerMethod)));
Assert.assertEquals(1, nodes.size());
caller = nodes.iterator().next();
}
String calleeClass = edge.nextToken();
String calleeMethod = edge.nextToken();
Set<CGNode> nodes = staticCG.getNodes(MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + calleeClass), Selector.make(calleeMethod)));
Assert.assertEquals(1, nodes.size());
CGNode callee = nodes.iterator().next();
Assert.assertTrue("no edge for " + caller + " --> " + callee, staticCG.getPossibleSites(caller, callee).hasNext());
System.err.println("found expected edge" + caller + " --> " + callee);
}
dynamicEdgesFile.close();
Assert.assertTrue("more than one edge", lines > 0);
}
@Test
public void testGraph() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassHierarchyException, CancelException {
instrument();
run(null);
instrument(testJarLocation);
run(testMain, null);
CallGraph staticCG = staticCG(null);
check(staticCG);
checkEdges(staticCG);
}
@Test
public void testExclusions() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassHierarchyException, CancelException {
instrument();
run("ShrikeTestExclusions.txt");
instrument(testJarLocation);
run(testMain, "ShrikeTestExclusions.txt");
CallGraph staticCG = staticCG("ShrikeTestExclusions.txt");
check(staticCG);
checkEdges(staticCG);
}
}

View File

@ -0,0 +1,186 @@
package com.ibm.wala.core.tests.shrike;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.GZIPInputStream;
import org.junit.Assert;
import com.ibm.wala.core.tests.util.WalaTestCase;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.shrike.cg.DynamicCallGraph;
import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.Predicate;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.io.TemporaryFile;
public abstract class DynamicCallGraphTestBase extends WalaTestCase {
protected static String getClasspathEntry(String elt) {
for (String s : System.getProperty("java.class.path").split(File.pathSeparator)) {
if (s.indexOf(elt) >= 0) {
File e = new File(s);
Assert.assertTrue(elt + " expected to exist", e.exists());
if (e.isDirectory() && !s.endsWith("/")) {
s = s + "/";
}
return s;
}
}
Assert.assertFalse("cannot find " + elt, true);
return null;
}
private boolean instrumentedJarBuilt = false;
private static String instrumentedJarLocation = System.getProperty("java.io.tmpdir") + File.separator + "test.jar";
private static String cgLocation = System.getProperty("java.io.tmpdir") + File.separator + "cg.txt";
protected void instrument(String testJarLocation) throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException {
if (! instrumentedJarBuilt) {
System.err.println("core data jar to instrument: " + testJarLocation);
if (new File(instrumentedJarLocation).exists()) {
assert new File(instrumentedJarLocation).delete();
}
DynamicCallGraph.main(new String[]{testJarLocation, "-o", instrumentedJarLocation});
Assert.assertTrue("expected to create /tmp/test.jar", new File(instrumentedJarLocation).exists());
instrumentedJarBuilt = true;
}
}
protected void run(String mainClass, String exclusionsFile) throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
String shrikeBin = getClasspathEntry("com.ibm.wala.shrike");
String utilBin = getClasspathEntry("com.ibm.wala.util");
URLClassLoader jcl = new URLClassLoader(new URL[]{ new URL("file://" + instrumentedJarLocation), new URL("file://" + shrikeBin), new URL("file://" + utilBin) }, DynamicCallGraphTestBase.class.getClassLoader().getParent());
Class<?> testClass = jcl.loadClass(mainClass);
Assert.assertNotNull(testClass);
Method testMain = testClass.getDeclaredMethod("main", String[].class);
Assert.assertNotNull(testMain);
System.setProperty("dynamicCGFile", cgLocation);
if (exclusionsFile != null) {
File tmpFile = TemporaryFile.urlToFile("exclusions.txt", getClass().getClassLoader().getResource(exclusionsFile));
System.setProperty("dynamicCGFilter", tmpFile.getCanonicalPath());
}
try {
testMain.invoke(null, (Object)new String[0]);
} catch (Throwable e) {
// exceptions here are from program being instrumented
// this is fine, since we are collecting its call graph
// and exceptions are possible behavior.
}
// the VM is not exiting, so stop tracing explicitly
Class<?> runtimeClass = jcl.loadClass("com.ibm.wala.shrike.cg.Runtime");
Assert.assertNotNull(runtimeClass);
Method endTrace = runtimeClass.getDeclaredMethod("endTrace");
Assert.assertNotNull(endTrace);
endTrace.invoke(null);
Assert.assertTrue("expected to create call graph", new File(System.getProperty("dynamicCGFile")).exists());
}
interface EdgesTest {
void edgesTest(CallGraph staticCG, CGNode caller, MethodReference callee);
}
private MethodReference callee(String calleeClass, String calleeMethod) {
return MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + calleeClass), Selector.make(calleeMethod));
}
protected void checkEdges(CallGraph staticCG) throws IOException {
checkEdges(staticCG, Predicate.<MethodReference>truePred());
}
protected void checkEdges(CallGraph staticCG, Predicate<MethodReference> filter) throws IOException {
check(staticCG, new EdgesTest() {
@Override
public void edgesTest(CallGraph staticCG, CGNode caller, MethodReference calleeRef) {
Set<CGNode> nodes = staticCG.getNodes(calleeRef);
Assert.assertEquals(1, nodes.size());
CGNode callee = nodes.iterator().next();
Assert.assertTrue("no edge for " + caller + " --> " + callee, staticCG.getPossibleSites(caller, callee).hasNext());
System.err.println("found expected edge" + caller + " --> " + callee);
}
}, filter);
}
protected void checkNodes(CallGraph staticCG) throws IOException {
checkNodes(staticCG, Predicate.<MethodReference>truePred());
}
protected void checkNodes(CallGraph staticCG, Predicate<MethodReference> filter) throws IOException {
final Set<MethodReference> notFound = HashSetFactory.make();
check(staticCG, new EdgesTest() {
@Override
public void edgesTest(CallGraph staticCG, CGNode caller, MethodReference callee) {
boolean checkForCallee = !staticCG.getNodes(callee).isEmpty();
if (!checkForCallee) {
notFound.add(callee);
} else {
System.err.println("found expected node " + callee);
}
}
}, filter);
Assert.assertTrue("could not find " + notFound, notFound.isEmpty());
}
protected void check(CallGraph staticCG, EdgesTest test, Predicate<MethodReference> filter) throws IOException {
BufferedReader dynamicEdgesFile = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(System.getProperty("dynamicCGFile")))));
String line;
int lines = 0;
loop: while ((line = dynamicEdgesFile.readLine()) != null) {
lines++;
StringTokenizer edge = new StringTokenizer(line, "\t");
CGNode caller;
String callerClass = edge.nextToken();
if ("root".equals(callerClass)) {
caller = staticCG.getFakeRootNode();
} else {
String callerMethod = edge.nextToken();
MethodReference callerRef = MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + callerClass), Selector.make(callerMethod));
Set<CGNode> nodes = staticCG.getNodes(callerRef);
if (! filter.test(callerRef)) {
continue loop;
}
Assert.assertEquals(1, nodes.size());
caller = nodes.iterator().next();
}
String calleeClass = edge.nextToken();
String calleeMethod = edge.nextToken();
MethodReference callee = callee(calleeClass, calleeMethod);
if (! filter.test(callee)) {
continue loop;
}
test.edgesTest(staticCG, caller, callee);
}
dynamicEdgesFile.close();
Assert.assertTrue("more than one edge", lines > 0);
}
}

View File

@ -12,6 +12,7 @@ Bundle-ActivationPolicy: lazy
Export-Package: .,
com.ibm.wala.analysis.pointers,
com.ibm.wala.analysis.reflection,
com.ibm.wala.analysis.reflection.java7,
com.ibm.wala.analysis.stackMachine,
com.ibm.wala.analysis.typeInference,
com.ibm.wala.cfg,

View File

@ -0,0 +1,310 @@
/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.analysis.reflection.java7;
import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.Map;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextItem;
import com.ibm.wala.ipa.callgraph.ContextKey;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.MethodSummary;
import com.ibm.wala.ipa.summaries.SummarizedMethod;
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAFieldAccessInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.Filter;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.MapIterator;
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]);
private static ContextKey METHOD_KEY = new ContextKey() {
@Override
public String toString() {
return "METHOD_KEY";
}
};
public static class MethodItem implements ContextItem {
private final MethodReference method;
public MethodItem(MethodReference method) {
super();
this.method = method;
}
public MethodReference getMethod() {
return method;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((method == null) ? 0 : method.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MethodItem other = (MethodItem) obj;
if (method == null) {
if (other.method != null)
return false;
} else if (!method.equals(other.method))
return false;
return true;
}
}
public static class MethodContext implements Context {
private final Context base;
private final MethodReference method;
public MethodContext(Context base, MethodReference method) {
this.base = base;
this.method = method;
}
@Override
public ContextItem get(ContextKey name) {
if (METHOD_KEY.equals(name)) {
return new MethodItem(method);
} else {
return base.get(name);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((base == null) ? 0 : base.hashCode());
result = prime * result + ((method == null) ? 0 : method.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MethodContext other = (MethodContext) obj;
if (base == null) {
if (other.base != null)
return false;
} else if (!base.equals(other.base))
return false;
if (method == null) {
if (other.method != null)
return false;
} else if (!method.equals(other.method))
return false;
return true;
}
@Override
public String toString() {
return "ctxt:" + method.getName();
}
}
public static class ContextSelectorImpl implements ContextSelector {
private final ContextSelector base;
public ContextSelectorImpl(ContextSelector base) {
this.base = base;
}
@Override
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] actualParameters) {
Context baseContext = base.getCalleeTarget(caller, site, callee, actualParameters);
if ((isInvoke(callee) || isType(callee)) && callee.getDeclaringClass().getReference().equals(TypeReference.JavaLangInvokeMethodHandle)) {
if (actualParameters != null && actualParameters.length > 0) {
InstanceKey selfKey = actualParameters[0];
if (selfKey instanceof ConstantKey && ((ConstantKey)selfKey).getConcreteType().getReference().equals(TypeReference.JavaLangInvokeMethodHandle)) {
MethodReference ref = ((IMethod) ((ConstantKey)selfKey).getValue()).getReference();
return new MethodContext(baseContext, ref);
}
}
}
return baseContext;
}
@Override
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
return self;
}
}
private static boolean isInvoke(IMethod node) {
return node.getName().toString().startsWith("invoke");
}
private static boolean isType(IMethod node) {
return node.getName().toString().equals("type");
}
private static boolean isInvoke(CGNode node) {
return isInvoke(node.getMethod());
}
private static boolean isType(CGNode node) {
return isType(node.getMethod());
}
public static class ContextInterpreterImpl implements SSAContextInterpreter {
private final Map<CGNode, SoftReference<IR>> irs = HashMapFactory.make();
@Override
public Iterator<NewSiteReference> iterateNewSites(CGNode node) {
return getIR(node).iterateNewSites();
}
public Iterator<FieldReference> iterateFields(CGNode node, Filter<SSAInstruction> filter) {
return
new MapIterator<SSAInstruction,FieldReference>(
new FilterIterator<SSAInstruction>(getIR(node).iterateNormalInstructions(), filter),
new Function<SSAInstruction,FieldReference>() {
@Override
public FieldReference apply(SSAInstruction object) {
return ((SSAFieldAccessInstruction)object).getDeclaredField();
}
});
}
@Override
public Iterator<FieldReference> iterateFieldsRead(CGNode node) {
return iterateFields(node, new Filter<SSAInstruction>() {
@Override
public boolean accepts(SSAInstruction o) {
return o instanceof SSAGetInstruction;
}
});
}
@Override
public Iterator<FieldReference> iterateFieldsWritten(CGNode node) {
return iterateFields(node, new Filter<SSAInstruction>() {
@Override
public boolean accepts(SSAInstruction o) {
return o instanceof SSAPutInstruction;
}
});
}
@Override
public boolean recordFactoryType(CGNode node, IClass klass) {
return false;
}
@Override
public boolean understands(CGNode node) {
return (isInvoke(node) || isType(node)) && node.getContext() instanceof MethodContext;
}
@Override
public Iterator<CallSiteReference> iterateCallSites(CGNode node) {
return getIR(node).iterateCallSites();
}
@Override
public IR getIR(CGNode node) {
if (!irs.containsKey(node) || irs.get(node).get() == null) {
MethodSummary code = new MethodSummary(node.getMethod().getReference());
SummarizedMethod m = new SummarizedMethod(node.getMethod().getReference(), code, node.getMethod().getDeclaringClass());
SSAInstructionFactory insts = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().instructionFactory();
assert node.getContext() instanceof MethodContext;
MethodReference ref = ((MethodContext)node.getContext()).method;
boolean isStatic = node.getClassHierarchy().resolveMethod(ref).isStatic();
if (isInvoke(node)) {
String name = node.getMethod().getName().toString();
if ("invokeWithArguments".equals(name)) {
int nargs = ref.getNumberOfParameters();
int params[] = new int[nargs];
for(int i = 0; i < nargs; i++) {
code.addConstant(i+nargs+3, new ConstantValue(i));
code.addStatement(insts.ArrayLoadInstruction(i+3, 1, i+nargs+3, TypeReference.JavaLangObject));
params[i] = i+3;
}
CallSiteReference site = CallSiteReference.make(nargs+1, ref, isStatic? Dispatch.STATIC: Dispatch.SPECIAL);
code.addStatement(insts.InvokeInstruction(2*nargs+3, params, 2*nargs+4, site));
code.addStatement(insts.ReturnInstruction(2*nargs+3, false));
} else {
int nargs = node.getMethod().getNumberOfParameters();
}
} else {
assert isType(node);
code.addStatement(insts.LoadMetadataInstruction(2, TypeReference.JavaLangInvokeMethodType, ref.getDescriptor()));
code.addStatement(insts.ReturnInstruction(2, false));
}
irs.put(node, new SoftReference<IR>(m.makeIR(node.getContext(), SSAOptions.defaultOptions())));
}
return irs.get(node).get();
}
@Override
public DefUse getDU(CGNode node) {
return new DefUse(getIR(node));
}
@Override
public int getNumberOfStatements(CGNode node) {
return getIR(node).getInstructions().length;
}
@Override
public ControlFlowGraph<SSAInstruction, ISSABasicBlock> getCFG(CGNode n) {
return getIR(n).getControlFlowGraph();
}
}
}

View File

@ -529,7 +529,7 @@ public class TypeInference extends SSAInference<TypeVariable> implements FixedPo
@Override
public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
IClass jlClassKlass = cha.lookupClass(language.getMetadataType());
IClass jlClassKlass = cha.lookupClass(instruction.getType());
assert jlClassKlass != null;
result = new DeclaredTypeOperator(new ConeType(jlClassKlass));
}

View File

@ -11,6 +11,8 @@
*/
package com.ibm.wala.classLoader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -28,6 +30,7 @@ import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.Instruction;
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.SSAAddressOfInstruction;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
@ -58,8 +61,10 @@ import com.ibm.wala.ssa.SSASwitchInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.shrike.Exceptions.MethodResolutionFailure;
@ -480,6 +485,10 @@ public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Cons
} else if (o instanceof IMethod) {
IMethod m = (IMethod) o;
return m.isInit() ? TypeReference.JavaLangReflectConstructor : TypeReference.JavaLangReflectMethod;
} else if (o instanceof MethodHandle || o instanceof ReferenceToken) {
return TypeReference.JavaLangInvokeMethodHandle;
} else if (o instanceof MethodType) {
return TypeReference.JavaLangInvokeMethodType;
} else {
assert false : "unknown constant " + o + ": " + o.getClass();
return null;
@ -654,7 +663,9 @@ public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Cons
@Override
public boolean isMetadataType(TypeReference type) {
return type == TypeReference.JavaLangClass;
return type == TypeReference.JavaLangClass ||
type == TypeReference.JavaLangInvokeMethodHandle ||
type == TypeReference.JavaLangInvokeMethodType;
}
@Override
@ -675,7 +686,13 @@ public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Cons
@Override
public Object getMetadataToken(Object value) {
if (value instanceof ClassToken) {
return ShrikeUtil.makeTypeReference(ClassLoaderReference.Primordial, ((ClassToken) value).getTypeName());
return ShrikeUtil.makeTypeReference(ClassLoaderReference.Application, ((ClassToken) value).getTypeName());
} else if (value instanceof ReferenceToken) {
ReferenceToken tok = (ReferenceToken)value;
TypeReference cls = ShrikeUtil.makeTypeReference(ClassLoaderReference.Application, "L" + tok.getClassName());
return MethodReference.findOrCreate(cls, new Selector(Atom.findOrCreateUnicodeAtom(tok.getElementName()), Descriptor.findOrCreateUTF8(tok.getDescriptor())));
} else if (value instanceof MethodHandle || value instanceof MethodType) {
return value;
} else {
assert value instanceof TypeReference;
return value;
@ -687,11 +704,6 @@ public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Cons
throw new UnsupportedOperationException("Java does not permit explicit pointers");
}
@Override
public TypeReference getMetadataType() {
return TypeReference.JavaLangClass;
}
@Override
public TypeReference getStringType() {
return TypeReference.JavaLangString;

View File

@ -131,11 +131,6 @@ public interface Language {
TypeReference getStringType();
/**
* get the metadata type for the language, e.g., java.lang.Class for Java
*/
TypeReference getMetadataType();
TypeReference getPointerType(TypeReference pointee);
/**

View File

@ -58,8 +58,8 @@ class ThisFilteringHeapModel implements HeapModel {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return delegate.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return delegate.getInstanceKeyForMetadataObject(obj, objType);
}
@Override

View File

@ -486,7 +486,7 @@ public class DemandPointerFlowGraph extends AbstractDemandFlowGraph implements I
public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
PointerKey def = heapModel.getPointerKeyForLocal(node, instruction.getDef());
assert instruction.getType() == TypeReference.JavaLangClass;
InstanceKey iKey = heapModel.getInstanceKeyForClassObject((TypeReference) instruction.getToken());
InstanceKey iKey = heapModel.getInstanceKeyForMetadataObject(instruction.getToken(), (TypeReference) instruction.getToken());
g.addNode(iKey);
g.addNode(def);

View File

@ -423,15 +423,18 @@ public class Util {
*/
public static SSAPropagationCallGraphBuilder makeZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
IClassHierarchy cha, AnalysisScope scope) {
return makeZeroOneContainerCFABuilder(options, cache, cha, scope, null, null);
}
public static SSAPropagationCallGraphBuilder makeZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
IClassHierarchy cha, AnalysisScope scope, ContextSelector appSelector, SSAContextInterpreter appInterpreter) {
if (options == null) {
throw new IllegalArgumentException("options is null");
}
addDefaultSelectors(options, cha);
addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
ContextSelector appSelector = null;
SSAContextInterpreter appInterpreter = null;
return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.SMUSH_MANY | ZeroXInstanceKeys.SMUSH_PRIMITIVE_HOLDERS
| ZeroXInstanceKeys.SMUSH_STRINGS | ZeroXInstanceKeys.SMUSH_THROWABLES);
}

View File

@ -121,8 +121,8 @@ public class AllocationSiteInNodeFactory implements InstanceKeyFactory {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return classBased.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return classBased.getInstanceKeyForMetadataObject(obj, objType);
}
}

View File

@ -12,11 +12,14 @@ package com.ibm.wala.ipa.callgraph.propagation;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.ProgramCounter;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.debug.Assertions;
@ -126,15 +129,30 @@ public class ClassBasedInstanceKeys implements InstanceKeyFactory {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
IClass klass = cha.lookupClass(type);
if (klass == null) {
return new ConcreteTypeKey(cha.lookupClass(TypeReference.JavaLangClass));
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
IClass cls = cha.lookupClass(objType);
assert cls != null : objType;
if (obj instanceof TypeReference) {
IClass klass = cha.lookupClass((TypeReference)obj);
if (klass == null) {
return new ConcreteTypeKey(cls);
} else {
// return the IClass itself, wrapped as a constant!
return new ConstantKey<IClass>(klass, cls);
}
} else if (obj instanceof MethodReference) {
IMethod m = cha.resolveMethod((MethodReference)obj);
if (m == null) {
return new ConcreteTypeKey(cls);
} else {
return new ConstantKey<IMethod>(m, cls);
}
} else if (obj instanceof Descriptor) {
return new ConstantKey<Descriptor>((Descriptor)obj, cls);
} else {
// return the IClass itself, wrapped as a constant!
return new ConstantKey<IClass>(klass, cha.lookupClass(TypeReference.JavaLangClass));
// other cases
throw new Error();
}
}
/**

View File

@ -45,8 +45,9 @@ public interface InstanceKeyFactory {
public abstract InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter instr, TypeReference type);
/**
* @return the instance key that represents the class object of type _type_.
* @param objType TODO
* @return the instance key that represents the metadata object obj
*/
public abstract InstanceKey getInstanceKeyForClassObject(TypeReference type);
public abstract InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType);
}

View File

@ -482,8 +482,8 @@ public class PointerAnalysisImpl extends AbstractPointerAnalysis {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return iKeyFactory.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return iKeyFactory.getInstanceKeyForMetadataObject(obj, objType);
}
/*

View File

@ -759,8 +759,8 @@ public abstract class PropagationCallGraphBuilder implements CallGraphBuilder {
return instanceKeyFactory.getInstanceKeyForConstant(type, S);
}
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return instanceKeyFactory.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return instanceKeyFactory.getInstanceKeyForMetadataObject(obj, objType);
}
public boolean haveAlreadyVisited(CGNode node) {

View File

@ -612,8 +612,8 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
return getBuilder().getInstanceKeyForPEI(node, instr, type);
}
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return getBuilder().getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForClassObject(Object obj, TypeReference type) {
return getBuilder().getInstanceKeyForMetadataObject(obj, type);
}
public CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {
@ -1385,13 +1385,15 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
@Override
public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
PointerKey def = getPointerKeyForLocal(instruction.getDef());
assert instruction.getType() == TypeReference.JavaLangClass;
InstanceKey iKey = getInstanceKeyForClassObject((TypeReference) instruction.getToken());
IClass klass = getClassHierarchy().lookupClass((TypeReference) instruction.getToken());
if (klass != null) {
processClassInitializer(klass);
InstanceKey iKey = getInstanceKeyForClassObject(instruction.getToken(), instruction.getType());
if (instruction.getToken() instanceof TypeReference) {
IClass klass = getClassHierarchy().lookupClass((TypeReference) instruction.getToken());
if (klass != null) {
processClassInitializer(klass);
}
}
if (!contentsAreInvariant(symbolTable, du, instruction.getDef())) {
system.newConstraint(def, iKey);
} else {
@ -1789,7 +1791,19 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
@Override
public int hashCode() {
return node.hashCode() + 90289 * call.hashCode();
int h = 1;
if (constParams != null) {
for(InstanceKey[] cs : constParams) {
if (cs != null) {
for(InstanceKey c : cs) {
if (c != null) {
h = h ^ c.hashCode();
}
}
}
}
}
return h * node.hashCode() + 90289 * call.hashCode();
}
@Override

View File

@ -1,4 +1,4 @@
package com.ibm.wala.cast.js.ipa.callgraph;
package com.ibm.wala.ipa.callgraph.propagation;
import java.util.HashMap;
import java.util.Map;
@ -6,8 +6,6 @@ import java.util.Map;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextItem;
import com.ibm.wala.ipa.callgraph.ContextKey;
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
/**
* A selective Cartesian product context that enforces object sensitivity on some set

View File

@ -94,8 +94,8 @@ public class SmushedAllocationSiteInstanceKeys implements InstanceKeyFactory {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return classBased.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return classBased.getInstanceKeyForMetadataObject(obj, objType);
}
}

View File

@ -282,8 +282,8 @@ public class ZeroXInstanceKeys implements InstanceKeyFactory {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return classBased.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return classBased.getInstanceKeyForMetadataObject(obj, objType);
}
/**

View File

@ -218,7 +218,7 @@ public class TypeBasedHeapModel implements HeapModel {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) throws UnimplementedError {
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) throws UnimplementedError {
Assertions.UNREACHABLE();
return null;
}

View File

@ -54,8 +54,8 @@ public class DelegatingExtendedHeapModel implements ExtendedHeapModel {
}
@Override
public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
return h.getInstanceKeyForClassObject(type);
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
return h.getInstanceKeyForMetadataObject(obj, objType);
}
@Override

View File

@ -154,6 +154,14 @@ public final class TypeReference implements Serializable {
public final static TypeReference JavaLangClass = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassName);
private final static TypeName JavaLangInvokeMethodHandleName = TypeName.string2TypeName("Ljava/lang/invoke/MethodHandle");
public final static TypeReference JavaLangInvokeMethodHandle = findOrCreate(ClassLoaderReference.Primordial, JavaLangInvokeMethodHandleName);
private final static TypeName JavaLangInvokeMethodTypeName = TypeName.string2TypeName("Ljava/lang/invoke/MethodType");
public final static TypeReference JavaLangInvokeMethodType = findOrCreate(ClassLoaderReference.Primordial, JavaLangInvokeMethodTypeName);
private final static TypeName JavaLangClassCastExceptionName = TypeName.string2TypeName("Ljava/lang/ClassCastException");
public final static TypeReference JavaLangClassCastException = findOrCreate(ClassLoaderReference.Primordial,

View File

@ -2,6 +2,6 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/Java SE 8"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -16,5 +16,5 @@ Export-Package: com.ibm.wala.shrike.bench,
com.ibm.wala.shrikeBT.shrikeCT.tools,
com.ibm.wala.shrikeBT.tools,
com.ibm.wala.shrikeCT
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: com.ibm.wala.util

View File

@ -0,0 +1 @@
04939cec7b68b22597b102231ba91e19f5797997

View File

@ -10,11 +10,65 @@
*******************************************************************************/
package com.ibm.wala.shrikeBT;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.shrikeCT.ConstantPoolParser;
/**
* A ConstantInstruction pushes some constant value onto the stack.
*/
public abstract class ConstantInstruction extends Instruction {
public static class InvokeDynamicToken {
private final BootstrapMethod bootstrapMethod;
private final String name;
private final String type;
public InvokeDynamicToken(BootstrapMethod bootstrapMethod, String name, String type) {
this.bootstrapMethod = bootstrapMethod;
this.name = name;
this.type = type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((bootstrapMethod == null) ? 0 : bootstrapMethod.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InvokeDynamicToken other = (InvokeDynamicToken) obj;
if (bootstrapMethod == null) {
if (other.bootstrapMethod != null)
return false;
} else if (!bootstrapMethod.equals(other.bootstrapMethod))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
}
public static class ClassToken {
private final String typeName;
@ -53,56 +107,6 @@ public abstract class ConstantInstruction extends Instruction {
}
public static class ReferenceToken {
private final String className;
private final String elementName;
private final String descriptor;
public ReferenceToken(String className, String elementName, String descriptor) {
super();
this.className = className;
this.elementName = elementName;
this.descriptor = descriptor;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((className == null) ? 0 : className.hashCode());
result = prime * result + ((descriptor == null) ? 0 : descriptor.hashCode());
result = prime * result + ((elementName == null) ? 0 : elementName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReferenceToken other = (ReferenceToken) obj;
if (className == null) {
if (other.className != null)
return false;
} else if (!className.equals(other.className))
return false;
if (descriptor == null) {
if (other.descriptor != null)
return false;
} else if (!descriptor.equals(other.descriptor))
return false;
if (elementName == null) {
if (other.elementName != null)
return false;
} else if (!elementName.equals(other.elementName))
return false;
return true;
}
}
public ConstantInstruction(short opcode) {
super(opcode);
}
@ -631,7 +635,7 @@ public abstract class ConstantInstruction extends Instruction {
String className = cp.getConstantPoolHandleClassType(getCPIndex());
String eltName = cp.getConstantPoolHandleName(getCPIndex());
String eltDesc = cp.getConstantPoolHandleType(getCPIndex());
value = new ReferenceToken(className, eltName, eltDesc);
value = new ConstantPoolParser.ReferenceToken(className, eltName, eltDesc);
}
return value;
}
@ -646,7 +650,61 @@ public abstract class ConstantInstruction extends Instruction {
return index;
}
}
static class ConstInvokeDynamic extends ConstantInstruction {
protected Object value;
public ConstInvokeDynamic(short opcode, Object value) {
super(opcode);
this.value = value;
}
@Override
public Object getValue() {
return value;
}
@Override
public String getType() {
return null;
}
}
static class LazyInvokeDynamic extends ConstMethodHandle {
final private ConstantPoolReader cp;
final private int index;
LazyInvokeDynamic(short opcode, ConstantPoolReader cp, int index) {
super(opcode, null);
this.cp = cp;
this.index = index;
}
@Override
public Object getValue() {
if (value == null) {
BootstrapMethod bootstrap = cp.getConstantPoolDynamicBootstrap(index);
String name = cp.getConstantPoolDynamicName(index);
String type = cp.getConstantPoolDynamicType(index);
value = new InvokeDynamicToken(bootstrap, name, type);
}
return value;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
/**
* @return the constant value pushed: an Integer, a Long, a Float, a Double, a String, or null
*/
@ -729,6 +787,8 @@ public abstract class ConstantInstruction extends Instruction {
return new LazyMethodHandle(OP_ldc_w, cp, index);
case CONSTANT_MethodType:
return new LazyMethodType(OP_ldc_w, cp, index);
case CONSTANT_InvokeDynamic:
return new LazyInvokeDynamic(OP_ldc_w, cp, index);
default:
return null;
}

View File

@ -10,6 +10,8 @@
*******************************************************************************/
package com.ibm.wala.shrikeBT;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
/**
* This class provides read-only access to a constant pool. It gets subclassed for each class reader/editor toolkit you want to work
* with.
@ -121,4 +123,10 @@ public abstract class ConstantPoolReader {
*/
public abstract String getConstantPoolHandleType(int index);
public abstract BootstrapMethod getConstantPoolDynamicBootstrap(int index);
public abstract String getConstantPoolDynamicName(int index);
public abstract String getConstantPoolDynamicType(int index);
}

View File

@ -406,7 +406,7 @@ public interface Constants {
public static final short OP_invokeinterface = 185;
public static final short OP_xxxunusedxxx = 186;
public static final short OP_invokedynamic = 186;
public static final short OP_new = 187;
@ -490,6 +490,8 @@ public interface Constants {
public static final byte CONSTANT_MethodType = 16;
public static final byte CONSTANT_InvokeDynamic = 18;
public static final byte T_BOOLEAN = 4;
public static final byte T_CHAR = 5;

View File

@ -639,6 +639,12 @@ public abstract class Decoder implements Constants {
index += 4;
break;
}
case OP_invokedynamic: {
int m = decodeUShort(index);
i = InvokeDynamicInstruction.make(constantPool, m, opcode);
index += 4;
break;
}
case OP_new:
i = NewInstruction.make(constantPool.getConstantPoolClassType(decodeUShort(index)), 0);
index += 2;

View File

@ -0,0 +1,156 @@
package com.ibm.wala.shrikeBT;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
public class InvokeDynamicInstruction extends Instruction implements IInvokeInstruction {
protected BootstrapMethod bootstrap;
protected String methodName;
protected String methodType;
public InvokeDynamicInstruction(short opcode, BootstrapMethod bootstrap, String methodName, String methodType) {
super(opcode);
this.bootstrap = bootstrap;
this.methodName = methodName;
this.methodType = methodType;
}
@Override
public boolean isPEI() {
return true;
}
@Override
public IDispatch getInvocationCode() {
int invokeType = getBootstrap().invokeType();
switch (invokeType) {
case 5: return Dispatch.VIRTUAL;
case 6: return Dispatch.STATIC;
case 7: return Dispatch.SPECIAL;
case 9: return Dispatch.INTERFACE;
default:
throw new Error("unexpected dynamic invoke type " + invokeType);
}
}
@Override
final public int getPoppedCount() {
return (getInvocationCode().equals(Dispatch.STATIC) ? 0 : 1) + Util.getParamsCount(getMethodSignature());
}
@Override
final public String getPushedType(String[] types) {
String t = Util.getReturnType(getMethodSignature());
if (t.equals(Constants.TYPE_void)) {
return null;
} else {
return t;
}
}
@Override
final public byte getPushedWordSize() {
String t = getMethodSignature();
int index = t.lastIndexOf(')');
return Util.getWordSize(t, index + 1);
}
public BootstrapMethod getBootstrap() {
return bootstrap;
}
@Override
public String getMethodSignature() {
return methodType;
}
@Override
public String getMethodName() {
return methodName;
}
@Override
public String getClassType() {
return getBootstrap().methodClass();
}
@Override
public void visit(Visitor v) {
v.visitInvoke(this);
}
@Override
public String toString() {
return "InvokeDynamic [" + getBootstrap() + "] " + getMethodName() + getMethodSignature();
}
final static class Lazy extends InvokeDynamicInstruction {
final private ConstantPoolReader cp;
final private int index;
Lazy(short opcode, ConstantPoolReader cp, int index) {
super(opcode, null, null, null);
this.index = index;
this.cp = cp;
}
int getCPIndex() {
return index;
}
@Override
public BootstrapMethod getBootstrap() {
if (bootstrap == null) {
bootstrap = cp.getConstantPoolDynamicBootstrap(index);
}
return bootstrap;
}
@Override
public String getMethodName() {
if (methodName == null) {
methodName = cp.getConstantPoolDynamicName(index);
}
return methodName;
}
@Override
public String getMethodSignature() {
if (methodType == null) {
methodType = cp.getConstantPoolDynamicType(index);
}
return methodType;
}
}
public CallSite bootstrap(Class cl) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
ClassLoader bootstrapCL = cl.getClassLoader();
Class<?> bootstrapClass = Class.forName(getBootstrap().methodClass().replace('/', '.'), false, bootstrapCL);
MethodType bt = MethodType.fromMethodDescriptorString(bootstrap.methodType(), bootstrapCL);
Method bootstrap = bootstrapClass.getMethod(this.bootstrap.methodName(), bt.parameterList().toArray(new Class[ bt.parameterCount() ]));
Object[] args = new Object[ bt.parameterCount() ];
args[0] = MethodHandles.lookup().in(cl);
args[1] = getMethodName();
args[2] = MethodType.fromMethodDescriptorString(getMethodSignature(), cl.getClassLoader());
for(int i = 3; i < bt.parameterCount(); i++) {
args[i] = getBootstrap().callArgument(i-3);
}
return (CallSite) bootstrap.invoke(null, args);
}
static InvokeDynamicInstruction make(ConstantPoolReader cp, int index, int mode) {
if (mode != OP_invokedynamic) {
throw new IllegalArgumentException("Unknown mode: " + mode);
}
return new Lazy((short) mode, cp, index);
}
}

View File

@ -35,6 +35,7 @@ import com.ibm.wala.shrikeBT.IShiftInstruction;
import com.ibm.wala.shrikeBT.IStoreInstruction;
import com.ibm.wala.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.InvokeDynamicInstruction;
import com.ibm.wala.shrikeBT.MethodData;
import com.ibm.wala.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrikeBT.NewInstruction;
@ -235,7 +236,7 @@ public final class Verifier extends Analyzer {
String classType = instruction.getClassType();
String signature = instruction.getMethodSignature();
String thisClass = instruction.getInvocationCode() == IInvokeInstruction.Dispatch.STATIC ? null : classType;
String thisClass = instruction.getInvocationCode() == IInvokeInstruction.Dispatch.STATIC ? null : classType;
String[] params = Util.getParamsTypes(thisClass, signature);
for (int i = 0; i < params.length; i++) {

View File

@ -12,6 +12,7 @@ package com.ibm.wala.shrikeBT.shrikeCT;
import com.ibm.wala.shrikeBT.ConstantPoolReader;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.CodeReader;
import com.ibm.wala.shrikeCT.ConstantPoolParser;
@ -199,5 +200,32 @@ final public class CTDecoder extends Decoder {
throw convertToError(e);
}
}
@Override
public BootstrapMethod getConstantPoolDynamicBootstrap(int index) {
try {
return cp.getCPDynBootstrap(index);
} catch (InvalidClassFileException e) {
throw convertToError(e);
}
}
@Override
public String getConstantPoolDynamicName(int index) {
try {
return cp.getCPDynName(index);
} catch (InvalidClassFileException e) {
throw convertToError(e);
}
}
@Override
public String getConstantPoolDynamicType(int index) {
try {
return cp.getCPDynType(index);
} catch (InvalidClassFileException e) {
throw convertToError(e);
}
}
}
}

View File

@ -0,0 +1,129 @@
/*******************************************************************************
* Copyright (c) 2002,2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.shrikeBT.shrikeCT.tools;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import com.ibm.wala.shrikeBT.Decoder.InvalidBytecodeException;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.InvokeDynamicInstruction;
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
import com.ibm.wala.shrikeBT.shrikeCT.ClassInstrumenter;
import com.ibm.wala.shrikeBT.shrikeCT.OfflineInstrumenter;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.CodeReader;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
public class BootstrapDumper {
final private PrintWriter w;
/**
* Get ready to print a class to the given output stream.
*/
public BootstrapDumper(PrintWriter w) {
this.w = w;
}
public static void main(String[] args) throws Exception {
OfflineInstrumenter oi = new OfflineInstrumenter();
String[] classpathEntries = oi.parseStandardArgs(args);
PrintWriter w = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
BootstrapDumper p = new BootstrapDumper(w);
URL[] urls = new URL[ classpathEntries.length-1 ];
for(int i = 1; i < classpathEntries.length; i++) {
System.err.println(classpathEntries[i]);
urls[i-1] = new URL(classpathEntries[i]);
}
URLClassLoader image = new URLClassLoader(urls, BootstrapDumper.class.getClassLoader().getParent());
System.err.println(image);
ClassInstrumenter ci;
oi.beginTraversal();
while ((ci = oi.nextClass()) != null) {
try {
p.doClass(image, ci.getReader());
} finally {
w.flush();
}
}
oi.close();
}
private void dumpAttributes(Class cl, ClassReader cr, int i, ClassReader.AttrIterator attrs) throws InvalidClassFileException,
InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (; attrs.isValid(); attrs.advance()) {
String name = attrs.getName();
if (name.equals("Code")) {
CodeReader code = new CodeReader(attrs);
CTDecoder decoder = new CTDecoder(code);
decoder.decode();
IInstruction[] insts = decoder.getInstructions();
for(IInstruction inst : insts) {
if (inst instanceof InvokeDynamicInstruction) {
CallSite target = ((InvokeDynamicInstruction)inst).bootstrap(cl);
w.println(target.dynamicInvoker());
w.println(target.getTarget());
/*
* only in Java 8. Uncomment when we mandate Java 8.
try {
w.println(MethodHandles.reflectAs(Method.class, target.dynamicInvoker()));
} catch (Throwable e) {
System.out.println(e);
}
*/
}
}
}
}
}
/**
* Print a class.
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws SecurityException
* @throws NoSuchMethodException
* @throws ClassNotFoundException
*
* @throws IllegalArgumentException if cr is null
*/
public void doClass(ClassLoader image, final ClassReader cr) throws InvalidClassFileException, InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (cr == null) {
throw new IllegalArgumentException("cr is null");
}
ClassReader.AttrIterator attrs = new ClassReader.AttrIterator();
cr.initClassAttributeIterator(attrs);
int methodCount = cr.getMethodCount();
for (int i = 0; i < methodCount; i++) {
cr.initMethodAttributeIterator(i, attrs);
dumpAttributes(Class.forName(cr.getName(), false, image), cr, i, attrs);
}
}
}

View File

@ -0,0 +1,122 @@
package com.ibm.wala.shrikeCT;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
public class BootstrapMethodsReader extends AttributeReader {
public interface BootstrapMethod {
int invokeType();
String methodClass();
String methodName();
String methodType();
Object callArgument(int i);
int callArgumentKind(int i);
}
private BootstrapMethod entries[];
protected BootstrapMethodsReader(AttrIterator attr) throws InvalidClassFileException {
super(attr, "BootstrapMethods");
readBootstrapEntries();
}
private void readBootstrapEntries() throws InvalidClassFileException {
final ConstantPoolParser cp = cr.getCP();
entries = new BootstrapMethod[cr.getUShort(attr + 6)];
int base = 8;
for(int i = 0; i < entries.length; i++) {
final int methodHandleOffset = cr.getUShort(attr + base);
final int argsBase = attr + base + 4;
final int argumentCount = cr.getUShort(attr + base + 2);
entries[i] = new BootstrapMethod() {
private final int invokeType = cp.getCPHandleKind(methodHandleOffset);
private final String methodClass = cp.getCPHandleClass(methodHandleOffset);
private final String methodName = cp.getCPHandleName(methodHandleOffset);
private final String methodType = cp.getCPHandleType(methodHandleOffset);
@Override
public String toString() {
return methodClass + ":" + methodName + methodType;
}
@Override
public int invokeType() {
return invokeType;
}
@Override
public String methodClass() {
return methodClass;
}
@Override
public String methodName() {
return methodName;
}
@Override
public String methodType() {
return methodType;
}
@Override
public int callArgumentKind(int i) {
assert 0 <= i && i < argumentCount;
int index = argsBase + (2*i);
return cp.getItemType(cr.getUShort(index));
}
@Override
public Object callArgument(int i) {
try {
int index = cr.getUShort(argsBase + (2*i));
int t = callArgumentKind(i);
switch (t) {
case ClassConstants.CONSTANT_Utf8:
return cp.getCPUtf8(index);
case ClassConstants.CONSTANT_Class:
return cp.getCPClass(index);
case ClassConstants.CONSTANT_String:
return cp.getCPString(index);
case ClassConstants.CONSTANT_Integer:
return cp.getCPInt(index);
case ClassConstants.CONSTANT_Float:
return cp.getCPFloat(index);
case ClassConstants.CONSTANT_Double:
return cp.getCPDouble(index);
case ClassConstants.CONSTANT_Long:
return cp.getCPLong(index);
case ClassConstants.CONSTANT_MethodHandle:
String className = cp.getCPHandleClass(index);
String eltName = cp.getCPHandleName(index);
String eltDesc = cp.getCPHandleType(index);
return new ReferenceToken(className, eltName, eltDesc);
case ClassConstants.CONSTANT_MethodType:
return cp.getCPMethodType(index);
default:
assert false : "invalid type " + t;
}
} catch (IllegalArgumentException e) {
assert false : e;
} catch (InvalidClassFileException e) {
assert false : e;
}
return null;
}
};
base += (argumentCount*2) + 4;
}
}
public int count() {
return entries.length;
}
public BootstrapMethod getEntry(int i) {
return entries[i];
}
}

View File

@ -10,23 +10,102 @@
*******************************************************************************/
package com.ibm.wala.shrikeCT;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
/**
* A ConstantPoolParser provides read-only access to the constant pool of a class file.
*/
public final class ConstantPoolParser implements ClassConstants {
public static class ReferenceToken {
private final String className;
private final String elementName;
private final String descriptor;
public ReferenceToken(String className, String elementName, String descriptor) {
super();
this.className = className;
this.elementName = elementName;
this.descriptor = descriptor;
}
public String getClassName() {
return className;
}
public String getElementName() {
return elementName;
}
public String getDescriptor() {
return descriptor;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((className == null) ? 0 : className.hashCode());
result = prime * result + ((descriptor == null) ? 0 : descriptor.hashCode());
result = prime * result + ((elementName == null) ? 0 : elementName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReferenceToken other = (ReferenceToken) obj;
if (className == null) {
if (other.className != null)
return false;
} else if (!className.equals(other.className))
return false;
if (descriptor == null) {
if (other.descriptor != null)
return false;
} else if (!descriptor.equals(other.descriptor))
return false;
if (elementName == null) {
if (other.elementName != null)
return false;
} else if (!elementName.equals(other.elementName))
return false;
return true;
}
}
final private byte[] bytes;
private int[] cpOffsets;
private String[] cpItems;
private BootstrapMethodsReader invokeDynamicBootstraps;
// TODO: use JVM spec limit here?
private final static int MAX_CP_ITEMS = Integer.MAX_VALUE / 4;
private BootstrapMethodsReader getBootstrapReader() throws InvalidClassFileException {
if (invokeDynamicBootstraps == null) {
ClassReader thisClass = new ClassReader(bytes);
AttrIterator attrs = new AttrIterator();
thisClass.initClassAttributeIterator(attrs);
invokeDynamicBootstraps = new BootstrapMethodsReader(attrs);
}
return invokeDynamicBootstraps;
}
/**
* @param bytes the raw class file data
* @param offset the start of the constant pool data
* @param itemCount the number of items in the pool
* @param classReader
*/
public ConstantPoolParser(byte[] bytes, int offset, int itemCount) throws InvalidClassFileException {
this.bytes = bytes;
@ -398,6 +477,56 @@ public final class ConstantPoolParser implements ClassConstants {
return getDouble(offset + 1);
}
/**
* @return the BootstrapMethodTable index of the bootstrap method for this invokedynamic
*/
public BootstrapMethod getCPDynBootstrap(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_InvokeDynamic) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not an InvokeDynamic");
}
try {
int index = getUShort(offset + 1);
return getBootstrapReader().getEntry(index);
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(offset, "Invalid Ref class at constant pool item #" + i + ": " + ex.getMessage());
}
}
public String getCPDynName(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_InvokeDynamic) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not an InvokeDynamic");
}
try {
return getCPNATName(getUShort(offset + 3));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(offset, "Invalid Ref class at constant pool item #" + i + ": " + ex.getMessage());
}
}
public String getCPDynType(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_InvokeDynamic) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not an InvokeDynamic");
}
try {
return getCPNATType(getUShort(offset + 3));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(offset, "Invalid Ref class at constant pool item #" + i + ": " + ex.getMessage());
}
}
private InvalidClassFileException invalidUtf8(int item, int offset) {
return new InvalidClassFileException(offset, "Constant pool item #" + item + " starting at " + cpOffsets[item]
+ ", is an invalid Java Utf8 string (byte is " + getByte(offset) + ")");
@ -494,6 +623,9 @@ public final class ConstantPoolParser implements ClassConstants {
case CONSTANT_MethodType:
itemLen = 2;
break;
case CONSTANT_InvokeDynamic:
itemLen = 4;
break;
default:
throw new InvalidClassFileException(offset, "unknown constant pool entry type" + tag);
}