All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.infoflow.entryPointCreators.BaseEntryPointCreator Maven / Gradle / Ivy

There is a newer version: 2.9.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2012 Secure Software Engineering Group at EC SPRIDE.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors: Christian Fritz, Steven Arzt, Siegfried Rasthofer, Eric
 * Bodden, and others.
 ******************************************************************************/
package soot.jimple.infoflow.entryPointCreators;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import soot.ArrayType;
import soot.Body;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.Local;
import soot.LongType;
import soot.PrimType;
import soot.RefType;
import soot.Scene;
import soot.ShortType;
import soot.SootClass;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.VoidType;
import soot.javaToJimple.LocalGenerator;
import soot.jimple.AssignStmt;
import soot.jimple.DoubleConstant;
import soot.jimple.EqExpr;
import soot.jimple.FloatConstant;
import soot.jimple.IfStmt;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.Jimple;
import soot.jimple.LongConstant;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewExpr;
import soot.jimple.NullConstant;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import soot.jimple.infoflow.util.SystemClassHandler;

/**
 * Common base class for all entry point creators. Implementors must override
 * the createDummyMainInternal method to provide their entry point
 * implementation.
 */
public abstract class BaseEntryPointCreator implements IEntryPointCreator {

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	protected Map localVarsForClasses = new HashMap<>();
	private final Set failedClasses = new HashSet<>();

	private boolean substituteCallParams = false;
	private List substituteClasses;
	private boolean allowSelfReferences = false;
	private boolean ignoreSystemClassParams = true;

	private final Set failedMethods = new HashSet<>();

	/**
	 * Default name of the class containing the dummy main method
	 */
	protected String dummyClassName = "dummyMainClass";
	/**
	 * Default name of the dummy main method
	 */
	protected String dummyMethodName = "dummyMainMethod";

	protected boolean shallowMode = false;
	protected boolean overwriteDummyMainMethod = false;
	protected boolean warnOnConstructorLoop = false;

	protected Value intCounter;
	protected int conditionCounter;

	protected SootMethod mainMethod = null;
	protected Body body = null;
	protected LocalGenerator generator = null;

	/**
	 * Returns a copy of all classes that could not be instantiated properly
	 * 
	 * @return The classes where the constructor could not be generated
	 */
	public Set getFailedClasses() {
		return new HashSet(failedClasses);
	}

	/**
	 * Returns all methods from from methodsToCall, where no call was possible
	 * 
	 * @return A Set of methods that were not called in the main method
	 */
	public Set getFailedMethods() {
		return new HashSet(failedMethods);
	}

	public void setSubstituteCallParams(boolean b) {
		substituteCallParams = b;
	}

	@Override
	public void setSubstituteClasses(List l) {
		substituteClasses = l;
	}

	@Override
	public SootMethod createDummyMain() {
		// Load the substitution classes
		if (substituteCallParams)
			for (String className : substituteClasses)
				Scene.v().forceResolve(className, SootClass.BODIES).setApplicationClass();

		// Create the empty main method
		createAdditionalFields();
		createAdditionalMethods();
		createEmptyMainMethod();
		body = mainMethod.getActiveBody();

		// We provide some helper objects
		final Body body = mainMethod.getActiveBody();
		generator = new LocalGenerator(body);

		// Make sure that we have an opaque predicate
		conditionCounter = 0;
		intCounter = generator.generateLocal(IntType.v());
		body.getUnits().add(Jimple.v().newAssignStmt(intCounter, IntConstant.v(conditionCounter)));

		return createDummyMainInternal();
	}

	/**
	 * Implementors need to overwrite this method for providing the actual dummy
	 * main method
	 * 
	 * @return The generated dummy main method
	 */
	protected abstract SootMethod createDummyMainInternal();

	/**
	 * Gets the class that contains the dummy main method. If such a class does not
	 * exist yet, it is created
	 * 
	 * @return The class tha contains the dummy main method
	 */
	protected SootClass getOrCreateDummyMainClass() {
		SootClass mainClass = Scene.v().getSootClassUnsafe(dummyClassName);
		if (mainClass == null) {
			mainClass = Scene.v().makeSootClass(dummyClassName);
			mainClass.setResolvingLevel(SootClass.BODIES);
			Scene.v().addClass(mainClass);
		}
		return mainClass;
	}

