360 lines
13 KiB
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();
|
|
}
|
|
|
|
}
|
|
|