org.scandroid.model.AppModelMethod Maven / Gradle / Ivy
Show all versions of com.ibm.wala.scandroid Show documentation
/*
* 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.
*
* This file is a derivative of code released under the terms listed below.
*
*/
/*
* Copyright (c) 2009-2012,
*
* Galois, Inc. (Aaron Tomb , Rogan Creswick ) Steve Suh
*
*
* All rights reserved.
*
*
Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
*
1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
*
2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided with
* the distribution.
*
*
3. The names of the contributors may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.scandroid.model;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.MethodSummary;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction.IDispatch;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.strings.Atom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.scandroid.spec.AndroidSpecs;
import org.scandroid.spec.MethodNamePattern;
import org.scandroid.util.LoaderUtils;
public class AppModelMethod {
int nextLocal;
/** A mapping from String (variable name) -> Integer (local number) */
private Map symbolTable = null;
private MethodSummary methodSummary;
private final IClassHierarchy cha;
private final AnalysisScope scope;
private final Map constant2ValueNumber = HashMapFactory.make();
SSAInstructionFactory insts;
// Maps a Type to variable name
private final Map typeToID = new HashMap<>();
// innerclass dependencies
private final Map> icDependencies = new HashMap<>();
// all callbacks to consider
private final List callBacks = new ArrayList<>();
private final Map aClassToTR = new HashMap<>();
private static class MethodParams {
public IMethod im;
public int params[];
private MethodParams(IMethod method) {
im = method;
}
private void setParams(int p[]) {
params = p;
}
private IMethod getIMethod() {
return im;
}
private int[] getParams() {
return params;
}
}
public AppModelMethod(IClassHierarchy cha, AnalysisScope scope, AndroidSpecs specs) {
this.cha = cha;
this.scope = scope;
Language lang = scope.getLanguage(ClassLoaderReference.Application.getLanguage());
insts = lang.instructionFactory();
startMethod();
buildTypeMap(specs);
processTypeMap();
processCallBackParams();
createLoopAndSwitch();
}
private void createLoopAndSwitch() {
int callbackSize = callBacks.size();
// start of while loop
int loopLabel = methodSummary.getNumberOfStatements();
int switchValue = nextLocal++;
// default label, for now same as case1
int defLabel = loopLabel + 1;
int[] casesAndLabels = new int[2 * callbackSize];
for (int i = 0; i < callbackSize; i++) {
casesAndLabels[i * 2] = i + 1;
casesAndLabels[i * 2 + 1] = defLabel + i * 2;
}
methodSummary.addStatement(
insts.SwitchInstruction(
methodSummary.getNumberOfStatements(), switchValue, defLabel, casesAndLabels));
for (MethodParams mp : callBacks) {
IMethod im = mp.getIMethod();
IDispatch dispatch;
if (im.isInit()) {
dispatch = IInvokeInstruction.Dispatch.SPECIAL;
} else if (im.isAbstract()) {
dispatch = IInvokeInstruction.Dispatch.INTERFACE;
} else if (im.isStatic()) {
dispatch = IInvokeInstruction.Dispatch.STATIC;
} else dispatch = IInvokeInstruction.Dispatch.VIRTUAL;
addInvocation(
mp.getParams(),
CallSiteReference.make(
methodSummary.getNumberOfStatements(), mp.getIMethod().getReference(), dispatch));
methodSummary.addStatement(
insts.GotoInstruction(methodSummary.getNumberOfStatements(), loopLabel));
}
}
private void startMethod() {
String className = "Lcom/SCanDroid/AppModel";
String methodName = "entry";
TypeReference governingClass =
TypeReference.findOrCreate(
ClassLoaderReference.Application, TypeName.string2TypeName(className));
Atom mName = Atom.findOrCreateUnicodeAtom(methodName);
Language lang = scope.getLanguage(ClassLoaderReference.Application.getLanguage());
Descriptor D = Descriptor.findOrCreateUTF8(lang, "()V");
MethodReference mref = MethodReference.findOrCreate(governingClass, mName, D);
methodSummary = new MethodSummary(mref);
methodSummary.setStatic(true);
methodSummary.setFactory(false);
int nParams = mref.getNumberOfParameters();
nextLocal = nParams + 1;
symbolTable = HashMapFactory.make(5);
for (int i = 0; i < nParams; i++) {
symbolTable.put("arg" + i, i + 1);
}
}
private void buildTypeMap(AndroidSpecs specs) {
// Go through all possible callbacks found in Application code
// Associate their TypeReference with a unique number in typeToID.
// Also keep track of all anonymous classes found.
for (MethodNamePattern mnp : specs.getCallBacks()) {
for (IMethod im : mnp.getPossibleTargets(cha)) {
// limit to functions defined within the application
if (LoaderUtils.fromLoader(im, ClassLoaderReference.Application)) {
callBacks.add(new MethodParams(im));
TypeReference tr = im.getDeclaringClass().getReference();
if (!typeToID.containsKey(tr)) {
typeToID.put(tr, nextLocal++);
// class is an innerclass
if (tr.getName().getClassName().toString().contains("$")) {
addDependencies(tr);
}
}
}
}
}
}
private void addDependencies(TypeReference tr) {
String packageName = 'L' + tr.getName().getPackage().toString() + '/';
String outerClassName;
String innerClassName = tr.getName().getClassName().toString();
LinkedList trLL = new LinkedList<>();
trLL.push(tr);
int index = innerClassName.lastIndexOf('$');
while (index != -1) {
outerClassName = innerClassName.substring(0, index);
TypeReference innerTR =
TypeReference.findOrCreate(
ClassLoaderReference.Application, packageName + outerClassName);
trLL.push(innerTR);
if (!typeToID.containsKey(innerTR)) {
typeToID.put(innerTR, nextLocal++);
aClassToTR.put(innerTR, tr);
}
innerClassName = outerClassName;
index = outerClassName.lastIndexOf('$');
}
icDependencies.put(tr, trLL);
}
private void processTypeMap() {
Set createdIDs = new HashSet<>();
for (Entry eSet : typeToID.entrySet()) {
Integer i = eSet.getValue();
if (createdIDs.contains(i)) continue;
TypeReference tr = eSet.getKey();
String className = tr.getName().getClassName().toString();
// Not an anonymous innerclass
if (!className.contains("$")) {
processAllocation(tr, i, false);
createdIDs.add(i);
}
// Is an anonymous innerclass
else {
LinkedList deps = icDependencies.get(tr);
if (deps == null) {
tr = aClassToTR.get(tr);
}
for (TypeReference trD : icDependencies.get(tr)) {
Integer j = typeToID.get(trD);
if (!createdIDs.contains(j)) {
String depClassName = trD.getName().getClassName().toString();
processAllocation(trD, j, depClassName.contains("$"));
createdIDs.add(j);
}
}
}
}
assert (createdIDs.size() == typeToID.size()) : "typeToID and createdID size do not match";
}
private void processCallBackParams() {
for (MethodParams mp : callBacks) {
int params[] = new int[mp.getIMethod().getNumberOfParameters()];
int startPos;
if (mp.getIMethod().isStatic()) {
startPos = 0;
} else {
params[0] = typeToID.get(mp.getIMethod().getDeclaringClass().getReference());
startPos = 1;
}
for (int i = startPos; i < params.length; i++) {
params[i] = makeArgument(mp.getIMethod().getParameterType(i));
}
mp.setParams(params);
}
}
private int makeArgument(TypeReference tr) {
if (tr.isPrimitiveType()) return addLocal();
else {
SSANewInstruction n = processAllocation(tr, nextLocal++, false);
return (n == null) ? -1 : n.getDef();
}
}
private int addLocal() {
return nextLocal++;
}
private SSANewInstruction processAllocation(TypeReference tr, Integer i, boolean isInner) {
// create the allocation statement and add it to the method summary
NewSiteReference ref = NewSiteReference.make(methodSummary.getNumberOfStatements(), tr);
SSANewInstruction a = null;
if (tr.isArrayType()) {
int[] sizes = new int[((ArrayClass) cha.lookupClass(tr)).getDimensionality()];
Arrays.fill(sizes, getValueNumberForIntConstant(1));
a = insts.NewInstruction(methodSummary.getNumberOfStatements(), i, ref, sizes);
} else {
a = insts.NewInstruction(methodSummary.getNumberOfStatements(), i, ref);
}
methodSummary.addStatement(a);
IClass klass = cha.lookupClass(tr);
if (klass == null) {
return null;
}
if (klass.isArrayClass()) {
int arrayRef = a.getDef();
TypeReference e = klass.getReference().getArrayElementType();
while (e != null && !e.isPrimitiveType()) {
// allocate an instance for the array contents
NewSiteReference n = NewSiteReference.make(methodSummary.getNumberOfStatements(), e);
int alloc = nextLocal++;
SSANewInstruction ni = null;
if (e.isArrayType()) {
int[] sizes = new int[((ArrayClass) cha.lookupClass(tr)).getDimensionality()];
Arrays.fill(sizes, getValueNumberForIntConstant(1));
ni = insts.NewInstruction(methodSummary.getNumberOfStatements(), alloc, n, sizes);
} else {
ni = insts.NewInstruction(methodSummary.getNumberOfStatements(), alloc, n);
}
methodSummary.addStatement(ni);
// emit an astore
SSAArrayStoreInstruction store =
insts.ArrayStoreInstruction(
methodSummary.getNumberOfStatements(),
arrayRef,
getValueNumberForIntConstant(0),
alloc,
e);
methodSummary.addStatement(store);
e = e.isArrayType() ? e.getArrayElementType() : null;
arrayRef = alloc;
}
}
// invoke constructor
IMethod ctor = cha.resolveMethod(klass, MethodReference.initSelector);
if (ctor != null) {
// only check for more constructors when we're looking through the inner classes?
if (isInner
&& !ctor.getDeclaringClass().getName().toString().equals(klass.getName().toString())) {
boolean foundValidCtor = false;
for (IMethod im : klass.getAllMethods()) {
if (im.getDeclaringClass().getName().toString().equals(klass.getName().toString())
&& im.getSelector()
.getName()
.toString()
.equals(MethodReference.initAtom.toString())) {
ctor = im;
foundValidCtor = true;
// found a default constructor that takes only the outer class as a parameter
if (im.getDescriptor().getNumberOfParameters() == 1) {
break;
}
}
}
if (!foundValidCtor) {
throw new UnimplementedError(
"Check for other constructors, or just use default Object constructor");
}
}
int[] params;
if (ctor.getDescriptor().getNumberOfParameters() == 0) params = new int[] {i};
else {
params = new int[ctor.getNumberOfParameters()];
params[0] = i;
LinkedList deps = icDependencies.get(tr);
if (deps == null) {
deps = icDependencies.get(aClassToTR.get(tr));
int index = deps.lastIndexOf(tr);
TypeReference otr = deps.get(index - 1);
assert (ctor.getParameterType(1).equals(otr)) : "Type Mismatch";
params[1] = typeToID.get(otr);
} else {
TypeReference otr = deps.get(deps.size() - 2);
assert (ctor.getParameterType(1).equals(otr)) : "Type Mismatch";
params[1] = typeToID.get(otr);
}
// Allocate new instances for each of the other parameters
// in the current constructor.
for (int pI = 2; pI < params.length; pI++) {
params[pI] = makeArgument(ctor.getParameterType(pI));
}
}
addInvocation(
params,
CallSiteReference.make(
methodSummary.getNumberOfStatements(),
ctor.getReference(),
IInvokeInstruction.Dispatch.SPECIAL));
}
return a;
}
public SSAAbstractInvokeInstruction addInvocation(int[] params, CallSiteReference site) {
if (site == null) {
throw new IllegalArgumentException("site is null");
}
CallSiteReference newSite =
CallSiteReference.make(
methodSummary.getNumberOfStatements(),
site.getDeclaredTarget(),
site.getInvocationCode());
SSAAbstractInvokeInstruction s = null;
if (newSite.getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
s =
insts.InvokeInstruction(
methodSummary.getNumberOfStatements(), params, nextLocal++, newSite, null);
} else {
s =
insts.InvokeInstruction(
methodSummary.getNumberOfStatements(),
nextLocal++,
params,
nextLocal++,
newSite,
null);
}
methodSummary.addStatement(s);
// cache.invalidate(this, Everywhere.EVERYWHERE);
return s;
}
protected int getValueNumberForIntConstant(int c) {
ConstantValue v = new ConstantValue(c);
Integer result = constant2ValueNumber.get(v);
if (result == null) {
result = nextLocal++;
constant2ValueNumber.put(v, result);
}
return result;
}
public MethodSummary getSummary() {
return methodSummary;
}
}