	/**
	 * Creates a new, empty main method containing the given body
	 * 
	 * @return The newly generated main method
	 */
	protected void createEmptyMainMethod() {
		// If we already have a main class, we need to make sure to use a fresh
		// method name
		int methodIndex = 0;
		String methodName = dummyMethodName;
		SootClass mainClass = getOrCreateDummyMainClass();
		if (!overwriteDummyMainMethod)
			while (mainClass.declaresMethodByName(methodName))
				methodName = dummyMethodName + "_" + methodIndex++;

		Type stringArrayType = ArrayType.v(RefType.v("java.lang.String"), 1);

		Body body;
		mainMethod = mainClass.getMethodByNameUnsafe(methodName);

		// Remove the existing main method if necessary. Do not clear the
		// existing one, this would take much too long.
		if (mainMethod != null) {
			mainClass.removeMethod(mainMethod);
			mainMethod = null;
		}

		// Create the method
		mainMethod = Scene.v().makeSootMethod(methodName, Collections.singletonList(stringArrayType), VoidType.v());

		// Create the body
		body = Jimple.v().newBody();
		body.setMethod(mainMethod);
		mainMethod.setActiveBody(body);

		// Add the method to the class
		mainClass.addMethod(mainMethod);

		// First add class to scene, then make it an application class
		// as addClass contains a call to "setLibraryClass"
		mainClass.setApplicationClass();
		mainMethod.setModifiers(Modifier.PUBLIC | Modifier.STATIC);

		// Add a parameter reference to the body
		LocalGenerator lg = new LocalGenerator(body);
		Local paramLocal = lg.generateLocal(stringArrayType);
		body.getUnits()
				.addFirst(Jimple.v().newIdentityStmt(paramLocal, Jimple.v().newParameterRef(stringArrayType, 0)));
	}

	/**
	 * Creates additional fields in the entry point class that are required by the
	 * dummy main method
	 */
	protected void createAdditionalFields() {
		// empty in default implementation
	}

	/**
	 * Creates additional methods in the entry point class that are required by the
	 * dummy main method
	 */
	protected void createAdditionalMethods() {
		// empty in default implementation
	}

	/**
	 * Gets a field name that is not already in use by some field
	 * 
	 * @param baseName The base name, i.e., prefix of the new field
	 * @return A field name that is still free
	 */
	protected String getNonCollidingFieldName(String baseName) {
		String fieldName = baseName;
		int fieldIdx = 0;
		final SootClass mainClass = getOrCreateDummyMainClass();
		while (mainClass.declaresFieldByName(fieldName))
			fieldName = baseName + "_" + fieldIdx++;
		return fieldName;
	}

	/**
	 * Builds a new invocation statement that invokes the given method
	 * 
	 * @param methodToCall The method to call
	 * @param classLocal   The local containing an instance of the class on which to
	 *                     invoke the method
	 * @return The newly created invocation statement
	 */
	protected Stmt buildMethodCall(SootMethod methodToCall, Local classLocal) {
		return buildMethodCall(methodToCall, classLocal, Collections.emptySet());
	}

