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
This commit is contained in:
Julian Dolby 2014-12-11 21:48:23 -05:00
parent 6aa3646849
commit 096e2f796f
28 changed files with 475 additions and 127 deletions

View File

@ -2,8 +2,13 @@ function f(g) {
g();
}
function x() {
}
function h() {
f.call(null, k);
x.apply(null, []);
}
function k() {}

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}
}
}
}

View File

@ -329,7 +329,11 @@ public abstract class BytecodeClass<T extends IClassLoader> implements IClass {
*/
@Override
public Collection<IField> 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<T extends IClassLoader> implements IClass {
}
}
}
// didn't find it yet. special logic for interfaces
if (isInterface() || isAbstract()) {
final Iterator<IClass> 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<Selector, IMethod>(5);
}
inheritCache.put(selector, null);
return null;
}
protected void populateFieldArrayFromList(List<FieldImpl> L, IField[] A) {

View File

@ -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),

View File

@ -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));
}
}
}
/**

View File

@ -1,4 +1,3 @@
java\/awt\/.*
javax\/swing\/.*
java\/io\/ObjectStreamClass*
org\/apache\/xerces\/.*

View File

@ -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));

View File

@ -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<CallGraph, PointerAnalysis<InstanceKey>> makeAPKCallGraph(String apkFileName) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException {
return makeAPKCallGraph(apkFileName, new NullProgressMonitor());
}
public static Pair<CallGraph, PointerAnalysis<InstanceKey>> 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<NewSiteReference> iterateNewSites(CGNode node) {
return
new MapIterator<SSAInstruction,NewSiteReference>(
new FilterIterator<SSAInstruction>(
node.getIR().iterateAllInstructions(),
new Predicate<SSAInstruction>() {
@Override
public boolean test(SSAInstruction t) {
return t instanceof SSANewInstruction;
}
}),
new Function<SSAInstruction,NewSiteReference>() {
@Override
public NewSiteReference apply(SSAInstruction object) {
return ((SSANewInstruction)object).getNewSite();
}
}
);
}
};
}
public static Pair<CallGraph, PointerAnalysis<InstanceKey>> 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);

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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) {

View File

@ -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());
}

View File

@ -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);

View File

@ -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);

View File

@ -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;";

View File

@ -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;

View File

@ -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)
*

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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<MethodData> 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<MethodData> dumpAttributes(ClassInstrumenter ci, int i, ClassReader.AttrIterator attrs) throws InvalidClassFileException,
InvalidBytecodeException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
Set<MethodData> 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<MethodData> 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<MethodData> result = HashSetFactory.make();
for (int i = 0; i < methodCount; i++) {
cr.initMethodAttributeIterator(i, attrs);
result.addAll(dumpAttributes(ci, i, attrs));
}
return result;
}
}

View File

@ -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:

View File

@ -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);

View File

@ -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: {

View File

@ -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) {