From 096e2f796f3351bf7c4f296ccd23b6fc98983d40 Mon Sep 17 00:00:00 2001 From: Julian Dolby Date: Thu, 11 Dec 2014 21:48:23 -0500 Subject: [PATCH] 1) new support for function.prototype.apply in field-based CGs 2) fixes to Dalvik bytecode reader 3) fixes to Shrike writing Java 7 byte code --- .../tests/fieldbased/reflective_calls.js | 5 + com.ibm.wala.cast.js/dat/prologue.js | 31 +++- .../FieldBasedCallGraphBuilder.java | 21 ++- .../OptimisticCallgraphBuilder.java | 5 +- .../ibm/wala/classLoader/BytecodeClass.java | 24 ++- .../wala/ipa/callgraph/AnalysisOptions.java | 1 + .../SSAPropagationCallGraphBuilder.java | 26 +-- .../data/AndroidRegressionExclusions.txt | 1 - .../dalvik/drivers/APKCallGraphDriver.java | 4 +- .../callGraph/DalvikCallGraphTestBase.java | 44 ++++- .../ssa/AbstractIntRegisterMachine.java | 6 +- .../ibm/wala/dalvik/ssa/DexSSABuilder.java | 22 ++- .../ibm/wala/shrike/cg/DynamicCallGraph.java | 6 +- .../src/com/ibm/wala/shrike/cg/Runtime.java | 21 ++- .../src/com/ibm/wala/shrikeBT/Compiler.java | 12 ++ .../wala/shrikeBT/ConstantInstruction.java | 7 +- .../ibm/wala/shrikeBT/ConstantPoolReader.java | 2 + .../src/com/ibm/wala/shrikeBT/Constants.java | 2 + .../shrikeBT/InvokeDynamicInstruction.java | 2 +- .../wala/shrikeBT/shrikeCT/CTCompiler.java | 11 ++ .../ibm/wala/shrikeBT/shrikeCT/CTDecoder.java | 9 + .../shrikeBT/shrikeCT/ClassInstrumenter.java | 2 +- .../shrikeCT/tools/BootstrapDumper.java | 2 - .../shrikeCT/tools/BootstrapInstrumentor.java | 157 ++++++++++++++++++ .../wala/shrikeCT/BootstrapMethodsReader.java | 19 ++- .../com/ibm/wala/shrikeCT/ClassReader.java | 2 +- .../com/ibm/wala/shrikeCT/ClassWriter.java | 144 +++++++++++----- .../ibm/wala/shrikeCT/ConstantPoolParser.java | 14 +- 28 files changed, 475 insertions(+), 127 deletions(-) create mode 100644 com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/tools/BootstrapInstrumentor.java diff --git a/com.ibm.wala.cast.js.test.data/examples-src/tests/fieldbased/reflective_calls.js b/com.ibm.wala.cast.js.test.data/examples-src/tests/fieldbased/reflective_calls.js index 3fcdb95da..f21f1d5f5 100644 --- a/com.ibm.wala.cast.js.test.data/examples-src/tests/fieldbased/reflective_calls.js +++ b/com.ibm.wala.cast.js.test.data/examples-src/tests/fieldbased/reflective_calls.js @@ -2,8 +2,13 @@ function f(g) { g(); } +function x() { + +} + function h() { f.call(null, k); + x.apply(null, []); } function k() {} diff --git a/com.ibm.wala.cast.js/dat/prologue.js b/com.ibm.wala.cast.js/dat/prologue.js index 0107fc760..b395d9970 100755 --- a/com.ibm.wala.cast.js/dat/prologue.js +++ b/com.ibm.wala.cast.js/dat/prologue.js @@ -71,8 +71,8 @@ escape = function escape(str){ /************************************************************************/ /* Object properties, see spec 15.2 */ /************************************************************************/ - -Object.prototype = { + +Object$proto$__WALA__ = { prototype: null, @@ -103,13 +103,13 @@ Object.prototype = { } }; - +Object.prototype = Object$proto$__WALA__; /************************************************************************/ /* Function properties, see spec 15.3 */ /************************************************************************/ -local_function.prototype = { +Function$proto$__WALA__ = { constructor: Function, @@ -134,6 +134,8 @@ local_function.prototype = { } }; +local_function.prototype = Function$proto$__WALA__; + local_function.__proto__ = Function.prototype; /************************************************************************/ @@ -142,7 +144,7 @@ local_function.__proto__ = Function.prototype; local_array.__proto__ = Function.prototype; -local_array.prototype = { +Array$proto$__WALA__ = { __proto__: Object.prototype, @@ -327,6 +329,7 @@ Array.isArray = function Array_isArray(a) { return true || false; }; +local_array.prototype = Array$proto$__WALA__; /************************************************************************/ /* String properties, see spec 15.4 */ @@ -334,7 +337,7 @@ Array.isArray = function Array_isArray(a) { local_string.__proto__ = Function.prototype; -local_string.prototype = { +String$proto$__WALA__ = { __proto__: Object.prototype, @@ -436,6 +439,7 @@ local_string.prototype = { }; +local_string.prototype = String$proto$__WALA__; /************************************************************************/ /* Number properties, see spec 15.7 */ @@ -443,7 +447,7 @@ local_string.prototype = { local_number.__proto__ = Function.prototype; -local_number.prototype = { +Number$proto$__WALA__ = { __proto__: Object.prototype, @@ -457,6 +461,7 @@ local_number.prototype = { }; +local_number.prototype = Number$proto$__WALA__; /************************************************************************/ /* Math properties, see spec 15.8 */ @@ -537,7 +542,7 @@ Math = { local_regexp.__proto__ = Function.prototype; -local_regexp.prototype = { +RegExp$proto$__WALA__ = { __proto__: Object.prototype, @@ -553,6 +558,7 @@ local_regexp.prototype = { }; +local_regexp.prototype = RegExp$proto$__WALA__; /************************************************************************/ /* Date properties, see spec 15.9 */ @@ -560,7 +566,7 @@ local_regexp.prototype = { Date = function Date() {}; -Date.prototype = { +Data$proto$__WALA__ = { __proto__: Object.prototype, @@ -636,6 +642,13 @@ Date.now = function Date_now() { return new Date().valueOf(); }; +Date.prototype = Data$proto$__WALA__; + + +/************************************************************************/ +/* internal stuff +/************************************************************************/ + function Error(str) { this.message = new String(); } diff --git a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/FieldBasedCallGraphBuilder.java b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/FieldBasedCallGraphBuilder.java index d3ac06836..36904a2c5 100644 --- a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/FieldBasedCallGraphBuilder.java +++ b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/FieldBasedCallGraphBuilder.java @@ -24,6 +24,8 @@ import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VertexFactor import com.ibm.wala.cast.js.ipa.callgraph.JSAnalysisOptions; import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraph; import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptConstructTargetSelector; +import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptFunctionApplyContextInterpreter; +import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptFunctionApplyTargetSelector; import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptFunctionDotCallTargetSelector; import com.ibm.wala.cast.js.ipa.summaries.JavaScriptConstructorFunctions; import com.ibm.wala.cast.js.ipa.summaries.JavaScriptConstructorFunctions.JavaScriptConstructor; @@ -87,8 +89,7 @@ public abstract class FieldBasedCallGraphBuilder { private MethodTargetSelector setupMethodTargetSelector(IClassHierarchy cha, JavaScriptConstructorFunctions constructors2, AnalysisOptions options) { MethodTargetSelector result = new JavaScriptConstructTargetSelector(constructors2, options.getMethodTargetSelector()); if (options instanceof JSAnalysisOptions && ((JSAnalysisOptions)options).handleCallApply()) { - // TODO handle Function.prototype.apply - result = new JavaScriptFunctionDotCallTargetSelector(result); + result = new JavaScriptFunctionApplyTargetSelector(new JavaScriptFunctionDotCallTargetSelector(result)); } return result; } @@ -141,7 +142,13 @@ public abstract class FieldBasedCallGraphBuilder { // set up call graph final JSCallGraph cg = new JSCallGraph(cha, options, cache); cg.init(); - cg.setInterpreter(new DelegatingSSAContextInterpreter(new AstContextInsensitiveSSAContextInterpreter(options, cache), new DefaultSSAInterpreter(options, cache))); + + // setup context interpreters + DelegatingSSAContextInterpreter interpreter = new DelegatingSSAContextInterpreter(new AstContextInsensitiveSSAContextInterpreter(options, cache), new DefaultSSAInterpreter(options, cache)); + if (options instanceof JSAnalysisOptions && ((JSAnalysisOptions)options).handleCallApply()) { + interpreter = new DelegatingSSAContextInterpreter(new JavaScriptFunctionApplyContextInterpreter(options, cache), interpreter); + } + cg.setInterpreter(interpreter); // set up call edges from fake root to all script nodes AbstractRootMethod fakeRootMethod = (AbstractRootMethod)cg.getFakeRootNode().getMethod(); @@ -167,9 +174,11 @@ public abstract class FieldBasedCallGraphBuilder { IMethod target = targetSelector.getCalleeTarget(caller, site, targetVertex.getConcreteType()); boolean isFunctionPrototypeCall = target != null && target.getName().toString().startsWith(JavaScriptFunctionDotCallTargetSelector.SYNTHETIC_CALL_METHOD_PREFIX); + boolean isFunctionPrototypeApply = target != null + && target.getName().toString().startsWith(JavaScriptFunctionApplyTargetSelector.SYNTHETIC_APPLY_METHOD_PREFIX); - if (isFunctionPrototypeCall) { - handleFunctionPrototypeCallInvocation(flowgraph, monitor, cg, callVertex, caller, site, target); + if (isFunctionPrototypeCall || isFunctionPrototypeApply) { + handleFunctionCallOrApplyInvocation(flowgraph, monitor, cg, callVertex, caller, site, target); } else { addEdgeToJSCallGraph(cg, site, target, caller); @@ -197,7 +206,7 @@ public abstract class FieldBasedCallGraphBuilder { return cg; } - private boolean handleFunctionPrototypeCallInvocation(FlowGraph flowgraph, IProgressMonitor monitor, final JSCallGraph cg, + private boolean handleFunctionCallOrApplyInvocation(FlowGraph flowgraph, IProgressMonitor monitor, final JSCallGraph cg, CallVertex callVertex, CGNode caller, CallSiteReference site, IMethod target) throws CancelException { // use to get 1-level of call string for Function.prototype.call, to diff --git a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/OptimisticCallgraphBuilder.java b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/OptimisticCallgraphBuilder.java index bb5cd895e..a150a3df8 100644 --- a/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/OptimisticCallgraphBuilder.java +++ b/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/OptimisticCallgraphBuilder.java @@ -83,8 +83,11 @@ public class OptimisticCallgraphBuilder extends FieldBasedCallGraphBuilder { // special handling of invocations of Function.prototype.call // TODO: since we've just added some edges to the flow graph, its transitive closure will be // recomputed here, which is slow and unnecessary - if(handleCallApply && edge.snd.getFullName().equals("Lprologue.js/Function_prototype_call")) + if(handleCallApply && + (edge.snd.getFullName().equals("Lprologue.js/Function_prototype_call") || + edge.snd.getFullName().equals("Lprologue.js/Function_prototype_apply"))) { addReflectiveCallEdge(flowgraph, edge.fst, monitor); + } } } } diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java index ef310dda8..1df680a09 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java @@ -329,7 +329,11 @@ public abstract class BytecodeClass implements IClass { */ @Override public Collection getDeclaredInstanceFields() { - return Collections.unmodifiableList(Arrays.asList(instanceFields)); + if (instanceFields == null) { + return Collections.emptySet(); + } else { + return Collections.unmodifiableList(Arrays.asList(instanceFields)); + } } /* @@ -454,21 +458,13 @@ public abstract class BytecodeClass implements IClass { } } } - - // didn't find it yet. special logic for interfaces - if (isInterface() || isAbstract()) { - final Iterator it = getAllImplementedInterfaces().iterator(); - // try each superinterface - while (it.hasNext()) { - IClass k = it.next(); - result = k.getMethod(selector); - if (result != null) { - return result; - } - } + + // no method found + if (inheritCache == null) { + inheritCache = new BimodalMap(5); } + inheritCache.put(selector, null); return null; - } protected void populateFieldArrayFromList(List L, IField[] A) { diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/AnalysisOptions.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/AnalysisOptions.java index b0d27d3cb..1c0e6c15a 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/AnalysisOptions.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/AnalysisOptions.java @@ -67,6 +67,7 @@ public class AnalysisOptions { FULL("full", Integer.MAX_VALUE, false, false, false), APPLICATION_GET_METHOD("application_get_method", Integer.MAX_VALUE, false, false, true), NO_FLOW_TO_CASTS("no_flow_to_casts", 0, false, false, false), + NO_FLOW_TO_CASTS_APPLICATION_GET_METHOD("no_flow_to_casts_application_get_method", 0, false, false, true), NO_METHOD_INVOKE("no_method_invoke", Integer.MAX_VALUE, true, false, false), NO_FLOW_TO_CASTS_NO_METHOD_INVOKE("no_flow_to_casts_no_method_invoke", 0, true, false, false), ONE_FLOW_TO_CASTS_NO_METHOD_INVOKE("one_flow_to_casts_no_method_invoke", 1, true, false, false), 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 8f9750cc2..4e844eaa4 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 @@ -1085,7 +1085,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap @Override public void act(int x) { if (!contentsAreInvariant(symbolTable, du, instruction.getUse(x))) { - pks.add(getBuilder().getPointerKeyForLocal(node, instruction.getUse(x))); + pks.add(getBuilder().getPointerKeyForLocal(node, instruction.getUse(x))); } } }); @@ -1532,7 +1532,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap if (constParams != null && constParams[i] != null) { InstanceKey[] ik = constParams[i]; for (int j = 0; j < ik.length; j++) { - system.newConstraint(formal, ik[j]); + system.newConstraint(formal, ik[j]); } } else { if (instruction.getUse(i) < 0) { @@ -2081,21 +2081,25 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap FilteredPointerKey.TypeFilter filter = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[index]); if (filter != null && !filter.isRootFilter()) { - return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, filter); + return getFilteredPointerKeyForLocal(target, vn, filter); - } else if (index == 0 && !target.getMethod().isStatic()) { + } else { // the context does not select a particular concrete type for the // receiver, so use the type of the method - IClass C = getReceiverClass(target.getMethod()); - if (C.getClassHierarchy().getRootClass().equals(C)) { - return pointerKeyFactory.getPointerKeyForLocal(target, vn); + IClass C; + if (index == 0 && !target.getMethod().isStatic()) { + C = getReceiverClass(target.getMethod()); } else { - return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, new FilteredPointerKey.SingleClassFilter(C)); + C = cha.lookupClass(target.getMethod().getParameterType(index)); } - } else { - return pointerKeyFactory.getPointerKeyForLocal(target, vn); - } + if (C == null || C.getClassHierarchy().getRootClass().equals(C)) { + return getPointerKeyForLocal(target, vn); + } else { + return getFilteredPointerKeyForLocal(target, vn, new FilteredPointerKey.SingleClassFilter(C)); + } + + } } /** diff --git a/com.ibm.wala.dalvik.test/data/AndroidRegressionExclusions.txt b/com.ibm.wala.dalvik.test/data/AndroidRegressionExclusions.txt index 6668f7d8e..1bbcf014c 100644 --- a/com.ibm.wala.dalvik.test/data/AndroidRegressionExclusions.txt +++ b/com.ibm.wala.dalvik.test/data/AndroidRegressionExclusions.txt @@ -1,4 +1,3 @@ java\/awt\/.* javax\/swing\/.* -java\/io\/ObjectStreamClass* org\/apache\/xerces\/.* diff --git a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/drivers/APKCallGraphDriver.java b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/drivers/APKCallGraphDriver.java index 21dc1feb8..7ebe10db5 100644 --- a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/drivers/APKCallGraphDriver.java +++ b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/drivers/APKCallGraphDriver.java @@ -7,6 +7,7 @@ import java.util.Set; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.dalvik.test.callGraph.DalvikCallGraphTestBase; +import com.ibm.wala.ipa.callgraph.AnalysisOptions.ReflectionOptions; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.CallGraph; import com.ibm.wala.ipa.cha.ClassHierarchyException; @@ -46,6 +47,7 @@ public class APKCallGraphDriver { protected static void doApk(File apk) throws IOException, ClassHierarchyException, CancelException { + System.gc(); System.err.println("Analyzing " + apk + "..."); try { long time = System.currentTimeMillis(); @@ -95,7 +97,7 @@ public class APKCallGraphDriver { return "timeout"; } }; - CG = DalvikCallGraphTestBase.makeAPKCallGraph(apk.getAbsolutePath(), pm).fst; + CG = DalvikCallGraphTestBase.makeAPKCallGraph(apk.getAbsolutePath(), pm, ReflectionOptions.NONE).fst; } System.err.println("Analyzed " + apk + " in " + (System.currentTimeMillis() - time)); diff --git a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DalvikCallGraphTestBase.java b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DalvikCallGraphTestBase.java index 14194610b..e2c5cda26 100644 --- a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DalvikCallGraphTestBase.java +++ b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DalvikCallGraphTestBase.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; @@ -28,6 +29,7 @@ import java.util.Set; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.JarFileModule; import com.ibm.wala.classLoader.Module; +import com.ibm.wala.classLoader.NewSiteReference; import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil; import com.ibm.wala.core.tests.shrike.DynamicCallGraphTestBase; import com.ibm.wala.dalvik.classLoader.DexIRFactory; @@ -36,21 +38,25 @@ import com.ibm.wala.dalvik.util.AndroidEntryPointLocator; import com.ibm.wala.dalvik.util.AndroidEntryPointLocator.LocatorFlags; import com.ibm.wala.ipa.callgraph.AnalysisCache; import com.ibm.wala.ipa.callgraph.AnalysisOptions; +import com.ibm.wala.ipa.callgraph.AnalysisOptions.ReflectionOptions; 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.callgraph.AnalysisOptions.ReflectionOptions; import com.ibm.wala.ipa.callgraph.impl.Util; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis; +import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter; import com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder; +import com.ibm.wala.ipa.callgraph.propagation.cfa.DefaultSSAInterpreter; import com.ibm.wala.ipa.cha.ClassHierarchy; import com.ibm.wala.ipa.cha.ClassHierarchyException; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.properties.WalaProperties; import com.ibm.wala.shrikeBT.analysis.Analyzer.FailureException; import com.ibm.wala.shrikeCT.InvalidClassFileException; +import com.ibm.wala.ssa.SSAInstruction; +import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeReference; @@ -59,7 +65,9 @@ import com.ibm.wala.util.MonitorUtil.IProgressMonitor; import com.ibm.wala.util.NullProgressMonitor; import com.ibm.wala.util.Predicate; import com.ibm.wala.util.WalaException; +import com.ibm.wala.util.collections.FilterIterator; import com.ibm.wala.util.collections.HashSetFactory; +import com.ibm.wala.util.collections.MapIterator; import com.ibm.wala.util.collections.Pair; import com.ibm.wala.util.functions.Function; import com.ibm.wala.util.io.TemporaryFile; @@ -128,8 +136,37 @@ public class DalvikCallGraphTestBase extends DynamicCallGraphTestBase { public static Pair> makeAPKCallGraph(String apkFileName) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException { return makeAPKCallGraph(apkFileName, new NullProgressMonitor()); } - + public static Pair> makeAPKCallGraph(String apkFileName, IProgressMonitor monitor) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException { + return makeAPKCallGraph(apkFileName, monitor, ReflectionOptions.ONE_FLOW_TO_CASTS_APPLICATION_GET_METHOD); + } + + private static SSAContextInterpreter makeDefaultInterpreter(AnalysisOptions options, AnalysisCache cache) { + return new DefaultSSAInterpreter(options, cache) { + @Override + public Iterator iterateNewSites(CGNode node) { + return + new MapIterator( + new FilterIterator( + node.getIR().iterateAllInstructions(), + new Predicate() { + @Override + public boolean test(SSAInstruction t) { + return t instanceof SSANewInstruction; + } + }), + new Function() { + @Override + public NewSiteReference apply(SSAInstruction object) { + return ((SSANewInstruction)object).getNewSite(); + } + } + ); + } + }; + } + + public static Pair> makeAPKCallGraph(String apkFileName, IProgressMonitor monitor, ReflectionOptions policy) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException { AnalysisScope scope = AndroidAnalysisScope.setUpAndroidAnalysisScope( new File(apkFileName).toURI(), @@ -150,8 +187,9 @@ public class DalvikCallGraphTestBase extends DynamicCallGraphTestBase { assert ! es.isEmpty(); AnalysisOptions options = new AnalysisOptions(scope, es); - options.setReflectionOptions(ReflectionOptions.ONE_FLOW_TO_CASTS_APPLICATION_GET_METHOD); + options.setReflectionOptions(policy); + // SSAPropagationCallGraphBuilder cgb = Util.makeZeroCFABuilder(options, cache, cha, scope, null, makeDefaultInterpreter(options, cache)); SSAPropagationCallGraphBuilder cgb = Util.makeZeroCFABuilder(options, cache, cha, scope); CallGraph callGraph = cgb.makeCallGraph(options, monitor); diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/AbstractIntRegisterMachine.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/AbstractIntRegisterMachine.java index 48c58d150..7e53abbac 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/AbstractIntRegisterMachine.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/AbstractIntRegisterMachine.java @@ -300,11 +300,7 @@ public abstract class AbstractIntRegisterMachine implements FixedPointConstants // e.printStackTrace(); // return NOT_CHANGED; - if (cfg.getDexMethod().getReference().toString().equals("< Application, Lcom/google/android/gms/tagmanager/v$a, onOpen(Landroid/database/sqlite/SQLiteDatabase;)V >")) { - System.err.println("got here"); - } - - if (!bb.isCatchBlock()) { + if (!bb.isCatchBlock()) { return meet(lhs, rhs, bb, meeter) ? CHANGED : NOT_CHANGED; } else { return meetForCatchBlock(lhs, rhs, bb, meeter) ? CHANGED : NOT_CHANGED; diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/DexSSABuilder.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/DexSSABuilder.java index bbb5107fb..92bd5814a 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/DexSSABuilder.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/DexSSABuilder.java @@ -609,6 +609,7 @@ public class DexSSABuilder extends AbstractIntRegisterMachine { // int val = workingState.pop(); // dex does not use this result, but we need it for the SSA CheckCastInstruction int result = reuseOrCreateDef(); + workingState.setLocal(instruction.object, result); // workingState.push(result); // TypeReference t = instruction.getType(); emitInstruction(insts.CheckCastInstruction(getCurrentInstructionIndex(), result, val, instruction.type, instruction.isPEI())); @@ -1160,9 +1161,6 @@ public class DexSSABuilder extends AbstractIntRegisterMachine { //System.out.println("Instruction: " + getCurrentInstructionIndex()); int val = workingState.getLocal(instruction.source); // int val = workingState.pop(); - int dest = instruction.destination; - int result = reuseOrCreateDef(); - setLocal(dest, result); // workingState.push(result); if(instruction.isConversion()) { @@ -1234,13 +1232,14 @@ public class DexSSABuilder extends AbstractIntRegisterMachine { default: throw new IllegalArgumentException("unknown conversion type "+instruction.op+" in unary instruction: "+instruction); } + int dest = instruction.destination; + int result = reuseOrCreateDef(); + setLocal(dest, result); emitInstruction(insts.ConversionInstruction(getCurrentInstructionIndex(), result, val, fromType, toType, overflows)); } else { - // emitInstruction(insts.UnaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), result, val)); - - if (instruction.op == UnaryOperation.OpID.MOVE) { + if (instruction.op == UnaryOperation.OpID.MOVE) { setLocal(instruction.destination, workingState.getLocal(instruction.source)); } else if (instruction.op == UnaryOperation.OpID.MOVE_WIDE) { @@ -1249,6 +1248,11 @@ public class DexSSABuilder extends AbstractIntRegisterMachine { setLocal(instruction.destination+1, workingState.getLocal(instruction.source)); else setLocal(instruction.destination+1, workingState.getLocal(instruction.source+1)); + } else { + int dest = instruction.destination; + int result = reuseOrCreateDef(); + setLocal(dest, result); + emitInstruction(insts.UnaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), result, val)); } } } @@ -1377,12 +1381,6 @@ public class DexSSABuilder extends AbstractIntRegisterMachine { */ public void build() { try { - if (method.getName().toString().contains("newCNfaPair")) { - System.err.println(method); - } - if (method.getName().toString().contains("expandEscape")) { - System.err.println(method); - } solve(); if (localMap != null) { localMap.finishLocalMap(this); diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/DynamicCallGraph.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/DynamicCallGraph.java index a65ecdd03..16802efc2 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/DynamicCallGraph.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/DynamicCallGraph.java @@ -163,7 +163,7 @@ public class DynamicCallGraph { else w.emit(Util.makeGet(runtime, "NULL_TAG")); // w.emit(ConstantInstruction.make(Constants.TYPE_null, null)); - w.emit(Util.makeInvoke(runtime, "execution", new Class[] {String.class, String.class, Object.class})); + w.emit(Util.makeInvoke(runtime, "execution", new Class[] {Class.class, String.class, Object.class})); } }); @@ -246,10 +246,10 @@ public class DynamicCallGraph { entries.put(p.getCPUtf8(i), i); break; case CONSTANT_String: - entries.put(new CWString(p.getCPString(i)), i); + entries.put(new CWStringItem(p.getCPString(i), CONSTANT_String), i); break; case CONSTANT_Class: - entries.put(new CWClass(p.getCPClass(i)), i); + entries.put(new CWStringItem(p.getCPClass(i), CONSTANT_Class), i); break; case CONSTANT_MethodHandle: case CONSTANT_MethodType: diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/Runtime.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/Runtime.java index 80e214a84..a0463101c 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/Runtime.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrike/cg/Runtime.java @@ -78,8 +78,18 @@ public class Runtime { } }; - public static void execution(String klass, String method, Object receiver) { - if (runtime.filter == null || ! runtime.filter.contains(klass)) { + public static String bashToDescriptor(String className) { + if (className.startsWith("class ")) { + className = className.substring(6); + } + if (className.indexOf('.') >= 0) { + className = className.replace('.', '/'); + } + return className; + } + + public static void execution(Class klass, String method, Object receiver) { + if (runtime.filter == null || ! runtime.filter.contains(bashToDescriptor(klass.getName()))) { if (runtime.output != null) { String caller = runtime.callStacks.get().peek(); @@ -90,21 +100,22 @@ public class Runtime { // frames: me(0), callee(1), caller(2) StackTraceElement callerFrame = stack[2]; if (! caller.contains(callerFrame.getMethodName()) || - ! caller.contains(callerFrame.getClassName().replace('.', '/'))) { + ! caller.contains(bashToDescriptor(callerFrame.getClassName()))) { break checkValid; } } } - String line = caller + "\t" + klass + "\t" + method + "\n"; + String line = String.valueOf(caller) + "\t" + bashToDescriptor(String.valueOf(klass)) + "\t" + String.valueOf(method) + "\n"; synchronized (runtime) { runtime.output.printf(line); + runtime.output.flush(); } } } } - runtime.callStacks.get().push(klass + "\t" + method); + runtime.callStacks.get().push(bashToDescriptor(klass.getName()) + "\t" + method); } public static void termination(String klass, String method, Object receiver, boolean exception) { diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Compiler.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Compiler.java index 13918e15c..b9c04c44d 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Compiler.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/Compiler.java @@ -15,9 +15,11 @@ import java.util.Arrays; import java.util.BitSet; import java.util.Iterator; +import com.ibm.wala.shrikeBT.ConstantInstruction.ClassToken; import com.ibm.wala.shrikeBT.IBinaryOpInstruction.Operator; import com.ibm.wala.shrikeBT.analysis.ClassHierarchyProvider; import com.ibm.wala.shrikeBT.analysis.Verifier; +import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken; /** * This class generates Java bytecode from ShrikeBT Instructions. @@ -160,6 +162,10 @@ public abstract class Compiler implements Constants { protected abstract int allocateConstantPoolClassType(String c); + protected abstract int allocateConstantPoolMethodType(String c); + + protected abstract int allocateConstantPoolMethodHandle(ReferenceToken c); + protected abstract int allocateConstantPoolField(String c, String name, String type); protected abstract int allocateConstantPoolMethod(String c, String name, String sig); @@ -710,6 +716,12 @@ public abstract class Compiler implements Constants { cpIndex = allocateConstantPoolInteger(((ConstantInstruction.ConstInt) instr).getIntValue()); } else if (t.equals(TYPE_String)) { cpIndex = allocateConstantPoolString((String) ((ConstantInstruction.ConstString) instr).getValue()); + } else if (t.equals(TYPE_Class)) { + cpIndex = allocateConstantPoolClassType(((ClassToken) ((ConstantInstruction.ConstClass) instr).getValue()).getTypeName()); + } else if (t.equals(TYPE_MethodType)) { + cpIndex = allocateConstantPoolMethodType(((String) ((ConstantInstruction.ConstMethodType) instr).getValue())); + } else if (t.equals(TYPE_MethodHandle)) { + cpIndex = allocateConstantPoolMethodHandle(((ReferenceToken) ((ConstantInstruction.ConstMethodHandle) instr).getValue())); } else { cpIndex = allocateConstantPoolFloat(((ConstantInstruction.ConstFloat) instr).getFloatValue()); } 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 946a24118..d6188fe17 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 @@ -565,7 +565,7 @@ public abstract class ConstantInstruction extends Instruction { @Override public String getType() { - return TYPE_String; + return TYPE_MethodType; } } @@ -636,7 +636,8 @@ public abstract class ConstantInstruction extends Instruction { String className = cp.getConstantPoolHandleClassType(getCPIndex()); String eltName = cp.getConstantPoolHandleName(getCPIndex()); String eltDesc = cp.getConstantPoolHandleType(getCPIndex()); - value = new ConstantPoolParser.ReferenceToken(className, eltName, eltDesc); + byte kind = cp.getConstantPoolHandleKind(getCPIndex()); + value = new ConstantPoolParser.ReferenceToken(kind, className, eltName, eltDesc); } return value; } @@ -770,7 +771,7 @@ public abstract class ConstantInstruction extends Instruction { return ConstClass.makeInternal(s); } - static ConstantInstruction make(ConstantPoolReader cp, int index) { + public static ConstantInstruction make(ConstantPoolReader cp, int index) { switch (cp.getConstantPoolItemType(index)) { case CONSTANT_Integer: return new LazyInt(OP_ldc_w, cp, index); 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 79ee8a3bf..b1b8a422b 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 @@ -123,6 +123,8 @@ public abstract class ConstantPoolReader { */ public abstract String getConstantPoolHandleType(int index); + public abstract byte getConstantPoolHandleKind(int index); + public abstract BootstrapMethod getConstantPoolDynamicBootstrap(int index); public abstract String getConstantPoolDynamicName(int index); 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 b2627f199..8c870c5a0 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 @@ -530,6 +530,8 @@ public interface Constants { public static final String TYPE_MethodHandle = "Ljava/lang/invoke/MethodHandle;"; + public static final String TYPE_MethodType = "Ljava/lang/invoke/MethodType;"; + public static final String TYPE_Object = "Ljava/lang/Object;"; public static final String TYPE_Throwable = "Ljava/lang/Throwable;"; 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 index f7b351c93..ca1babf12 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/InvokeDynamicInstruction.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/InvokeDynamicInstruction.java @@ -43,7 +43,7 @@ public class InvokeDynamicInstruction extends Instruction implements IInvokeInst } @Override - public IDispatch getInvocationCode() { + public Dispatch getInvocationCode() { int invokeType = getBootstrap().invokeType(); switch (invokeType) { case 5: return Dispatch.VIRTUAL; diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/CTCompiler.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/CTCompiler.java index 64d1bfca2..4382ddcd0 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/CTCompiler.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/CTCompiler.java @@ -15,6 +15,7 @@ import java.util.Random; import com.ibm.wala.shrikeBT.Compiler; import com.ibm.wala.shrikeBT.MethodData; import com.ibm.wala.shrikeCT.ClassWriter; +import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken; /** * This class lets you compile ShrikeBT intermediate code into real Java bytecodes using ShrikeCT. @@ -62,6 +63,16 @@ final public class CTCompiler extends Compiler { return cw.addCPClass(convertTypeToClass(c)); } + @Override + protected int allocateConstantPoolMethodType(String c) { + return cw.addCPMethodType(c); + } + + @Override + protected int allocateConstantPoolMethodHandle(ReferenceToken c) { + return cw.addCPMethodHandle(c); + } + /** * Convert a JVM type to the internal JVM class name (e.g., Ljava/lang/Object; to java/lang/Object) * 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 9bd0a2b1c..309ecdbb6 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 @@ -201,6 +201,15 @@ final public class CTDecoder extends Decoder { } } + @Override + public byte getConstantPoolHandleKind(int index) { + try { + return cp.getCPHandleKind(index); + } catch (InvalidClassFileException e) { + throw convertToError(e); + } + } + @Override public BootstrapMethod getConstantPoolDynamicBootstrap(int index) { try { diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/ClassInstrumenter.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/ClassInstrumenter.java index 6786a8329..dacbb1c38 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/ClassInstrumenter.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/ClassInstrumenter.java @@ -443,7 +443,7 @@ final public class ClassInstrumenter { } } - stacks = new StackMapTableWriter(w, md, output, cha, varTypes /*, sm*/); + stacks = new StackMapTableWriter(w, md, output, cha, varTypes , sm); codeAttrCount++; } catch (IOException | FailureException e) { // TODO Auto-generated catch block 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 index 3db6ddf83..fd1e96382 100644 --- 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 @@ -15,9 +15,7 @@ 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; diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/tools/BootstrapInstrumentor.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/tools/BootstrapInstrumentor.java new file mode 100644 index 000000000..00647272d --- /dev/null +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/shrikeCT/tools/BootstrapInstrumentor.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * 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.reflect.InvocationTargetException; +import java.util.Set; + +import com.ibm.wala.shrikeBT.ConstantInstruction; +import com.ibm.wala.shrikeBT.ConstantPoolReader; +import com.ibm.wala.shrikeBT.Constants; +import com.ibm.wala.shrikeBT.Decoder.InvalidBytecodeException; +import com.ibm.wala.shrikeBT.IInstruction; +import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch; +import com.ibm.wala.shrikeBT.InvokeDynamicInstruction; +import com.ibm.wala.shrikeBT.InvokeInstruction; +import com.ibm.wala.shrikeBT.MethodData; +import com.ibm.wala.shrikeBT.ReturnInstruction; +import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder; +import com.ibm.wala.shrikeBT.shrikeCT.CTUtils; +import com.ibm.wala.shrikeBT.shrikeCT.ClassInstrumenter; +import com.ibm.wala.shrikeBT.shrikeCT.OfflineInstrumenter; +import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod; +import com.ibm.wala.shrikeCT.ClassReader; +import com.ibm.wala.shrikeCT.ClassWriter; +import com.ibm.wala.shrikeCT.CodeReader; +import com.ibm.wala.shrikeCT.InvalidClassFileException; +import com.ibm.wala.util.collections.HashSetFactory; + +public class BootstrapInstrumentor { + final private PrintWriter w; + + private int idx; + + /** + * Get ready to print a class to the given output stream. + */ + public BootstrapInstrumentor(PrintWriter w) { + this.w = w; + } + + public static void main(String[] args) throws Exception { + PrintWriter w = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); + BootstrapInstrumentor p = new BootstrapInstrumentor(w); + p.doit(args); + } + + public void doit(String[] args) throws Exception { + OfflineInstrumenter oi = new OfflineInstrumenter(true); + oi.parseStandardArgs(args); + + oi.setPassUnmodifiedClasses(true); + + ClassInstrumenter ci; + oi.beginTraversal(); + while ((ci = oi.nextClass()) != null) { + try { + idx = 0; + Set bss = doClass(ci); + ClassWriter cw = ci.emitClass(); + for(MethodData md : bss) { + CTUtils.compileAndAddMethodToClassWriter(md, cw, null); + } + oi.outputModifiedClass(ci, cw); + } finally { + w.flush(); + } + } + + oi.close(); + } + + private Set dumpAttributes(ClassInstrumenter ci, int i, ClassReader.AttrIterator attrs) throws InvalidClassFileException, + InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { + Set result = HashSetFactory.make(); + ClassReader cr = ci.getReader(); + 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(); + ConstantPoolReader cpr = decoder.getConstantPool(); + IInstruction[] origInsts = decoder.getInstructions(); + for(IInstruction inst : origInsts) { + if (inst instanceof InvokeDynamicInstruction) { + InvokeDynamicInstruction x = (InvokeDynamicInstruction) inst; + BootstrapMethod m = x.getBootstrap(); + + IInstruction insts[] = new IInstruction[ m.callArgumentCount() + 8]; + int arg = 0; + + insts[arg++] = InvokeInstruction.make("()Ljava/lang/invoke/MethodHandles$Lookup;", "java/lang/invoke/MethodHandles", "lookup", Dispatch.STATIC); + + insts[arg++] = ConstantInstruction.makeString(x.getMethodName()); + + insts[arg++] = ConstantInstruction.makeString(x.getMethodSignature()); + insts[arg++] = ConstantInstruction.makeClass(cr.getName()); + insts[arg++] = InvokeInstruction.make("()Ljava/lang/ClassLoader;", "java/lang/Class", "getClassLoader", Dispatch.VIRTUAL); + insts[arg++] = InvokeInstruction.make("(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", "java/lang/invoke/MethodType", "fromMethodDescriptorString", Dispatch.STATIC); + + for(int an = 0; an < m.callArgumentCount(); an++) { + insts[arg++] = ConstantInstruction.make(cpr, m.callArgumentIndex(an)); + } + + insts[arg++] = InvokeInstruction.make(m.methodType(), m.methodClass(), m.methodName(), ((InvokeDynamicInstruction) inst).getInvocationCode()); + insts[arg++] = ReturnInstruction.make("Ljava/lang/invoke/CallSite;"); + + result.add(MethodData.makeWithDefaultHandlersAndInstToBytecodes(Constants.ACC_PUBLIC|Constants.ACC_STATIC, cr.getName(), "bs" + (idx++), "()Ljava/lang/invoke/CallSite;", insts)); + } + } + } + } + + return result; + } + + + + /** + * Print a class. + * @throws InvocationTargetException + * @throws IllegalAccessException + * @throws SecurityException + * @throws NoSuchMethodException + * @throws ClassNotFoundException + * + * @throws IllegalArgumentException if cr is null + * @throws NoSuchFieldException + */ + public Set doClass(final ClassInstrumenter ci) throws InvalidClassFileException, InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { + ClassReader cr = ci.getReader(); + ClassReader.AttrIterator attrs = new ClassReader.AttrIterator(); + cr.initClassAttributeIterator(attrs); + int methodCount = cr.getMethodCount(); + + Set result = HashSetFactory.make(); + for (int i = 0; i < methodCount; i++) { + cr.initMethodAttributeIterator(i, attrs); + result.addAll(dumpAttributes(ci, i, attrs)); + } + + return result; + } +} \ 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 index 1ff6b2a7c..3de8b3204 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/BootstrapMethodsReader.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/BootstrapMethodsReader.java @@ -25,7 +25,9 @@ public class BootstrapMethodsReader extends AttributeReader { String methodClass(); String methodName(); String methodType(); + int callArgumentCount(); Object callArgument(ClassLoader cl, int i); + int callArgumentIndex(int i); int callArgumentKind(int i); } @@ -77,17 +79,28 @@ public class BootstrapMethodsReader extends AttributeReader { return methodType; } + + @Override + public int callArgumentCount() { + return argumentCount; + } + @Override public int callArgumentKind(int i) { + return cp.getItemType(callArgumentIndex(i)); + } + + @Override + public int callArgumentIndex(int i) { assert 0 <= i && i < argumentCount; int index = argsBase + (2*i); - return cp.getItemType(cr.getUShort(index)); + return cr.getUShort(index); } - + @Override public Object callArgument(ClassLoader cl, int i) { try { - int index = cr.getUShort(argsBase + (2*i)); + int index = callArgumentIndex(i); int t = callArgumentKind(i); switch (t) { case ClassConstants.CONSTANT_Utf8: diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ClassReader.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ClassReader.java index e4ac853f8..a18382be2 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ClassReader.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ClassReader.java @@ -617,7 +617,7 @@ public final class ClassReader implements ClassConstants { } /** - * @return the method descriptor of method m in JVM format (e.g., V(ILjava/lang/Object;) ) + * @return the method descriptor of method m in JVM format (e.g., (ILjava/lang/Object;)V ) */ public String getMethodType(int m) throws InvalidClassFileException { verifyMethodIndex(m); diff --git a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ClassWriter.java b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ClassWriter.java index f53fe7fa8..472cd7b49 100644 --- a/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ClassWriter.java +++ b/com.ibm.wala.shrike/src/com/ibm/wala/shrikeCT/ClassWriter.java @@ -13,6 +13,8 @@ package com.ibm.wala.shrikeCT; import java.util.ArrayList; import java.util.HashMap; +import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken; + /** * This class formats and writes class data into JVM format. */ @@ -82,16 +84,18 @@ public class ClassWriter implements ClassConstants { abstract byte getType(); } - public static class CWString extends CWItem { + public static class CWStringItem extends CWItem { final private String s; + final private byte type; - public CWString(String s) { + public CWStringItem(String s, byte type) { this.s = s; + this.type = type; } @Override public boolean equals(Object o) { - return o instanceof CWString && ((CWString) o).s.equals(s); + return o != null && o.getClass().equals(getClass()) && ((CWStringItem) o).s.equals(s); } @Override @@ -101,39 +105,16 @@ public class ClassWriter implements ClassConstants { @Override byte getType() { - return CONSTANT_String; - } - } - - public static class CWClass extends CWItem { - final private String c; - - public CWClass(String c) { - this.c = c; - } - - @Override - public boolean equals(Object o) { - return o instanceof CWClass && ((CWClass) o).c.equals(c); - } - - @Override - public int hashCode() { - return c.hashCode() + 1431; - } - - @Override - byte getType() { - return CONSTANT_Class; + return type; } } static class CWRef extends CWItem { - final private String c; + final protected String c; - final private String n; + final protected String n; - final private String t; + final protected String t; final private byte type; @@ -146,7 +127,7 @@ public class ClassWriter implements ClassConstants { @Override public boolean equals(Object o) { - if (o instanceof CWRef) { + if (o.getClass().equals(getClass())) { CWRef r = (CWRef) o; return r.type == type && r.c.equals(c) && r.n.equals(n) && r.t.equals(t); } else { @@ -165,6 +146,29 @@ public class ClassWriter implements ClassConstants { } } + static class CWHandle extends CWRef { + private final byte kind; + + CWHandle(byte type, byte kind, String c, String n, String t) { + super(type, c, n, t); + this.kind = kind; + } + + @Override + public int hashCode() { + return super.hashCode() * kind; + } + + @Override + public boolean equals(Object o) { + return super.equals(o) && ((CWHandle)o).kind == kind; + } + + public byte getKind() { + return kind; + } + } + static class CWNAT extends CWItem { final private String n; @@ -224,11 +228,15 @@ public class ClassWriter implements ClassConstants { byte t = cp.getItemType(i); switch (t) { case CONSTANT_String: - cachedCPEntries.put(new CWString(cp.getCPString(i)), new Integer(i)); + cachedCPEntries.put(new CWStringItem(cp.getCPString(i), CONSTANT_String), new Integer(i)); break; case CONSTANT_Class: - cachedCPEntries.put(new CWClass(cp.getCPClass(i)), new Integer(i)); + cachedCPEntries.put(new CWStringItem(cp.getCPClass(i), CONSTANT_Class), new Integer(i)); break; + case CONSTANT_MethodType: + cachedCPEntries.put(new CWStringItem(cp.getCPMethodType(i), CONSTANT_MethodType), new Integer(i)); + break; + case CONSTANT_MethodHandle: case CONSTANT_FieldRef: case CONSTANT_InterfaceMethodRef: case CONSTANT_MethodRef: @@ -331,13 +339,27 @@ public class ClassWriter implements ClassConstants { return addCPEntry(new Double(d), 2); } + private int addCPString(String s, byte type) { + if (s == null) { + throw new IllegalArgumentException("null s: " + s); + } + return addCPEntry(new CWStringItem(s, type), 1); + } + + public int addCPMethodHandle(ReferenceToken c) { + if (c == null) { + throw new IllegalArgumentException("null c: " + c); + } + return addCPEntry(new CWHandle(CONSTANT_MethodHandle, c.getKind(), c.getClassName(), c.getElementName(), c.getDescriptor()), 1); + } + /** * Add a String to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPString(String s) { - return addCPEntry(new CWString(s), 1); + return addCPString(s, CONSTANT_String); } /** @@ -347,10 +369,17 @@ public class ClassWriter implements ClassConstants { * @return the index of a constant pool item with the right value */ public int addCPClass(String s) { - if (s == null) { - throw new IllegalArgumentException("null s: " + s); - } - return addCPEntry(new CWClass(s), 1); + return addCPString(s, CONSTANT_Class); + } + + /** + * Add a Class to the constant pool if necessary. + * + * @param s the class name, in JVM format (e.g., java/lang/Object) + * @return the index of a constant pool item with the right value + */ + public int addCPMethodType(String s) { + return addCPString(s, CONSTANT_MethodType); } /** @@ -717,12 +746,10 @@ public class ClassWriter implements ClassConstants { int offset; switch (t) { case CONSTANT_Class: - offset = reserveBuf(3); - setUShort(buf, offset + 1, addCPUtf8(((CWClass) item).c)); - break; case CONSTANT_String: + case CONSTANT_MethodType: offset = reserveBuf(3); - setUShort(buf, offset + 1, addCPUtf8(((CWString) item).s)); + setUShort(buf, offset + 1, addCPUtf8(((CWStringItem) item).s)); break; case CONSTANT_NameAndType: { offset = reserveBuf(5); @@ -731,6 +758,39 @@ public class ClassWriter implements ClassConstants { setUShort(buf, offset + 3, addCPUtf8(nat.t)); break; } + case CONSTANT_MethodHandle: { + offset = reserveBuf(4); + CWHandle handle = (CWHandle) item; + setUByte(buf, offset + 1, handle.getKind()); + switch (handle.getKind()) { + case REF_getStatic: + case REF_getField: + case REF_putField: + case REF_putStatic: { + int x = addCPFieldRef(handle.c, handle.n, handle.t); + setUShort(buf, offset + 2, x); + break; + } + case REF_invokeVirtual: + case REF_newInvokeSpecial: { + int x = addCPMethodRef(handle.c, handle.n, handle.t); + setUShort(buf, offset + 2, x); + break; + } + case REF_invokeSpecial: + case REF_invokeStatic: { + int x = addCPMethodRef(handle.c, handle.n, handle.t); + setUShort(buf, offset + 2, x); + break; + } + case REF_invokeInterface: { + int x = addCPInterfaceMethodRef(handle.c, handle.n, handle.t); + setUShort(buf, offset + 2, x); + break; + } + } + break; + } case CONSTANT_MethodRef: case CONSTANT_FieldRef: case CONSTANT_InterfaceMethodRef: { 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 f4601291c..7b967bdbd 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 @@ -18,17 +18,22 @@ import com.ibm.wala.shrikeCT.ClassReader.AttrIterator; */ public final class ConstantPoolParser implements ClassConstants { public static class ReferenceToken { + private final byte kind; private final String className; private final String elementName; private final String descriptor; - public ReferenceToken(String className, String elementName, String descriptor) { - super(); + public ReferenceToken(byte kind, String className, String elementName, String descriptor) { + this.kind = kind; this.className = className; this.elementName = elementName; this.descriptor = descriptor; } + public byte getKind() { + return kind; + } + public String getClassName() { return className; } @@ -60,6 +65,9 @@ public final class ConstantPoolParser implements ClassConstants { if (getClass() != obj.getClass()) return false; ReferenceToken other = (ReferenceToken) obj; + if (kind != other.kind) { + return false; + } if (className == null) { if (other.className != null) return false; @@ -410,7 +418,7 @@ public final class ConstantPoolParser implements ClassConstants { } /** - * @return the type of the MethodHandle at constant pool item i, in JVM format (e.g., I, Z, or Ljava/lang/Object;) + * @return the type of the MethodHandle at constant pool item i */ public byte getCPHandleKind(int i) throws InvalidClassFileException, IllegalArgumentException { if (i < 1 || i >= cpItems.length) {