	/**
	 * Builds a new invocation statement that invokes the given method
	 * 
	 * @param methodToCall  The method to call
	 * @param classLocal    The local containing an instance of the class on which
	 *                      to invoke the method
	 * @param parentClasses The classes for which we already have instances that
	 *                      shall be reused
	 * @return The newly created invocation statement
	 */
	protected Stmt buildMethodCall(SootMethod methodToCall, Local classLocal, Set parentClasses) {
		// If we don't have a method, we cannot call it (sad but true)
		if (methodToCall == null)
			return null;

		if (classLocal == null && !methodToCall.isStatic()) {
			logger.warn("Cannot call method {}, because there is no local for base object: {}", methodToCall,
					methodToCall.getDeclaringClass());
			failedMethods.add(methodToCall);
			return null;
		}

		final InvokeExpr invokeExpr;
		List args = new LinkedList();
		if (methodToCall.getParameterCount() > 0) {
			for (Type tp : methodToCall.getParameterTypes()) {
				Set constructionStack = new HashSet();
				if (!allowSelfReferences)
					constructionStack.add(methodToCall.getDeclaringClass());
				args.add(getValueForType(tp, constructionStack, parentClasses));
			}

			if (methodToCall.isStatic())
				invokeExpr = Jimple.v().newStaticInvokeExpr(methodToCall.makeRef(), args);
			else {
				assert classLocal != null : "Class local method was null for non-static method call";
				if (methodToCall.isConstructor())
					invokeExpr = Jimple.v().newSpecialInvokeExpr(classLocal, methodToCall.makeRef(), args);
				else
					invokeExpr = Jimple.v().newVirtualInvokeExpr(classLocal, methodToCall.makeRef(), args);
			}
		} else {
			if (methodToCall.isStatic()) {
				invokeExpr = Jimple.v().newStaticInvokeExpr(methodToCall.makeRef());
			} else {
				assert classLocal != null : "Class local method was null for non-static method call";
				if (methodToCall.isConstructor())
					invokeExpr = Jimple.v().newSpecialInvokeExpr(classLocal, methodToCall.makeRef());
				else
					invokeExpr = Jimple.v().newVirtualInvokeExpr(classLocal, methodToCall.makeRef());
			}
		}

		Stmt stmt;
		if (!(methodToCall.getReturnType() instanceof VoidType)) {
			Local returnLocal = generator.generateLocal(methodToCall.getReturnType());
			stmt = Jimple.v().newAssignStmt(returnLocal, invokeExpr);

		} else {
			stmt = Jimple.v().newInvokeStmt(invokeExpr);
		}
		body.getUnits().add(stmt);

		// Clean up. If we re-use parent objects, do not destroy those. We can
		// only clean up what we have created.
		for (Value val : args)
			if (val instanceof Local && val.getType() instanceof RefType) {
				if (!parentClasses.contains(((RefType) val.getType()).getSootClass())) {
					body.getUnits().add(Jimple.v().newAssignStmt(val, NullConstant.v()));
					localVarsForClasses.remove(((RefType) val.getType()).getSootClass());
				}
			}

		return stmt;
	}

	/**
	 * Creates a value of the given type to be used as a substitution in method
	 * invocations or fields
	 * 
	 * @param tp                The type for which to get a value
	 * @param constructionStack The set of classes we're currently constructing.
	 *                          Attempts to create a parameter of one of these
	 *                          classes will trigger the constructor loop check and
	 *                          the respective parameter will be substituted by
	 *                          null.
	 * @param parentClasses     If the given type is compatible with one of the
	 *                          types in this list, the already-created object is
	 *                          used instead of creating a new one.
	 * @return The generated value, or null if no value could be generated
	 */
	protected Value getValueForType(Type tp, Set constructionStack, Set parentClasses) {
		return getValueForType(tp, constructionStack, parentClasses, null);
	}

	/**
	 * Creates a value of the given type to be used as a substitution in method
	 * invocations or fields
	 * 
	 * @param tp                The type for which to get a value
	 * @param constructionStack The set of classes we're currently constructing.
	 *                          Attempts to create a parameter of one of these
	 *                          classes will trigger the constructor loop check and
	 *                          the respective parameter will be substituted by
	 *                          null.
	 * @param parentClasses     If the given type is compatible with one of the
	 *                          types in this list, the already-created object is
	 *                          used instead of creating a new one.
	 * @param generatedLocals   The set that receives all (temporary) locals created
	 *                          to provide a value of the requested type
	 * @return The generated value, or null if no value could be generated
	 */
	protected Value getValueForType(Type tp, Set constructionStack, Set parentClasses,
			Set generatedLocals) {
		// Depending on the parameter type, we try to find a suitable
		// concrete substitution
		if (isSimpleType(tp.toString()))
			return getSimpleDefaultValue(tp);
		else if (tp instanceof RefType) {
			SootClass classToType = ((RefType) tp).getSootClass();

			if (classToType != null) {
				// If we have a parent class compatible with this type, we use
				// it before we check any other option
				for (SootClass parent : parentClasses)
					if (isCompatible(parent, classToType)) {
						Value val = this.localVarsForClasses.get(parent);
						if (val != null)
							return val;
					}

				// If this is a system class, we may want to skip it
				if (ignoreSystemClassParams && SystemClassHandler.v().isClassInSystemPackage(classToType.getName()))
					return NullConstant.v();

				// Create a new instance to plug in here
				Value val = generateClassConstructor(classToType, constructionStack, parentClasses, generatedLocals);

				// If we cannot create a parameter, we try a null reference.
				// Better than not creating the whole invocation...
				if (val == null)
					return NullConstant.v();

				// Keep track of the locals we generate
				if (generatedLocals != null && val instanceof Local)
					generatedLocals.add((Local) val);

				return val;
			}
		} else if (tp instanceof ArrayType) {
			Value arrVal = buildArrayOfType((ArrayType) tp, constructionStack, parentClasses, generatedLocals);
			if (arrVal == null) {
				logger.warn("Array parameter substituted by null");
				return NullConstant.v();
			}
			return arrVal;
		} else {
			logger.warn("Unsupported parameter type: {}", tp.toString());
			return null;
		}
		throw new RuntimeException("Should never see me");
	}

	/**
	 * Constructs an array of the given type with a single element of this type in
	 * the given method
	 * 
	 * @param tp                The type of which to create the array
	 * @param constructionStack Set of classes currently being built to avoid
	 *                          constructor loops
	 * @param parentClasses     If a requested type is compatible with one of the
	 *                          types in this list, the already-created object is
	 *                          used instead of creating a new one.
	 * @param generatedLocals   A set that receives the (temporary) locals that were
	 *                          generated to create the requested array
	 * @return The local referencing the newly created array, or null if the array
	 *         generation failed
	 */
	private Value buildArrayOfType(ArrayType tp, Set constructionStack, Set parentClasses,
			Set generatedLocals) {
		// Generate a single element in the array
		Value singleElement = getValueForType(tp.getElementType(), constructionStack, parentClasses);

		// Generate a new single-element array
		Local local = generator.generateLocal(tp);
		NewArrayExpr newArrayExpr = Jimple.v().newNewArrayExpr(tp.getElementType(), IntConstant.v(1));
		AssignStmt assignArray = Jimple.v().newAssignStmt(local, newArrayExpr);
		body.getUnits().add(assignArray);

		// Assign the element to the first element of the array
		AssignStmt assign = Jimple.v().newAssignStmt(Jimple.v().newArrayRef(local, IntConstant.v(0)), singleElement);
		body.getUnits().add(assign);
		return local;
	}

	/**
	 * Generates code which creates a new instance of the given class.
	 * 
	 * @param createdClass The class of which to create an instance
	 * @return The local containing the new object instance if the operation
	 *         completed successfully, otherwise null.
	 */
	protected Local generateClassConstructor(SootClass createdClass) {
		return this.generateClassConstructor(createdClass, new HashSet(), Collections.emptySet(),
				null);
	}

	/**
	 * Generates code which creates a new instance of the given class.
	 * 
	 * @param createdClass  The class of which to create an instance
	 * @param parentClasses If a constructor call requires an object of a type which
	 *                      is compatible with one of the types in this list, the
	 *                      already-created object is used instead of creating a new
	 *                      one.
	 * @return The local containing the new object instance if the operation
	 *         completed successfully, otherwise null.
	 */
	protected Local generateClassConstructor(SootClass createdClass, Set parentClasses) {
		return this.generateClassConstructor(createdClass, new HashSet(), parentClasses, null);
	}

	/**
	 * Determines whether a class is accepted for generating a constructor.
	 * 
	 * @param clazz The class of which to create an instance
	 * @return Whether the class is accepted for generating a constructor
	 */
	protected boolean acceptClass(SootClass clazz) {
		// We cannot create instances of phantom classes as we do not have any
		// constructor information for them
		if (clazz.isPhantom() || clazz.isPhantomClass()) {
			logger.warn("Cannot generate constructor for phantom class {}", clazz.getName());
			return false;
		}

		return true;
	}

	/**
	 * Generates code which creates a new instance of the given class.
	 * 
	 * @param createdClass      The class of which to create an instance
	 * @param constructionStack The stack of classes currently under construction.
	 *                          This is used to detect constructor loops. If a
	 *                          constructor requires a parameter of a type that is
	 *                          already on the stack, this value is substituted by
	 *                          null.
	 * @param parentClasses     If a constructor call requires an object of a type
	 *                          which is compatible with one of the types in this
	 *                          list, the already-created object is used instead of
	 *                          creating a new one.
	 * @param tempLocals        The set that receives all generated temporary locals
	 *                          that were necessary for calling the constructor of
	 *                          the requested class
	 * @return The local containing the new object instance if the operation
	 *         completed successfully, otherwise null.
	 */
	protected Local generateClassConstructor(SootClass createdClass, Set constructionStack,
			Set parentClasses, Set tempLocals) {
		if (createdClass == null || this.failedClasses.contains(createdClass))
			return null;

		// If we already have a class local of that type, we re-use it
		Local existingLocal = localVarsForClasses.get(createdClass);
		if (existingLocal != null)
			return existingLocal;

		if (!acceptClass(createdClass)) {
			failedClasses.add(createdClass);
			return null;
		}

		// if sootClass is simpleClass:
		if (isSimpleType(createdClass.toString())) {
			Local varLocal = generator.generateLocal(getSimpleTypeFromType(createdClass.getType()));

			AssignStmt aStmt = Jimple.v().newAssignStmt(varLocal, getSimpleDefaultValue(createdClass.getType()));
			body.getUnits().add(aStmt);
			return varLocal;
		}

		boolean isInnerClass = createdClass.getName().contains("$");
		SootClass outerClass = isInnerClass
				? Scene.v().getSootClassUnsafe(
						createdClass.getName().substring(0, createdClass.getName().lastIndexOf("$")))
				: null;

		// Make sure that we don't run into loops
		if (!constructionStack.add(createdClass)) {
			if (warnOnConstructorLoop) {
				logger.warn("Ran into a constructor generation loop for class " + createdClass
						+ ", substituting with null...");
			}
			Local tempLocal = generator.generateLocal(RefType.v(createdClass));
			AssignStmt assignStmt = Jimple.v().newAssignStmt(tempLocal, NullConstant.v());
			body.getUnits().add(assignStmt);
			return tempLocal;
		}
		if (createdClass.isInterface() || createdClass.isAbstract()) {
			return generateSubstitutedClassConstructor(createdClass, constructionStack, parentClasses);
		} else {
			// Find a constructor we can invoke. We do this first as we don't
			// want
			// to change anything in our method body if we cannot create a class
			// instance anyway.
			List constructors = new ArrayList<>();
			for (SootMethod currentMethod : createdClass.getMethods()) {
				if (currentMethod.isPrivate() || currentMethod.isProtected() || !currentMethod.isConstructor())
					continue;
				constructors.add(currentMethod);
			}

			// The fewer parameters a constructor has, the better for us
			Collections.sort(constructors, new Comparator() {

				@Override
				public int compare(SootMethod o1, SootMethod o2) {
					if (o1.getParameterCount() == o2.getParameterCount()) {
						int o1Prims = 0, o2Prims = 0;
						for (int i = 0; i < o1.getParameterCount(); i++)
							if (o1.getParameterType(i) instanceof PrimType)
								o1Prims++;
						for (int i = 0; i < o2.getParameterCount(); i++)
							if (o2.getParameterType(i) instanceof PrimType)
								o2Prims++;
						return o1Prims - o2Prims;
					}
					return o1.getParameterCount() - o2.getParameterCount();
				}

			});

			if (!constructors.isEmpty()) {
				SootMethod currentMethod = constructors.remove(0);
				List params = new LinkedList();
				for (Type type : currentMethod.getParameterTypes()) {
					// We need to reset the construction stack. Just because we
					// already created a class instance for parameter 1, there is no reason for
					// not being able to create the same class instance again for parameter 2.
					Set newStack = new HashSet<>(constructionStack);

					// We need to check whether we have a reference to the
					// outer class. In this case, we do not generate a new
					// instance, but use the one we already have.
					SootClass typeClass = type instanceof RefType ? ((RefType) type).getSootClass() : null;
					if (typeClass != null && isInnerClass && typeClass == outerClass
							&& this.localVarsForClasses.containsKey(outerClass))
						params.add(this.localVarsForClasses.get(outerClass));
					else if (shallowMode) {
						if (isSimpleType(type.toString()))
							params.add(getSimpleDefaultValue(type));
						else
							params.add(NullConstant.v());
					} else {
						Value val = getValueForType(type, newStack, parentClasses, tempLocals);
						params.add(val);
					}
				}

				// Build the "new" expression
				NewExpr newExpr = Jimple.v().newNewExpr(RefType.v(createdClass));
				Local tempLocal = generator.generateLocal(RefType.v(createdClass));
				AssignStmt assignStmt = Jimple.v().newAssignStmt(tempLocal, newExpr);
				body.getUnits().add(assignStmt);

				// Create the constructor invocation
				InvokeExpr vInvokeExpr;
				if (params.isEmpty() || params.contains(null))
					vInvokeExpr = Jimple.v().newSpecialInvokeExpr(tempLocal, currentMethod.makeRef());
				else
					vInvokeExpr = Jimple.v().newSpecialInvokeExpr(tempLocal, currentMethod.makeRef(), params);

				// We don't need return values
				body.getUnits().add(Jimple.v().newInvokeStmt(vInvokeExpr));
				if (tempLocals != null)
					tempLocals.add(tempLocal);
				return tempLocal;
			}

			this.failedClasses.add(createdClass);
			return null;
		}
	}

