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:
parent
53af910339
commit
26f5254e3e
|
@ -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) {
|
||||
|
@ -914,18 +926,6 @@ 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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,\
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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";;
|
|
@ -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>
|
|
@ -24,7 +24,8 @@
|
|||
<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry path="3" projectName="com.ibm.wala.core.testdata" type="1"/> "/>
|
||||
</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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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/");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
@ -112,53 +32,20 @@ public class DynamicCallGraphTest extends WalaTestCase {
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -423,14 +423,17 @@ 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
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(cha.lookupClass(TypeReference.JavaLangClass));
|
||||
return new ConcreteTypeKey(cls);
|
||||
} else {
|
||||
// return the IClass itself, wrapped as a constant!
|
||||
return new ConstantKey<IClass>(klass, cha.lookupClass(TypeReference.JavaLangClass));
|
||||
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 {
|
||||
// other cases
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,12 +1385,14 @@ 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());
|
||||
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);
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
04939cec7b68b22597b102231ba91e19f5797997
|
|
@ -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;
|
||||
}
|
||||
|
@ -647,6 +651,60 @@ public abstract class ConstantInstruction extends Instruction {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue