Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package soot;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 1997 - 1999 Raja Vallee-Rai
* Copyright (C) 2004 Ondrej Lhotak
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.dava.DavaBody;
import soot.dava.toolkits.base.renamer.RemoveFullyQualifiedName;
import soot.jimple.toolkits.callgraph.VirtualCalls;
import soot.options.Options;
import soot.tagkit.AbstractHost;
import soot.util.IterableSet;
import soot.util.Numberable;
import soot.util.NumberedString;
/**
* Soot representation of a Java method. Can be declared to belong to a {@link SootClass}. Does not contain the actual code,
* which belongs to a {@link Body}. The {@link #getActiveBody()} method points to the currently-active body.
*/
public class SootMethod extends AbstractHost implements ClassMember, Numberable, MethodOrMethodContext, SootMethodInterface {
private static final Logger logger = LoggerFactory.getLogger(SootMethod.class);
public static final String constructorName = "";
public static final String staticInitializerName = "";
/** Name of the current method. */
protected String name;
/**
* An array of parameter types taken by this {@link SootMethod} object, in declaration order.
*/
protected Type[] parameterTypes;
/** The return type of this object. */
protected Type returnType;
/**
* True when some {@link SootClass} object declares this {@link SootMethod} object.
*/
protected boolean isDeclared;
/** Holds the class which declares this SootClass method. */
protected SootClass declaringClass;
/**
* Modifiers associated with this {@link SootMethod} (e.g. private, protected, etc.)
*/
protected int modifiers;
/** Is this method a phantom method? */
protected boolean isPhantom = false;
/** Declared exceptions thrown by this method. Created upon demand. */
protected List exceptions = null;
/** Active body associated with this method. */
protected volatile Body activeBody;
/** Tells this method how to find out where its body lives. */
protected volatile MethodSource ms;
protected volatile String sig;
protected volatile String subSig;
/**
* Constructs a {@link SootMethod} with the given name, parameter types and return type.
*/
public SootMethod(String name, List parameterTypes, Type returnType) {
this(name, parameterTypes, returnType, 0, Collections.emptyList());
}
/**
* Constructs a {@link SootMethod} with the given name, parameter types, return type and modifiers.
*/
public SootMethod(String name, List parameterTypes, Type returnType, int modifiers) {
this(name, parameterTypes, returnType, modifiers, Collections.emptyList());
}
/**
* Constructs a {@link SootMethod} with the given name, parameter types, return type, and list of thrown exceptions.
*/
public SootMethod(String name, List parameterTypes, Type returnType, int modifiers,
List thrownExceptions) {
this.name = name;
if (parameterTypes != null && !parameterTypes.isEmpty()) {
this.parameterTypes = parameterTypes.toArray(new Type[parameterTypes.size()]);
}
this.returnType = returnType;
this.modifiers = modifiers;
if (exceptions == null && !thrownExceptions.isEmpty()) {
exceptions = new ArrayList();
this.exceptions.addAll(thrownExceptions);
}
subsignature = Scene.v().getSubSigNumberer().findOrAdd(getSubSignature());
}
/**
* Returns a hash code for this method consistent with structural equality.
*/
public int equivHashCode() {
return returnType.hashCode() * 101 + modifiers * 17 + name.hashCode();
}
/** Returns the name of this method. */
public String getName() {
return name;
}
/** Sets the name of this method. */
public synchronized void setName(String name) {
boolean wasDeclared = isDeclared;
SootClass oldDeclaringClass = declaringClass;
if (wasDeclared) {
oldDeclaringClass.removeMethod(this);
}
this.name = name;
subSig = null;
sig = null;
subsignature = Scene.v().getSubSigNumberer().findOrAdd(getSubSignature());
if (wasDeclared) {
oldDeclaringClass.addMethod(this);
}
}
/** Sets the declaring class */
public synchronized void setDeclaringClass(SootClass declClass) {
// There is nothing to stop this field from being null except when it actually gets in
// other classes such as SootMethodRef (when it tries to resolve the method). However, if
// the method is not declared, it should not be trying to resolve it anyways. So I see no
// problem with having it able to be null.
if (declClass != null) {
Scene.v().getMethodNumberer().add(this);
}
// We could call setDeclared here, however, when SootClass adds a method, it checks isDeclared
// and throws an exception if set. So we currently cannot call setDeclared here.
declaringClass = declClass;
sig = null;
}
/** Returns the class which declares the current {@link SootMethod}. */
@Override
public SootClass getDeclaringClass() {
if (!isDeclared) {
throw new RuntimeException("not declared: " + getName());
}
return declaringClass;
}
public void setDeclared(boolean isDeclared) {
this.isDeclared = isDeclared;
}
/**
* Returns true when some {@link SootClass} object declares this {@link SootMethod} object.
*/
@Override
public boolean isDeclared() {
return isDeclared;
}
/** Returns true when this {@link SootMethod} object is phantom. */
@Override
public boolean isPhantom() {
return isPhantom;
}
/** Returns true if this method is not phantom, abstract or native, i.e. this method can have a body. */
public boolean isConcrete() {
return !isPhantom() && !isAbstract() && !isNative();
}
/** Sets the phantom flag on this method. */
@Override
public void setPhantom(boolean value) {
if (value) {
if (!Scene.v().allowsPhantomRefs()) {
throw new RuntimeException("Phantom refs not allowed");
}
if (!Options.v().allow_phantom_elms() && declaringClass != null && !declaringClass.isPhantom()) {
throw new RuntimeException("Declaring class would have to be phantom");
}
}
isPhantom = value;
}
/**
* Gets the modifiers of this method.
*
* @see soot.Modifier
*/
@Override
public int getModifiers() {
return modifiers;
}
/**
* Sets the modifiers of this method.
*
* @see soot.Modifier
*/
@Override
public void setModifiers(int modifiers) {
this.modifiers = modifiers;
}
/** Returns the return type of this method. */
public Type getReturnType() {
return returnType;
}
/** Sets the return type of this method. */
public synchronized void setReturnType(Type t) {
boolean wasDeclared = isDeclared;
SootClass oldDeclaringClass = declaringClass;
if (wasDeclared) {
oldDeclaringClass.removeMethod(this);
}
returnType = t;
subSig = null;
sig = null;
subsignature = Scene.v().getSubSigNumberer().findOrAdd(getSubSignature());
if (wasDeclared) {
oldDeclaringClass.addMethod(this);
}
}
/** Returns the number of parameters taken by this method. */
public int getParameterCount() {
return parameterTypes == null ? 0 : parameterTypes.length;
}
/** Gets the type of the nth parameter of this method. */
public Type getParameterType(int n) {
return parameterTypes[n];
}
/**
* Returns a read-only list of the parameter types of this method.
*/
public List getParameterTypes() {
return parameterTypes == null ? Collections.emptyList() : Arrays.asList(parameterTypes);
}
/**
* Changes the set of parameter types of this method.
*/
public synchronized void setParameterTypes(List l) {
boolean wasDeclared = isDeclared;
SootClass oldDeclaringClass = declaringClass;
if (wasDeclared) {
oldDeclaringClass.removeMethod(this);
}
this.parameterTypes = l.toArray(new Type[l.size()]);
subSig = null;
sig = null;
subsignature = Scene.v().getSubSigNumberer().findOrAdd(getSubSignature());
if (wasDeclared) {
oldDeclaringClass.addMethod(this);
}
}
/** Returns the {@link MethodSource} of the current {@link SootMethod}. */
public MethodSource getSource() {
return ms;
}
/** Sets the {@link MethodSource} of the current {@link SootMethod}. */
public synchronized void setSource(MethodSource ms) {
this.ms = ms;
}
/**
* Retrieves the active body for this method.
*/
@SuppressWarnings("deprecation")
public Body getActiveBody() {
// Retrieve the active body so thread changes do not affect the
// synchronization between if the body exists and the returned body.
// This is a quick check just in case the activeBody exists.
Body activeBody = this.activeBody;
if (activeBody != null) {
return activeBody;
}
// Synchronize because we are operating on two fields that may be updated
// separately otherwise.
synchronized (this) {
// Re-check the activeBody because things might have changed
activeBody = this.activeBody;
if (activeBody != null) {
return activeBody;
}
if (declaringClass != null) {
declaringClass.checkLevel(SootClass.BODIES);
}
if ((declaringClass != null && declaringClass.isPhantomClass()) || isPhantom()) {
throw new RuntimeException("cannot get active body for phantom method: " + getSignature());
}
// ignore empty body exceptions if we are just computing coffi metrics
if (!soot.jbco.Main.metrics) {
throw new RuntimeException("no active body present for method " + getSignature());
}
return null;
}
}
/**
* Sets the active body for this method.
*/
public synchronized void setActiveBody(Body body) {
if ((declaringClass != null) && declaringClass.isPhantomClass()) {
throw new RuntimeException("cannot set active body for phantom class! " + this);
}
// If someone sets a body for a phantom method, this method then is no
// longer phantom
setPhantom(false);
if (!isConcrete()) {
throw new RuntimeException("cannot set body for non-concrete method! " + this);
}
if (body != null && body.getMethod() != this) {
body.setMethod(this);
}
this.activeBody = body;
}
/**
* Returns the active body if present, else constructs an active body and returns that.
*
* If you called Scene.v().loadClassAndSupport() for a class yourself, it will not be an application class, so you cannot
* get retrieve its active body. Please call {@link SootClass#setApplicationClass()} on the relevant class.
*/
public Body retrieveActiveBody() {
// Retrieve the active body so thread changes do not affect the
// synchronization between if the body exists and the returned body.
// This is a quick check just in case the activeBody exists.
Body activeBody = this.activeBody;
if (activeBody != null) {
return activeBody;
}
// Synchronize because we are operating on multiple fields that may be updated
// separately otherwise.
synchronized (this) {
// Re-check the activeBody because things might have changed
activeBody = this.activeBody;
if (activeBody != null) {
return activeBody;
}
if (declaringClass != null) {
declaringClass.checkLevel(SootClass.BODIES);
}
if ((declaringClass != null && declaringClass.isPhantomClass()) || isPhantom()) {
throw new RuntimeException("cannot get resident body for phantom method : " + this);
}
if (ms == null) {
throw new RuntimeException("No method source set for method " + this);
}
// Method sources are not expected to be thread safe
activeBody = ms.getBody(this, "jb");
setActiveBody(activeBody);
// If configured, we drop the method source to save memory
if (Options.v().drop_bodies_after_load()) {
ms = null;
}
return activeBody;
}
}
/** Returns true if this method has an active body. */
public boolean hasActiveBody() {
return activeBody != null;
}
/** Releases the active body associated with this method. */
public synchronized void releaseActiveBody() {
activeBody = null;
}
/**
* Adds the given exception to the list of exceptions thrown by this method unless the exception is already in the list.
*/
public void addExceptionIfAbsent(SootClass e) {
if (!throwsException(e)) {
addException(e);
}
}
/**
* Adds the given exception to the list of exceptions thrown by this method.
*/
public void addException(SootClass e) {
logger.trace("Adding exception {}", e);
if (exceptions == null) {
exceptions = new ArrayList();
} else if (exceptions.contains(e)) {
throw new RuntimeException("already throws exception " + e.getName());
}
exceptions.add(e);
}
/**
* Removes the given exception from the list of exceptions thrown by this method.
*/
public void removeException(SootClass e) {
logger.trace("Removing exception {}", e);
if (exceptions == null) {
throw new RuntimeException("does not throw exception " + e.getName());
}
if (!exceptions.contains(e)) {
throw new RuntimeException("does not throw exception " + e.getName());
}
exceptions.remove(e);
}
/** Returns true if this method throws exception e. */
public boolean throwsException(SootClass e) {
return exceptions != null && exceptions.contains(e);
}
public void setExceptions(List exceptions) {
if (exceptions != null && !exceptions.isEmpty()) {
this.exceptions = new ArrayList(exceptions);
} else {
this.exceptions = null;
}
}
/**
* Returns a backed list of the exceptions thrown by this method.
*/
public List getExceptions() {
if (exceptions == null) {
exceptions = new ArrayList();
}
return exceptions;
}
public List getExceptionsUnsafe() {
return exceptions;
}
/**
* Convenience method returning true if this method is static.
*/
@Override
public boolean isStatic() {
return Modifier.isStatic(this.getModifiers());
}
/**
* Convenience method returning true if this method is private.
*/
@Override
public boolean isPrivate() {
return Modifier.isPrivate(this.getModifiers());
}
/**
* Convenience method returning true if this method is public.
*/
@Override
public boolean isPublic() {
return Modifier.isPublic(this.getModifiers());
}
/**
* Convenience method returning true if this method is protected.
*/
@Override
public boolean isProtected() {
return Modifier.isProtected(this.getModifiers());
}
/**
* Convenience method returning true if this method is abstract.
*/
public boolean isAbstract() {
return Modifier.isAbstract(this.getModifiers());
}
/**
* Convenience method returning true if this method is final.
*/
public boolean isFinal() {
return Modifier.isFinal(this.getModifiers());
}
/**
* Convenience method returning true if this method is native.
*/
public boolean isNative() {
return Modifier.isNative(this.getModifiers());
}
/**
* Convenience method returning true if this method is synchronized.
*/
public boolean isSynchronized() {
return Modifier.isSynchronized(this.getModifiers());
}
/**
*
* @return yes if this is the main method
*/
public boolean isMain() {
if (isPublic() && isStatic()) {
NumberedString main_sig = Scene.v().getSubSigNumberer().findOrAdd("void main(java.lang.String[])");
if (main_sig.equals(subsignature)) {
return true;
}
}
return false;
}
/**
*
* @return yes, if this function is a constructor. Please not that methods are not treated as constructors in this
* method.
*/
public boolean isConstructor() {
return name.equals(constructorName);
}
/**
*
* @return yes, if this function is a static initializer.
*/
public boolean isStaticInitializer() {
return name.equals(staticInitializerName);
}
/**
* @return yes, if this is a class initializer or main function.
*/
public boolean isEntryMethod() {
if (isStatic() && subsignature.equals(VirtualCalls.v().sigClinit)) {
return true;
}
return isMain();
}
/**
* We rely on the JDK class recognition to decide if a method is JDK method.
*/
public boolean isJavaLibraryMethod() {
SootClass cl = getDeclaringClass();
return cl.isJavaLibraryClass();
}
/**
* Returns the parameters part of the signature in the format in which it appears in bytecode.
*/
public String getBytecodeParms() {
StringBuffer buffer = new StringBuffer();
for (Iterator typeIt = getParameterTypes().iterator(); typeIt.hasNext();) {
final Type type = typeIt.next();
buffer.append(AbstractJasminClass.jasminDescriptorOf(type));
}
return buffer.toString().intern();
}
/**
* Returns the signature of this method in the format in which it appears in bytecode (eg. [Ljava/lang/Object instead of
* java.lang.Object[]).
*/
public String getBytecodeSignature() {
String name = getName();
StringBuffer buffer = new StringBuffer();
buffer.append("<" + Scene.v().quotedNameOf(getDeclaringClass().getName()) + ": ");
buffer.append(name);
buffer.append(AbstractJasminClass.jasminDescriptorOf(makeRef()));
buffer.append(">");
return buffer.toString().intern();
}
/**
* Returns the Soot signature of this method. Used to refer to methods unambiguously.
*/
public String getSignature() {
if (sig == null) {
synchronized (this) {
if (sig == null) {
sig = getSignature(getDeclaringClass(), getSubSignature());
}
}
}
return sig;
}
public static String getSignature(SootClass cl, String name, List params, Type returnType) {
return getSignature(cl, getSubSignatureImpl(name, params, returnType));
}
public static String getSignature(SootClass cl, String subSignature) {
StringBuilder buffer = new StringBuilder();
buffer.append("<");
buffer.append(Scene.v().quotedNameOf(cl.getName()));
buffer.append(": ");
buffer.append(subSignature);
buffer.append(">");
return buffer.toString();
}
/**
* Returns the Soot subsignature of this method. Used to refer to methods unambiguously.
*/
public String getSubSignature() {
if (subSig == null) {
synchronized (this) {
if (subSig == null) {
subSig = getSubSignatureImpl(getName(), getParameterTypes(), getReturnType());
}
}
}
return subSig;
}
public static String getSubSignature(String name, List params, Type returnType) {
return getSubSignatureImpl(name, params, returnType);
}
private static String getSubSignatureImpl(String name, List params, Type returnType) {
StringBuilder buffer = new StringBuilder();
buffer.append(returnType.toQuotedString());
buffer.append(" ");
buffer.append(Scene.v().quotedNameOf(name));
buffer.append("(");
if (params != null) {
for (int i = 0; i < params.size(); i++) {
buffer.append(params.get(i).toQuotedString());
if (i < params.size() - 1) {
buffer.append(",");
}
}
}
buffer.append(")");
return buffer.toString();
}
protected NumberedString subsignature;
public NumberedString getNumberedSubSignature() {
return subsignature;
}
/** Returns the signature of this method. */
@Override
public String toString() {
return getSignature();
}
/*
* TODO: Nomair A. Naeem .... 8th Feb 2006 This is really messy coding So much for modularization!! Should some day look
* into creating the DavaDeclaration from within DavaBody
*/
public String getDavaDeclaration() {
if (getName().equals(staticInitializerName)) {
return "static";
}
StringBuffer buffer = new StringBuffer();
// modifiers
StringTokenizer st = new StringTokenizer(Modifier.toString(this.getModifiers()));
if (st.hasMoreTokens()) {
buffer.append(st.nextToken());
}
while (st.hasMoreTokens()) {
buffer.append(" " + st.nextToken());
}
if (buffer.length() != 0) {
buffer.append(" ");
}
// return type + name
if (getName().equals(constructorName)) {
buffer.append(getDeclaringClass().getShortJavaStyleName());
} else {
Type t = this.getReturnType();
String tempString = t.toString();
/*
* Added code to handle RuntimeExcepotion thrown by getActiveBody
*/
if (hasActiveBody()) {
DavaBody body = (DavaBody) getActiveBody();
IterableSet importSet = body.getImportList();
if (!importSet.contains(tempString)) {
body.addToImportList(tempString);
}
tempString = RemoveFullyQualifiedName.getReducedName(importSet, tempString, t);
}
buffer.append(tempString + " ");
buffer.append(Scene.v().quotedNameOf(this.getName()));
}
buffer.append("(");
// parameters
Iterator typeIt = this.getParameterTypes().iterator();
int count = 0;
while (typeIt.hasNext()) {
Type t = typeIt.next();
String tempString = t.toString();
/*
* Nomair A. Naeem 7th Feb 2006 It is nice to remove the fully qualified type names of parameters if the package they
* belong to have been imported javax.swing.ImageIcon should be just ImageIcon if javax.swing is imported If not
* imported WHY NOT..import it!!
*/
if (hasActiveBody()) {
DavaBody body = (DavaBody) getActiveBody();
IterableSet importSet = body.getImportList();
if (!importSet.contains(tempString)) {
body.addToImportList(tempString);
}
tempString = RemoveFullyQualifiedName.getReducedName(importSet, tempString, t);
}
buffer.append(tempString + " ");
buffer.append(" ");
if (hasActiveBody()) {
buffer.append(((DavaBody) getActiveBody()).get_ParamMap().get(new Integer(count++)));
} else {
if (t == BooleanType.v()) {
buffer.append("z" + count++);
} else if (t == ByteType.v()) {
buffer.append("b" + count++);
} else if (t == ShortType.v()) {
buffer.append("s" + count++);
} else if (t == CharType.v()) {
buffer.append("c" + count++);
} else if (t == IntType.v()) {
buffer.append("i" + count++);
} else if (t == LongType.v()) {
buffer.append("l" + count++);
} else if (t == DoubleType.v()) {
buffer.append("d" + count++);
} else if (t == FloatType.v()) {
buffer.append("f" + count++);
} else if (t == StmtAddressType.v()) {
buffer.append("a" + count++);
} else if (t == ErroneousType.v()) {
buffer.append("e" + count++);
} else if (t == NullType.v()) {
buffer.append("n" + count++);
} else {
buffer.append("r" + count++);
}
}
if (typeIt.hasNext()) {
buffer.append(", ");
}
}
buffer.append(")");
// Print exceptions
if (exceptions != null) {
Iterator exceptionIt = this.getExceptions().iterator();
if (exceptionIt.hasNext()) {
buffer.append(" throws " + exceptionIt.next().getName());
while (exceptionIt.hasNext()) {
buffer.append(", " + exceptionIt.next().getName());
}
}
}
return buffer.toString().intern();
}
/**
* Returns the declaration of this method, as used at the top of textual body representations (before the {}'s containing
* the code for representation.)
*/
public String getDeclaration() {
StringBuffer buffer = new StringBuffer();
// modifiers
StringTokenizer st = new StringTokenizer(Modifier.toString(this.getModifiers()));
if (st.hasMoreTokens()) {
buffer.append(st.nextToken());
}
while (st.hasMoreTokens()) {
buffer.append(" " + st.nextToken());
}
if (buffer.length() != 0) {
buffer.append(" ");
}
// return type + name
buffer.append(this.getReturnType().toQuotedString() + " ");
buffer.append(Scene.v().quotedNameOf(this.getName()));
buffer.append("(");
// parameters
Iterator typeIt = this.getParameterTypes().iterator();
// int count = 0;
while (typeIt.hasNext()) {
Type t = typeIt.next();
buffer.append(t.toQuotedString());
if (typeIt.hasNext()) {
buffer.append(", ");
}
}
buffer.append(")");
// Print exceptions
if (exceptions != null) {
Iterator exceptionIt = this.getExceptions().iterator();
if (exceptionIt.hasNext()) {
buffer.append(" throws " + Scene.v().quotedNameOf(exceptionIt.next().getName()));
while (exceptionIt.hasNext()) {
buffer.append(", " + Scene.v().quotedNameOf(exceptionIt.next().getName()));
}
}
}
return buffer.toString().intern();
}
@Override
public final int getNumber() {
return number;
}
@Override
public final void setNumber(int number) {
this.number = number;
}
protected int number = 0;
@Override
public SootMethod method() {
return this;
}
@Override
public Context context() {
return null;
}
public SootMethodRef makeRef() {
return Scene.v().makeMethodRef(declaringClass, name, parameterTypes == null ? null : Arrays.asList(parameterTypes),
returnType, isStatic());
}
@Override
public int getJavaSourceStartLineNumber() {
super.getJavaSourceStartLineNumber();
// search statements for first line number
if (line == -1 && hasActiveBody()) {
PatchingChain unit = getActiveBody().getUnits();
for (Unit u : unit) {
int l = u.getJavaSourceStartLineNumber();
if (l > -1) {
// store l-1, as method header is usually one line before
// 1st statement
line = l - 1;
break;
}
}
}
return line;
}
}