	/**
	 * Generates a call to a constructor for a an interface or an abstract class
	 * that is substituted with an actual implementation
	 * 
	 * @param createdClass      The class for which to create a constructor call
	 * @param constructionStack The stack for making sure that we do not run into
	 *                          loops
	 * @param parentClasses     If a constructor call requires an object of a type
	 *                          which is compatible with one of the types in this
	 *                          list, the already-created object is used instead of
	 *                          creating a new one.
	 * @return The local containing the new object instance if the operation
	 *         completed successfully, otherwise null.
	 */
	private Local generateSubstitutedClassConstructor(SootClass createdClass, Set constructionStack,
			Set parentClasses) {
		// This feature must be enabled explicitly
		if (!substituteCallParams) {
			logger.warn("Cannot create valid constructor for {}, because it is {} and cannot substitute with subclass",
					createdClass,
					(createdClass.isInterface() ? "an interface" : (createdClass.isAbstract() ? "abstract" : "")));
			this.failedClasses.add(createdClass);
			return null;
		}

		// Find a matching implementor of the interface
		List classes;
		if (createdClass.isInterface())
			classes = Scene.v().getActiveHierarchy().getImplementersOf(createdClass);
		else
			classes = Scene.v().getActiveHierarchy().getSubclassesOf(createdClass);

		// Generate an instance of the substitution class. If we fail,
		// try the next substitution. If we don't find any possible
		// substitution, we're in trouble
		for (SootClass sClass : classes)
			if (substituteClasses.contains(sClass.toString())) {
				Local cons = generateClassConstructor(sClass, constructionStack, parentClasses, null);
				if (cons == null)
					continue;
				return cons;
			}
		logger.warn("Cannot create valid constructor for {}, because it is {} and cannot substitute with subclass",
				createdClass,
				(createdClass.isInterface() ? "an interface" : (createdClass.isAbstract() ? "abstract" : "")));
		this.failedClasses.add(createdClass);
		return null;
	}

	protected Type getSimpleTypeFromType(Type type) {
		if (type.toString().equals("java.lang.String")) {
			assert type instanceof RefType;
			return RefType.v(((RefType) type).getSootClass());
		}
		if (type.toString().equals("void"))
			return soot.VoidType.v();
		if (type.toString().equals("char"))
			return soot.CharType.v();
		if (type.toString().equals("byte"))
			return soot.ByteType.v();
		if (type.toString().equals("short"))
			return soot.ShortType.v();
		if (type.toString().equals("int"))
			return soot.IntType.v();
		if (type.toString().equals("float"))
			return soot.FloatType.v();
		if (type.toString().equals("long"))
			return soot.LongType.v();
		if (type.toString().equals("double"))
			return soot.DoubleType.v();
		if (type.toString().equals("boolean"))
			return soot.BooleanType.v();
		throw new RuntimeException("Unknown simple type: " + type);
	}

