696 lines
27 KiB
Java
696 lines
27 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.parameters;
|
|
|
|
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.analysis.typeInference.ConeType;
|
|
import com.ibm.wala.analysis.typeInference.PrimitiveType;
|
|
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
|
|
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.NewSiteReference;
|
|
import com.ibm.wala.ipa.callgraph.AnalysisScope;
|
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
|
import com.ibm.wala.ipa.summaries.VolatileMethodSummary;
|
|
import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
|
import com.ibm.wala.ssa.ConstantValue;
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
|
import com.ibm.wala.ssa.SSANewInstruction;
|
|
import com.ibm.wala.types.MethodReference;
|
|
import com.ibm.wala.types.TypeReference;
|
|
import com.ibm.wala.util.ssa.IInstantiator;
|
|
import com.ibm.wala.util.ssa.ParameterAccessor;
|
|
import com.ibm.wala.util.ssa.SSAValue;
|
|
import com.ibm.wala.util.ssa.SSAValue.UniqueKey;
|
|
import com.ibm.wala.util.ssa.SSAValue.VariableKey;
|
|
import com.ibm.wala.util.ssa.SSAValueManager;
|
|
import com.ibm.wala.util.ssa.TypeSafeInstructionFactory;
|
|
|
|
/**
|
|
* Add code to create an instance of a type in a synthetic method.
|
|
*
|
|
* This variant limits recursion depth.
|
|
*
|
|
* @author Tobias Blaschke <code@tobiasblaschke.de>
|
|
*/
|
|
public class FlatInstantiator implements IInstantiator {
|
|
|
|
final IClassHierarchy cha;
|
|
final VolatileMethodSummary body;
|
|
final TypeSafeInstructionFactory instructionFactory;
|
|
final SSAValueManager pm;
|
|
final MethodReference scope;
|
|
final AnalysisScope analysisScope;
|
|
final int maxDepth;
|
|
|
|
public FlatInstantiator(final VolatileMethodSummary body, final TypeSafeInstructionFactory instructionFactory,
|
|
final SSAValueManager pm, final IClassHierarchy cha, final MethodReference scope, final AnalysisScope analysisScope) {
|
|
this.body = body;
|
|
this.instructionFactory = instructionFactory;
|
|
this.pm = pm;
|
|
this.cha = cha;
|
|
this.scope = scope;
|
|
this.analysisScope = analysisScope;
|
|
this.maxDepth = 1;
|
|
}
|
|
|
|
public FlatInstantiator(final VolatileMethodSummary body, final TypeSafeInstructionFactory instructionFactory,
|
|
final SSAValueManager pm, final IClassHierarchy cha, final MethodReference scope, final AnalysisScope analysisScope,
|
|
final int maxDepth) {
|
|
this.body = body;
|
|
this.instructionFactory = instructionFactory;
|
|
this.pm = pm;
|
|
this.cha = cha;
|
|
this.scope = scope;
|
|
this.analysisScope = analysisScope;
|
|
this.maxDepth = maxDepth;
|
|
}
|
|
|
|
|
|
private boolean isExcluded(IClass cls) {
|
|
if (this.analysisScope.getExclusions().contains(cls.getName().toString())) { // XXX FUUUUU
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new instance of type calling all that's necessary.
|
|
*
|
|
* If T is a class-type all its constructors are searched for the one found best suited (takes the least arguments, ...).
|
|
* New instances are created for all parameters, then the constructor is called.
|
|
*
|
|
* If T represents multiple types (is an interface, abstract class, ...) _all_ implementors of that type are instantiated
|
|
* After that they get Phi-ed together.
|
|
*
|
|
* If T is an array-type a new array of length 1 is generated.
|
|
*
|
|
* @todo Do we want to mix in REUSE-Parameters?
|
|
*/
|
|
public SSAValue createInstance(final TypeReference T, final boolean asManaged, VariableKey key, Set<? extends SSAValue> seen) {
|
|
return createInstance(T, asManaged, key, seen, 0);
|
|
}
|
|
|
|
private SSAValue createInstance(final TypeReference T, final boolean asManaged, VariableKey key, Set<? extends SSAValue> seen, int currentDepth) {
|
|
if (T == null) {
|
|
throw new IllegalArgumentException("Can't create an instance of null");
|
|
}
|
|
if (seen == null) {
|
|
|
|
seen = new HashSet<SSAValue>();
|
|
}
|
|
|
|
if (currentDepth > this.maxDepth) {
|
|
final SSAValue instance = this.pm.getUnmanaged(T, key);
|
|
instance.setAssigned();
|
|
return instance;
|
|
}
|
|
|
|
{ // Special type?
|
|
final SpecializedInstantiator sInst = new SpecializedInstantiator(body, instructionFactory, pm,
|
|
cha, scope, analysisScope, this);
|
|
if (sInst.understands(T)) {
|
|
return sInst.createInstance(T, asManaged, key, seen, currentDepth);
|
|
}
|
|
}
|
|
|
|
final IClass klass = this.cha.lookupClass(T);
|
|
final SSAValue instance;
|
|
{ // fetch new value
|
|
if (asManaged) {
|
|
if (key == null) {
|
|
throw new IllegalArgumentException("A managed variable needs a key - null given.");
|
|
}
|
|
if ((klass != null) && (klass.isAbstract() || klass.isInterface())) {
|
|
// We'll need a phi
|
|
instance = this.pm.getFree(T, key);
|
|
} else {
|
|
instance = this.pm.getUnallocated(T, key);
|
|
}
|
|
} else {
|
|
if (key == null) {
|
|
key = new UniqueKey();
|
|
}
|
|
instance = this.pm.getUnmanaged(T, key);
|
|
}
|
|
}
|
|
|
|
if (T.isPrimitiveType()) {
|
|
createPrimitive(instance);
|
|
return instance;
|
|
} else if (klass == null) {
|
|
if (! T.getName().toString().startsWith("Landroid/")) {
|
|
|
|
} else {
|
|
|
|
}
|
|
this.body.addConstant(instance.getNumber(), new ConstantValue(null));
|
|
instance.setAssigned();
|
|
return instance;
|
|
} else if (isExcluded(klass)) {
|
|
this.body.addConstant(instance.getNumber(), new ConstantValue(null)); // TODO: null or nothing?
|
|
instance.setAssigned();
|
|
return instance;
|
|
}
|
|
|
|
final Set<TypeReference> types = getTypes(T);
|
|
|
|
|
|
if (types.isEmpty()) {
|
|
throw new IllegalStateException("Types of " + T + " are empty");
|
|
}
|
|
|
|
|
|
if ((types.size() == 1) && (! klass.isAbstract()) && (! klass.isArrayClass()) && (! klass.isInterface() )) {
|
|
// It's a "regular" class
|
|
final SSANewInstruction newInst = addNew(instance);
|
|
selectAndCallCtor(instance, seen, currentDepth);
|
|
if (asManaged) {
|
|
this.pm.setAllocation(instance, newInst);
|
|
}
|
|
assert(newInst.getDef() == instance.getNumber());
|
|
return instance;
|
|
} else if (klass.isArrayClass()) {
|
|
|
|
|
|
final TypeReference payloadType = T.getArrayElementType();
|
|
SSAValue payload = null;
|
|
{
|
|
for (final SSAValue see : seen) {
|
|
if (ParameterAccessor.isAssignable(see.getType(), payloadType, this.cha)) {
|
|
// Happens on Array of interfaces
|
|
|
|
payload = see;
|
|
}
|
|
}
|
|
if (payload == null) {
|
|
payload = createInstance(payloadType, false, new UniqueKey(), seen, currentDepth);
|
|
}
|
|
}
|
|
//assert (types.size() == 1); // TODO
|
|
|
|
// Generate an array of length 1
|
|
final SSANewInstruction newInst;
|
|
{
|
|
final int pc = this.body.getNextProgramCounter();
|
|
final NewSiteReference nRef = NewSiteReference.make(pc, instance.getType());
|
|
|
|
final SSAValue arrayLength = this.pm.getUnmanaged(TypeReference.Int, new UniqueKey());
|
|
this.body.addConstant(arrayLength.getNumber(), new ConstantValue(1));
|
|
arrayLength.setAssigned();
|
|
|
|
final ArrayList<SSAValue> params = new ArrayList<SSAValue>(1);
|
|
params.add(arrayLength);
|
|
|
|
newInst = this.instructionFactory.NewInstruction(pc, instance, nRef, params);
|
|
this.body.addStatement(newInst);
|
|
assert(instance.getNumber() == newInst.getDef());
|
|
}
|
|
|
|
// Put a payload into the array
|
|
{
|
|
final int pc = this.body.getNextProgramCounter();
|
|
final SSAInstruction write = this.instructionFactory.ArrayStoreInstruction(pc, instance, 0, payload);
|
|
body.addStatement(write);
|
|
}
|
|
|
|
assert(newInst.getDef() == instance.getNumber());
|
|
return instance;
|
|
} else {
|
|
// Abstract, Interface or array
|
|
|
|
final Set<SSAValue> subInstances = new HashSet<SSAValue>();
|
|
for (final TypeReference type : types) {
|
|
final IClass subKlass = this.cha.lookupClass(type);
|
|
|
|
if (subKlass.isAbstract() || subKlass.isInterface()) {
|
|
// All "regular" classes in consideration should already be in types
|
|
continue;
|
|
}
|
|
|
|
{ // Create instance of subInstance
|
|
final SSAValue subInstance = pm.getUnmanaged(type, new UniqueKey());
|
|
final SSANewInstruction newInst = addNew(subInstance);
|
|
selectAndCallCtor(subInstance, seen, currentDepth);
|
|
assert (subInstance.getNumber() == newInst.getDef()) : "Unexpected: number and def differ: " + subInstance.getNumber() + ", " +
|
|
newInst.getDef();
|
|
final Set<SSAValue> newSeen = new HashSet<SSAValue>(); // Narf
|
|
newSeen.addAll(seen);
|
|
newSeen.add(subInstance);
|
|
seen = newSeen;
|
|
subInstances.add(subInstance);
|
|
}
|
|
}
|
|
|
|
TypeAbstraction abstraction = null;
|
|
{ // Build the abstraction
|
|
for (final TypeReference type : types) {
|
|
final IClass cls;
|
|
if (type.isPrimitiveType()) {
|
|
cls = null;
|
|
} else {
|
|
cls = this.cha.lookupClass(type);
|
|
assert(cls != null);
|
|
}
|
|
|
|
if (abstraction == null) {
|
|
// TODO: assert primitive stays primitive
|
|
if (type.isPrimitiveType()) { // XXX: May this happen here?
|
|
abstraction = PrimitiveType.getPrimitive(type);
|
|
} else {
|
|
abstraction = new ConeType(cls);
|
|
}
|
|
} else {
|
|
if (type.isPrimitiveType()) {
|
|
abstraction = abstraction.meet(PrimitiveType.getPrimitive(type));
|
|
} else {
|
|
abstraction = abstraction.meet(new ConeType(cls));
|
|
}
|
|
}
|
|
}
|
|
|
|
// XXX: What to do with the abstraction now?
|
|
}
|
|
|
|
{ // Phi together everything
|
|
if (subInstances.size() > 0) {
|
|
final int pc = this.body.getNextProgramCounter();
|
|
final SSAInstruction phi = this.instructionFactory.PhiInstruction(pc, instance, subInstances);
|
|
body.addStatement(phi);
|
|
if (asManaged) {
|
|
this.pm.setPhi(instance, phi);
|
|
}
|
|
} else {
|
|
|
|
this.body.addConstant(instance.getNumber(), new ConstantValue(null));
|
|
instance.setAssigned();
|
|
}
|
|
}
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* @return an unallocated SSAVariable
|
|
*/
|
|
private void createPrimitive(SSAValue instance) {
|
|
// XXX; something else?
|
|
instance.setAssigned();
|
|
}
|
|
|
|
/**
|
|
* Add a NewInstruction to the body.
|
|
*/
|
|
private SSANewInstruction addNew(SSAValue val) {
|
|
final int pc = this.body.getNextProgramCounter();
|
|
final NewSiteReference nRef = NewSiteReference.make(pc, val.getType());
|
|
final SSANewInstruction newInstr = this.instructionFactory.NewInstruction(pc, val, nRef);
|
|
this.body.addStatement(newInstr);
|
|
assert(val.getNumber() == newInstr.getDef());
|
|
return newInstr;
|
|
}
|
|
|
|
/**
|
|
* Add a call to a single clinit to the body.
|
|
*
|
|
* @param val the "this" to call clinit on
|
|
* @param inClass the class to call its clinit of
|
|
*/
|
|
/*private void addCallCLinit(SSAValue val, TypeReference inClass) {
|
|
final int pc = this.body.getNextProgramCounter();
|
|
final MethodReference mRef = MethodReference.findOrCreate(inClass, MethodReference.clinitSelector);
|
|
final SSAValue exception = pm.getException();
|
|
final CallSiteReference site = CallSiteReference.make(pc, mRef, IInvokeInstruction.Dispatch.STATIC);
|
|
final List<SSAValue> params = new ArrayList<SSAValue>(1);
|
|
params.add(val);
|
|
final SSAInstruction ctorCall = instructionFactory.InvokeInstruction(pc, params, exception, site);
|
|
body.addStatement(ctorCall);
|
|
} */
|
|
|
|
/**
|
|
* Add a call to the given constructor to the body.
|
|
*
|
|
* @param self the "this" to call the constructor on
|
|
* @param ctor the constructor to call
|
|
* @param params parameters to the ctor _without_ implicit this
|
|
*/
|
|
protected void addCallCtor(SSAValue self, MethodReference ctor, List<SSAValue> ctorParams) {
|
|
final int pc = this.body.getNextProgramCounter();
|
|
final SSAValue exception = pm.getException();
|
|
final CallSiteReference site = CallSiteReference.make(pc, ctor, IInvokeInstruction.Dispatch.SPECIAL);
|
|
final List<SSAValue> params = new ArrayList<SSAValue>(1 + ctorParams.size());
|
|
params.add(self);
|
|
params.addAll(ctorParams);
|
|
final SSAInstruction ctorCall = instructionFactory.InvokeInstruction(pc, params, exception, site);
|
|
body.addStatement(ctorCall);
|
|
}
|
|
|
|
private MethodReference selectAndCallCtor(SSAValue val, final Set<? extends SSAValue> overrides, final int currentDepth) {
|
|
final IMethod cTor = lookupConstructor(val.getType());
|
|
final ParameterAccessor ctorAcc = new ParameterAccessor(cTor);
|
|
assert (ctorAcc.hasImplicitThis()) : "CTor detected as not having implicit this pointer";
|
|
|
|
final ParameterAccessor acc = new ParameterAccessor(this.scope, false); // TODO pm needs a connectThrough too!
|
|
// TODO false is false
|
|
// TODO: The overrides may lead to use before definition
|
|
//if (acc.firstOf(val.getType().getName()) != null) {
|
|
final SSAValue nullSelf = pm.getUnmanaged(val.getType(), new UniqueKey());
|
|
this.body.addConstant(nullSelf.getNumber(), new ConstantValue(null));
|
|
nullSelf.setAssigned();
|
|
//}
|
|
final Set<SSAValue> seen = new HashSet<SSAValue>(1 + overrides.size());
|
|
seen.add(nullSelf);
|
|
seen.addAll(overrides);
|
|
|
|
|
|
|
|
final List<SSAValue> ctorParams = acc.connectThrough(ctorAcc, overrides, /* defaults */ null, this.cha,
|
|
this, /* managed */ false, /* key */ null, seen, currentDepth + 1); // XXX This starts the recursion!
|
|
addCallCtor(val, cTor.getReference(), ctorParams);
|
|
return cTor.getReference();
|
|
}
|
|
|
|
/**
|
|
* Get all sub-types a type represents until concrete ones are reached.
|
|
*
|
|
* A concrete type only represents itself.
|
|
*
|
|
* @throws IllegalArgumentException if T is a primitive.
|
|
*/
|
|
private Set<TypeReference> getTypes(final TypeReference T) {
|
|
final IClass cls = this.cha.lookupClass(T);
|
|
if (isExcluded(cls)) {
|
|
return new HashSet<TypeReference>();
|
|
}
|
|
return getTypes(T, Collections.EMPTY_SET);
|
|
}
|
|
|
|
/**
|
|
* Used internally to avoid endless recursion on getTypes().
|
|
*/
|
|
private Set<TypeReference> getTypes(final TypeReference T, final Set<TypeReference> seen) {
|
|
final Set<TypeReference> ret = new HashSet<TypeReference>();
|
|
ret.add(T);
|
|
|
|
if (T.isPrimitiveType()) {
|
|
|
|
return ret;
|
|
//throw new IllegalArgumentException("Not you that call primitive type on :P");
|
|
}
|
|
|
|
final IClass cls = this.cha.lookupClass(T);
|
|
if (cls == null) {
|
|
|
|
return ret;
|
|
//throw new IllegalArgumentException("The type " + T + " is not in the ClassHierarchy");
|
|
} else if (isExcluded(cls)) {
|
|
return ret;
|
|
} else if (seen.contains(T)) {
|
|
return ret;
|
|
}
|
|
|
|
if (cls.isInterface()) {
|
|
final Set<IClass> impls = cha.getImplementors(T);
|
|
if (impls.isEmpty()) {
|
|
//throw new IllegalStateException("The interface " + T + " has no known implementors");
|
|
if (! T.getName().toString().startsWith("Landroid/")) {
|
|
|
|
} else {
|
|
|
|
}
|
|
return ret; // XXX: This is a bad idea?
|
|
} else {
|
|
// ADD all
|
|
for (IClass impl: impls) {
|
|
if (impl.isAbstract()) {
|
|
ret.addAll(getTypes(impl.getReference(), ret)); // impl added through recursion
|
|
} else {
|
|
ret.add(impl.getReference());
|
|
}
|
|
}
|
|
}
|
|
} else if (cls.isAbstract()) {
|
|
final Collection<IClass> subs = cha.computeSubClasses(T);
|
|
if (subs.isEmpty()) {
|
|
throw new IllegalStateException("The class " + T + " is abstract but has no subclasses known to the ClassHierarchy");
|
|
} else {
|
|
for (final IClass sub: subs) {
|
|
if (seen.contains(sub.getReference())) {
|
|
|
|
continue;
|
|
}
|
|
if (sub.isAbstract()) {
|
|
// Recurse on abstract classes
|
|
ret.addAll(getTypes(sub.getReference(), ret)); // sub added through recursion
|
|
} else {
|
|
ret.add(sub.getReference());
|
|
}
|
|
}
|
|
}
|
|
} else if (cls.isArrayClass()) {
|
|
final ArrayClass aCls = (ArrayClass) cls;
|
|
final int dim = aCls.getDimensionality();
|
|
|
|
if (aCls.isOfPrimitives()) {
|
|
ret.add(aCls.getReference());
|
|
} else {
|
|
final IClass inner = aCls.getInnermostElementClass();
|
|
|
|
if (inner == null) {
|
|
throw new IllegalStateException("The array " + T + " has no inner class");
|
|
}
|
|
|
|
if ((inner.isInterface()) || (inner.isAbstract())) {
|
|
final Set<TypeReference> innerTypes = getTypes(inner.getReference(), Collections.EMPTY_SET);
|
|
for (TypeReference iT : innerTypes) {
|
|
TypeReference aT = TypeReference.findOrCreateArrayOf(iT);
|
|
for (int i = 1; i < dim; ++i) {
|
|
aT = TypeReference.findOrCreateArrayOf(aT);
|
|
}
|
|
ret.add(aT);
|
|
}
|
|
} else {
|
|
ret.add(TypeReference.findOrCreateArrayOf(inner.getReference()));
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Path back to Object (including T itself).
|
|
*/
|
|
// private List<TypeReference> getAllSuper(final TypeReference T) {
|
|
// if (T.isPrimitiveType()) {
|
|
// throw new IllegalArgumentException("Not you that call primitive type on :P");
|
|
// }
|
|
// final List<TypeReference> ret = new ArrayList<TypeReference>();
|
|
//
|
|
// IClass cls = this.cha.lookupClass(T);
|
|
// if (cls == null) {
|
|
// throw new IllegalArgumentException("The type " + T + " is not in the ClassHierarchy");
|
|
// }
|
|
//
|
|
// while (cls != null) {
|
|
// ret.add(cls.getReference());
|
|
// cls = cls.getSuperclass();
|
|
// }
|
|
//
|
|
// return ret;
|
|
// }
|
|
|
|
/**
|
|
* The Constructor starts with 'this()' or 'super()'.
|
|
*/
|
|
/*private boolean callsCtor(MethodReference ctor) {
|
|
if (ctor == null) {
|
|
throw new IllegalArgumentException("Null ctor");
|
|
}
|
|
|
|
final Set<IMethod> methods = cha.getPossibleTargets(ctor);
|
|
|
|
if (methods == null) {
|
|
throw new IllegalArgumentException("Unable to look up IMethod for ctor " + ctor);
|
|
}
|
|
|
|
if (methods.size() != 1) {
|
|
throw new UnsupportedOperationException("Unexpected multiple candidates for ctor " + ctor + " are " + methods);
|
|
}
|
|
|
|
final IMethod method = methods.iterator().next();
|
|
assert (method.isInit());
|
|
final SSAInstruction firstInstruction = this.cache.getIR(method).iterateAllInstructions().next();
|
|
|
|
if (firstInstruction instanceof SSAAbstractInvokeInstruction) {
|
|
final SSAAbstractInvokeInstruction invokation = (SSAAbstractInvokeInstruction) firstInstruction;
|
|
return invokation.isSpecial(); // Always?
|
|
}
|
|
|
|
return false;
|
|
}*/
|
|
|
|
/**
|
|
* Selects the constructor of T found to be bes suited.
|
|
*/
|
|
private IMethod lookupConstructor(TypeReference T) {
|
|
IMethod ctor = null;
|
|
int score = -10000;
|
|
final IClass klass = cha.lookupClass(T);
|
|
|
|
if (klass == null) {
|
|
throw new IllegalArgumentException("Unable to look up the class for " + T);
|
|
}
|
|
|
|
if (klass.isInterface() || klass.isAbstract()) {
|
|
throw new IllegalArgumentException("Class is interface or abstract");
|
|
}
|
|
|
|
for (final IMethod im: klass.getDeclaredMethods()) {
|
|
if (! im.isInit()) continue;
|
|
|
|
int candidScore = 0;
|
|
final int paramCount = im.getNumberOfParameters();
|
|
|
|
if (im.isPrivate()) {
|
|
score -= 10;
|
|
} else if (im.isProtected()) {
|
|
score -= 1;
|
|
}
|
|
|
|
for (int i = 1; i < paramCount; ++i) {
|
|
final TypeReference paramType = im.getParameterType(i);
|
|
|
|
if (paramType.isPrimitiveType()) {
|
|
candidScore -= 1;
|
|
} else if (paramType.isArrayType()) { // TODO: Reevaluate scores
|
|
candidScore-=30;
|
|
|
|
if (paramType.getInnermostElementType().equals(T)) {
|
|
// Array of itself
|
|
candidScore -= 1000;
|
|
}
|
|
} else if (paramType.isClassType()) {
|
|
candidScore-=101;
|
|
} else if (paramType.isReferenceType()) { // TODO: Avoid interfaces
|
|
if (paramType.equals(T)) {
|
|
candidScore -= 1000;
|
|
} else {
|
|
candidScore -= 7;
|
|
}
|
|
|
|
if (paramType.equals(TypeReference.JavaLangObject)) {
|
|
candidScore -= 1500;
|
|
}
|
|
} else {
|
|
// ?!
|
|
candidScore -= 800;
|
|
}
|
|
}
|
|
|
|
if (candidScore > score) {
|
|
ctor = im;
|
|
score = candidScore;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (ctor == null) {
|
|
|
|
return cha.resolveMethod(klass, MethodReference.initSelector);
|
|
} else {
|
|
return ctor;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Satisfy the interface.
|
|
*/
|
|
public int createInstance(TypeReference type, Object... instantiatorArgs) {
|
|
// public SSAValue createInstance(final TypeReference T, final boolean asManaged, VariableKey key, Set<SSAValue> seen) {
|
|
if (! (instantiatorArgs[0] instanceof Boolean)) {
|
|
throw new IllegalArgumentException("Argument 0 to createInstance has to be boolean.");
|
|
}
|
|
if (! ((instantiatorArgs[1] == null) || (instantiatorArgs[1] instanceof VariableKey))) {
|
|
throw new IllegalArgumentException("Argument 1 to createInstance has to be null or an instance of VariableKey");
|
|
}
|
|
if (! ((instantiatorArgs[2] == null) || (instantiatorArgs[2] instanceof Set))) {
|
|
throw new IllegalArgumentException("Argument 2 to createInstance has to be null or an instance of Set<? extends SSAValue>, " +
|
|
"got: " + instantiatorArgs[2].getClass());
|
|
}
|
|
final int currentDepth;
|
|
{
|
|
if (instantiatorArgs.length == 4) {
|
|
currentDepth = (Integer) instantiatorArgs[3];
|
|
} else {
|
|
currentDepth = 0;
|
|
}
|
|
}
|
|
if (instantiatorArgs[2] != null) {
|
|
final Set seen = (Set) instantiatorArgs[2];
|
|
if (! seen.isEmpty()) {
|
|
final Object o = seen.iterator().next();
|
|
if (! (o instanceof SSAValue)) {
|
|
throw new IllegalArgumentException("Argument 2 to createInstance has to be null or an instance of Set<? extends SSAValue>, " +
|
|
"got Set<" + o.getClass() + ">");
|
|
}
|
|
}
|
|
}
|
|
|
|
return createInstance(type, (Boolean) instantiatorArgs[0], (VariableKey) instantiatorArgs[1], (Set<? extends SSAValue>)
|
|
instantiatorArgs[2], currentDepth).getNumber();
|
|
}
|
|
}
|