WALA/com.ibm.wala.core/src/com/ibm/wala/util/ssa/ParameterAccessor.java

1701 lines
70 KiB
Java

/******************************************************************************
* Copyright (c) 2002 - 2014 IBM Corporation.
* All rights reserved. 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
*
* Contributors:
* IBM Corporation - initial API and implementation
*****************************************************************************/
/*
* 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.util.ssa;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
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.PrimitiveAssignability;
import com.ibm.wala.util.ssa.SSAValue.WeaklyNamedKey;
/**
* Access parameters without confusion on their numbers.
*
* Depending on the representation of a method (IMethod, MethodReference) parameters are placed at a
* different position. Functions furthermore may have an implicit this-pointer which alters the
* positions again.
*
* Accessing parameters of these functions by their numbers only is error prone and leads to confusion.
* This class tries to leverage parameter-access.
*
* You can use this class using now numbers at all. However if you choose to use numbers this class has yet
* another numbering convention (jupeee): 1 is the first parameter appearing in the Selector, no matter if
* the Method has an implicit this. It is not zero as Java initializes new integer-arrays with zero.
*
* If you want to alter the values of the incoming parameters you may also want to use the ParameterManager
* which tracks the changes.
*
* @author Tobias Blaschke <code@tobiasblaschke.de>
* @since 2013-10-19
*/
public class ParameterAccessor {
private final static boolean DEBUG = false;
/**
* The Constructor used to create ParameterAccessor influences the parameter-offset.
*
* If this enum is extended many functions will throw if not also extended.
*/
public enum BasedOn {
/** ParameterAccessor was constructed using an IMethod */
IMETHOD,
/** ParameterAccessor was constructed using a MethodReference */
METHOD_REFERENCE
}
/**
* The kind of parameter.
*
* Extending this enum should not introduce any problems in ParameterAccessor.
*/
public enum ParamerterDisposition {
/** Parameter is an implicit this-pointer */
THIS,
/** Parameter is a regular parameter occurring in the Descriptor */
PARAM,
/** The return-value of a method (has to be crafted manually) */
RETURN,
NEW
}
/**
* This key is identified by type and parameter number.
*/
public static class ParameterKey extends WeaklyNamedKey {
final int paramNo;
public ParameterKey(final TypeName type, final int no, final String name) {
super(type, ((name==null)?"param_" + no:name));
this.paramNo = no;
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
@Override
public int hashCode() {
return this.type.hashCode();
}
@Override
public String toString() {
return "<ParameterKey no=" + this.paramNo + " type=" + this.type + " name=\"" + this.name + "\" />";
}
}
/**
* The representation of a Parameter handled using a ParameterAccessor.
*
* It basically consists of a SSA-Value and an associated TypeReference.
*
* Use .getNumber() to access the associated SSA-Value.
*
* @author Tobias Blaschke <code@tobiasblaschke.de>
* @since 2013-10-19
*/
public static class Parameter extends SSAValue {
/** Is Accessor constructed with IMethod or MethodReference */
private final BasedOn basedOn;
/** Implicit this or regular parameter? */
private final ParamerterDisposition disp;
/** Add to number to get position in descriptor */
private final int descriptorOffset;
/**
* Create Parameters using ParameterAccessor.
*
* @param number SSA-Value to access this parameter
* @param name Optional variable-name - may be null
* @param type Variable Type to this parameter
* @param disp Implicit this, regular parameter or return value?
* @param basedOn Is Accessor constructed with IMethod or MethodReference
* @param mRef Method this parameter belongs to
* @param descriptorOffset add to number to get position in descriptor
*/
protected Parameter(final int number, final String name, final TypeReference type, final ParamerterDisposition disp,
final BasedOn basedOn, final MethodReference mRef, final int descriptorOffset) {
super(number, type, mRef, new ParameterKey(type.getName(), number + descriptorOffset, name));
if (mRef == null) {
throw new IllegalArgumentException("MethodReference (mRef) of a Parameter may not be null");
}
if (basedOn == null) {
throw new IllegalArgumentException("Argument basedOn of a Parameter may not be null");
}
if (disp == null) {
throw new IllegalArgumentException("ParamerterDisposition (disp) of a Parameter may not be null");
}
// If type was null the call to super failed
if ((number < 1) && (basedOn == BasedOn.METHOD_REFERENCE)) {
throw new IllegalArgumentException("The first accessible SSA-Value of a MethodReference is 1 but the " +
"Value-Number given is " + number);
}
if ((number < 0) && (basedOn == BasedOn.IMETHOD)) {
throw new IllegalArgumentException("The first accessible SSA-Value of an IMethod is 0 but the " +
"Value-Number given is " + number);
}
if ((disp == ParamerterDisposition.PARAM) && (number + descriptorOffset > mRef.getNumberOfParameters())) {
throw new IllegalArgumentException("The SSA-Value " + number + " (with added offset " + descriptorOffset +
") is beyond the number of Arguments (" + mRef.getNumberOfParameters() + ") of the Method " +
mRef.getName() + "\n" + mRef.getSignature());
}
if ((disp == ParamerterDisposition.THIS) && (basedOn == BasedOn.METHOD_REFERENCE) && (number != 1)) {
throw new IllegalArgumentException("The implicit this-pointer of a MethodReference is located at SSA-Value 1. " +
"The SSA-Value given is " + number);
}
if ((disp == ParamerterDisposition.THIS) && (basedOn == BasedOn.IMETHOD) && (number != 0)) {
throw new IllegalArgumentException("The implicit this-pointer of an IMethod is located at SSA-Value 0. " +
"The SSA-Value given is " + number);
}
if ((descriptorOffset < -2) || (descriptorOffset > 1)) {
throw new IllegalArgumentException("The descriptor-offset given is not within its expected bounds: " +
"-1 (for a method without implicit this-pointer) to 1. The given offset is " + descriptorOffset);
}
this.disp = disp;
this.basedOn = basedOn;
this.descriptorOffset = descriptorOffset;
super.isAssigned();
}
/**
* The position of the parameter in the methods Desciptor starting with 1.
*/
public int getNumberInDescriptor() { // TODO: Verify all descriptorOffset stuff!
//if (this.descriptorOffset < 0) {
// return this.number;
//} else {
return this.number + this.descriptorOffset;
//}
}
public ParamerterDisposition getDisposition() {
return this.disp;
}
/**
* @throws IllegalArgumentException if you compare this to an object totally different.
*/
@Override
public boolean equals(final Object o) {
if (o instanceof Parameter) {
final Parameter other = (Parameter) o;
return ((this.type.equals(other.type)) && (this.number == other.number) && (this.mRef.equals(other.mRef)));
}
if (o instanceof SSAValue) {
return super.equals(o);
}
throw new IllegalArgumentException("Can't compare Parameter to " + o.getClass());
}
/**
* Clashes deliberately with SSAValue as it's basically the same thing.
*/
@Override
public final int hashCode() {
return super.hashCode();
}
@Override
public String toString() {
switch (this.disp) {
case THIS:
return "Implicit this-parameter of " + this.mRef.getName() + " as " + this.type + " accessible using " +
"SSA-Value " + this.number;
case PARAM:
if (this.key instanceof NamedKey) {
return "Parameter " + getNumberInDescriptor() + " \"" + getVariableName() + "\" of " + this.mRef.getName() +
" is " + this.type + " accessible using SSA-Value " + this.number;
} else {
return "Parameter " + getNumberInDescriptor() + " of " + this.mRef.getName() + " is " + this.type +
" accessible using SSA-Value " + this.number;
}
case RETURN:
return "Return Value of " + this.mRef.getName() + " as " + this.type + " accessible using SSA-Value " +
this.number;
case NEW:
return "New instance of " + this.type + " accessible in " + this.mRef.getName() + " using number " + this.number;
default:
return "Parameter " + getNumberInDescriptor() + " - " + this.disp + " of " + this.mRef.getName() + " as " +
this.type + " accessible using SSA-Value " + this.number;
}
}
}
/** The Constructor used to create this ParameterAceesor */
private final BasedOn base;
/** The Method associated to this ParameterAceesor if constructed using a mRef */
private final MethodReference mRef;
/** The Method associated to this ParameterAceesor if constructed using an IMethod */
private final IMethod method;
/** The Value-Number for the implicit this-pointer or -1 if there is none */
private final int implicitThis;
/** SSA-Number + descriptorOffset yield the parameters position in the Descriptor starting with 1 */
private final int descriptorOffset;
/** Number of parameters _excluding_ implicit this */
private final int numberOfParameters;
/**
* Reads the parameters of a MethodReference CAUTION:.
*
* Do _not_ use ParameterAceesor(IMethod.getReference()), but ParameterAceesor(IMehod)!
*
* Using this Constructor influences the SSA-Values returned later. The cha is needed to
* determine whether mRef is static. If this is already known one should prefer the faster
* {@link #ParameterAccessor(ParameterAccessor, boolean)}.
*
* @param mRef The method to read the parameters from.
*/
public ParameterAccessor(final MethodReference mRef, final IClassHierarchy cha) {
if (mRef == null) {
throw new IllegalArgumentException("Can't read the arguments from null.");
}
this.mRef = mRef;
this.method = null;
this.base = BasedOn.METHOD_REFERENCE;
this.numberOfParameters = mRef.getNumberOfParameters();
final boolean hasImplicitThis;
Set<IMethod> targets = cha.getPossibleTargets(mRef);
if (targets.size() < 1) {
warn("Unable to look up the method {} starting extensive search...", mRef);
targets = new HashSet<IMethod>();
final TypeReference mClass = mRef.getDeclaringClass();
final Selector mSel = mRef.getSelector();
final Set<IClass> testClasses = new HashSet<IClass>();
// Look up all classes matching exactly
for (IClassLoader loader : cha.getLoaders()) {
final IClass cand = loader.lookupClass(mClass.getName());
if (cand != null) {
testClasses.add(cand);
}
}
// Try lookupClass..
final IClass lookedUp;
lookedUp = cha.lookupClass(mClass);
if (lookedUp != null) {
debug("Found using cha.lookupClass()");
testClasses.add(lookedUp);
}
info("Searching the classes {} for the method", testClasses);
for (IClass testClass : testClasses) {
final IMethod cand = testClass.getMethod(mSel);
if (cand != null) {
targets.add(cand);
}
}
if (targets.size() < 1) {
warn("Still no candidates for the method - continuing with super-classes (TODO)");
// TODO
{ // DEBUG
for (IClass testClass : testClasses) {
info("Known Methods in " + testClass);
for (IMethod contained : testClass.getAllMethods()) {
System.out.println(contained);
info("\t" + contained);
}
}
} // */
throw new IllegalStateException("Unable to look up the method " + mRef);
}
}
{ // Iterate all candidates
final Iterator<IMethod> it = targets.iterator();
final boolean testStatic = it.next().isStatic();
while (it.hasNext()) {
final boolean tmpStatic = it.next().isStatic();
if (testStatic != tmpStatic) {
throw new IllegalStateException("The ClassHierarchy knows multiple (" + targets.size() + ") targets for " + mRef +
". The targets contradict themselves if they have an implicit this!");
}
}
hasImplicitThis = (! testStatic);
}
if (hasImplicitThis) {
info("The method {} has an implicit this pointer", mRef);
this.implicitThis = 1;
this.descriptorOffset = -1;
} else {
info("The method {} has no implicit this pointer", mRef);
this.implicitThis = -1;
this.descriptorOffset = 0;
}
}
/**
* Reads the parameters of a MethodReference CAUTION:.
*
* Do _not_ use ParameterAceesor(IMethod.getReference()), but ParameterAceesor(IMehod)!
*
* This constructor is faster than {@link #ParameterAccessor(MethodReference, IClassHierarchy}.
*
* @param mRef The method to read the parameters from.
*/
public ParameterAccessor(final MethodReference mRef, final boolean hasImplicitThis) {
if (mRef == null) {
throw new IllegalArgumentException("Can't read the arguments from null.");
}
this.mRef = mRef;
this.method = null;
this.base = BasedOn.METHOD_REFERENCE;
this.numberOfParameters = mRef.getNumberOfParameters();
if (hasImplicitThis) {
info("The method {} has an implicit this pointer", mRef);
this.implicitThis = 1;
this.descriptorOffset = -1;
} else {
info("The method {} has no implicit this pointer", mRef);
this.implicitThis = -1;
this.descriptorOffset = 0;
}
}
/**
* Read the parameters from an IMethod.
*
* Using this Constructor influences the SSA-Values returned later.
*
* @param method The method to read the parameters from.
*/
public ParameterAccessor(final IMethod method) {
if (method == null) {
throw new IllegalArgumentException("Can't read the arguments from null.");
}
// Don't make a mRef but keep the IMethod!
this.mRef = null;
this.method = method;
this.base = BasedOn.IMETHOD;
this.numberOfParameters = method.getReference().getNumberOfParameters();
if (method.isStatic() && (! method.isInit())) {
assert(method.getNumberOfParameters() == method.getReference().getNumberOfParameters()) : "WTF!" + method;
this.implicitThis = -1;
this.descriptorOffset = 0;
} else {
assert(method.getNumberOfParameters() == 1 + method.getReference().getNumberOfParameters()) : "WTF!" + method;
this.implicitThis = 1;
this.descriptorOffset = -1;
}
}
/**
* Make an Parameter Object using a Descriptor-based numbering (starting with 1).
*
* Number 1 is the first parameter in the methods Selector. No matter if the function has an
* implicit this pointer.
*
* If the Function has an implicit this-pointer you can access it using getThis().
*
* @param no the number in the Selector
* @return new Parameter-Object for no
* @throws IllegalArgumentExceptions if the parameter is zero
* @throws ArrayIndexOutOfBoundsException if no is not within bounds [1 to numberOfParameters]
*/
public Parameter getParameter(final int no) {
// no is checked by getParameterNo(int)
final int newNo = getParameterNo(no);
switch (this.base) {
case IMETHOD: // TODO: Try reading parameter name
return new Parameter(newNo, null, this.method.getParameterType(no), ParamerterDisposition.PARAM,
this.base, this.method.getReference(), this.descriptorOffset);
case METHOD_REFERENCE:
return new Parameter(newNo, null, this.mRef.getParameterType(no - 1), ParamerterDisposition.PARAM,
this.base, this.mRef, this.descriptorOffset);
default:
throw new UnsupportedOperationException("No implementation of getParameter() for base " + this.base);
}
}
/**
* Return the SSA-Value to access a parameter using a Descriptor-based numbering (starting with 1).
*
* Number 1 is the first parameter in the methods Selector. No matter if the function has an
* implicit this pointer.
*
* If the Function has an implicit this-pointer you can acess it using getThisNo().
*
* @param no the number in the Selector
* @return the offseted number for accessing the parameter
* @throws IllegalArgumentException if the parameter is zero
* @throws ArrayIndexOutOfBoundsException if no is not within bounds [1 to numberOfParameters]
*/
public int getParameterNo(final int no) {
if (no == 0) {
throw new IllegalArgumentException("Parameter numbers start with 1. Use getThis() to access a potential implicit this.");
}
if ((no < 0) || (no > this.numberOfParameters)) {
throw new ArrayIndexOutOfBoundsException("The given number (" + no + ") was not within bounds (1 to " + this.numberOfParameters +
") when acessing a parameter of " + this);
}
switch (this.base) {
case IMETHOD:
return no + this.implicitThis; // + this.implicitThis; // TODO: Verify
case METHOD_REFERENCE:
if (this.implicitThis > 0) {
return no + this.implicitThis; //
} else {
return no;
}
default:
throw new UnsupportedOperationException("No implementation of getParameter() for base " + this.base);
}
}
/**
* Same as Parameter.getNumber().
*
* @return SSA-Value to access the parameters contents.
*/
public int getParameterNo(final Parameter param) {
if (param == null) {
throw new IllegalArgumentException("Parameter may not be null");
}
return param.getNumber();
}
/**
* This list _excludes_ the implicit this-pointer (if any).
*
* If you want the implicit this-pointer use getThis().
*
* @return All parameters appearing in the Selector.
*/
public List<Parameter> all() {
// TODO: Cache!
List<Parameter> all = new ArrayList<Parameter>(this.getNumberOfParameters());
if (this.getNumberOfParameters() == 0) {
return all;
} else {
switch (this.base) {
case IMETHOD:
{
final int firstInSelector = firstInSelector();
for (int i = ((hasImplicitThis())?1:0); i < this.method.getNumberOfParameters(); ++i) {
debug("all() adding: Parameter({}, {}, {}, {}, {})", (i + 1), this.method.getParameterType(i),
this.base, this.method, this.descriptorOffset);
all.add(new Parameter(i + 1, null, this.method.getParameterType(i), ParamerterDisposition.PARAM,
this.base, this.method.getReference(), this.descriptorOffset));
}
}
break;
case METHOD_REFERENCE:
{
final int firstInSelector = firstInSelector();
for (int i = 0 /*firstInSelector()*/; i < this.numberOfParameters; ++i) { // TODO:
all.add(new Parameter(i + firstInSelector, null, this.mRef.getParameterType(i), ParamerterDisposition.PARAM,
this.base, this.mRef, this.descriptorOffset));
}
}
break;
default:
throw new UnsupportedOperationException("No implementation of all() for base " + this.base);
}
}
return all;
}
/**
* Return the implicit this-pointer (or throw).
*
* This obviously only works on non-static methods. You probably want to check if the method has
* such an implicit this using hasImplicitThis() as this method will throw if there is none.
*
* If you only want the number use the more lightweight getThisNo().
*
* @return Object containing all Information on the parameter.
* @throws IllegalStateException if the function has no implicit this
*/
public Parameter getThis() {
final int self = getThisNo();
final TypeReference selfType;
switch (this.base) {
case IMETHOD:
selfType = this.method.getParameterType(self);
break;
case METHOD_REFERENCE:
selfType = this.mRef.getDeclaringClass();
break;
default:
throw new UnsupportedOperationException("No implementation of getThis() for base " + this.base);
}
return getThisAs(selfType);
}
/**
* Return the implicit this-pointer as a supertype.
*
* @param asType A type of a super-class of this
*/
public Parameter getThisAs(final TypeReference asType) {
final int self = getThisNo();
switch (this.base) {
case IMETHOD:
final IClassHierarchy cha = this.method.getClassHierarchy();
try {
if (! isSubclassOf(this.method.getParameterType(self), asType, cha) ) {
throw new IllegalArgumentException("Class " + asType + " is not a super-class of " +
this.method.getParameterType(self));
}
} catch (ClassLookupException e) {
// Cant't test assume all fitts
}
return new Parameter(self, "self", asType, ParamerterDisposition.THIS,
this.base, this.method.getReference(), this.descriptorOffset);
case METHOD_REFERENCE:
// TODO assert asType is a subtype of self.type - we need cha to do that :(
return new Parameter(self, "self", asType, ParamerterDisposition.THIS,
this.base, this.mRef, this.descriptorOffset);
default:
throw new UnsupportedOperationException("No implementation of getThis() for base " + this.base);
}
}
/**
* Return the SSA-Value of the implicit this-pointer (or throw).
*
* This obviously only works on non-static methods. You probably want to check if the method has
* such an implicit this using hasImplicitThis() as this method will throw if there is none.
*
* @return Number of the this.
* @throws IllegalStateException if the function has no implicit this.
*/
public int getThisNo() {
if (this.implicitThis >= 0) {
return this.implicitThis;
} else {
throw new IllegalStateException("getThisNo called for a method that has no implicit this");
}
}
/**
* If the method has an implicit this parameter.
*/
public boolean hasImplicitThis() {
return (this.implicitThis >= 0);
}
/**
* Create a "Parameter" containing the Return-Type w/o Type-checking.
*
* This should be of rather theoretical use.
*
* @throws IllegalStateException if used on a void-Function
*/
public Parameter makeReturn(final int ssa) {
if (! hasReturn()) {
throw new IllegalStateException("Can't generate a return-value for a void-function.");
}
switch (this.base) {
case IMETHOD:
return new Parameter(ssa, "retVal", getReturnType(), ParamerterDisposition.RETURN, this.base,
this.method.getReference(), this.descriptorOffset);
case METHOD_REFERENCE:
return new Parameter(ssa, "retVal", getReturnType(), ParamerterDisposition.RETURN, this.base,
this.mRef, this.descriptorOffset);
default:
throw new UnsupportedOperationException("No implementation of getReturn() for base " + this.base);
}
}
/**
* Create a "Parameter" containing the Return-Type with Type-checking.
*
* @param ssa The value to return
* @param type The type of ssa
* @param cha The ClassHierarchy to use for the assignability test
* @throws IllegalStateException if used on a void-Function
*/
public Parameter makeReturn(final int ssa, final TypeReference type, final IClassHierarchy cha) {
if (! hasReturn()) {
throw new IllegalStateException("Can't generate a return-value for a void-function.");
}
final TypeReference returnType = getReturnType();
if (returnType.equals(type)) {
return makeReturn(ssa);
} else if (cha == null) {
throw new IllegalArgumentException("Needed to test assignability but no cha given."); // TODO: Throw always or never
} else if (isAssignable(type, returnType, cha)) {
return makeReturn(ssa);
} else {
throw new IllegalStateException("Return type " + returnType + " is not assignable from " + type);
}
}
/**
* The SSA-Value to acces the parameter appearing first in the Descriptor with.
*
* @throws IllegalArgumentException if the method has no parameters in its Descriptor.
*/
public int firstInSelector() {
if (this.numberOfParameters == 0) {
if (this.method != null) {
throw new IllegalArgumentException("The method " + this.method.toString() + " has no explicit parameters.");
} else {
throw new IllegalArgumentException("The method " + this.mRef.toString() + " has no explicit parameters.");
}
}
if (this.implicitThis > 1) {
throw new IllegalStateException("An internal error in ParameterAccessor locating the implicit this pointer occurred! Invalid: " + this.implicitThis);
}
switch (this.base) {
case IMETHOD:
if (this.hasImplicitThis()) { // XXX TODO BUG!
debug("This IMethod {} has an implicit this pointer at {}, so firstInSelector is accessible using SSA-Value {}", this.method, this.implicitThis, (this.implicitThis + 1));
return this.implicitThis + 1;
} else {
debug("This IMethod {} has no implicit this pointer, so firstInSelector is accessible using SSA-Value 1" , this.method);
return 1;
}
case METHOD_REFERENCE:
if (this.hasImplicitThis()) {
debug("This IMethod {} has an implicit this pointer at {}, so firstInSelector is accessible using SSA-Value {}", this.mRef, this.implicitThis, (this.implicitThis + 1));
return this.implicitThis + 1;
} else {
debug("This mRef {} has no implicit this pointer, so firstInSelector is accessible using SSA-Value 1", this.mRef);
return 1;
}
default:
throw new UnsupportedOperationException("No implementation of firstInSelector() for base " + this.base);
}
}
/**
* Prefer: getParameter(int no) or all().
*
* Get the type of the parameter (not this) using a fixed numbering.
*
* Number 1 is the first parameter in the methods Selector. No matter if the function has an
* implicit this pointer.
*
* Use all() if you want to get all parameter-types.
*
* @param no the number in the Selector
* @return the type of the parameter
*/
public TypeReference getParameterType(final int no) { // XXX Remove?
switch (this.base) {
case IMETHOD:
return this.method.getParameterType(getParameterNo(no));
case METHOD_REFERENCE:
return this.method.getParameterType(getParameterNo(no));
default:
throw new UnsupportedOperationException("No implementation of getParameterType() for base " + this.base);
}
}
/**
* First parameter in the selector that matches _exactly_.
*
* @return first parameter found or null if there is none
* @throws IllegalArgumentException if searching for void or null
*/
public Parameter firstOf(final TypeName tName) {
if (tName == null) {
throw new IllegalArgumentException("Search-name may not be null");
}
if (tName.equals(TypeReference.VoidName)) {
throw new IllegalArgumentException("You are searching for 'void' as a parameter.");
}
final List<Parameter> all = all();
// ****
// Implementation starts here
for (final Parameter cand : all) {
if (cand.getType().getName().equals(tName)) {
return cand;
}
}
return null;
}
/**
* First parameter in the selector that matches _exactly_.
*
* @return first parameter found or null if there is none
* @throws IllegalArgumentException if searching for void or null
*/
public Parameter firstOf(final TypeReference tRef) {
if (tRef == null) {
throw new IllegalArgumentException("Search-name may not be null");
}
if (tRef.equals(TypeReference.Void)) {
throw new IllegalArgumentException("You are searching for 'void' as a parameter.");
}
final List<Parameter> all = all();
// ****
// Implementation starts here
for (final Parameter cand : all) {
if (cand.getType().equals(tRef)) {
return cand;
}
}
return null;
}
/**
* All parameters in the selector that are a subclass of tName (slow).
*
* TypeNames have to be looked up first, do prefer the variant with the TypeReference if
* one is available.
*
* @throws IllegalArgumentException if searching for void or null
*/
public List<Parameter> allExtend(final TypeName tName, final IClassHierarchy cha) {
if (tName == null) {
throw new IllegalArgumentException("Search-name may not be null");
}
if (tName.equals(TypeReference.VoidName)) {
throw new IllegalArgumentException("You are searching for 'void' as a parameter.");
}
if (cha == null) {
throw new IllegalArgumentException("Can't search ClassHierarchy without having a ClassHierarchy (is null)");
}
final List<Parameter> all = all();
final List<Parameter> allExctends = new ArrayList<>();
IClass searchType = null;
final IClassLoader[] allLoaders = cha.getLoaders();
// ****
// Implementation starts here
{ // Retrieve a reference of the type
for (final IClassLoader loader : allLoaders) {
searchType = loader.lookupClass(tName);
if (searchType != null) {
break;
}
}
}
if (searchType == null) {
throw new IllegalStateException("Could not find " + tName + " in any loader!");
} else {
debug("Retrieved {} as {}", tName, searchType);
}
for (final Parameter cand : all) {
final IClass candClass = cha.lookupClass(cand.getType());
if (candClass != null) { // TODO: Extra function
if (cha.isSubclassOf(candClass, searchType)) {
allExctends.add(cand);
}
} else {
for (final IClassLoader loader: cha.getLoaders()) {
final IClass c = loader.lookupClass(cand.getType().getName());
if (c != null) {
info("Using alternative for from: {}", cand);
if (cha.isSubclassOf(c, searchType)) {
allExctends.add(cand);
}
}
}
// TODO: That's true for base-type too
warn("Unable to look up IClass of {}", cand);
}
}
return allExctends;
}
/**
* All parameters in the selector that are a subclass of tRef (slow).
*
* @throws IllegalArgumentException if searching for void or null
*/
public List<Parameter> allExtend(final TypeReference tRef, final IClassHierarchy cha) {
if (tRef == null) {
throw new IllegalArgumentException("Search TypeReference may not be null");
}
if (tRef.equals(TypeReference.Void)) {
throw new IllegalArgumentException("You are searching for 'void' as a parameter.");
}
if (cha == null) {
throw new IllegalArgumentException("Can't search ClassHierarchy without having a ClassHierarchy (is null)");
}
// ****
// Implementation starts here
final IClass searchType = cha.lookupClass(tRef);
final List<Parameter> all = all();
final List<Parameter> allExctends = new ArrayList<>();
if (searchType == null) {
throw new IllegalStateException("Could not find the IClass of " + tRef);
} else {
debug("Reteived {} as {}", tRef, searchType);
}
for (final Parameter cand : all) {
final IClass candClass = cha.lookupClass(cand.getType());
if (candClass != null) {
if (cha.isSubclassOf(candClass, searchType)) {
allExctends.add(cand);
}
} else {
// TODO: That's true for base-type too
warn("Unable to look up IClass of {}", cand);
}
}
return allExctends;
}
/**
* First parameter in the selector that is a subclass of tName (slow).
*
* TypeNames have to be lloked up first, do prefer the variant with the TypeReference if
* one is available.
*
* @return first parameter found or null if there is none
* @throws IllegalArgumentException if searching for void or null
*/
public Parameter firstExtends(final TypeName tName, final IClassHierarchy cha) {
if (tName == null) {
throw new IllegalArgumentException("Search-name may not be null");
}
if (tName.equals(TypeReference.VoidName)) {
throw new IllegalArgumentException("You are searching for 'void' as a parameter.");
}
if (cha == null) {
throw new IllegalArgumentException("Can't search ClassHierarchy without having a ClassHierarchy (is null)");
}
final List<Parameter> all = all();
IClass searchType = null;
final IClassLoader[] allLoaders = cha.getLoaders();
// ****
// Implementation starts here
{ // Reteive a reference of the type
for (final IClassLoader loader : allLoaders) {
searchType = loader.lookupClass(tName);
if (searchType != null) {
break;
}
}
}
if (searchType == null) {
throw new IllegalStateException("Could not find " + tName + " in any loader!");
} else {
debug("Reteived {} as {}", tName, searchType);
}
for (final Parameter cand : all) {
final IClass candClass = cha.lookupClass(cand.getType());
if (candClass != null) {
if (cha.isSubclassOf(candClass, searchType)) {
return cand;
}
} else {
for (final IClassLoader loader: cha.getLoaders()) {
final IClass c = loader.lookupClass(cand.getType().getName());
if (c != null) {
info("Using alternative for from: {}", cand);
if (cha.isSubclassOf(c, searchType)) {
return cand;
}
}
}
// TODO: That's true for primitive-type too
warn("Unable to look up IClass of {}", cand);
}
}
return null;
}
/**
* First parameter in the selector that is a subclass of tRef (slow).
*
* @return first parameter found or null if there is none
* @throws IllegalArgumentException if searching for void or null
*/
public Parameter firstExtends(final TypeReference tRef, final IClassHierarchy cha) {
if (tRef == null) {
throw new IllegalArgumentException("Search TypeReference may not be null");
}
if (tRef.equals(TypeReference.Void)) {
throw new IllegalArgumentException("You are searching for 'void' as a parameter.");
}
if (cha == null) {
throw new IllegalArgumentException("Can't search ClassHierarchy without having a ClassHierarchy (is null)");
}
// ****
// Implementation starts here
final IClass searchType = cha.lookupClass(tRef);
final List<Parameter> all = all();
if (searchType == null) {
throw new IllegalStateException("Could not find the IClass of " + tRef);
} else {
debug("Reteived {} as {}", tRef, searchType);
}
for (final Parameter cand : all) {
final IClass candClass = cha.lookupClass(cand.getType());
if (candClass != null) {
if (cha.isSubclassOf(candClass, searchType)) {
return cand;
}
} else {
// TODO: That's true for base-type too
warn("Unable to look up IClass of {}", cand);
}
}
return null;
}
/**
* The first SSA-Number after the parameters.
*
* This is useful for making synthetic methods.
*/
public int getFirstAfter() {
return this.numberOfParameters + 2; // Should be +1 ?
}
/**
* Generate the params-param for an InvokeIstruction w/o type checking.
*
* @param args list to build the arguments from - without implicit this
*/
public int[] forInvokeStatic(final List<? extends SSAValue> args) {
if (args == null) { // XXX Allow?
throw new IllegalArgumentException("args is null");
}
int[] params = new int[args.size()];
if (params.length == 0) {
return params;
}
if ((args.get(1) instanceof Parameter) && (((Parameter)args.get(1)).getDisposition() == ParamerterDisposition.THIS)) {
warn("The first argument is an implicit this: {} this may be ok however.", args.get(1));
}
// ****
// Implementation starts here
for (int i = 0; i < params.length; ++i) {
params[i] = args.get(i).getNumber();
}
return params;
}
/**
* Generate the params-param for an InvokeIstruction with type checking.
*
* @param args list to build the arguments from - without implicit this
* @param target the method to be called - for type checking only
* @param cha if types don't match exactly needed for the assignability check (may be null if that check is not wanted)
* @throws IllegalArgumentException if you call this method on a target that needs an implicit this
* @throws IllegalArgumentException if args length does not match the targets param-length
* @throws IllegalArgumentException if a parameter is unassignable
*/
public int[] forInvokeStatic(final List<? extends SSAValue> args, final ParameterAccessor target, final IClassHierarchy cha) {
if (args == null) {
throw new IllegalArgumentException("args is null");
}
if (target == null) {
throw new IllegalArgumentException("ParameterAccessor for the target is null");
}
if (target.hasImplicitThis()) {
throw new IllegalArgumentException("You used forInvokeStatic on a method that has an implicit this pointer");
}
if (target.getNumberOfParameters() != args.size()) {
throw new IllegalArgumentException("Number of arguments mismatch: " + args.size() + " given on a method that " +
"needs " + target.getNumberOfParameters() + " arguments. Arguments given were " + args + " for a static " +
"call to " + target);
}
int[] params = new int[args.size()];
if (params.length == 0) {
return params;
}
if ((args.get(1) instanceof Parameter) && (((Parameter)args.get(1)).getDisposition() == ParamerterDisposition.THIS)) {
warn("The first argument is an implicit this: {} this may be ok however.", args.get(1));
}
// ****
// Implementation starts here
for (int i = 0; i < params.length; ++i) {
final SSAValue param = args.get(i);
if (param.getType().equals(target.getParameter(i).getType())) {
params[i] = param.getNumber();
} else {
if (cha == null) {
throw new IllegalArgumentException("Parameter " + i + " (" + param + ") of the Arguments list " +
"is not equal to param " + i + " ( " + target.getParameter(i) + ") of " + target +
"and no ClassHierarchy was given to test assignability");
} else if (isAssignable(param, target.getParameter(i), cha)) {
params[i] = param.getNumber();
} else {
throw new IllegalArgumentException("Parameter " + i + " (" + param + ") of the Arguments list " +
"is not assignable to param " + i + " ( " + target.getParameter(i) + ") of " +
target);
}
}
}
return params;
}
/**
* Generate the params-param for an InvokeIstruction w/o type checking.
*
* @param self the this-pointer to use
* @param args the rest of the arguments. Be shure it does not start with a
* this pointer. This is _not_ checked so you can use a this-pointer as
* an argument. However a warning is issued.
* @throws IllegalArgumentException if the value of self is to small in the current
* method
*/
public int[] forInvokeVirtual(final int self, final List<? extends SSAValue> args) {
if (args == null) {
throw new IllegalArgumentException("args is null");
}
if ((this.base == BasedOn.METHOD_REFERENCE) && (self < 1)) {
throw new IllegalArgumentException("The first SSA-Value of a MethodReference is 1. The given this (self) is " +
self);
} else if (self < 0) {
throw new IllegalArgumentException("self = " + self + " < 0");
}
int[] params = new int[args.size() + 1];
if ((params.length > 1) && (args.get(1) instanceof Parameter) && (((Parameter)args.get(1)).getDisposition() ==
ParamerterDisposition.THIS)) {
warn("The first argument is an implicit this: {} this may be ok however.", args.get(1));
}
// ****
// Implementation starts here
//
params[0] = self;
for (int i = 1; i < params.length; ++i) {
params[i] = args.get(i - 1).getNumber();
}
return params;
}
/**
* Generate the params-param for an InvokeIstruction with type checking.
*
* @param self the this-pointer to use
* @param args list to build the arguments from - without implicit this
* @param target the method to be called - for type checking only
* @param cha if types don't match exactly needed for the assignability check (may be null if that check is not wanted)
* @throws IllegalArgumentException if you call this method on a target that needs an implicit this
* @throws IllegalArgumentException if args length does not match the targets param-length
* @throws IllegalArgumentException if a parameter is unassignable
*/
public int[] forInvokeVirtual(final int self, final List<? extends SSAValue> args, final ParameterAccessor target, final IClassHierarchy cha) {
if (args == null) {
throw new IllegalArgumentException("args is null");
}
if ((this.base == BasedOn.METHOD_REFERENCE) && (self < 1)) {
throw new IllegalArgumentException("The first SSA-Value of a MethodReference is 1. The given this (self) is " +
self);
} else if (self < 0) {
throw new IllegalArgumentException("self = " + self + " < 0");
}
if (target == null) {
throw new IllegalArgumentException("ParameterAccessor for the target is null");
}
if (! target.hasImplicitThis()) {
throw new IllegalArgumentException("You used forInvokeVirtual on a method that has no implicit this pointer");
}
if (target.getNumberOfParameters() != args.size() + 1) { // TODO: Verify
throw new IllegalArgumentException("Number of arguments mismatch: " + args.size() + " given on a method that " +
"needs " + target.getNumberOfParameters() + " arguments. Arguments given were " + args + " for a static " +
"call to " + target);
}
int[] params = new int[args.size() + 1];
if ((params.length > 1) && (args.get(1) instanceof Parameter) && (((Parameter)args.get(1)).getDisposition() ==
ParamerterDisposition.THIS)) {
warn("The first argument is an implicit this: {} this may be ok however.", args.get(1));
}
// ****
// Implementation starts here
params[0] = self; // TODO: Can't typecheck this!
for (int i = 1; i < params.length; ++i) {
final SSAValue param = args.get(i - 1);
if (param.getType().equals(target.getParameter(i).getType())) {
params[i] = param.getNumber();
} else {
if (cha == null) {
throw new IllegalArgumentException("Parameter " + i + " (" + param + ") of the Arguments list " +
"is not equal to param " + i + " ( " + target.getParameter(i) + ") of " + target +
"and no ClassHierarchy was given to test assignability");
} else if (isAssignable(param, target.getParameter(i), cha)) {
params[i] = param.getNumber();
} else {
throw new IllegalArgumentException("Parameter " + i + " (" + param + ") of the Arguments list " +
"is not assignable to param " + i + " ( " + target.getParameter(i) + ") of " +
target);
}
}
}
return params;
}
/**
* Connects though parameters from the calling function (overridable) - CAUTION:.
*
* This functions makes is decisions based on Type-Referes only so if a TypeReference occurs multiple
* times in the caller or callee it may make surprising connections.
*
* The List of Parameters is generated based on the overrides, than parameters in 'this' are searched, finally we'll
* fall back to defaults. A "perfect match" is searched.
*
* If a parameter was not assigned yet these three sources are considdered again but cha.isAssignableFrom is used.
*
* If the parameter was still not found a value of 'null' is used.
*
* This funktion is useful when generating wrapper-functions.
*
* @param callee The function to generate the parameter-list for
* @param overrides If a parameter occurs here, it is preferred over the ones present in this
* @param defaults If a parameter is not present in this or the overrides, defaults are searched. If the parameter is not present there null is assigned.
* @param cha Optional class hierarchy for testing assignability
* @return the parameter-list for the call of toMethod
*/
@SuppressWarnings("unchecked") // TODO: Can we do this for overrides and defaults only?
public List<SSAValue> connectThrough(final ParameterAccessor callee, Set<? extends SSAValue> overrides, Set<? extends SSAValue> defaults, final IClassHierarchy cha, IInstantiator instantiator, Object... instantiatorArgs) {
if (callee == null) {
throw new IllegalArgumentException("Cannot connect through to null-callee");
}
if (overrides == null) {
overrides = Collections.EMPTY_SET;
}
if (defaults == null) {
defaults = Collections.EMPTY_SET;
}
if (callee.getNumberOfParameters() == 0) {
return new ArrayList<SSAValue>(0);
}
final List<SSAValue> assigned = new ArrayList<SSAValue>(); // TODO: Set initial size
final List<Parameter> calleeParams = callee.all();
final List<Parameter> thisParams = all();
// ****
// Implementation starts here
debug("Collecting parameters for callee {}", ((callee.mRef!=null)?callee.mRef:callee.method));
debug("\tThe calling function is {}", ((this.mRef!=null)?this.mRef:this.method));
forEachParameter:
for (final Parameter param : calleeParams) {
debug("\tSearching candidate for {}", param);
final TypeReference paramType = param.getType();
{ // Exact match in overrides
for (final SSAValue cand : overrides) {
if (cand.getType().getName().equals(paramType.getName())) { // XXX: What about the loader?
assigned.add(cand);
debug("\t\tAsigning: {} from the overrides (eq)", cand);
continue forEachParameter;
} else {
debug("\t\tSkipping: {} of the overrides (eq)", cand);
}
}
}
{ // Exact match in this params
for (final Parameter cand : thisParams) {
if (cand.getType().getName().equals(paramType.getName())) {
assigned.add(cand);
debug("\t\tAsigning: {} from callers params (eq)", cand);
continue forEachParameter;
} else {
debug("\t\tSkipping: {} of the callers params (eq)", cand);
}
}
}
{ // Exact match in defaults
for (final SSAValue cand : defaults) {
if (cand.getType().getName().equals(paramType.getName())) {
assigned.add(cand);
debug("\t\tAsigning: {} from the defaults (eq)", cand);
continue forEachParameter;
}
}
}
debug("\tThe parameter is still not found - try again using an assignability check...");
// If we got here we need cha
if (cha != null) {
{ // Assignable from overrides
try {
for (final SSAValue cand : overrides) {
if (isAssignable(cand, param, cha)) {
assigned.add(cand);
debug("\t\tAsigning: {} from the overrides (ass)", cand);
continue forEachParameter;
}
}
} catch (ClassLookupException e) {
}
}
{ // Assignable from this params
for (final Parameter cand : thisParams) {
try {
if (isAssignable(cand, param, cha)) {
assigned.add(cand);
debug("\t\tAsigning: {} from the callrs params (ass)", cand);
continue forEachParameter;
}
} catch (ClassLookupException e) {
}
}
}
{ // Assignable from defaults
for (final SSAValue cand : defaults) {
if (isAssignable(cand, param, cha)) {
assigned.add(cand);
debug("\t\tAsigning: {} from the defaults (ass)", cand);
continue forEachParameter;
}
}
}
if (instantiator != null) {
info("Creating new instance of: {} in call to {}", param, callee);
/*{ // DEBUG
System.out.println("Creating new instance of: " + param);
System.out.println("in connectThrough");
System.out.println("\tCaller:\t\t" + this.forMethod());
System.out.println("\tCallee:\t\t" + callee.forMethod());
System.out.println("\tOverrides:\t" + overrides);
System.out.println("\tDefaults:\t" + defaults);
} // */
final int inst = instantiator.createInstance(param.getType(), instantiatorArgs);
if (inst < 0) {
warn("No type was assignable and the instantiator returned an invalidone! Using null for {}", param);
assigned.add(null);
continue forEachParameter;
} else {
final Parameter newParam;
if (this.base == BasedOn.IMETHOD) {
newParam = new Parameter(inst, "craftedForCall", param.getType(), ParamerterDisposition.NEW,
this.base, this.method.getReference(), this.descriptorOffset);
} else if (this.base == BasedOn.METHOD_REFERENCE) {
newParam = new Parameter(inst, "craftedForCall", param.getType(), ParamerterDisposition.NEW,
this.base, this.mRef, this.descriptorOffset);
} else {
throw new UnsupportedOperationException("Can't handle base " + this.base);
}
assigned.add(newParam);
continue forEachParameter;
}
} else {
warn("No IInstantiator given and no known parameter assignable - using null");
assigned.add(null);
continue forEachParameter;
}
} else {
// TODO: CreateInstance Call-Back
warn("No type was equal. We can't ask isAssignable since we have no cha!");
assigned.add(null);
continue forEachParameter;
} // of (cha != null)
//Assertions.UNREACHABLE(); // Well it's unreachable
} // of final Parameter param : calleeParams
if(assigned.size() != calleeParams.size()) {
System.err.println("Assigned " + assigned.size() + " params to a method taking " + calleeParams.size() + " params!");
System.err.println("The call takes the parameters");
for (Parameter param : calleeParams) {
System.err.println("\t" + param);
}
System.err.println("The following were assigned:");
for (int i = 0; i < assigned.size(); ++i) {
System.err.println("\tAssigned parameter " + (i + 1) + " is " + assigned.get(i));
}
throw new IllegalStateException("Parameter mismatch!");
}
return assigned;
}
// *****************************************************************************
//
// Private helper functions follow...
/**
* Does "to x := from" hold?.
*/
public static boolean isAssignable(final TypeReference from, final TypeReference to, final IClassHierarchy cha) {
if (cha == null) {
throw new IllegalArgumentException("ClassHierarchy may not be null");
}
if (from.getName().equals(to.getName())) return true;
if (from.isPrimitiveType() && to.isPrimitiveType()) {
//return PrimitiveAssignability.isAssignableFrom(from.getName(), to.getName());
return PrimitiveAssignability.isAssignableFrom(to.getName(), from.getName()); // TODO: Which way
}
if (from.isPrimitiveType() || to.isPrimitiveType()) {
return false;
}
IClass fromClass = cha.lookupClass(from);
IClass toClass = cha.lookupClass(to);
if (fromClass == null) {
debug("Unable to look up the type of from=" + from + " in the ClassHierarchy - tying other loaders...");
for (final IClassLoader loader: cha.getLoaders()) {
final IClass cand = loader.lookupClass(from.getName());
if (cand != null) {
debug("Using alternative for from: {}", cand);
fromClass = cand;
break;
}
}
if (fromClass == null) {
throw new ClassLookupException("Unable to look up the type of from=" + from +
" in the ClassHierarchy");
//return false; // TODO
}
}
if (toClass == null) {
debug("Unable to look up the type of to=" + to + " in the ClassHierarchy - tying other loaders...");
for (final IClassLoader loader: cha.getLoaders()) {
final IClass cand = loader.lookupClass(to.getName());
if (cand != null) {
debug("Using alternative for to: {}", cand);
toClass = cand;
break;
}
}
if (toClass == null) {
error("Unable to look up the type of to={} in the ClassHierarchy", to);
return false;
//throw new ClassLookupException("Unable to look up the type of to=" + to +
// " in the ClassHierarchy");
}
}
// cha.isAssignableFrom (IClass c1, IClass c2)
// Does an expression c1 x := c2 y typecheck?
trace("isAssignableFrom({}, {}) = {}", toClass, fromClass, cha.isAssignableFrom(toClass, fromClass));
return cha.isAssignableFrom(toClass, fromClass);
}
/**
* Is sub a subclass of superC (or the same).
*/
public static boolean isSubclassOf(final TypeReference sub, final TypeReference superC, final IClassHierarchy cha)
throws ClassLookupException {
if (cha == null) {
throw new IllegalArgumentException("ClassHierarchy may not be null");
}
if (sub.getName().equals(superC.getName())) return true;
if (sub.isPrimitiveType() || superC.isPrimitiveType()) {
return false;
}
IClass subClass = cha.lookupClass(sub);
IClass superClass = cha.lookupClass(superC);
if (subClass == null) {
debug("Unable to look up the type of from=" + sub + " in the ClassHierarchy - tying other loaders...");
for (final IClassLoader loader: cha.getLoaders()) {
final IClass cand = loader.lookupClass(sub.getName());
if (cand != null) {
debug("Using alternative for from: {}", cand);
subClass = cand;
break;
}
}
if (subClass == null) {
throw new ClassLookupException("Unable to look up the type of from=" + sub +
" in the ClassHierarchy");
}
}
if (superClass == null) {
debug("Unable to look up the type of to=" + superC + " in the ClassHierarchy - tying other loaders...");
for (final IClassLoader loader: cha.getLoaders()) {
final IClass cand = loader.lookupClass(superC.getName());
if (cand != null) {
debug("Using alternative for to: {}", cand);
superClass = cand;
break;
}
}
if (superClass == null) {
error("Unable to look up the type of to={} in the ClassHierarchy", superC);
throw new ClassLookupException("Unable to look up the type of to=" + superC +
" in the ClassHierarchy");
}
}
return cha.isSubclassOf(subClass, superClass);
}
/**
* The method this accessor reads the parameters from.
*/
public MethodReference forMethod() {
if (this.mRef != null) {
return this.mRef;
} else {
return this.method.getReference();
}
}
// *****************************************************************************
//
// Shorthand functions follow...
/**
* Does "to x := from" hold?.
*/
protected boolean isAssignable(final SSAValue from, final SSAValue to, final IClassHierarchy cha) {
return isAssignable(from.getType(), to.getType(), cha);
}
/**
* Shorthand for forInvokeStatic(final List<? extends Parameter> args, final ParameterAccessor target, final IClassHierarchy cha).
*
* Generates a new ParameterAccessor for target and hands the call through.
*/
public int[] forInvokeStatic(final List<?extends Parameter> args, final MethodReference target, final IClassHierarchy cha) {
return forInvokeStatic(args, new ParameterAccessor(target, cha), cha);
}
/**
* Shorthand for forInvokeVirtual(final int self, final List<? extends Parameter> args, final ParameterAccessor target, final IClassHierarchy cha).
*
* Generates a new ParameterAccessor for target and hands the call through.
*/
public int[] forInvokeVirtual (final int self, final List<? extends Parameter> args, final MethodReference target, final IClassHierarchy cha) {
return forInvokeVirtual (self, args, new ParameterAccessor(target, cha), cha);
}
/**
* If the method returns a value eg is non-void.
*/
public boolean hasReturn() {
return (getReturnType() != TypeReference.Void);
}
/**
* Assign parameters to a call based on their type.
*
* this variant of connectThrough cannot create new instances if needed.
*
* @param callee The function to generate the parameter-list for
* @param overrides If a parameter occurs here, it is preferred over the ones present in this
* @param defaults If a parameter is not present in this or the overrides, defaults are searched. If the parameter is not present there null is assigned.
* @param cha Optional class hierarchy for testing assignability
* @return the parameter-list for the call of toMethod
*/
public List<SSAValue> connectThrough(final ParameterAccessor callee, Set<? extends SSAValue> overrides, Set<? extends SSAValue> defaults,
final IClassHierarchy cha) {
return connectThrough(callee, overrides, defaults, cha, null);
}
// *****************************************************************************
//
// Hand-through functions follow...
/**
* Handed through to the IMethod / MethodReference
*/
public TypeReference getReturnType() {
switch (this.base) {
case IMETHOD:
return this.method.getReturnType();
case METHOD_REFERENCE:
return this.mRef.getReturnType();
default:
throw new UnsupportedOperationException("No implementation of getReturnType() for base " + this.base);
}
}
/**
* Number of parameters _excluding_ implicit this
*/
public int getNumberOfParameters() {
return this.numberOfParameters;
}
/**
* Extensive output for debugging purposes.
*/
public String dump() {
String ret = "Parameter Accessor for " + ((this.mRef!=null)?"mRef:" + this.mRef.toString():"IMethod: " + this.method.toString()) +
"\nContains " + this.numberOfParameters + " Parameters " + this.base + "\n";
/*for (int i = 1; i <= this.numberOfParameters; ++i) {
try {
ret += "\t" + getParameter(i).toString() + "\n";
} catch (Exception e) {
ret += "\tNone at " + i + "\n";
}
}*/
ret += "\nAnd all is:\n";
for (Parameter p : all()) {
ret += "\t" + p + "\n";
}
if (hasImplicitThis ()) {
ret +="This: " + getThis();
} else {
ret +="Is static";
}
return ret;
}
public String toString() {
return "<ParamAccessor forMethod=" + this.forMethod() + " />";
}
private static void debug(String s, Object ... args) {
if (DEBUG) { System.err.printf(s, args); }
}
private static void info(String s, Object ... args) {
if (DEBUG) { System.err.printf(s, args); }
}
private static void warn(String s, Object ... args) {
if (DEBUG) { System.err.printf(s, args); }
}
private static void trace(String s, Object ... args) {
if (DEBUG) { System.err.printf(s, args); }
}
private static void error(String s, Object ... args) {
if (DEBUG) { System.err.printf(s, args); }
}
}