	protected static boolean isSimpleType(String t) {
		if (t.equals("java.lang.String") || t.equals("void") || t.equals("char") || t.equals("byte")
				|| t.equals("short") || t.equals("int") || t.equals("float") || t.equals("long") || t.equals("double")
				|| t.equals("boolean")) {
			return true;
		} else {
			return false;
		}
	}

	protected Value getSimpleDefaultValue(Type t) {
		if (t == RefType.v("java.lang.String"))
			return StringConstant.v("");
		if (t instanceof CharType)
			return IntConstant.v(0);
		if (t instanceof ByteType)
			return IntConstant.v(0);
		if (t instanceof ShortType)
			return IntConstant.v(0);
		if (t instanceof IntType)
			return IntConstant.v(0);
		if (t instanceof FloatType)
			return FloatConstant.v(0);
		if (t instanceof LongType)
			return LongConstant.v(0);
		if (t instanceof DoubleType)
			return DoubleConstant.v(0);
		if (t instanceof BooleanType)
			return IntConstant.v(0);

		// also for arrays etc.
		return NullConstant.v();
	}

	/**
	 * Finds a method with the given signature in the given class or one of its
	 * super classes
	 * 
	 * @param currentClass The current class in which to start the search
	 * @param subsignature The subsignature of the method to find
	 * @return The method with the given signature if it has been found, otherwise
	 *         null
	 */
	protected SootMethod findMethod(SootClass currentClass, String subsignature) {
		SootMethod m = currentClass.getMethodUnsafe(subsignature);
		if (m != null) {
			return m;
		}
		if (currentClass.hasSuperclass()) {
			return findMethod(currentClass.getSuperclass(), subsignature);
		}
		return null;
	}

	/**
	 * Checks whether an object of type "actual" can be inserted where an object of
	 * type "expected" is required.
	 * 
	 * @param actual   The actual type (the substitution candidate)
	 * @param expected The requested type
	 * @return True if the two types are compatible and "actual" can be used as a
	 *         substitute for "expected", otherwise false
	 */
	protected boolean isCompatible(SootClass actual, SootClass expected) {
		return Scene.v().getOrMakeFastHierarchy().canStoreType(actual.getType(), expected.getType());
	}

	/**
	 * Eliminates all loops of length 0 (if a goto )
	 */
	protected void eliminateSelfLoops() {
		// Get rid of self-loops
		for (Iterator unitIt = body.getUnits().iterator(); unitIt.hasNext();) {
			Unit u = unitIt.next();
			if (u instanceof IfStmt) {
				IfStmt ifStmt = (IfStmt) u;
				if (ifStmt.getTarget() == ifStmt)
					unitIt.remove();
			}
		}
	}

	/**
	 * Sets the name that shall be used for the new class containing the dummy main
	 * method
	 * 
	 * @param dummyMethodName The name for the new class containing the dummy main
	 *                        method
	 */
	public void setDummyClassName(String dummyClassName) {
		this.dummyClassName = dummyClassName;
	}

	/**
	 * Sets the name that shall be used for the new dummy main method
	 * 
	 * @param dummyMethodName The name for the new dummy main method
	 */
	public void setDummyMethodName(String dummyMethodName) {
		this.dummyMethodName = dummyMethodName;
	}

	/**
	 * Sets whether a call to a method A.foo() may receive an instance of A as a
	 * parameter. If this is not allowed, other type-compatible class instances are
	 * taken. If they don't exist, null is used.
	 * 
	 * @param value True if method calls may receive instances of their containing
	 *              class as parameter values, otherwise false
	 */
	public void setAllowSelfReferences(boolean value) {
		this.allowSelfReferences = value;
	}

