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

360 lines
13 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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.FieldImpl;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticClass;
import com.ibm.wala.dalvik.util.AndroidEntryPointManager;
import com.ibm.wala.ipa.cha.IClassHierarchy;
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.shrikeCT.ClassConstants;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.ClassLoaderReference;
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.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.ssa.SSAValue;
import com.ibm.wala.util.ssa.TypeSafeInstructionFactory;
import com.ibm.wala.util.strings.Atom;
/**
* Encapsulates synthetic methods for modeling Androids lifecycle.
*
* In the generated code this class may be found as "Lcom/ibm/wala/AndroidModelClass"
*
* @see com.ibm.wala.dalvik.ipa.callgraph.impl.FakeRootClass
*
* @author Tobias Blaschke <code@tobiasblaschke.de>
* @todo Move this class into an other loader? Currently: Primordial
*/
public final /* singleton */ class AndroidModelClass extends SyntheticClass {
public static final TypeReference ANDROID_MODEL_CLASS = TypeReference.findOrCreate(
ClassLoaderReference.Primordial, TypeName.string2TypeName("Lcom/ibm/wala/AndroidModelClass"));
private static IClassHierarchy cha;
private static class AndroidModelClassHolder {
private static final AndroidModelClass INSTANCE = new AndroidModelClass(AndroidModelClass.cha);
}
public static AndroidModelClass getInstance(IClassHierarchy cha) {
if (AndroidModelClass.cha == null) {
if (cha == null) {
throw new IllegalArgumentException("Cha may not be null if there had not been an Instance AndroidModelClass before!");
} else {
AndroidModelClass.cha = cha;
}
} else {
if (! cha.equals(AndroidModelClass.cha)) {
throw new IllegalArgumentException("Cha differs!");
}
}
return AndroidModelClassHolder.INSTANCE;
}
private AndroidModelClass(IClassHierarchy cha) {
super(ANDROID_MODEL_CLASS, cha);
this.addMethod(this.clinit());
if (AndroidModelClassHolder.INSTANCE != null) { // May be caused when using reflection
throw new IllegalStateException("AndroidModelClass is a singleton and already instantiated!");
}
}
/**
* Generate clinit for AndroidModelClass.
*
* clinit initializes AndroidComponents
*/
private SummarizedMethod clinit() {
final MethodReference clinitRef = MethodReference.findOrCreate(this.getReference(), MethodReference.clinitSelector);
final VolatileMethodSummary clinit = new VolatileMethodSummary(new MethodSummary(clinitRef));
clinit.setStatic(true);
final TypeSafeInstructionFactory instructionFactory = new TypeSafeInstructionFactory(cha);
final Set<TypeReference> components = AndroidEntryPointManager.MANAGER.getComponents();
int ssaNo = 1;
if (AndroidEntryPointManager.MANAGER.doFlatComponents()) {
for (TypeReference component : components) {
final SSAValue instance = new SSAValue(ssaNo++, component, clinitRef);
{ // New
final int pc = clinit.getNextProgramCounter();
final NewSiteReference nRef = NewSiteReference.make(pc, component);
final SSAInstruction instr = instructionFactory.NewInstruction(pc, instance, nRef);
clinit.addStatement(instr);
}
{ // Call cTor
final int pc = clinit.getNextProgramCounter();
final MethodReference ctor = MethodReference.findOrCreate(component, MethodReference.initSelector);
final CallSiteReference site = CallSiteReference.make(pc, ctor, IInvokeInstruction.Dispatch.SPECIAL);
final SSAValue exception = new SSAValue(ssaNo++, TypeReference.JavaLangException, clinitRef);
final List<SSAValue> params = new ArrayList<SSAValue>();
params.add(instance);
final SSAInstruction ctorCall = instructionFactory.InvokeInstruction(pc, params, exception, site);
clinit.addStatement(ctorCall);
}
{ // Put into AndroidModelClass
final Atom fdName = component.getName().getClassName();
putField(fdName, component);
final int pc = clinit.getNextProgramCounter();
final FieldReference fdRef = FieldReference.findOrCreate(this.getReference(), fdName, component);
final SSAInstruction putInst = instructionFactory.PutInstruction(pc, instance, fdRef);
clinit.addStatement(putInst);
}
}
}
return new SummarizedMethodWithNames(clinitRef, clinit, this);
}
//
// Contents of the class: Methods
//
private IMethod macroModel = null;
// private IMethod allActivitiesModel = null;
private Map<Selector, IMethod> methods = HashMapFactory.make(); // does not contain macroModel
public boolean containsMethod(Selector selector) {
return (
((macroModel != null) && macroModel.getSelector().equals(selector)) ||
methods.containsKey(selector));
}
@Override
public IMethod getMethod(Selector selector) {
//assert (macroModel != null) : "Macro Model was not set yet!";
if ((macroModel != null) && (macroModel.getSelector().equals(selector))) {
return macroModel;
}
if (methods.containsKey(selector)) {
return methods.get(selector);
}
if (selector.equals(MethodReference.initSelector)) {
return null;
}
throw new IllegalArgumentException("Could not resolve " + selector);
}
@Override
public Collection<IMethod> getDeclaredMethods() {
Set<IMethod> methods = HashSetFactory.make();
if ( this.macroModel != null ) {
methods.add(macroModel);
}
methods.addAll(this.methods.values());
return Collections.unmodifiableCollection(methods);
}
@Override
public Collection<IMethod> getAllMethods() {
return getDeclaredMethods();
}
/* package private */ void setMacroModel(IMethod model) {
assert(this.macroModel == null);
this.macroModel = model;
}
public void addMethod(IMethod method) {
if (this.methods.containsKey(method.getSelector())) {
// TODO: Check this matches on signature not on contents!
// TODO: What on different Context versions
throw new IllegalStateException("The AndroidModelClass already contains a Method called" + method.getName());
}
assert(this.methods != null);
this.methods.put(method.getSelector(), method);
}
@Override
public IMethod getClassInitializer() {
return getMethod(MethodReference.clinitSelector);
}
//
// Contents of the class: Fields
// We have none...
//
private Map<Atom, IField> fields = new HashMap<Atom, IField>();
@Override
public IField getField(Atom name) {
if (fields.containsKey(name)) {
return fields.get(name);
} else {
return null;
}
}
public void putField(Atom name, TypeReference type) {
final FieldReference fdRef = FieldReference.findOrCreate(this.getReference(), name, type);
final int accessFlags = ClassConstants.ACC_STATIC | ClassConstants.ACC_PUBLIC;
final IField field = new FieldImpl(this, fdRef, accessFlags, null);
this.fields.put(name, field);
}
/**
* This class does not contain any fields.
*/
@Override
public Collection<IField> getAllFields() {
return fields.values();
}
/**
* This class does not contain any fields.
*/
@Override
public Collection<IField> getDeclaredStaticFields() {
return fields.values();
}
/**
* This class does not contain any fields.
*/
@Override
public Collection<IField> getAllStaticFields() {
return fields.values();
}
/**
* This class does not contain any fields.
*/
@Override
public Collection<IField> getDeclaredInstanceFields() throws UnsupportedOperationException {
return Collections.emptySet();
}
/**
* This class does not contain any fields.
*/
@Override
public Collection<IField> getAllInstanceFields() {
return Collections.emptySet();
}
//
// Class Modifiers
//
/**
* This is a public final class.
*/
@Override
public int getModifiers() {
return ClassConstants.ACC_PUBLIC |
ClassConstants.ACC_FINAL;
}
@Override
public boolean isPublic() { return true; }
@Override
public boolean isPrivate() { return false; }
@Override
public boolean isInterface() { return false; }
@Override
public boolean isAbstract() { return false; }
@Override
public boolean isArrayClass () { return false; }
/**
* This is a subclass of the root class.
*/
@Override
public IClass getSuperclass() throws UnsupportedOperationException {
return getClassHierarchy().getRootClass();
}
/**
* This class does not impement any interfaces.
*/
@Override
public Collection<IClass> getAllImplementedInterfaces() {
return Collections.emptySet();
}
@Override
public Collection<IClass> getDirectInterfaces() {
return Collections.emptySet();
}
//
// Misc
//
@Override
public boolean isReferenceType() {
return getReference().isReferenceType();
}
}