diff --git a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JSSSAPropagationCallGraphBuilder.java b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JSSSAPropagationCallGraphBuilder.java index 83fedd8e1..bf3d0ab77 100755 --- a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JSSSAPropagationCallGraphBuilder.java +++ b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JSSSAPropagationCallGraphBuilder.java @@ -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 instanceKeys, PointerKeyFactory pointerKeys, InstanceKeyFactory iKeyFactory) { @@ -913,19 +925,7 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph } }; - - private final FieldReference prototypeRef; - { - FieldReference x = null; - try { - byte[] utf8 = "__proto__".getBytes("UTF-8"); - x = FieldReference.findOrCreate(JavaScriptTypes.Root, Atom.findOrCreate(utf8, 0, utf8.length), JavaScriptTypes.Root); - } catch (UnsupportedEncodingException e) { - assert false; - } - prototypeRef = x; - } - + @Override public void visitSetPrototype(SetPrototype instruction) { visitPutInternal(instruction.getUse(1), instruction.getUse(0), false, prototypeRef); diff --git a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JSZeroOrOneXCFABuilder.java b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JSZeroOrOneXCFABuilder.java index f50564d82..84199f534 100755 --- a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JSZeroOrOneXCFABuilder.java +++ b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JSZeroOrOneXCFABuilder.java @@ -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); diff --git a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JavaScriptConstructorInstanceKeys.java b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JavaScriptConstructorInstanceKeys.java index 8bea6b236..689400eba 100644 --- a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JavaScriptConstructorInstanceKeys.java +++ b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/JavaScriptConstructorInstanceKeys.java @@ -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 diff --git a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/PropertyNameContextSelector.java b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/PropertyNameContextSelector.java index 79cd70e9b..17a4167ce 100755 --- a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/PropertyNameContextSelector.java +++ b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/PropertyNameContextSelector.java @@ -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; diff --git a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/loader/JavaScriptLoader.java b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/loader/JavaScriptLoader.java index 2a78e2bdc..5e7a97935 100755 --- a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/loader/JavaScriptLoader.java +++ b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/loader/JavaScriptLoader.java @@ -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); diff --git a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/AstSSAPropagationCallGraphBuilder.java b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/AstSSAPropagationCallGraphBuilder.java index 0c7430ebc..da9d0020a 100644 --- a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/AstSSAPropagationCallGraphBuilder.java +++ b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/AstSSAPropagationCallGraphBuilder.java @@ -623,6 +623,9 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa System.err.println(("looking up lexical parent " + definer)); Set 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); diff --git a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/CrossLanguageInstanceKeys.java b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/CrossLanguageInstanceKeys.java index 80b8fd4a4..b1d11c1a3 100644 --- a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/CrossLanguageInstanceKeys.java +++ b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/CrossLanguageInstanceKeys.java @@ -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); } } diff --git a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/ScopeMappingInstanceKeys.java b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/ScopeMappingInstanceKeys.java index 05b5e7569..146beee77 100644 --- a/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/ScopeMappingInstanceKeys.java +++ b/com.ibm.wala.cast/source/java/com/ibm/wala/cast/ipa/callgraph/ScopeMappingInstanceKeys.java @@ -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) { diff --git a/com.ibm.wala.core.testdata/build.properties b/com.ibm.wala.core.testdata/build.properties index 894aadec0..68c985a89 100644 --- a/com.ibm.wala.core.testdata/build.properties +++ b/com.ibm.wala.core.testdata/build.properties @@ -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,\ diff --git a/com.ibm.wala.core.testdata/build.xml b/com.ibm.wala.core.testdata/build.xml index 511f105a7..cafa750df 100644 --- a/com.ibm.wala.core.testdata/build.xml +++ b/com.ibm.wala.core.testdata/build.xml @@ -14,15 +14,6 @@ - - - - - - - @@ -60,6 +51,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -70,7 +87,7 @@ - + diff --git a/com.ibm.wala.core.testdata/ocaml/hello_hash.ml b/com.ibm.wala.core.testdata/ocaml/hello_hash.ml new file mode 100644 index 000000000..04f724188 --- /dev/null +++ b/com.ibm.wala.core.testdata/ocaml/hello_hash.ml @@ -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";; diff --git a/com.ibm.wala.core.tests/launchers/ClassPrinter.launch b/com.ibm.wala.core.tests/launchers/ClassPrinter.launch deleted file mode 100644 index c31008c09..000000000 --- a/com.ibm.wala.core.tests/launchers/ClassPrinter.launch +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/com.ibm.wala.core.tests/launchers/wala.core.launch b/com.ibm.wala.core.tests/launchers/wala.core.launch index 1556677b7..35ba24ef0 100644 --- a/com.ibm.wala.core.tests/launchers/wala.core.launch +++ b/com.ibm.wala.core.tests/launchers/wala.core.launch @@ -24,7 +24,8 @@ + - + diff --git a/com.ibm.wala.core.tests/pom.xml b/com.ibm.wala.core.tests/pom.xml index 63c8bccd9..81ebde6c3 100644 --- a/com.ibm.wala.core.tests/pom.xml +++ b/com.ibm.wala.core.tests/pom.xml @@ -20,7 +20,7 @@ test ${project.build.outputDirectory} - -Xmx800M -Dcom.ibm.wala.junit.profile=short -Dcom.ibm.wala.junit.analyzingJar=true -ea + -XX:-UseSplitVerifier -Xmx800M -Dcom.ibm.wala.junit.profile=short -Dcom.ibm.wala.junit.analyzingJar=true -ea true diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/Java7CallGraphTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/Java7CallGraphTest.java new file mode 100644 index 000000000..2d235219f --- /dev/null +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/callGraph/Java7CallGraphTest.java @@ -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 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() { + @Override + public boolean test(MethodReference t) { + String s = t.toString(); + return s.contains("Lpack/") || s.contains("Locaml/stdlib/"); + } + }); + } + +} diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTest.java index 77331566f..d86eef67b 100644 --- a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTest.java +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTest.java @@ -1,108 +1,28 @@ package com.ibm.wala.core.tests.shrike; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.zip.GZIPInputStream; -import org.junit.Assert; import org.junit.Test; import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil; import com.ibm.wala.core.tests.util.TestConstants; -import com.ibm.wala.core.tests.util.WalaTestCase; import com.ibm.wala.ipa.callgraph.AnalysisCache; import com.ibm.wala.ipa.callgraph.AnalysisOptions; import com.ibm.wala.ipa.callgraph.AnalysisScope; -import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.CallGraph; import com.ibm.wala.ipa.callgraph.Entrypoint; import com.ibm.wala.ipa.cha.ClassHierarchy; import com.ibm.wala.ipa.cha.ClassHierarchyException; -import com.ibm.wala.shrike.cg.DynamicCallGraph; import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException; import com.ibm.wala.shrikeCT.InvalidClassFileException; -import com.ibm.wala.types.ClassLoaderReference; -import com.ibm.wala.types.MethodReference; -import com.ibm.wala.types.Selector; -import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.CancelException; -import com.ibm.wala.util.io.TemporaryFile; -public class DynamicCallGraphTest extends WalaTestCase { - - private static String getClasspathEntry(String elt) { - for (String s : System.getProperty("java.class.path").split(File.pathSeparator)) { - if (s.indexOf(elt) >= 0) { - File e = new File(s); - Assert.assertTrue(elt + " expected to exist", e.exists()); - if (e.isDirectory() && !s.endsWith("/")) { - s = s + "/"; - } - return s; - } - } - Assert.assertFalse("cannot find " + elt, true); - return null; - } - +public class DynamicCallGraphTest extends DynamicCallGraphTestBase { + private static String testJarLocation = getClasspathEntry("com.ibm.wala.core.testdata"); - private boolean instrumentedJarBuilt = false; - - private static String instrumentedJarLocation = System.getProperty("java.io.tmpdir") + File.separator + "test.jar"; - - private static String cgLocation = System.getProperty("java.io.tmpdir") + File.separator + "cg.txt"; - - private void instrument() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException { - if (! instrumentedJarBuilt) { - System.err.println("core data jar to instrument: " + testJarLocation); - DynamicCallGraph.main(new String[]{testJarLocation, "-o", instrumentedJarLocation}); - Assert.assertTrue("expected to create /tmp/test.jar", new File(instrumentedJarLocation).exists()); - instrumentedJarBuilt = true; - } - } - - private void run(String exclusionsFile) throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { - String shrikeBin = getClasspathEntry("com.ibm.wala.shrike"); - String utilBin = getClasspathEntry("com.ibm.wala.util"); - URLClassLoader jcl = new URLClassLoader(new URL[]{ new URL("file://" + instrumentedJarLocation), new URL("file://" + shrikeBin), new URL("file://" + utilBin) }, DynamicCallGraphTest.class.getClassLoader().getParent()); - - Class testClass = jcl.loadClass("dynamicCG.MainClass"); - Assert.assertNotNull(testClass); - Method testMain = testClass.getDeclaredMethod("main", String[].class); - Assert.assertNotNull(testMain); - - System.setProperty("dynamicCGFile", cgLocation); - if (exclusionsFile != null) { - File tmpFile = TemporaryFile.urlToFile("exclusions.txt", getClass().getClassLoader().getResource(exclusionsFile)); - System.setProperty("dynamicCGFilter", tmpFile.getCanonicalPath()); - } - try { - testMain.invoke(null, (Object)new String[0]); - } catch (Throwable e) { - // exceptions here are from program being instrumented - // this is fine, since we are collecting its call graph - // and exceptions are possible behavior. - } - - // the VM is not exiting, so stop tracing explicitly - Class runtimeClass = jcl.loadClass("com.ibm.wala.shrike.cg.Runtime"); - Assert.assertNotNull(runtimeClass); - Method endTrace = runtimeClass.getDeclaredMethod("endTrace"); - Assert.assertNotNull(endTrace); - endTrace.invoke(null); - - Assert.assertTrue("expected to create call graph", new File(System.getProperty("dynamicCGFile")).exists()); - } + private static String testMain = "dynamicCG.MainClass"; private CallGraph staticCG(String exclusionsFile) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException { AnalysisScope scope = CallGraphTestUtil.makeJ2SEAnalysisScope(TestConstants.WALA_TESTDATA, exclusionsFile != null? exclusionsFile: CallGraphTestUtil.REGRESSION_EXCLUSIONS); @@ -111,54 +31,21 @@ public class DynamicCallGraphTest extends WalaTestCase { AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints); return CallGraphTestUtil.buildZeroOneCFA(options, new AnalysisCache(), cha, scope, false); } - - private void check(CallGraph staticCG) throws IOException { - BufferedReader dynamicEdgesFile = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(System.getProperty("dynamicCGFile"))))); - String line; - int lines = 0; - while ((line = dynamicEdgesFile.readLine()) != null) { - lines++; - StringTokenizer edge = new StringTokenizer(line, "\t"); - - CGNode caller; - String callerClass = edge.nextToken(); - if ("root".equals(callerClass)) { - caller = staticCG.getFakeRootNode(); - } else { - String callerMethod = edge.nextToken(); - Set 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 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); } + } diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTestBase.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTestBase.java new file mode 100644 index 000000000..ea8829c2c --- /dev/null +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/shrike/DynamicCallGraphTestBase.java @@ -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.truePred()); + } + + protected void checkEdges(CallGraph staticCG, Predicate filter) throws IOException { + check(staticCG, new EdgesTest() { + @Override + public void edgesTest(CallGraph staticCG, CGNode caller, MethodReference calleeRef) { + Set 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.truePred()); + } + + protected void checkNodes(CallGraph staticCG, Predicate filter) throws IOException { + final Set 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 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 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); + } + +} diff --git a/com.ibm.wala.core/META-INF/MANIFEST.MF b/com.ibm.wala.core/META-INF/MANIFEST.MF index 3a5c69b10..8e0d6274b 100644 --- a/com.ibm.wala.core/META-INF/MANIFEST.MF +++ b/com.ibm.wala.core/META-INF/MANIFEST.MF @@ -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, diff --git a/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java b/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java new file mode 100644 index 000000000..46b9df498 --- /dev/null +++ b/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/java7/MethodHandles.java @@ -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> irs = HashMapFactory.make(); + + @Override + public Iterator iterateNewSites(CGNode node) { + return getIR(node).iterateNewSites(); + } + + public Iterator iterateFields(CGNode node, Filter filter) { + return + new MapIterator( + new FilterIterator(getIR(node).iterateNormalInstructions(), filter), + new Function() { + @Override + public FieldReference apply(SSAInstruction object) { + return ((SSAFieldAccessInstruction)object).getDeclaredField(); + } + }); + } + + @Override + public Iterator iterateFieldsRead(CGNode node) { + return iterateFields(node, new Filter() { + @Override + public boolean accepts(SSAInstruction o) { + return o instanceof SSAGetInstruction; + } + }); + } + + @Override + public Iterator iterateFieldsWritten(CGNode node) { + return iterateFields(node, new Filter() { + @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 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(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 getCFG(CGNode n) { + return getIR(n).getControlFlowGraph(); + } + + } +} diff --git a/com.ibm.wala.core/src/com/ibm/wala/analysis/typeInference/TypeInference.java b/com.ibm.wala.core/src/com/ibm/wala/analysis/typeInference/TypeInference.java index 1aeed8022..38da0c3d9 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/analysis/typeInference/TypeInference.java +++ b/com.ibm.wala.core/src/com/ibm/wala/analysis/typeInference/TypeInference.java @@ -529,7 +529,7 @@ public class TypeInference extends SSAInference 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)); } diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/JavaLanguage.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/JavaLanguage.java index 69971453c..7b4a73126 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/JavaLanguage.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/JavaLanguage.java @@ -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; diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/Language.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/Language.java index 896f88a52..c5d70c4b6 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/Language.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/Language.java @@ -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); /** diff --git a/com.ibm.wala.core/src/com/ibm/wala/demandpa/alg/ThisFilteringHeapModel.java b/com.ibm.wala.core/src/com/ibm/wala/demandpa/alg/ThisFilteringHeapModel.java index 673d2d377..d7d732c00 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/demandpa/alg/ThisFilteringHeapModel.java +++ b/com.ibm.wala.core/src/com/ibm/wala/demandpa/alg/ThisFilteringHeapModel.java @@ -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 diff --git a/com.ibm.wala.core/src/com/ibm/wala/demandpa/flowgraph/DemandPointerFlowGraph.java b/com.ibm.wala.core/src/com/ibm/wala/demandpa/flowgraph/DemandPointerFlowGraph.java index f5ace68e0..fb9b9b475 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/demandpa/flowgraph/DemandPointerFlowGraph.java +++ b/com.ibm.wala.core/src/com/ibm/wala/demandpa/flowgraph/DemandPointerFlowGraph.java @@ -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); diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/impl/Util.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/impl/Util.java index aa7ff380d..c531ad5a8 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/impl/Util.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/impl/Util.java @@ -423,15 +423,18 @@ public class Util { */ public static SSAPropagationCallGraphBuilder makeZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache, IClassHierarchy cha, AnalysisScope scope) { + return makeZeroOneContainerCFABuilder(options, cache, cha, scope, null, null); + } + + public static SSAPropagationCallGraphBuilder makeZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache, + IClassHierarchy cha, AnalysisScope scope, ContextSelector appSelector, SSAContextInterpreter appInterpreter) { if (options == null) { throw new IllegalArgumentException("options is null"); } addDefaultSelectors(options, cha); addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha); - ContextSelector appSelector = null; - SSAContextInterpreter appInterpreter = null; - + return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.SMUSH_MANY | ZeroXInstanceKeys.SMUSH_PRIMITIVE_HOLDERS | ZeroXInstanceKeys.SMUSH_STRINGS | ZeroXInstanceKeys.SMUSH_THROWABLES); } diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/AllocationSiteInNodeFactory.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/AllocationSiteInNodeFactory.java index f7b3bb207..e4e51b2da 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/AllocationSiteInNodeFactory.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/AllocationSiteInNodeFactory.java @@ -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); } } diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/ClassBasedInstanceKeys.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/ClassBasedInstanceKeys.java index 7bdb5cf06..c23961736 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/ClassBasedInstanceKeys.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/ClassBasedInstanceKeys.java @@ -12,11 +12,14 @@ package com.ibm.wala.ipa.callgraph.propagation; import com.ibm.wala.classLoader.ArrayClass; import com.ibm.wala.classLoader.IClass; +import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.classLoader.NewSiteReference; import com.ibm.wala.classLoader.ProgramCounter; import com.ibm.wala.ipa.callgraph.AnalysisOptions; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.cha.IClassHierarchy; +import com.ibm.wala.types.Descriptor; +import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.debug.Assertions; @@ -126,15 +129,30 @@ public class ClassBasedInstanceKeys implements InstanceKeyFactory { } @Override - public InstanceKey getInstanceKeyForClassObject(TypeReference type) { - IClass klass = cha.lookupClass(type); - if (klass == null) { - return new ConcreteTypeKey(cha.lookupClass(TypeReference.JavaLangClass)); + public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) { + IClass cls = cha.lookupClass(objType); + assert cls != null : objType; + if (obj instanceof TypeReference) { + IClass klass = cha.lookupClass((TypeReference)obj); + if (klass == null) { + return new ConcreteTypeKey(cls); + } else { + // return the IClass itself, wrapped as a constant! + return new ConstantKey(klass, cls); + } + } else if (obj instanceof MethodReference) { + IMethod m = cha.resolveMethod((MethodReference)obj); + if (m == null) { + return new ConcreteTypeKey(cls); + } else { + return new ConstantKey(m, cls); + } + } else if (obj instanceof Descriptor) { + return new ConstantKey((Descriptor)obj, cls); } else { - // return the IClass itself, wrapped as a constant! - return new ConstantKey(klass, cha.lookupClass(TypeReference.JavaLangClass)); + // other cases + throw new Error(); } - } /** diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/InstanceKeyFactory.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/InstanceKeyFactory.java index 77a1229d0..8b18bbd11 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/InstanceKeyFactory.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/InstanceKeyFactory.java @@ -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); } diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PointerAnalysisImpl.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PointerAnalysisImpl.java index 183bdab6c..81cad23fc 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PointerAnalysisImpl.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PointerAnalysisImpl.java @@ -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); } /* diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java index 13c3abf28..cd895f274 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationCallGraphBuilder.java @@ -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) { diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SSAPropagationCallGraphBuilder.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SSAPropagationCallGraphBuilder.java index 532e73aed..61c2ecc2c 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SSAPropagationCallGraphBuilder.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SSAPropagationCallGraphBuilder.java @@ -612,8 +612,8 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap return getBuilder().getInstanceKeyForPEI(node, instr, type); } - public InstanceKey getInstanceKeyForClassObject(TypeReference type) { - return getBuilder().getInstanceKeyForClassObject(type); + public InstanceKey getInstanceKeyForClassObject(Object obj, TypeReference type) { + return getBuilder().getInstanceKeyForMetadataObject(obj, type); } public CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) { @@ -1385,13 +1385,15 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap @Override public void visitLoadMetadata(SSALoadMetadataInstruction instruction) { PointerKey def = getPointerKeyForLocal(instruction.getDef()); - assert instruction.getType() == TypeReference.JavaLangClass; - InstanceKey iKey = getInstanceKeyForClassObject((TypeReference) instruction.getToken()); - IClass klass = getClassHierarchy().lookupClass((TypeReference) instruction.getToken()); - if (klass != null) { - processClassInitializer(klass); + InstanceKey iKey = getInstanceKeyForClassObject(instruction.getToken(), instruction.getType()); + + if (instruction.getToken() instanceof TypeReference) { + IClass klass = getClassHierarchy().lookupClass((TypeReference) instruction.getToken()); + if (klass != null) { + processClassInitializer(klass); + } } - + if (!contentsAreInvariant(symbolTable, du, instruction.getDef())) { system.newConstraint(def, iKey); } else { @@ -1789,7 +1791,19 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap @Override public int hashCode() { - return node.hashCode() + 90289 * call.hashCode(); + int h = 1; + if (constParams != null) { + for(InstanceKey[] cs : constParams) { + if (cs != null) { + for(InstanceKey c : cs) { + if (c != null) { + h = h ^ c.hashCode(); + } + } + } + } + } + return h * node.hashCode() + 90289 * call.hashCode(); } @Override diff --git a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/SelectiveCPAContext.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SelectiveCPAContext.java similarity index 91% rename from com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/SelectiveCPAContext.java rename to com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SelectiveCPAContext.java index 993f500f3..1912a1466 100644 --- a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/SelectiveCPAContext.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SelectiveCPAContext.java @@ -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 diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SmushedAllocationSiteInstanceKeys.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SmushedAllocationSiteInstanceKeys.java index 99df2b486..e33cbd49c 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SmushedAllocationSiteInstanceKeys.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/SmushedAllocationSiteInstanceKeys.java @@ -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); } } diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/cfa/ZeroXInstanceKeys.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/cfa/ZeroXInstanceKeys.java index 03c9ed271..ef2c398c8 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/cfa/ZeroXInstanceKeys.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/cfa/ZeroXInstanceKeys.java @@ -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); } /** diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/rta/TypeBasedHeapModel.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/rta/TypeBasedHeapModel.java index a2844d8e8..712414e7a 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/rta/TypeBasedHeapModel.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/rta/TypeBasedHeapModel.java @@ -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; } diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/modref/DelegatingExtendedHeapModel.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/modref/DelegatingExtendedHeapModel.java index dbfd755b7..2dd17940a 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/modref/DelegatingExtendedHeapModel.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/modref/DelegatingExtendedHeapModel.java @@ -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 diff --git a/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java b/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java index 0b929b259..f268a8b24 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java +++ b/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java @@ -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, diff --git a/com.ibm.wala.shrike/.classpath b/com.ibm.wala.shrike/.classpath index 4c62a8048..809ee064d 100644 --- a/com.ibm.wala.shrike/.classpath +++ b/com.ibm.wala.shrike/.classpath @@ -2,6 +2,6 @@ - + diff --git a/com.ibm.wala.shrike/META-INF/MANIFEST.MF b/com.ibm.wala.shrike/META-INF/MANIFEST.MF index 5504ee995..9178fbe40 100644 --- a/com.ibm.wala.shrike/META-INF/MANIFEST.MF +++ b/com.ibm.wala.shrike/META-INF/MANIFEST.MF @@ -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 diff --git a/com.ibm.wala.shrike/report.REMOVED.git-id b/com.ibm.wala.shrike/report.REMOVED.git-id new file mode 100644 index 000000000..fafd9f3bc --- /dev/null +++ b/com.ibm.wala.shrike/report.REMOVED.git-id @@ -0,0 +1 @@ +04939cec7b68b22597b102231ba91e19f5797997 \ No newline at end of file diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantInstruction.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantInstruction.java index 4ef9c1e46..36b126f27 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantInstruction.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantInstruction.java @@ -10,11 +10,65 @@ *******************************************************************************/ package com.ibm.wala.shrikeBT; +import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod; +import com.ibm.wala.shrikeCT.ConstantPoolParser; + /** * A ConstantInstruction pushes some constant value onto the stack. */ public abstract class ConstantInstruction extends Instruction { + public static class InvokeDynamicToken { + private final BootstrapMethod bootstrapMethod; + private final String name; + private final String type; + + public InvokeDynamicToken(BootstrapMethod bootstrapMethod, String name, String type) { + this.bootstrapMethod = bootstrapMethod; + this.name = name; + this.type = type; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bootstrapMethod == null) ? 0 : bootstrapMethod.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + InvokeDynamicToken other = (InvokeDynamicToken) obj; + if (bootstrapMethod == null) { + if (other.bootstrapMethod != null) + return false; + } else if (!bootstrapMethod.equals(other.bootstrapMethod)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } + + + } + public static class ClassToken { private final String typeName; @@ -53,56 +107,6 @@ public abstract class ConstantInstruction extends Instruction { } - public static class ReferenceToken { - private final String className; - private final String elementName; - private final String descriptor; - - public ReferenceToken(String className, String elementName, String descriptor) { - super(); - this.className = className; - this.elementName = elementName; - this.descriptor = descriptor; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((className == null) ? 0 : className.hashCode()); - result = prime * result + ((descriptor == null) ? 0 : descriptor.hashCode()); - result = prime * result + ((elementName == null) ? 0 : elementName.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ReferenceToken other = (ReferenceToken) obj; - if (className == null) { - if (other.className != null) - return false; - } else if (!className.equals(other.className)) - return false; - if (descriptor == null) { - if (other.descriptor != null) - return false; - } else if (!descriptor.equals(other.descriptor)) - return false; - if (elementName == null) { - if (other.elementName != null) - return false; - } else if (!elementName.equals(other.elementName)) - return false; - return true; - } - } - public ConstantInstruction(short opcode) { super(opcode); } @@ -631,7 +635,7 @@ public abstract class ConstantInstruction extends Instruction { String className = cp.getConstantPoolHandleClassType(getCPIndex()); String eltName = cp.getConstantPoolHandleName(getCPIndex()); String eltDesc = cp.getConstantPoolHandleType(getCPIndex()); - value = new ReferenceToken(className, eltName, eltDesc); + value = new ConstantPoolParser.ReferenceToken(className, eltName, eltDesc); } return value; } @@ -646,7 +650,61 @@ public abstract class ConstantInstruction extends Instruction { return index; } } - + + static class ConstInvokeDynamic extends ConstantInstruction { + protected Object value; + + public ConstInvokeDynamic(short opcode, Object value) { + super(opcode); + this.value = value; + } + + @Override + public Object getValue() { + return value; + } + + @Override + public String getType() { + return null; + } + + } + + static class LazyInvokeDynamic extends ConstMethodHandle { + final private ConstantPoolReader cp; + + final private int index; + + LazyInvokeDynamic(short opcode, ConstantPoolReader cp, int index) { + super(opcode, null); + this.cp = cp; + this.index = index; + } + + @Override + public Object getValue() { + if (value == null) { + BootstrapMethod bootstrap = cp.getConstantPoolDynamicBootstrap(index); + String name = cp.getConstantPoolDynamicName(index); + String type = cp.getConstantPoolDynamicType(index); + value = new InvokeDynamicToken(bootstrap, name, type); + } + + return value; + } + + @Override + public ConstantPoolReader getLazyConstantPool() { + return cp; + } + + @Override + public int getCPIndex() { + return index; + } + } + /** * @return the constant value pushed: an Integer, a Long, a Float, a Double, a String, or null */ @@ -729,6 +787,8 @@ public abstract class ConstantInstruction extends Instruction { return new LazyMethodHandle(OP_ldc_w, cp, index); case CONSTANT_MethodType: return new LazyMethodType(OP_ldc_w, cp, index); + case CONSTANT_InvokeDynamic: + return new LazyInvokeDynamic(OP_ldc_w, cp, index); default: return null; } diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantPoolReader.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantPoolReader.java index 8003a2b9a..79ee8a3bf 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantPoolReader.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantPoolReader.java @@ -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); + } \ No newline at end of file diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Constants.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Constants.java index 7a72e1f04..b2627f199 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Constants.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Constants.java @@ -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; diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Decoder.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Decoder.java index b4961a35a..92ed2e5a1 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Decoder.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Decoder.java @@ -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; diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/InvokeDynamicInstruction.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/InvokeDynamicInstruction.java new file mode 100644 index 000000000..a7ad11dd2 --- /dev/null +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/InvokeDynamicInstruction.java @@ -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); + } + +} diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/analysis/Verifier.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/analysis/Verifier.java index ecd7b297a..d33cd5224 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/analysis/Verifier.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/analysis/Verifier.java @@ -35,6 +35,7 @@ import com.ibm.wala.shrikeBT.IShiftInstruction; import com.ibm.wala.shrikeBT.IStoreInstruction; import com.ibm.wala.shrikeBT.ITypeTestInstruction; import com.ibm.wala.shrikeBT.IUnaryOpInstruction; +import com.ibm.wala.shrikeBT.InvokeDynamicInstruction; import com.ibm.wala.shrikeBT.MethodData; import com.ibm.wala.shrikeBT.MonitorInstruction; import com.ibm.wala.shrikeBT.NewInstruction; @@ -235,7 +236,7 @@ public final class Verifier extends Analyzer { String classType = instruction.getClassType(); String signature = instruction.getMethodSignature(); - String thisClass = instruction.getInvocationCode() == IInvokeInstruction.Dispatch.STATIC ? null : classType; + String thisClass = instruction.getInvocationCode() == IInvokeInstruction.Dispatch.STATIC ? null : classType; String[] params = Util.getParamsTypes(thisClass, signature); for (int i = 0; i < params.length; i++) { diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/CTDecoder.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/CTDecoder.java index 389e277e8..9bd0a2b1c 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/CTDecoder.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/CTDecoder.java @@ -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); + } + } } } \ No newline at end of file diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/tools/BootstrapDumper.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/tools/BootstrapDumper.java new file mode 100644 index 000000000..f4177d46d --- /dev/null +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/tools/BootstrapDumper.java @@ -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); + } + } +} \ No newline at end of file diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/BootstrapMethodsReader.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/BootstrapMethodsReader.java new file mode 100644 index 000000000..662011059 --- /dev/null +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/BootstrapMethodsReader.java @@ -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]; + } +} diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ConstantPoolParser.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ConstantPoolParser.java index c9a2fa67a..371bdfda8 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ConstantPoolParser.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ConstantPoolParser.java @@ -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); }