WALA/com.ibm.wala.core/src/com/ibm/wala/analysis/reflection/ReflectiveInvocationSelecto...

141 lines
5.9 KiB
Java

/*******************************************************************************
* Copyright (c) 2008 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.analysis.reflection;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.ReceiverInstanceContext;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.intset.EmptyIntSet;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
/**
* A {@link ContextSelector} to intercept calls to reflective method invocations such as Constructor.newInstance and Method.invoke
*/
class ReflectiveInvocationSelector implements ContextSelector {
public ReflectiveInvocationSelector() {
}
/**
* Creates a callee target based on the following criteria:
* <ol>
* <li>If the method being invoked through reflection is definitely static, then do not create a callee target for any
* <code>callee</code> method that is not static. In this case, return <code>null</code>.
* <li>If the method being invoked through reflection takes a constant number of parameters, <code>n</code>, then do not create a
* callee target for any <code>callee</code> method that takes a number of parameters different from <code>n</code>. In this case,
* return <code>null</code>.
* <li>Otherwise, return a new {@link ReceiverInstanceContext} for <code>receiver</code>.
* </ol>
*/
@Override
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
if (receiver == null || receiver.length == 0 || !mayUnderstand(caller, site, callee, receiver[0])) {
return null;
}
IR ir = caller.getIR();
SSAAbstractInvokeInstruction[] invokeInstructions = ir.getCalls(site);
if (invokeInstructions.length != 1) {
return new ReceiverInstanceContext(receiver[0]);
}
SymbolTable st = ir.getSymbolTable();
ConstantKey receiverConstantKey = (ConstantKey) receiver[0];
IMethod m = (IMethod) receiverConstantKey.getValue();
boolean isStatic = m.isStatic();
boolean isConstructor = isConstructorConstant(receiver[0]);
// If the method being invoked through reflection is not a constructor and is definitely static, then
// we should not create a callee target for any method that is not static
if (!isConstructor) {
int recvUse = invokeInstructions[0].getUse(1);
if (st.isNullConstant(recvUse) && !isStatic) {
return null;
}
}
// If the method being invoked through reflection is being passed n parameters,
// then we should not create a callee target for any method that takes a number
// of parameters different from n
int numberOfParams = isStatic ? m.getNumberOfParameters() : m.getNumberOfParameters() - 1;
// instruction[0] is a call to Method.invoke(), where the receiver is a specific method,
// the first parameter is the receiver of the method invocation, and the second parameter
// is an array of objects corresponding to the parameters passed to the method.
int paramIndex = isConstructor ? 1 : 2;
int paramUse = invokeInstructions[0].getUse(paramIndex);
SSAInstruction instr = caller.getDU().getDef(paramUse);
if (!(instr instanceof SSANewInstruction)) {
return new ReceiverInstanceContext(receiver[0]);
}
SSANewInstruction newInstr = (SSANewInstruction) instr;
if (!newInstr.getConcreteType().isArrayType()) {
return null;
}
int vn = newInstr.getUse(0);
try {
int arrayLength = st.getIntValue(vn);
if (arrayLength == numberOfParams) {
return new ReceiverInstanceContext(receiver[0]);
} else {
return new IllegalArgumentExceptionContext();
}
} catch (IllegalArgumentException e) {
return new ReceiverInstanceContext(receiver[0]);
}
}
/**
* This object may understand a dispatch to Constructor.newInstance().
*/
private static boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
if (instance instanceof ConstantKey) {
if (targetMethod.getReference().equals(ReflectiveInvocationInterpreter.METHOD_INVOKE) ||
isConstructorConstant(instance)
&& targetMethod.getReference().equals(ReflectiveInvocationInterpreter.CTOR_NEW_INSTANCE)) {
return true;
}
}
return false;
}
private static boolean isConstructorConstant(InstanceKey instance) {
if (instance instanceof ConstantKey) {
ConstantKey c = (ConstantKey) instance;
if (c.getConcreteType().getReference().equals(TypeReference.JavaLangReflectConstructor)) {
return true;
}
}
return false;
}
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
@Override
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
if (site.getDeclaredTarget().equals(ReflectiveInvocationInterpreter.METHOD_INVOKE) ||
site.getDeclaredTarget().equals(ReflectiveInvocationInterpreter.CTOR_NEW_INSTANCE)) {
return thisParameter;
} else {
return EmptyIntSet.instance;
}
}
}