WALA/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModel.java

945 lines
45 KiB
Java

/*
* 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) 2013,
* Tobias Blaschke <code@tobiasblaschke.de>
* 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 com.ibm.wala.dalvik.ipa.callgraph.androidModel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.FlatInstantiator;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.IInstantiationBehavior;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.IInstantiationBehavior.InstanceBehavior;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.Instantiator;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.ReuseParameters;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.structure.AbstractAndroidModel;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.stubs.AndroidBoot;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.stubs.AndroidStartComponentTool;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.stubs.ExternalModel;
import com.ibm.wala.dalvik.ipa.callgraph.androidModel.stubs.SystemServiceModel;
import com.ibm.wala.dalvik.ipa.callgraph.impl.AndroidEntryPoint;
import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters;
import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters.StarterFlags;
import com.ibm.wala.dalvik.util.AndroidComponent;
import com.ibm.wala.dalvik.util.AndroidEntryPointManager;
import com.ibm.wala.dalvik.util.AndroidTypes;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.cha.IClassHierarchyDweller;
import com.ibm.wala.ipa.summaries.MethodSummary;
import com.ibm.wala.ipa.summaries.SummarizedMethod;
import com.ibm.wala.ipa.summaries.SummarizedMethodWithNames;
import com.ibm.wala.ipa.summaries.VolatileMethodSummary;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.ssa.ParameterAccessor;
import com.ibm.wala.util.ssa.ParameterAccessor.Parameter;
import com.ibm.wala.util.ssa.SSAValue;
import com.ibm.wala.util.ssa.SSAValue.TypeKey;
import com.ibm.wala.util.ssa.SSAValue.VariableKey;
import com.ibm.wala.util.ssa.SSAValue.WeaklyNamedKey;
import com.ibm.wala.util.ssa.SSAValueManager;
import com.ibm.wala.util.ssa.TypeSafeInstructionFactory;
import com.ibm.wala.util.strings.Atom;
// For debug:
/**
* The model to be executed at application start.
*
* This method models the lifecycle of an Android Application. By doing so it calls all AndroidEntryPoints
* set in the AnalysisOptions.
* <p>
* Between the calls to the AndroidEntryPoints special behavior is inserted. You can change that behavior
* by implementing an AbstractAndroidModel or set one of the existing ones in the AnalysisOptions.
*
* Additionally care of how types are instantiated is taken. You can change this behavior by setting the
* IInstanciationBehavior in the AnalysisOptions.
*
* Smaller Models exist:
* * MiniModel calls all components of a specific type (for example all Activities)
* * MicroModel calls a single specific component
* * ExternalModel doesn't call anything but fiddles with the data on its own
*
* All these Models are added to a synthetic AndroidModelClass.
*
* @author Tobias Blaschke <code@tobiasblaschke.de>
*/
public class AndroidModel /* makes SummarizedMethod */
implements IClassHierarchyDweller {
private final Atom name = Atom.findOrCreateAsciiAtom("AndroidModel");
public MethodReference mRef;
protected IClassHierarchy cha;
protected AnalysisOptions options;
protected AnalysisCache cache;
private AbstractAndroidModel labelSpecial;
private IInstantiationBehavior instanceBehavior;
private SSAValueManager paramManager;
private ParameterAccessor modelAcc;
private ReuseParameters reuseParameters;
protected final AnalysisScope scope;
protected VolatileMethodSummary body;
// private JavaInstructionFactory instructionFactory;
private IProgressMonitor monitor;
private int maxProgress;
/*
* static: "boot" only once. How to assert done by the right one?
*/
protected static boolean doBoot = true;
protected IClass klass;
protected boolean built;
protected SummarizedMethod model;
public AndroidModel(final IClassHierarchy cha, final AnalysisOptions options, final AnalysisCache cache) {
this.options = options;
this.cha = cha;
this.cache = cache;
this.built = false;
this.scope = options.getAnalysisScope();
this.instanceBehavior = AndroidEntryPointManager.MANAGER.getInstantiationBehavior(cha);
}
/**
* Generates the model on a sub-set of Entrypoints.
*
* Asks {@link #selectEntryPoint(AndroidEntryPoint)} for each EntryPoint known to the AndroidEntryPointManager,
* if the EntryPoint should be included in the model. Then calls {@link #build(Atom, Iterable<? extends Entrypoint>)}
* on these.
*
* @param name The name the generated method will be known as
*/
protected void build(Atom name) throws CancelException {
final List<AndroidEntryPoint> restrictedEntries = new ArrayList<AndroidEntryPoint>();
for (AndroidEntryPoint ep: AndroidEntryPointManager.ENTRIES) {
if (selectEntryPoint(ep)) {
restrictedEntries.add(ep);
}
}
build(name, restrictedEntries);
}
public Atom getName() {
return this.name;
}
public boolean isStatic() {
return true;
}
public TypeName getReturnType() {
return TypeReference.VoidName;
}
public Descriptor getDescriptor() throws CancelException {
return getMethod().getDescriptor();
}
/**
* Generate the SummarizedMethod for the model (in this.model).
*
* The actual generated model depends on the on the properties of this overloaded class. Most
* generated methods should reside in the AndroidModelClass and take AndroidComponents as well
* as some parameters (these marked REUSE) to the EntryPoints of the components.
*
* Use {@link #getMethod()} to retrieve the method generated here or getMethodAs to get a version
* which is wrapped to another signature.
*
* @param name The name the generated method will be known as
* @param entrypoints The functions to call additionally to boot-code and XXX
*/
protected void build(Atom name, Collection<? extends AndroidEntryPoint> entrypoints) throws CancelException {
// register
this.klass = cha.lookupClass(AndroidModelClass.ANDROID_MODEL_CLASS);
if (this.klass == null) {
// add to cha
this.klass = AndroidModelClass.getInstance(cha);
cha.addClass(this.klass);
}
this.reuseParameters = new ReuseParameters(this.instanceBehavior, this);
// this.instructionFactory = new JavaInstructionFactory(); // TODO: TSIF
// Complete the signature of the method
reuseParameters.collectParameters(entrypoints);
this.mRef = reuseParameters.toMethodReference(null);
this.modelAcc = new ParameterAccessor(this.mRef, (! isStatic()));
this.paramManager = new SSAValueManager(modelAcc);
final Selector selector = this.mRef.getSelector();
final AndroidModelClass mClass = AndroidModelClass.getInstance(cha);
if (mClass.containsMethod(selector)) {
assert (mClass.getMethod(selector) instanceof SummarizedMethod);
this.model = (SummarizedMethod) mClass.getMethod(selector);
return;
}
this.body = new VolatileMethodSummary(new MethodSummary(this.mRef));
this.body.setStatic(true);
this.labelSpecial = AndroidEntryPointManager.MANAGER.makeModelBehavior(this.body, new TypeSafeInstructionFactory(cha),
this.paramManager, entrypoints);
this.monitor = AndroidEntryPointManager.MANAGER.getProgressMonitor();
this.maxProgress = entrypoints.size();
AndroidModel.doBoot &= AndroidEntryPointManager.MANAGER.getDoBootSequence();
// BUILD
this.monitor.beginTask("Building " + name, this.maxProgress);
populate(entrypoints);
assert (cha.lookupClass(AndroidModelClass.ANDROID_MODEL_CLASS) != null) : "Adding the class failed!";
if (this.klass == null) {
throw new IllegalStateException("Could not find ANDROID_MODEL_CLASS in cha.");
}
this.body.setLocalNames(this.paramManager.makeLocalNames());
this.model = new SummarizedMethodWithNames(this.mRef, this.body, this.klass) {
@Override
public TypeReference getParameterType (int i) {
IClassHierarchy cha = getClassHierarchy();
TypeReference tRef = super.getParameterType(i);
if (tRef.isClassType()) {
if (cha.lookupClass(tRef) != null) {
return tRef;
} else {
for (IClass c : cha) {
if (c.getName().toString().equals(tRef.getName().toString())) {
return c.getReference();
}
}
}
return tRef;
//throw new IllegalStateException("Error looking up " + tRef);
} else {
return tRef;
}
}
};
this.built = true;
}
private void register(SummarizedMethod model) {
IClass klass = getDeclaringClass();
((AndroidModelClass)klass).setMacroModel(model);
}
/**
* Building the SummarizedMethod is delayed upon the first class to this method.
*
* @return the method for this model as generated by build()
*/
public SummarizedMethod getMethod() throws CancelException {
if (!built) {
build(this.name);
register(this.model);
}
return this.model;
}
/**
* The class the Method representing this Model resides in.
*
* Most likely the AndroidModelClass.
*/
public IClass getDeclaringClass() {
return this.klass;
}
/**
* Overridden by models to restraint Entrypoints.
*
* For each entrypoint this method is queried if it should be part of the model.
*
* @param ep The EntryPoint in question
* @return if the given EntryPoint shall be part of the model
*/
protected boolean selectEntryPoint(AndroidEntryPoint ep) {
return true;
}
/**
* Add Instructions to the model.
*
* {@link #build(Iterable<?extends Entrypoint>)} prepares the MethodSummary, then calls populate() to
* add the instructions, then finishes the model. Populate is only an extra function to shorten build(),
* calling it doesn't make sense in an other context.
*/
private void populate(Iterable<? extends AndroidEntryPoint> entrypoints) throws CancelException {
assert (! built) : "You can only build once";
int currentProgress = 0;
final TypeSafeInstructionFactory tsif = new TypeSafeInstructionFactory(this.cha);
final Instantiator instantiator = new Instantiator (this.body, tsif, this.paramManager, this.cha, this.mRef, this.scope);
boolean enteredASection = false;
//
// Add preparing code to the model
//
if (AndroidModel.doBoot) {
// final Set<Parameter> allActivities = new HashSet<Parameter>(modelAcc.allExtend(AndroidTypes.ActivityName, getClassHierarchy()));
//assert(allActivities.size() > 0) : "There are no Activities in the Model"; // XXX
// final IntentStarters.StartInfo toolInfo = IntentStarters.StartInfo.makeContextFree(null);
// final AndroidStartComponentTool tool = new AndroidStartComponentTool(this.cha, this.mRef, toolInfo.getFlags(),
// /* caller */ null, tsif, modelAcc, this.paramManager, this.body, /* self */ null, toolInfo, /* callerNd */ null);
final SSAValue application;
{
final SSAValue tmpApp = modelAcc.firstExtends(AndroidTypes.ApplicationName, this.cha);
if (tmpApp != null) {
application = tmpApp;
} else {
// Generate a real one?
application = paramManager.getUnmanaged(AndroidTypes.Application, "app");
this.body.addConstant(application.getNumber(), new ConstantValue(null));
application.setAssigned();
}
}
final SSAValue nullIntent;
{
nullIntent = paramManager.getUnmanaged(AndroidTypes.Intent, "nullIntent");
this.body.addConstant(nullIntent.getNumber(), new ConstantValue(null));
nullIntent.setAssigned();
}
final SSAValue nullBinder;
{
nullBinder = paramManager.getUnmanaged(AndroidTypes.IBinder, "nullBinder");
this.body.addConstant(nullBinder.getNumber(), new ConstantValue(null));
nullBinder.setAssigned();
}
{
final AndroidBoot boot = new AndroidBoot(null);
boot.addBootCode(tsif, null, paramManager, this.body);
//tool.attachActivities(allActivities, application, boot.getMainThread(), /* Should be application context TODO */
// boot.getPackageContext(), nullBinder, nullIntent);
}
// TODO: Assign context to the other components
}
for (final AndroidEntryPoint ep : entrypoints) {
this.monitor.subTask(ep.getMethod().getReference().getSignature() );
if (! selectEntryPoint(ep)) {
assert(false): "The ep should not reach here!";
currentProgress++;
continue;
}
//
// Is special handling to be inserted?
//
if (this.labelSpecial.hadSectionSwitch(ep.order)) {
this.labelSpecial.enter(ep.getSection(), body.getNextProgramCounter());
enteredASection = true;
}
//
// Collect arguments to ep
// if there are multiple paramses call the entrypoint multiple times
//
List<List<SSAValue>> paramses = new ArrayList<List<SSAValue>>(1);
{
final List<Integer> mutliTypePositions = new ArrayList<Integer>();
{ // Add single-type parameters and collect positions for multi-type
final List<SSAValue> params = new ArrayList<SSAValue>(ep.getNumberOfParameters());
paramses.add(params);
for (int i = 0; i < ep.getNumberOfParameters(); ++i) {
if (ep.getParameterTypes(i).length != 1) {
mutliTypePositions.add(i);
params.add(null); // will get set later
} else {
for (final TypeReference type : ep.getParameterTypes(i)) {
if (this.instanceBehavior.getBehavior(type.getName(), ep.getMethod(), null) == InstanceBehavior.REUSE) {
params.add(this.paramManager.getCurrent(new TypeKey(type.getName())));
} else if (type.isPrimitiveType()) {
params.add(paramManager.getUnmanaged(type, "p"));
} else {
// It is an CREATE parameter
final boolean asManaged = false;
final VariableKey key = null; // auto-generates UniqueKey
final Set<SSAValue> seen = null;
params.add(instantiator.createInstance(type, asManaged, key, seen));
}
}
}
}
}
// Now for the mutliTypePositions: we'll build the Cartesian product for these
for (int positionInMutliTypePosition = 0; positionInMutliTypePosition < mutliTypePositions.size(); ++positionInMutliTypePosition) {
final Integer multiTypePosition = mutliTypePositions.get(positionInMutliTypePosition);
final TypeReference[] typesOnPosition = ep.getParameterTypes(multiTypePosition);
final int typeCountOnPosition = typesOnPosition.length;
{ // Extend the list size to hold the product
final List<List<SSAValue>> new_paramses = new ArrayList<List<SSAValue>>(paramses.size() * typeCountOnPosition);
for (int i = 0; i < typeCountOnPosition; ++i) {
//new_paramses.addAll(paramses); *grrr* JVM! You could copy at least null - but noooo...
for (final List<SSAValue> params : paramses) {
final List<SSAValue> new_params = new ArrayList<SSAValue>(params.size());
new_params.addAll(params);
new_paramses.add(new_params);
}
}
paramses = new_paramses;
}
{ // set the current multiTypePosition
for (int i = 0; i < paramses.size(); ++i) {
final List<SSAValue> params = paramses.get(i);
assert(params.get(multiTypePosition) == null) : "Expected null, got " + params.get(multiTypePosition) + " iter " + i;
// XXX: This could be faster, but well...
final TypeReference type = typesOnPosition[(i * (positionInMutliTypePosition + 1)) % typeCountOnPosition];
if (this.instanceBehavior.getBehavior(type.getName(), ep.getMethod(), null) == InstanceBehavior.REUSE) {
params.set(multiTypePosition, this.paramManager.getCurrent(new TypeKey(type.getName())));
} else if (type.isPrimitiveType()) {
params.set(multiTypePosition, paramManager.getUnmanaged(type, "p"));
} else {
// It is an CREATE parameter
final boolean asManaged = false;
final VariableKey key = null; // auto-generates UniqueKey
final Set<SSAValue> seen = null;
params.set(multiTypePosition, instantiator.createInstance(type, asManaged, key, seen));
}
}
}
}
}
/*{ // DEBUG
if (paramses.size() > 1) {
System.out.println("\n\nParamses on " + ep.getMethod().getSignature() + ":");
for (final List<SSAValue> params : paramses) {
System.out.println("\t" + params);
}
}
} // */
//
// Insert the call optionally handling its return value
//
for (final List<SSAValue> params : paramses) {
final int callPC = body.getNextProgramCounter();
final CallSiteReference site = ep.makeSite(callPC);
final SSAAbstractInvokeInstruction invokation;
final SSAValue exception = paramManager.getException(); // will hold the exception object of ep
if (ep.getMethod().getReturnType().equals(TypeReference.Void)) {
invokation = tsif.InvokeInstruction(callPC, params, exception, site);
this.body.addStatement(invokation);
} else {
// Check if we have to mix in the return value of this ep using a Phi
final TypeReference returnType = ep.getMethod().getReturnType();
final TypeKey returnKey = new TypeKey(returnType.getName());
if (this.paramManager.isSeen(returnKey)) {
// if it's seen it most likely is a REUSE-Type. However probably it makes sense for
// other types too so we don't test on isReuse.
final SSAValue oldValue = this.paramManager.getCurrent(returnKey);
this.paramManager.invalidate(returnKey);
final SSAValue returnValue = paramManager.getUnallocated(returnType, returnKey);
invokation = tsif.InvokeInstruction(callPC, returnValue, params, exception, site);
this.body.addStatement(invokation);
this.paramManager.setAllocation(returnValue, invokation);
// ... and Phi things together ...
this.paramManager.invalidate(returnKey);
final SSAValue newValue = this.paramManager.getFree(returnType, returnKey);
final int phiPC = body.getNextProgramCounter();
final List<SSAValue> toPhi = new ArrayList<SSAValue>(2);
toPhi.add(oldValue);
toPhi.add(returnValue);
final SSAPhiInstruction phi = tsif.PhiInstruction(phiPC, newValue, toPhi);
this.body.addStatement(phi);
this.paramManager.setPhi(newValue, phi);
} else {
// Just throw away the return value
final SSAValue returnValue = paramManager.getUnmanaged(returnType, new SSAValue.UniqueKey());
invokation = tsif.InvokeInstruction(callPC, returnValue, params, exception, site);
this.body.addStatement(invokation);
}
}
}
this.monitor.worked(++currentProgress);
MonitorUtil.throwExceptionIfCanceled(this.monitor);
}
// Close all sections by "jumping over" the remaining labels
if (enteredASection) {
labelSpecial.finish(body.getNextProgramCounter());
}
this.monitor.done();
}
/**
* Get method of the Model in an other Signature.
*
* Generates a new Method that wraps the model so it can be called using the given Signature.
* Flags control the behavior of that wrapper.
*
* Arguments to the wrapping function are "connected through" to the model based on their type only,
* so if there are multiple Arguments of the same type this may yield to unexpected connections.
*
* This method is called by the IntentCoentextInterpreter.
*
* @see com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentContextInterpreter
* @see com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters
*
* @param asMethod The signature to generate
* @param flags Control the behavior of the wrapper, may be null
* @param caller The class of the caller; only needed depending on the flags
* @param info The IntentSterter used
* @param callerNd CGNoodle of the caller - may be null
* @return A wrapper that calls the model
*/
public SummarizedMethod getMethodAs(MethodReference asMethod, TypeReference caller,
IntentStarters.StartInfo info, CGNode callerNd) throws CancelException {
Set<StarterFlags> flags = info.getFlags();
//System.out.println("\n\nAS: " + asMethod + "\n\n");
if (!built) {
getMethod();
}
if (asMethod == null) {
throw new IllegalArgumentException("asMethod may not be null");
}
if (flags == null) {
flags = Collections.emptySet();
}
final TypeSafeInstructionFactory instructionFactory = new TypeSafeInstructionFactory(getClassHierarchy());
final ParameterAccessor acc = new ParameterAccessor(asMethod, /* hasImplicitThis: */ true);
//final AndroidModelParameterManager pm = new AndroidModelParameterManager(acc);
final SSAValueManager pm = new SSAValueManager(acc);
if (callerNd != null) {
pm.breadCrumb = "Caller: " + caller + " Context: " + callerNd.getContext() + " Model: " + this.getClass() + " Name: " + this.getName();
} else {
pm.breadCrumb = "Caller: " + caller + " Model: " + this.getClass();
}
final VolatileMethodSummary redirect = new VolatileMethodSummary(new MethodSummary(asMethod));
redirect.setStatic(false);
final Instantiator instantiator = new Instantiator(redirect, instructionFactory, pm, this.cha, asMethod, this.scope);
final Parameter self;
{
self = acc.getThisAs(caller);
pm.setAllocation(self, null);
//self = acc.getThis();
}
final ParameterAccessor modelAcc = new ParameterAccessor(this.model);
/*{ // DEBUG
System.out.println("FOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO: Calleeeeeee" + this.model);
for (SSAValue calleeParam : modelAcc.all()) {
System.out.println("\tCalleeArg: " + calleeParam);
}
try {
System.out.println("\tCalleeP1: " + modelAcc.getParameter(1));
System.out.println("\tCalleeM0: " + this.model.getParameterType(0));
System.out.println("\tCalleeP2: " + modelAcc.getParameter(2));
System.out.println("\tCalleeM1: " + this.model.getParameterType(1));
MethodReference modelRef = this.model.getReference();
System.out.println("\tmRef: " + modelRef);
System.out.println("\tCalleeR0: " + modelRef.getParameterType(0));
System.out.println("\tCalleeR1: " + modelRef.getParameterType(1));
} catch (Exception e) { }
}*/
final List<Parameter> modelsActivities = modelAcc.allExtend(AndroidTypes.ActivityName, getClassHierarchy()); // are in models scope
final List<SSAValue> allActivities = new ArrayList<SSAValue>(modelsActivities.size()); // create instances in this scope
for (Parameter activity: modelsActivities) {
final TypeReference activityType = activity.getType();
final Parameter inAsMethod = acc.firstOf(activityType);
if (inAsMethod != null) {
allActivities.add(inAsMethod);
} else {
final Atom fdName = activityType.getName().getClassName();
final AndroidModelClass mClass = AndroidModelClass.getInstance(cha);
if (AndroidEntryPointManager.MANAGER.doFlatComponents()) {
if (mClass.getField(fdName) != null) {
final IField field = mClass.getField(fdName);
final int instPC = redirect.getNextProgramCounter();
final SSAValue target = pm.getUnallocated(activityType, new SSAValue.WeaklyNamedKey(activityType.getName(),
"got" + fdName.toString()));
final SSAInstruction getInst = instructionFactory.GetInstruction(instPC, target, field.getReference());
redirect.addStatement(getInst);
pm.setAllocation(target, getInst);
allActivities.add(target);
} else {
final SSAValue newInstance = instantiator.createInstance(activityType, false, null, null);
allActivities.add(newInstance);
mClass.putField(fdName, activityType);
final int instPC = redirect.getNextProgramCounter();
final FieldReference fdRef = FieldReference.findOrCreate(mClass.getReference(), fdName, activityType);
final SSAInstruction putInst = instructionFactory.PutInstruction(instPC, newInstance, fdRef);
redirect.addStatement(putInst);
System.out.println("All activities new: " + newInstance);
}
} else {
final SSAValue newInstance = instantiator.createInstance(activityType, false, null, null);
allActivities.add(newInstance);
}
}
}
assert(allActivities.size() == modelsActivities.size());
// The defaults for connectThrough
final Set<SSAValue> defaults = new HashSet<SSAValue>();
{ // Calls that don't take a bundle usually call through with a null-bundle
final SSAValue nullBundle = pm.getUnmanaged(AndroidTypes.Bundle, "nullBundle");
redirect.addConstant(nullBundle.getNumber(), new ConstantValue(null));
nullBundle.setAssigned();
defaults.add(nullBundle);
}
{ // We may have an incoming parameter of [Intent - unpack it to Intent
final TypeName intentArray = TypeName.findOrCreate("[Landroid/content/Intent");
final SSAValue incoming = acc.firstOf(intentArray);
// TODO: Take all not only the first
if (incoming != null) {
final VariableKey unpackedIntentKey = new WeaklyNamedKey(AndroidTypes.IntentName, "unpackedIntent");
final SSAValue unpackedIntent = pm.getUnallocated(AndroidTypes.Intent, unpackedIntentKey);
final int pc = redirect.getNextProgramCounter();
final SSAInstruction fetch = instructionFactory.ArrayLoadInstruction(pc, unpackedIntent, incoming, 0);
redirect.addStatement(fetch);
pm.setAllocation(unpackedIntent, fetch);
defaults.add(unpackedIntent);
}
}
final SSAValue intent = acc.firstExtends(AndroidTypes.Intent, cha);
final AndroidStartComponentTool tool = new AndroidStartComponentTool(getClassHierarchy(), asMethod, flags, caller, instructionFactory,
acc, pm, redirect, self, info, callerNd);
final AndroidTypes.AndroidContextType contextType;
final SSAValue androidContext; // of AndroidTypes.Context: The callers android-context
androidContext = tool.fetchCallerContext();
contextType = tool.typeOfCallerContext();
try { // Add additional Info if Exception occurs...
// TODO: Check, that caller is an activity where necessary!
//final SSAValue iBinder = tool.fetchIBinder(androidContext);
//tool.assignIBinder(iBinder, allActivities);
if (intent != null) {
tool.setIntent(intent, allActivities);
} else if (! info.isSystemService()) { // it's normal for SystemServices
}
// Call the model
{
final List<SSAValue> redirectParams = acc.connectThrough(modelAcc, new HashSet<SSAValue>(allActivities), defaults,
getClassHierarchy(), /* IInstantiator this.createInstance(type, redirect, pm) */ instantiator, false, null, null);
final int callPC = redirect.getNextProgramCounter();
final CallSiteReference site = CallSiteReference.make(callPC, this.model.getReference(),
IInvokeInstruction.Dispatch.STATIC);
final SSAAbstractInvokeInstruction invokation;
final SSAValue exception = pm.getException();
if (this.model.getReference().getReturnType().equals(TypeReference.Void)) {
invokation = instructionFactory.InvokeInstruction(callPC, redirectParams, exception, site);
} else {
// it's startExternal or SystemService
if (this instanceof SystemServiceModel) {
final SSAValue svc = pm.getUnmanaged(TypeReference.JavaLangObject, "systemService");
invokation = instructionFactory.InvokeInstruction(callPC, svc, redirectParams, exception, site);
// SHORTCUT:
redirect.addStatement(invokation);
if (instructionFactory.isAssignableFrom(svc.getType(), svc.getValidIn().getReturnType())) {
final int returnPC = redirect.getNextProgramCounter();
final SSAInstruction returnInstruction = instructionFactory.ReturnInstruction(returnPC, svc);
redirect.addStatement(returnInstruction);
}
final IClass declaringClass = this.cha.lookupClass(asMethod.getDeclaringClass());
if (declaringClass == null) {
throw new IllegalStateException("Unable to retreive te IClass of " + asMethod.getDeclaringClass() + " from " +
"Method " + asMethod.toString());
}
redirect.setLocalNames(pm.makeLocalNames());
SummarizedMethod override = new SummarizedMethodWithNames(mRef, redirect, declaringClass);
return override;
} else if (this instanceof ExternalModel) {
final SSAValue trash = pm.getUnmanaged(AndroidTypes.Intent, "trash");
invokation = instructionFactory.InvokeInstruction(callPC, trash, redirectParams, exception, site);
} else {
throw new UnsupportedOperationException("Can't handle a " + this.model.getClass());
}
}
redirect.addStatement(invokation);
}
// Optionally call onActivityResult
if (flags.contains(StarterFlags.CALL_ON_ACTIVITY_RESULT) && // TODO: Test multiple activities
(! flags.contains(StarterFlags.CONTEXT_FREE))) { // TODO: Doesn't this work without context?
// Collect all Activity.mResultCode and Activity.mResultData
// Result information of all activities.
final List<SSAValue> resultCodes = new ArrayList<SSAValue>();
final List<SSAValue> resultData = new ArrayList<SSAValue>();
final SSAValue mResultCode; // = Phi(resultCodes)
final SSAValue mResultData; // = Phi(resultData)
tool.fetchResults(resultCodes, resultData, allActivities);
if (resultCodes.size() == 0) {
throw new IllegalStateException("The call " + asMethod + " from " + caller + " failed, as the model " + this.model +
" did not take an activity to read the result from");
}
mResultCode = tool.addPhi(resultCodes);
mResultData = tool.addPhi(resultData);
{ // Send back the results
// TODO: Assert caller is an Activity
final SSAValue outRequestCode = acc.firstOf(TypeReference.Int); // TODO: Check is's the right parameter
final int callPC = redirect.getNextProgramCounter();
// void onActivityResult (int requestCode, int resultCode, Intent data)
final Selector mSel = Selector.make("onActivityResult(IILandroid/content/Intent;)V");
final MethodReference mRef = MethodReference.findOrCreate(caller, mSel);
final CallSiteReference site = CallSiteReference.make(callPC, mRef, IInvokeInstruction.Dispatch.VIRTUAL);
//final SSAValue exception = new SSAValue(pm.getUnmanaged(), TypeReference.JavaLangException, asMethod, "exception");
final SSAValue exception = pm.getException();
final List<SSAValue> params = new ArrayList<SSAValue>();
params.add(self);
params.add(outRequestCode); // Was an agument to start...
params.add(mResultCode);
params.add(mResultData);
final SSAInstruction invokation = instructionFactory.InvokeInstruction(callPC, params, exception, site);
redirect.addStatement(invokation);
} // */
}
final IClass declaringClass = this.cha.lookupClass(asMethod.getDeclaringClass());
if (declaringClass == null) {
throw new IllegalStateException("Unable to retreive te IClass of " + asMethod.getDeclaringClass() + " from " +
"Method " + asMethod.toString());
}
// TODO: Throw into an other loader
redirect.setLocalNames(pm.makeLocalNames());
SummarizedMethod override = new SummarizedMethodWithNames(mRef, redirect, declaringClass);
//assert(asMethod.getReturnType().equals(TypeReference.Void)) : "getMethodAs does not support return values. Requested: " +
// asMethod.getReturnType().toString(); // TODO: Implement
return override;
} catch (Exception e) {
e.printStackTrace();
System.err.println("\nOccured in getMethodAs with parameters of:");
System.err.println(acc.dump());
System.err.println("\tpm=\t" + pm);
System.err.println("\tself=\t" + self);
System.err.println("\tmodelAcc=\t" + acc.dump());
//final List<Parameter> modelsActivities = modelAcc.allExtend(AndroidTypes.ActivityName, getClassHierarchy()); // are in models scope
//final List<SSAValue> allActivities = new ArrayList<SSAValue>(modelsActivities.size()); // create instances in this scope
System.err.println("\tcontextType=\t" + contextType);
System.err.println("\tandroidContetx=\t" + androidContext);
System.err.println("\tasMethod=\t" + asMethod);
System.err.println("\tcaller=\t" + caller);
System.err.println("\tinfo=\t" + info);
System.err.println("\tcallerND=\t" + callerNd);
System.err.println("\tthis=\t" + this.getClass().toString());
System.err.println("\tthis.name=\t" + this.name);
throw new IllegalStateException(e);
}
}
/**
* Creates an "encapsulated" version of the model.
*
* The generated method will take no parameters. New instances for REUSE-Parameters will
* be created.
*
* This variant is useful for the start of an analysis.
*/
public IMethod getMethodEncap() throws CancelException {
final MethodReference asMethod;
{
final TypeReference clazz = AndroidModelClass.ANDROID_MODEL_CLASS;
final Atom methodName = Atom.concat(this.getName(), Atom.findOrCreateAsciiAtom("Encap"));
//final TypeName returnType = this.getReturnType();
final TypeName returnType = TypeReference.VoidName;
final Descriptor descr = Descriptor.findOrCreate(new TypeName[]{}, returnType);
asMethod = MethodReference.findOrCreate(clazz, methodName, descr);
}
final AndroidModelClass mClass = AndroidModelClass.getInstance(cha);
if (mClass.containsMethod(asMethod.getSelector())) {
// There's already an encap for this method
return mClass.getMethod(asMethod.getSelector());
}
final VolatileMethodSummary encap = new VolatileMethodSummary(new MethodSummary(asMethod));
encap.setStatic(true);
final TypeSafeInstructionFactory instructionFactory = new TypeSafeInstructionFactory(getClassHierarchy());
final ParameterAccessor acc = new ParameterAccessor(asMethod, /* hasImplicitThis: */ false);
final SSAValueManager pm = new SSAValueManager(acc);
pm.breadCrumb = "Encap: " + this.getClass().toString();
final SummarizedMethod model = getMethod();
final List<SSAValue> params = new ArrayList<SSAValue>();
{ // Collect Params
final FlatInstantiator instantiator = new FlatInstantiator(encap, instructionFactory, pm, this.cha, asMethod, this.scope);
for (int i = 0; i < model.getNumberOfParameters(); ++i) {
final TypeReference argT = model.getParameterType(i);
final SSAValue arg;
if ( ( AndroidEntryPointManager.MANAGER.doFlatComponents()) &&
(AndroidComponent.isAndroidComponent(argT, cha)) ) {
// Get / Put filed in AndroidModelClass for Android-Components
final Atom fdName = argT.getName().getClassName();
if (mClass.getField(fdName) != null) {
final IField field = mClass.getField(fdName);
final int instPC = encap.getNextProgramCounter();
arg = pm.getUnallocated(argT, new SSAValue.WeaklyNamedKey(argT.getName(),
"got" + fdName.toString()));
final SSAInstruction getInst = instructionFactory.GetInstruction(instPC, arg, field.getReference());
encap.addStatement(getInst);
pm.setAllocation(arg, getInst);
} else {
arg = instantiator.createInstance(argT, false, null, null);
mClass.putField(fdName, argT);
final int instPC = encap.getNextProgramCounter();
final FieldReference fdRef = FieldReference.findOrCreate(mClass.getReference(), fdName, argT);
final SSAInstruction putInst = instructionFactory.PutInstruction(instPC, arg, fdRef);
encap.addStatement(putInst);
}
} else {
final boolean managed = false;
final SSAValue.VariableKey key = new SSAValue.TypeKey(argT.getName());
arg = instantiator.createInstance (argT, managed, key, null);
}
params.add(arg);
}
}
{ // Call the model
final int callPC = encap.getNextProgramCounter();
final CallSiteReference site = CallSiteReference.make(callPC, model.getReference(), IInvokeInstruction.Dispatch.STATIC);
final SSAValue exception = pm.getException();
final SSAAbstractInvokeInstruction invokation = instructionFactory.InvokeInstruction(callPC, params, exception, site);
encap.addStatement(invokation);
}
encap.setLocalNames(pm.makeLocalNames());
final SummarizedMethod method = new SummarizedMethodWithNames(asMethod, encap, mClass);
mClass.addMethod(method);
return method;
}
public IClassHierarchy getClassHierarchy() {
return this.cha;
}
@Override
public String toString() {
return "<" + this.getClass() + " name=" + this.name + " />";
}
}