	/**
	 * Sets whether shallow mode shall be used. Normally, if a call to a method a()
	 * is to be created, this class first creates instances of all required
	 * parameter objects. If these, in turn, require other objects, they are
	 * instantiated as well. In shallow mode, this does not happen. Instead, all
	 * values on the first level are replaced with default values (e.g., null for
	 * objects).
	 * 
	 * @param shallowMode True if shallow mode shall be used, otherwise false
	 */
	public void setShallowMode(boolean shallowMode) {
		this.shallowMode = shallowMode;
	}

	/**
	 * Gets whether shallow mode shall be used. Normally, if a call to a method a()
	 * is to be created, this class first creates instances of all required
	 * parameter objects. If these, in turn, require other objects, they are
	 * instantiated as well. In shallow mode, this does not happen. Instead, all
	 * values on the first level are replaced with default values (e.g., null for
	 * objects).
	 * 
	 * @return True if shallow mode shall be used, otherwise false
	 */
	public boolean getShallowMode() {
		return this.shallowMode;
	}

	/**
	 * Sets whether the entry point creator shall always pass "null" if a method
	 * expects an object of a system class.
	 * 
	 * @param ignoreSystemClassParams
	 */
	public void setIgnoreSystemClassParams(boolean ignoreSystemClassParams) {
		this.ignoreSystemClassParams = ignoreSystemClassParams;
	}

	/**
	 * Sets whether the dummy main method shall be overwritten if it already exists.
	 * If this flag is set to "false", a new, non-conflicting method and class name
	 * is chosen.
	 * 
	 * @param reuseDummyMainValue True if existing methods that conflict with the
	 *                            entry point to be created shall be overwritten,
	 *                            false to automatically chose a new,
	 *                            non-conflicting name.
	 */
	public void setOverwriteDummyMainMethod(boolean overwriteDummyMainValue) {
		this.overwriteDummyMainMethod = overwriteDummyMainValue;
	}

	/**
	 * Gets whether the dummy main method shall be overwritten if it already exists.
	 * If this flag is set to "false", a new, non-conflicting method and class name
	 * is chosen.
	 * 
	 * @return True if existing methods that conflict with the entry point to be
	 *         created shall be overwritten, false to automatically chose a new,
	 *         non-conflicting name.
	 */
	public boolean getOverwriteDummyMainMethod() {
		return this.overwriteDummyMainMethod;
	}

	/**
	 * Sets whether a warning shall be written to the log when a constructor call
	 * cannot be generated because the analysis ran into a loop when trying to
	 * generate parameter values.
	 * 
	 * @param warnOnConstructorLoop True if a warning shall be written to the log
	 *                              when a constructor generation loop is
	 *                              encountered, otherwise false
	 */
	public void setWarnOnConstructorLoop(boolean warnOnConstructorLoop) {
		this.warnOnConstructorLoop = warnOnConstructorLoop;
	}

	/**
	 * Gets whether a warning shall be written to the log when a constructor call
	 * cannot be generated because the analysis ran into a loop when trying to
	 * generate parameter values.
	 * 
	 * @return True if a warning shall be written to the log when a constructor
	 *         generation loop is encountered, otherwise false
	 */
	public boolean getWarnOnConstructorLoop() {
		return this.warnOnConstructorLoop;
	}

	/**
	 * Resets the internal state to make sure that the entry point creator is
	 * re-usable. Note that this method will not reset the sets of failed classes
	 * and methods, because it doesn't make much sense to try them again and fail
	 * again on later re-runs.
	 */
	protected void reset() {
		localVarsForClasses.clear();
		conditionCounter = 0;
	}

	/**
	 * Creates an opaque predicate that jumps to the given target
	 * 
	 * @param target The target to which the opaque predicate shall jump
	 */
	protected void createIfStmt(Unit target) {
		if (target == null) {
			return;
		}
		final Jimple jimple = Jimple.v();
		EqExpr cond = jimple.newEqExpr(intCounter, IntConstant.v(conditionCounter++));
		IfStmt ifStmt = jimple.newIfStmt(cond, target);
		body.getUnits().add(ifStmt);
	}

	@Override
	public SootMethod getGeneratedMainMethod() {
		return mainMethod;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy